From 3f2e6a3ee534c7eae3efeca7532e19eb01b931a9 Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 12:03:58 +0200 Subject: [PATCH] =?UTF-8?q?feat(todo):=20unify=20view=20modes=20into=20sin?= =?UTF-8?q?gle=20route=20with=20Fokus/=C3=9Cbersicht/Matrix=20layouts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidate the two separate view systems (homepage paper pages + /kanban board) into one unified system on `/`. All three layout modes (Fokus, Übersicht, Matrix) share the same LocalBoardView data model and BoardViewRenderer, with layout switching via PillNav tabs instead of route navigation. - Add FokusLayout component (scroll-snap paper sheets with DnD) - Add activeLayoutMode setting (fokus/uebersicht/matrix) - Add layoutOverride prop to BoardViewRenderer - Rewrite homepage to use BoardViewRenderer + ViewSelector - Unify DnD type to 'task-dnd' across all layouts - Convert PillNav from route-based to state-based view switching - Delete /kanban route (redirect to / with uebersicht mode) - Update PWA shortcuts, settings, onboarding, help content Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/components/TodoToolbarContent.svelte | 13 +- .../board-views/BoardViewRenderer.svelte | 16 +- .../components/board-views/FokusLayout.svelte | 264 +++ .../components/board-views/ViewColumn.svelte | 10 +- .../src/lib/components/board-views/index.ts | 1 + .../apps/web/src/lib/content/help/index.ts | 4 +- .../todo/apps/web/src/lib/data/local-store.ts | 2 +- .../src/lib/stores/app-onboarding.svelte.ts | 8 +- .../web/src/lib/stores/settings.svelte.ts | 16 +- .../apps/web/src/routes/(app)/+layout.svelte | 41 +- .../apps/web/src/routes/(app)/+page.svelte | 1782 ++++------------- .../web/src/routes/(app)/kanban/+page.svelte | 479 ----- .../src/routes/(app)/settings/+page.svelte | 2 - apps/todo/apps/web/vite.config.ts | 6 - 14 files changed, 775 insertions(+), 1869 deletions(-) create mode 100644 apps/todo/apps/web/src/lib/components/board-views/FokusLayout.svelte delete mode 100644 apps/todo/apps/web/src/routes/(app)/kanban/+page.svelte diff --git a/apps/todo/apps/web/src/lib/components/TodoToolbarContent.svelte b/apps/todo/apps/web/src/lib/components/TodoToolbarContent.svelte index 4add77e43..cee058841 100644 --- a/apps/todo/apps/web/src/lib/components/TodoToolbarContent.svelte +++ b/apps/todo/apps/web/src/lib/components/TodoToolbarContent.svelte @@ -1,5 +1,4 @@ -{#if view.layout === 'grid'} +{#if activeLayout === 'fokus'} + +{:else if activeLayout === 'grid'} + import { dndzone, SHADOW_PLACEHOLDER_ITEM_ID, type DndEvent } from 'svelte-dnd-action'; + import type { Task } from '@todo/shared'; + import type { GroupedColumn } from '$lib/data/view-grouping'; + import KanbanTaskCard from '../kanban/KanbanTaskCard.svelte'; + import QuickAddTaskInline from '../kanban/QuickAddTaskInline.svelte'; + import ViewColumnHeader from './ViewColumnHeader.svelte'; + import { tasksStore } from '$lib/stores/tasks.svelte'; + import { todoSettings } from '$lib/stores/settings.svelte'; + + interface Props { + columns: GroupedColumn[]; + onTaskDrop: (taskId: string, columnId: string) => void; + onTaskToggle: (task: Task) => void; + onTaskDelete: (taskId: string) => void; + onTaskUpdate: (taskId: string, data: Partial) => void; + } + + let { columns, onTaskDrop, onTaskToggle, onTaskDelete, onTaskUpdate }: Props = $props(); + + const PAGE_WIDTH_MAP: Record = { + narrow: 'min(640px, 85vw)', + medium: 'min(840px, 85vw)', + wide: 'min(1024px, 92vw)', + full: '92vw', + }; + + let sheetWidth = $derived(PAGE_WIDTH_MAP[todoSettings.pageWidth] || PAGE_WIDTH_MAP.medium); + + // Track active page for dots + let scrollContainer: HTMLDivElement | undefined = $state(); + let activePage = $state(0); + + function handleScroll() { + if (!scrollContainer) return; + const sheets = scrollContainer.querySelectorAll('.fokus-sheet'); + const containerRect = scrollContainer.getBoundingClientRect(); + const center = containerRect.left + containerRect.width / 2; + let closest = 0; + let closestDist = Infinity; + sheets.forEach((sheet, i) => { + const rect = sheet.getBoundingClientRect(); + const dist = Math.abs(rect.left + rect.width / 2 - center); + if (dist < closestDist) { + closestDist = dist; + closest = i; + } + }); + activePage = closest; + } + + // Per-column local task state for DnD + let localTasksByColumn = $state>({}); + + $effect(() => { + const updated: Record = {}; + for (const col of columns) { + updated[col.id] = [...col.tasks]; + } + localTasksByColumn = updated; + }); + + function handleDndConsider(columnId: string, e: CustomEvent>) { + localTasksByColumn = { ...localTasksByColumn, [columnId]: e.detail.items }; + } + + function handleDndFinalize( + columnId: string, + column: GroupedColumn, + e: CustomEvent> + ) { + const newItems = e.detail.items.filter((t) => t.id !== SHADOW_PLACEHOLDER_ITEM_ID); + const movedTaskId = e.detail.info.id; + const wasInThisColumn = column.tasks.some((t) => t.id === movedTaskId); + + if (!wasInThisColumn) { + onTaskDrop(movedTaskId, column.id); + } else { + tasksStore.reorderTasks(newItems.map((t) => t.id)); + } + + localTasksByColumn = { ...localTasksByColumn, [columnId]: newItems }; + } + + function handleAddTask(column: GroupedColumn, title: string) { + const createData: Record = { title }; + if (column.onDrop) { + if (column.onDrop.setPriority) createData.priority = column.onDrop.setPriority; + if (column.onDrop.setProjectId !== undefined) + createData.projectId = column.onDrop.setProjectId; + } + tasksStore.createTask( + createData as { + title: string; + projectId?: string; + priority?: 'low' | 'medium' | 'high' | 'urgent'; + } + ); + } + + +
+
+ {#each columns as column (column.id)} + {@const tasks = localTasksByColumn[column.id] || column.tasks} +
+ + +
handleDndConsider(column.id, e)} + onfinalize={(e) => handleDndFinalize(column.id, column, e)} + > + {#each tasks.filter((t) => t.id !== SHADOW_PLACEHOLDER_ITEM_ID) as task (task.id)} +
+ onTaskToggle(task)} + onSave={(data) => onTaskUpdate(task.id, data)} + onDelete={() => onTaskDelete(task.id)} + /> +
+ {/each} +
+ + +
+ {/each} +
+ + + {#if columns.length > 1} +
+ {#each columns as _, i} +
+ {/each} +
+ {/if} +
+ + diff --git a/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte b/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte index f25561539..54ef1a119 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte @@ -90,7 +90,13 @@ createData.projectId = column.onDrop.setProjectId; } } - await tasksStore.createTask(createData as { title: string; projectId?: string; priority?: 'low' | 'medium' | 'high' | 'urgent' }); + await tasksStore.createTask( + createData as { + title: string; + projectId?: string; + priority?: 'low' | 'medium' | 'high' | 'urgent'; + } + ); } @@ -106,7 +112,7 @@ flipDurationMs, dropTargetStyle: {}, dropTargetClasses: ['drop-target'], - type: 'board-view-tasks', + type: 'task-dnd', }} onconsider={handleDndConsider} onfinalize={handleDndFinalize} diff --git a/apps/todo/apps/web/src/lib/components/board-views/index.ts b/apps/todo/apps/web/src/lib/components/board-views/index.ts index 745e5c418..e547bd5d9 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/index.ts +++ b/apps/todo/apps/web/src/lib/components/board-views/index.ts @@ -2,6 +2,7 @@ export { default as ViewColumnHeader } from './ViewColumnHeader.svelte'; export { default as ViewColumn } from './ViewColumn.svelte'; export { default as KanbanLayout } from './KanbanLayout.svelte'; export { default as GridLayout } from './GridLayout.svelte'; +export { default as FokusLayout } from './FokusLayout.svelte'; export { default as BoardViewRenderer } from './BoardViewRenderer.svelte'; export { default as ViewSelector } from './ViewSelector.svelte'; export { default as ViewEditorModal } from './ViewEditorModal.svelte'; diff --git a/apps/todo/apps/web/src/lib/content/help/index.ts b/apps/todo/apps/web/src/lib/content/help/index.ts index 03fcda5d9..a3ab0d616 100644 --- a/apps/todo/apps/web/src/lib/content/help/index.ts +++ b/apps/todo/apps/web/src/lib/content/help/index.ts @@ -41,8 +41,8 @@ export function getTodoHelpContent(locale: string): HelpContent { id: 'faq-kanban', question: isDE ? 'Was ist die Kanban-Ansicht?' : 'What is the Kanban view?', answer: isDE - ? '

Die Kanban-Ansicht zeigt deine Aufgaben als Karten in Spalten an. Du kannst:

  • Aufgaben per Drag & Drop zwischen Spalten verschieben
  • Den Fortschritt visuell verfolgen
  • Spalten nach Priorität oder Status organisieren

Wechsle mit Ctrl+2 zur Kanban-Ansicht.

' - : '

The Kanban view shows your tasks as cards in columns. You can:

  • Drag and drop tasks between columns
  • Track progress visually
  • Organize columns by priority or status

Switch to Kanban view with Ctrl+2.

', + ? '

Die Board-Ansicht zeigt deine Aufgaben als Karten in Spalten an. Du kannst:

  • Aufgaben per Drag & Drop zwischen Spalten verschieben
  • Den Fortschritt visuell verfolgen
  • Spalten nach Priorität oder Status organisieren

Wechsle über die Tabs (Fokus / Übersicht / Matrix) zwischen den Ansichten.

' + : '

The board view shows your tasks as cards in columns. You can:

  • Drag and drop tasks between columns
  • Track progress visually
  • Organize columns by priority or status

Switch between views using the tabs (Fokus / Übersicht / Matrix).

', category: 'features', order: 3, language: isDE ? 'de' : 'en', diff --git a/apps/todo/apps/web/src/lib/data/local-store.ts b/apps/todo/apps/web/src/lib/data/local-store.ts index b45fc9e0e..b748486f3 100644 --- a/apps/todo/apps/web/src/lib/data/local-store.ts +++ b/apps/todo/apps/web/src/lib/data/local-store.ts @@ -95,7 +95,7 @@ export interface LocalBoardView extends BaseRecord { groupBy: 'status' | 'priority' | 'project' | 'dueDate' | 'tag' | 'custom'; columns: ViewColumn[]; filter?: ViewFilter; - layout: 'kanban' | 'grid'; + layout: 'kanban' | 'grid' | 'fokus'; order: number; } diff --git a/apps/todo/apps/web/src/lib/stores/app-onboarding.svelte.ts b/apps/todo/apps/web/src/lib/stores/app-onboarding.svelte.ts index 314fa9009..8faad6264 100644 --- a/apps/todo/apps/web/src/lib/stores/app-onboarding.svelte.ts +++ b/apps/todo/apps/web/src/lib/stores/app-onboarding.svelte.ts @@ -40,12 +40,6 @@ const todoOnboardingSteps: AppOnboardingStep[] = [ description: 'Alle unsortierten Aufgaben', emoji: '📥', }, - { - id: 'kanban', - label: 'Kanban Board', - description: 'Spalten-basierte Aufgabenverwaltung', - emoji: '📊', - }, ], defaultValue: 'today', }, @@ -98,7 +92,7 @@ export const todoOnboarding = createAppOnboardingStore({ onComplete: async (preferences) => { // Apply default view const view = preferences.defaultView as string; - if (view === 'today' || view === 'inbox' || view === 'kanban') { + if (view === 'today' || view === 'inbox') { todoSettings.set('defaultView', view); } diff --git a/apps/todo/apps/web/src/lib/stores/settings.svelte.ts b/apps/todo/apps/web/src/lib/stores/settings.svelte.ts index ae782cbdc..0694754b2 100644 --- a/apps/todo/apps/web/src/lib/stores/settings.svelte.ts +++ b/apps/todo/apps/web/src/lib/stores/settings.svelte.ts @@ -9,7 +9,8 @@ import type { TaskPriority } from '@todo/shared'; // Settings types export type TodoView = 'inbox' | 'today' | 'upcoming' | 'kanban' | 'completed'; export type KanbanCardSize = 'compact' | 'normal' | 'large'; -export type PageMode = 'date' | 'priority' | 'custom'; +export type LayoutMode = 'fokus' | 'uebersicht' | 'matrix'; +export type PageMode = 'date' | 'priority' | 'custom'; // deprecated — will be replaced by BoardView export type PageIcon = | 'warning' @@ -76,7 +77,10 @@ export interface TodoAppSettings extends Record { pillNavCollapsed: boolean; filterStripCollapsed: boolean; - // Page mode + // View layout + activeLayoutMode: LayoutMode; + + // Page mode (deprecated — migrating to BoardView) pageMode: PageMode; pageWidth: PageWidth; customPages: PageConfig[]; @@ -123,7 +127,10 @@ const DEFAULT_SETTINGS: TodoAppSettings = { pillNavCollapsed: true, // PillNav hidden by default, shown via FAB filterStripCollapsed: false, // FilterStrip shown by default when PillNav is visible - // Page mode + // View layout + activeLayoutMode: 'fokus' as LayoutMode, + + // Page mode (deprecated) pageMode: 'priority' as PageMode, pageWidth: 'medium' as PageWidth, customPages: [] as PageConfig[], @@ -218,6 +225,9 @@ export const todoSettings = { get filterStripCollapsed() { return baseStore.settings.filterStripCollapsed; }, + get activeLayoutMode() { + return baseStore.settings.activeLayoutMode; + }, get pageMode() { return baseStore.settings.pageMode; }, diff --git a/apps/todo/apps/web/src/routes/(app)/+layout.svelte b/apps/todo/apps/web/src/routes/(app)/+layout.svelte index 8cc9043ef..bbd5fc114 100644 --- a/apps/todo/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/todo/apps/web/src/routes/(app)/+layout.svelte @@ -229,31 +229,25 @@ isTagStripVisible = !isTagStripVisible; } - // View routes for the tab group (pages that navigate) - const viewRoutes: Record = { - liste: '/', - kanban: '/kanban', - }; - - // Determine active view tab from current path - let activeViewTab = $derived( - Object.entries(viewRoutes).find(([_, path]) => $page.url.pathname === path)?.[0] || 'liste' - ); - - // Tab group for view switching (Liste, Kanban) - grouped in one pill + // View mode switching (state-based, not route-based) let viewTabGroup = $derived({ type: 'tabs' as const, options: [ - { id: 'liste', icon: 'list', label: 'Liste', title: 'Listenansicht' }, - { id: 'kanban', icon: 'columns', label: 'Kanban', title: 'Kanban-Board' }, + { id: 'fokus', icon: 'list', label: 'Fokus', title: 'Fokus-Ansicht' }, + { id: 'uebersicht', icon: 'columns', label: 'Übersicht', title: 'Übersicht' }, + { id: 'matrix', icon: 'grid', label: 'Matrix', title: 'Eisenhower-Matrix' }, ], - value: activeViewTab, + value: todoSettings.activeLayoutMode, onChange: (id: string) => { - const route = viewRoutes[id]; - if (route) goto(route); + todoSettings.set('activeLayoutMode', id as 'fokus' | 'uebersicht' | 'matrix'); + // Navigate to homepage if not already there + if ($page.url.pathname !== '/') goto('/'); }, }); + // Keep navRoutes for keyboard shortcuts (Ctrl+1-3) + const viewRoutes: Record = { fokus: '/', uebersicht: '/', matrix: '/' }; + // Handle edit mode toggle function handleEditToggle() { editMode = !editMode; @@ -384,8 +378,15 @@ await userSettings.load(); } - // Redirect to start page if on root and a custom start page is set + // Redirect /kanban to / with Übersicht mode const currentPath = window.location.pathname; + if (currentPath === '/kanban') { + todoSettings.set('activeLayoutMode', 'uebersicht'); + goto('/', { replaceState: true }); + return; + } + + // Redirect to start page if on root and a custom start page is set if (currentPath === '/' && userSettings.startPage && userSettings.startPage !== '/') { goto(userSettings.startPage, { replaceState: true }); } @@ -508,7 +509,7 @@ {/if} - {#if $page.url.pathname === '/' || $page.url.pathname === '/kanban'} + {#if $page.url.pathname === '/' || $page.url.pathname === '/kanban' || $page.url.pathname === '/statistics'}
{@render children()} diff --git a/apps/todo/apps/web/src/routes/(app)/+page.svelte b/apps/todo/apps/web/src/routes/(app)/+page.svelte index d66a0c6fe..62d9d1356 100644 --- a/apps/todo/apps/web/src/routes/(app)/+page.svelte +++ b/apps/todo/apps/web/src/routes/(app)/+page.svelte @@ -1,1388 +1,486 @@ - Todo + {pageTitle} - Todo - { - if (e.key === 'Escape' && editMode) { - editModeCtx.set(false); - return; - } - const target = e.target as HTMLElement; - const isInQuickInput = target.closest('.quick-input-bar'); - if (isInQuickInput && (e.key === 'ArrowUp' || (e.key === 'Tab' && !e.shiftKey))) { - const firstTitle = document.querySelector('.task-title[contenteditable]'); - if (firstTitle) { - e.preventDefault(); - firstTitle.focus(); - } - } +
+ + {#if !isMobile} + + {/if} + + +
+

{pageTitle}

+ +
+ + + {#if showFilters} +
+ (filterPriorities = priorities)} + onProjectChange={(projectId: string | null) => (filterProjectId = projectId)} + onLabelsChange={(labelIds: string[]) => (filterLabelIds = labelIds)} + onSearchChange={(query: string) => (filterSearchQuery = query)} + onClearFilters={clearFilters} + showSearch={true} + showLabels={true} + /> + {#if hasActiveFilters} +
+ + {#if activeView?.filter} + + {/if} +
+ {/if} +
+ {/if} + + +
+ {#if activeView} + 0 ? filterLabelIds : undefined, + priorities: filterPriorities.length > 0 ? filterPriorities : undefined, + } + : activeView.filter, + }} + {layoutOverride} + /> + {:else if boardViews.value.length === 0} +
+

Views werden geladen...

+
+ {/if} +
+ + + {#if isMobile} +
+ +
+ {/if} +
+ + + { + showEditor = false; + editingView = null; }} /> -{#if allTasks.loading} -
-
-
-
-
-{:else if allTasks.error} -
-
-
-
- {allTasks.error} -
-
-
-
-{:else if allEmpty && !editMode} -
-
-
-
-
-
-

Bereit für einen produktiven Tag

-
-

Tippe unten um loszulegen...

-
-
-
-

Schnellstart-Tipps

-
- {#each syntaxExamples as example} - - {/each} -
-
-
-
-
-
-
-{:else} -
- {#if editMode} -
- Seitenbreite -
- {#each WIDTH_OPTIONS as opt} - - {/each} -
-
- {/if} -
- {#if todoSettings.pageMode === 'custom' || editMode} - - {#each customPageData as { config, tasks }, pageIdx (config.id)} -
- {#if editMode && pageIdx > 0} - - {/if} -
- {#if editMode} -
- - updatePageLabel(config.id, (e.target as HTMLInputElement).value)} - /> - - -
- - {#if filterOpenId === config.id} -
-
- Icon -
- {#each ICON_OPTIONS as opt} - - {/each} -
-
-
- Prioritäten -
- {#each PRIORITY_CHIPS as chip} - - {/each} -
-
-
- Zeitraum - -
-
- -
-
- {/if} - {:else} -
p === 'urgent' || p === 'high' - )} - > - {#if config.icon === 'star'} - {:else if config.icon === 'lightning'} - {:else if config.icon === 'clock'} - {:else if config.icon === 'fire'} - {:else if config.icon === 'leaf'} - {:else if config.icon === 'heart'} - {:else if config.icon === 'check' || config.filter.completed} - {:else if config.icon === 'warning'} - {:else if config.icon === 'calendar'} - {:else} - {/if} - {config.label} - {#if tasks.length > 0}{tasks.length}{/if} -
- {/if} - -
- -
-
- {#if editMode && pageIdx < customPageData.length - 1} - - {/if} -
- {/each} - - {#if editMode} -
e.key === 'Enter' && addPage()} - > - - Neue Seite -
- {/if} - {:else if todoSettings.pageMode === 'priority'} - - {#if urgentImportant.length > 0} -
-
- Wichtig & Dringend{urgentImportant.length} -
-
- -
-
- {/if} -
-
- Wichtig & Später{importantLater.length} -
-
- - {#if showOnboardingTip && !tipDismissed} -
- 💡 - Tipp: Nutze !hoch oder !dringend um Tasks auf die erste - Seite zu setzen - -
- {/if} -
-
- {#if completedTasks.length > 0} -
-
- Erledigt{completedTasks.length} -
-
- -
-
- {/if} - {:else} - - {#if overdueTasks.length > 0} -
-
- Überfällig{overdueTasks.length} -
-
- -
-
- {/if} -
-
- Heute{#if todayTasks.length > 0}{todayTasks.length}{/if} -
-
- -
-
- {#if tomorrowTasks.length > 0} -
-
- Morgen{tomorrowTasks.length} -
-
- -
-
- {/if} - {#if upcomingCount > 0} -
-
- Demnächst{upcomingCount} -
-
- {#each groupedUpcomingTasks as group} -
-

{group.label}

- -
- {/each} -
-
- {/if} - {#if completedTasks.length > 0} -
-
- Erledigt{completedTasks.length} -
-
- -
-
- {/if} - {/if} -
- - - {#if sheetCount > 1 && !editMode} -
- {#each Array(sheetCount) as _, i} -
- {/each} -
- {/if} -
-{/if} - diff --git a/apps/todo/apps/web/src/routes/(app)/kanban/+page.svelte b/apps/todo/apps/web/src/routes/(app)/kanban/+page.svelte deleted file mode 100644 index 8a29e31a7..000000000 --- a/apps/todo/apps/web/src/routes/(app)/kanban/+page.svelte +++ /dev/null @@ -1,479 +0,0 @@ - - - - {boardTitle} - Todo - - -
- - {#if !isMobile} - - {/if} - - -
-

{boardTitle}

- -
- - - {#if showFilters} -
- (filterPriorities = priorities)} - onProjectChange={(projectId: string | null) => (filterProjectId = projectId)} - onLabelsChange={(labelIds: string[]) => (filterLabelIds = labelIds)} - onSearchChange={(query: string) => (filterSearchQuery = query)} - onClearFilters={clearFilters} - showSearch={true} - showLabels={true} - /> - {#if hasActiveFilters} -
- - {#if activeView?.filter} - - {/if} -
- {/if} -
- {/if} - - -
- {#if activeView} - 0 ? filterLabelIds : undefined, - priorities: filterPriorities.length > 0 ? filterPriorities : undefined, - } : activeView.filter, - }} /> - {:else if boardViews.value.length === 0} -
-

Board Views werden geladen...

-
- {/if} -
- - - {#if isMobile} -
- -
- {/if} -
- - - { - showEditor = false; - editingView = null; - }} -/> - - diff --git a/apps/todo/apps/web/src/routes/(app)/settings/+page.svelte b/apps/todo/apps/web/src/routes/(app)/settings/+page.svelte index 6e1a71113..e4b63a03f 100644 --- a/apps/todo/apps/web/src/routes/(app)/settings/+page.svelte +++ b/apps/todo/apps/web/src/routes/(app)/settings/+page.svelte @@ -37,7 +37,6 @@ { value: 'inbox', label: 'Inbox' }, { value: 'today', label: 'Heute' }, { value: 'upcoming', label: 'Anstehend' }, - { value: 'kanban', label: 'Kanban' }, { value: 'completed', label: 'Erledigt' }, ]; @@ -153,7 +152,6 @@ appId="todo" navItems={[ { href: '/', label: 'Aufgaben', icon: 'list' }, - { href: '/kanban', label: 'Kanban', icon: 'columns' }, { href: '/statistics', label: 'Statistiken', icon: 'chart' }, { href: '/tags', label: 'Tags', icon: 'tag' }, { href: '/network', label: 'Netzwerk', icon: 'share-2' }, diff --git a/apps/todo/apps/web/vite.config.ts b/apps/todo/apps/web/vite.config.ts index ec04bfba3..ae90d3fe2 100644 --- a/apps/todo/apps/web/vite.config.ts +++ b/apps/todo/apps/web/vite.config.ts @@ -24,12 +24,6 @@ export default defineConfig({ description: 'Neue Aufgabe erstellen', url: '/?action=new', }, - { - name: 'Kanban Board', - short_name: 'Kanban', - description: 'Kanban-Ansicht öffnen', - url: '/kanban', - }, { name: 'Einstellungen', short_name: 'Settings',