Quick add task from the header (owner): modal, g t shortcut, auto_handle + Daisy webhook
Some checks failed
Deploy production / deploy (push) Has been cancelled
Some checks failed
Deploy production / deploy (push) Has been cancelled
- + button next to Dashboard (desktop and mobile sheet), lazy-loaded native <dialog>
- POST /trackers/tasks/quick inserts source 'quick-add', notes, due date, priority
- tasks.tasks.auto_handle (default false) with a Daisy badge in the tracker
- DAISY_TASK_WEBHOOK_URL/KEY/HEADER: fire-and-forget POST {task_id, title}, 3 s timeout
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import { onMount, type Component, type Snippet } from 'svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import logo from '#lib/assets/brand/favicon-32x32.png?url';
|
||||
import { hidesFooter, isTyping } from '#lib/chrome.js';
|
||||
@@ -15,6 +15,7 @@
|
||||
import ExternalLink from 'lucide-svelte/icons/external-link';
|
||||
import Menu from 'lucide-svelte/icons/menu';
|
||||
import LayoutDashboard from 'lucide-svelte/icons/layout-dashboard';
|
||||
import Plus from 'lucide-svelte/icons/plus';
|
||||
import SiOsu from '~icons/simple-icons/osu';
|
||||
import SiSteam from '~icons/simple-icons/steam';
|
||||
import SiTwitch from '~icons/simple-icons/twitch';
|
||||
@@ -117,7 +118,8 @@
|
||||
return page.url.pathname === href || (href !== '/' && page.url.pathname.startsWith(href + '/'));
|
||||
}
|
||||
|
||||
// Owner shortcut: press g then d (outside text fields) to open the /start dashboard.
|
||||
// Owner shortcuts (outside text fields): g then d opens the /start dashboard, g then t
|
||||
// opens quick add task.
|
||||
let gPressedAt = 0;
|
||||
function handleShortcut(event: KeyboardEvent) {
|
||||
if (!data.isOwner || event.defaultPrevented || event.repeat) return;
|
||||
@@ -125,6 +127,10 @@
|
||||
const key = event.key.toLowerCase();
|
||||
if (key === 'g') {
|
||||
gPressedAt = event.timeStamp;
|
||||
} else if (key === 't' && gPressedAt && event.timeStamp - gPressedAt < 1500) {
|
||||
gPressedAt = 0;
|
||||
event.preventDefault();
|
||||
openQuickAdd();
|
||||
} else if (key === 'd' && gPressedAt && event.timeStamp - gPressedAt < 1500) {
|
||||
gPressedAt = 0;
|
||||
event.preventDefault();
|
||||
@@ -135,6 +141,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Quick add task: the modal's code is fetched on first open only.
|
||||
let QuickAdd = $state<Component<{ onclose: () => void; ondone: (m: string) => void }> | null>(
|
||||
null
|
||||
);
|
||||
let quickAddOpen = $state(false);
|
||||
let toast = $state('');
|
||||
let toastTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
async function openQuickAdd() {
|
||||
if (!data.isOwner) return;
|
||||
menuOpen = false;
|
||||
QuickAdd ??= (await import('#lib/components/QuickAddTask.svelte')).default;
|
||||
quickAddOpen = true;
|
||||
}
|
||||
function showToast(message: string) {
|
||||
toast = message;
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => (toast = ''), 4000);
|
||||
}
|
||||
|
||||
function handleMenuPointerEnter(event: PointerEvent, href: string) {
|
||||
if (event.pointerType === 'touch') return;
|
||||
openMenu = href;
|
||||
@@ -285,6 +310,14 @@
|
||||
aria-keyshortcuts="g d"
|
||||
><LayoutDashboard size={16} aria-hidden="true" />dashboard</Button
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onclick={openQuickAdd}
|
||||
aria-label="Quick add task"
|
||||
title="Quick add task (g then t)"
|
||||
aria-keyshortcuts="g t"><Plus size={18} aria-hidden="true" /></Button
|
||||
>
|
||||
{/if}
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -326,6 +359,9 @@
|
||||
onclick={() => (menuOpen = false)}
|
||||
><LayoutDashboard size={16} aria-hidden="true" />dashboard</Button
|
||||
>
|
||||
<Button variant="outline" class="mb-2 justify-start gap-2" onclick={openQuickAdd}
|
||||
><Plus size={16} aria-hidden="true" />quick add task</Button
|
||||
>
|
||||
{/if}
|
||||
{#each navigation as item (item.href)}
|
||||
<Button
|
||||
@@ -436,6 +472,18 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if QuickAdd && quickAddOpen}
|
||||
<QuickAdd onclose={() => (quickAddOpen = false)} ondone={showToast} />
|
||||
{/if}
|
||||
{#if toast}
|
||||
<div
|
||||
role="status"
|
||||
class="fixed bottom-4 left-1/2 z-[60] -translate-x-1/2 rounded-md border border-border bg-popover px-4 py-2 text-sm text-popover-foreground shadow-lg"
|
||||
>
|
||||
{toast}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
@media (min-width: 1100px) and (min-height: 700px) {
|
||||
.cod-footer {
|
||||
|
||||
Reference in New Issue
Block a user