diff --git a/apps/todo/apps/web/src/lib/components/pages/SecondaryPage.svelte b/apps/todo/apps/web/src/lib/components/pages/SecondaryPage.svelte index ce517fc72..ec82a0296 100644 --- a/apps/todo/apps/web/src/lib/components/pages/SecondaryPage.svelte +++ b/apps/todo/apps/web/src/lib/components/pages/SecondaryPage.svelte @@ -1,8 +1,9 @@
@@ -115,18 +157,35 @@ {meta.title} {filteredTasks.length}
- +
+ {#if onMinimize} + + {/if} + +
- {#if filteredTasks.length === 0} -
-

Keine Aufgaben

-
- {:else} + {#if pageId === 'completed'} {#each filteredTasks as task (task.id)} +
+ handleToggle(task)} + onSave={(data) => handleUpdate(task.id, data)} + onDelete={() => handleDelete(task.id)} + /> + {#if task.completedAt} + {formatCompletedTime(task.completedAt)} + {/if} +
+ {/each} + {:else} + {#each openTasks as task (task.id)}
{/each} + + {#if recentlyCompleted.length > 0} +
+ {$t('secondaryPage.recentlyCompleted')} + {#each recentlyCompleted as task (task.id)} +
+ handleToggle(task)} + onSave={(data) => handleUpdate(task.id, data)} + onDelete={() => handleDelete(task.id)} + /> + {#if task.completedAt} + {formatCompletedTime(task.completedAt)} + {/if} +
+ {/each} +
+ {/if} + +
+ + + + { + if (e.key === 'Enter') handleInlineCreate(); + }} + /> +
{/if}
@@ -213,7 +306,13 @@ color: #6b7280; } - .close-btn { + .header-actions { + display: flex; + align-items: center; + gap: 0.125rem; + } + + .header-btn { display: flex; align-items: center; justify-content: center; @@ -226,11 +325,11 @@ cursor: pointer; transition: all 0.15s; } - .close-btn:hover { + .header-btn:hover { background: rgba(0, 0, 0, 0.06); color: #374151; } - :global(.dark) .close-btn:hover { + :global(.dark) .header-btn:hover { background: rgba(255, 255, 255, 0.1); color: #f3f4f6; } @@ -248,14 +347,73 @@ margin-bottom: 0; } - .empty-state { + .completed-section { + margin-top: 1rem; + padding-top: 0.75rem; + border-top: 1px solid rgba(0, 0, 0, 0.06); + } + :global(.dark) .completed-section { + border-top-color: rgba(255, 255, 255, 0.08); + } + + .completed-label { + font-size: 0.6875rem; + font-weight: 500; + color: #9ca3af; + text-transform: uppercase; + letter-spacing: 0.04em; + margin-bottom: 0.5rem; + display: block; + } + + .completed-task { + position: relative; + opacity: 0.6; + } + + .completed-time { + position: absolute; + right: 0.5rem; + top: 0.5rem; + font-size: 0.6875rem; + color: #9ca3af; + font-variant-numeric: tabular-nums; + white-space: nowrap; + } + + .inline-create { display: flex; align-items: center; - justify-content: center; - height: 120px; + gap: 0.5rem; + padding: 0.5rem 0.25rem; + margin-top: 0.25rem; } - .empty-state p { + + .inline-create-icon { + flex-shrink: 0; + color: #d1d5db; + display: flex; + align-items: center; + } + :global(.dark) .inline-create-icon { + color: #4b5563; + } + + .inline-create-input { + flex: 1; + border: none; + background: transparent; font-size: 0.8125rem; - color: #9ca3af; + color: #374151; + outline: none; + } + .inline-create-input::placeholder { + color: #c0bfba; + } + :global(.dark) .inline-create-input { + color: #f3f4f6; + } + :global(.dark) .inline-create-input::placeholder { + color: #4b5563; } diff --git a/apps/todo/apps/web/src/lib/i18n/locales/de.json b/apps/todo/apps/web/src/lib/i18n/locales/de.json index 9c8b8ead5..2a4f06591 100644 --- a/apps/todo/apps/web/src/lib/i18n/locales/de.json +++ b/apps/todo/apps/web/src/lib/i18n/locales/de.json @@ -224,5 +224,11 @@ "toolbar": { "taskOptions": "Aufgaben-Optionen", "switchView": "Ansicht wechseln" + }, + "secondaryPage": { + "recentlyCompleted": "Kürzlich erledigt", + "completedAtTime": "{time} Uhr", + "completedAtDateTime": "{date}, {time} Uhr", + "newTaskPlaceholder": "Neue Aufgabe…" } } diff --git a/apps/todo/apps/web/src/lib/i18n/locales/en.json b/apps/todo/apps/web/src/lib/i18n/locales/en.json index 1ec33c145..0f698a42d 100644 --- a/apps/todo/apps/web/src/lib/i18n/locales/en.json +++ b/apps/todo/apps/web/src/lib/i18n/locales/en.json @@ -224,5 +224,11 @@ "toolbar": { "taskOptions": "Task options", "switchView": "Switch view" + }, + "secondaryPage": { + "recentlyCompleted": "Recently completed", + "completedAtTime": "{time}", + "completedAtDateTime": "{date}, {time}", + "newTaskPlaceholder": "New task…" } } diff --git a/apps/todo/apps/web/src/lib/i18n/locales/es.json b/apps/todo/apps/web/src/lib/i18n/locales/es.json index 593cde54b..b90b8643d 100644 --- a/apps/todo/apps/web/src/lib/i18n/locales/es.json +++ b/apps/todo/apps/web/src/lib/i18n/locales/es.json @@ -224,5 +224,11 @@ "toolbar": { "taskOptions": "Opciones de tareas", "switchView": "Cambiar vista" + }, + "secondaryPage": { + "recentlyCompleted": "Completadas recientemente", + "completedAtTime": "{time}", + "completedAtDateTime": "{date}, {time}", + "newTaskPlaceholder": "Nueva tarea…" } } diff --git a/apps/todo/apps/web/src/lib/i18n/locales/fr.json b/apps/todo/apps/web/src/lib/i18n/locales/fr.json index 8922dd96a..0ee7d17e9 100644 --- a/apps/todo/apps/web/src/lib/i18n/locales/fr.json +++ b/apps/todo/apps/web/src/lib/i18n/locales/fr.json @@ -224,5 +224,11 @@ "toolbar": { "taskOptions": "Options des tâches", "switchView": "Changer de vue" + }, + "secondaryPage": { + "recentlyCompleted": "Récemment terminées", + "completedAtTime": "{time} h", + "completedAtDateTime": "{date}, {time} h", + "newTaskPlaceholder": "Nouvelle tâche…" } } diff --git a/apps/todo/apps/web/src/lib/i18n/locales/it.json b/apps/todo/apps/web/src/lib/i18n/locales/it.json index 67b5efa8d..a8d9954ef 100644 --- a/apps/todo/apps/web/src/lib/i18n/locales/it.json +++ b/apps/todo/apps/web/src/lib/i18n/locales/it.json @@ -224,5 +224,11 @@ "toolbar": { "taskOptions": "Opzioni attività", "switchView": "Cambia vista" + }, + "secondaryPage": { + "recentlyCompleted": "Completate di recente", + "completedAtTime": "ore {time}", + "completedAtDateTime": "{date}, ore {time}", + "newTaskPlaceholder": "Nuova attività…" } } diff --git a/apps/todo/apps/web/src/routes/(app)/+page.svelte b/apps/todo/apps/web/src/routes/(app)/+page.svelte index 89f4c11c2..671a20bc5 100644 --- a/apps/todo/apps/web/src/routes/(app)/+page.svelte +++ b/apps/todo/apps/web/src/routes/(app)/+page.svelte @@ -4,7 +4,7 @@ import { BoardViewRenderer } from '$lib/components/board-views'; import { todoSettings, type PageWidth } from '$lib/stores/settings.svelte'; import { boardViewsStore } from '$lib/stores/board-views.svelte'; - import { Plus } from '@manacore/shared-icons'; + import { Plus, Minus, X } from '@manacore/shared-icons'; import PagePicker from '$lib/components/pages/PagePicker.svelte'; import SecondaryPage from '$lib/components/pages/SecondaryPage.svelte'; @@ -19,17 +19,31 @@ // ── Secondary Pages ───────────────────────────────────── let showPagePicker = $state(false); - let openPages = $state([]); + let openPages = $state<{ id: string; minimized: boolean }[]>([]); + + let expandedPages = $derived(openPages.filter((p) => !p.minimized)); + let minimizedPages = $derived(openPages.filter((p) => p.minimized)); function handleAddPage(pageId: string) { - if (!openPages.includes(pageId)) { - openPages = [...openPages, pageId]; + if (!openPages.some((p) => p.id === pageId)) { + openPages = [...openPages, { id: pageId, minimized: false }]; + } else { + // Restore if minimized + openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: false } : p)); } showPagePicker = false; } function handleRemovePage(pageId: string) { - openPages = openPages.filter((p) => p !== pageId); + openPages = openPages.filter((p) => p.id !== pageId); + } + + function handleMinimizePage(pageId: string) { + openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: true } : p)); + } + + function handleRestorePage(pageId: string) { + openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: false } : p)); } function togglePagePicker() { @@ -112,6 +126,25 @@ activeView?.groupBy === 'status' || activeView?.groupBy === 'custom' ); + const PAGE_META: Record = { + todo: { title: 'To Do', color: '#6B7280' }, + completed: { title: 'Erledigt', color: '#22C55E' }, + today: { title: 'Heute', color: '#F59E0B' }, + overdue: { title: 'Überfällig', color: '#EF4444' }, + all: { title: 'Alle Aufgaben', color: '#3B82F6' }, + 'high-priority': { title: 'Hohe Priorität', color: '#EF4444' }, + 'this-week': { title: 'Diese Woche', color: '#8B5CF6' }, + 'no-date': { title: 'Ohne Datum', color: '#6B7280' }, + }; + + let pagePickerEl = $state(null); + + $effect(() => { + if (showPagePicker && pagePickerEl) { + pagePickerEl.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); + } + }); + function handleKeydown(e: KeyboardEvent) { if (e.key === 'Escape' && editMode) { editModeCtx.set(false); @@ -169,6 +202,37 @@ {/if} + + {#if minimizedPages.length > 0} +
+ {#each minimizedPages as page (page.id)} + {@const meta = PAGE_META[page.id] ?? { title: page.id, color: '#6B7280' }} +
handleRestorePage(page.id)} + > + + {meta.title} + +
+ {/each} + +
+ {/if} + {#if activeView} {#snippet trailing()} - {#each openPages as pageId (pageId)} - handleRemovePage(pageId)} /> + {#each expandedPages as page (page.id)} + handleRemovePage(page.id)} + onMinimize={() => handleMinimizePage(page.id)} + /> {/each} {#if !editMode} {#if showPagePicker} - (showPagePicker = false)} - activePageIds={openPages} - /> +
+ (showPagePicker = false)} + activePageIds={openPages.map((p) => p.id)} + /> +
{:else}