From 778b7426f2dd4e30afafc57eb26a24f2e997258c Mon Sep 17 00:00:00 2001 From: "Dennis (via Claude+Gemma)" Date: Sat, 23 May 2026 08:29:17 +0200 Subject: [PATCH] feat(command-bar-actions): CommandPalette mit Aktionen (z.B. 'New TimeEntry', 'Toggle D [tsc:fail] --- .phase23-state.json | 5 +- GENERATION_LOG.md | 17 +++ apps/web/src/components/CommandPalette.tsx | 120 +++++++++++++-------- 3 files changed, 96 insertions(+), 46 deletions(-) diff --git a/.phase23-state.json b/.phase23-state.json index 8fece8a..77e9e59 100644 --- a/.phase23-state.json +++ b/.phase23-state.json @@ -1,8 +1,9 @@ { "completed_features": [], - "current_feature": "custom-themes", + "current_feature": "command-bar-actions", "started_at": "2026-05-23T08:25:49.746920", "attempted_features": [ - "workspace-logo" + "workspace-logo", + "custom-themes" ] } \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index d2f6754..53c91fc 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -2725,3 +2725,20 @@ src/index.ts(27,25): error TS2769: No overload matches this call. Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTy +- `08:28:06` **INFO** Committed feature custom-themes +- `08:28:06` **INFO** Pushed: rc=0 + +## Phase-3 Feature: command-bar-actions (2026-05-23 08:28:06) + +- `08:28:06` **INFO** Description: CommandPalette mit Aktionen (z.B. 'New TimeEntry', 'Toggle Dark') +- `08:28:06` **INFO** Generating apps/web/src/components/CommandPalette.tsx (ERWEITERT β€” behalte bestehende Navigation-Items. FΓΌge actions section:…) +- `08:29:15` **INFO** wrote 7876 chars in 68.7s (attempt 1) +- `08:29:15` **INFO** Running tsc --noEmit on api… +- `08:29:17` **WARN** tsc errors: +src/index.ts(27,25): error TS2769: No overload matches this call. + Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. + Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. + Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error | undefined) => void): void'. + Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. + Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. + Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTy diff --git a/apps/web/src/components/CommandPalette.tsx b/apps/web/src/components/CommandPalette.tsx index 3467cc3..2eace84 100644 --- a/apps/web/src/components/CommandPalette.tsx +++ b/apps/web/src/components/CommandPalette.tsx @@ -1,31 +1,44 @@ import React, { useState, useEffect, useMemo } from 'react'; import { useNavigate } from '@tanstack/react-router'; -import { Search, Command, X } from 'lucide-react'; +import { Search, Command, X, Plus, Moon, Sun, LogOut, Palette } from 'lucide-react'; interface CommandItem { id: string; label: string; - path: string; + path?: string; + action?: () => void; icon: React.ReactNode; category: string; } -const COMMAND_ITEMS: CommandItem[] = [ - { id: 'dash', label: 'Dashboard', path: '/', icon: πŸ“Š, category: 'General' }, - { id: 'time', label: 'Time Entries', path: '/time-entries', icon: ⏱️, category: 'Tracking' }, - { id: 'cust', label: 'Customers', path: '/customers', icon: πŸ‘₯, category: 'CRM' }, - { id: 'proj', label: 'Projects', path: '/projects', icon: πŸ“, category: 'CRM' }, - { id: 'cal', label: 'Calendar', path: '/calendar', icon: πŸ“…, category: 'General' }, - { id: 'sett', label: 'Settings', path: '/settings', icon: βš™οΈ, category: 'System' }, - { id: 'prof', label: 'Profile', path: '/profile', icon: πŸ‘€, category: 'System' }, -]; - -export default function CommandPalette() { +export default function CommandPalette({ + onQuickAdd, + toggleTheme, + onLogout +}: { + onQuickAdd?: () => void; + toggleTheme?: () => void; + onLogout?: () => void; +}) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(''); const [selectedIndex, setSelectedIndex] = useState(0); const navigate = useNavigate(); + const COMMAND_ITEMS: CommandItem[] = useMemo(() => [ + { id: 'dash', label: 'Dashboard', path: '/', icon: πŸ“Š, category: 'General' }, + { id: 'time', label: 'Time Entries', path: '/time-entries', icon: ⏱️, category: 'Tracking' }, + { id: 'cust', label: 'Customers', path: '/customers', icon: πŸ‘₯, category: 'CRM' }, + { id: 'proj', label: 'Projects', path: '/projects', icon: πŸ“, category: 'CRM' }, + { id: 'cal', label: 'Calendar', path: '/calendar', icon: πŸ“…, category: 'General' }, + { id: 'sett', label: 'Settings', path: '/settings', icon: βš™οΈ, category: 'System' }, + { id: 'prof', label: 'Profile', path: '/profile', icon: πŸ‘€, category: 'System' }, + { id: 'add-time', label: 'Neuer Time-Entry', action: onQuickAdd, icon: , category: 'Actions' }, + { id: 'toggle-theme', label: 'Dark/Light umschalten', action: toggleTheme, icon: , category: 'Actions' }, + { id: 'change-theme', label: 'Theme wechseln', action: () => {}, icon: , category: 'Actions' }, + { id: 'logout', label: 'Logout', action: onLogout, icon: , category: 'Actions' }, + ], [onQuickAdd, toggleTheme, onLogout]); + useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { @@ -45,7 +58,7 @@ export default function CommandPalette() { return COMMAND_ITEMS.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()) ); - }, [query]); + }, [query, COMMAND_ITEMS]); useEffect(() => { setSelectedIndex(0); @@ -60,7 +73,11 @@ export default function CommandPalette() { setSelectedIndex((prev) => (prev - 1 + filteredItems.length) % filteredItems.length); } else if (e.key === 'Enter' && filteredItems[selectedIndex]) { const item = filteredItems[selectedIndex]; - navigate({ to: item.path }); + if (item.path) { + navigate({ to: item.path }); + } else if (item.action) { + item.action(); + } setIsOpen(false); setQuery(''); } @@ -77,6 +94,7 @@ export default function CommandPalette() { className="w-full max-w-xl bg-white dark:bg-zinc-900 rounded-xl shadow-2xl border border-zinc-200 dark:border-zinc-800 overflow-hidden" onClick={(e) => e.stopPropagation()} onKeyDown={handleKeyDown} + tabIndex={-1} >
@@ -94,43 +112,57 @@ export default function CommandPalette() {
{filteredItems.length > 0 ? ( -
- {filteredItems.map((item, index) => ( -
{ - navigate({ to: item.path }); - setIsOpen(false); - setQuery(''); - }} - > -
- {item.icon} - {item.label} +
+ {['Actions', 'General', 'Tracking', 'CRM', 'System'].map(cat => { + const catItems = filteredItems.filter(i => i.category === cat); + if (catItems.length === 0) return null; + return ( +
+
+ {cat} +
+
+ {catItems.map((item) => { + const isSelected = filteredItems[selectedIndex]?.id === item.id; + return ( +
{ + if (item.path) navigate({ to: item.path }); + if (item.action) item.action(); + setIsOpen(false); + setQuery(''); + }} + > +
+ {item.icon} + {item.label} +
+ {item.path && {item.path}} +
+ ); + })} +
- {item.category} -
- ))} + ); + })}
) : ( -
+
No results found for "{query}"
)}
-
- Navigation -