From 4d1da380858be95f35f255ef51263fe2db56880f Mon Sep 17 00:00:00 2001 From: Neru_Han Date: Fri, 21 Aug 2026 22:32:02 +0900 Subject: [PATCH] fix(trash & sidebar): isolate trash view, sync list task counts in real-time, and fix restore/delete actions --- .cline-context.md | 3 +++ src/app/api/tasks/route.ts | 5 +++-- src/components/layout/AppShell.tsx | 32 ++++++++++++++++++++++++++---- src/components/layout/Sidebar.tsx | 25 ++++++++++++++++++++--- src/components/tasks/TaskList.tsx | 2 +- src/lib/mockData.ts | 22 ++++++++++++++++---- 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/.cline-context.md b/.cline-context.md index f6f1b74..38725a7 100644 --- a/.cline-context.md +++ b/.cline-context.md @@ -39,6 +39,9 @@ - Validated that `npm run lint` and `npm run build` execute with 0 errors and 0 warnings. - **Next Steps (Todo):** + - Fix sidebar list item counts calculation in demo & API modes. + - Completely isolate Trash view and Tag view from My Tasks list state in TaskList.tsx. + - Fix Restore and Permanent Delete in Trash view for both Demo and API modes. - Enhance keyboard accessibility & global shortcuts (shortcuts for switching list/kanban view, quick task navigation, Command Palette bindings). - Add optional automatic synchronization background polling / webhook integration if requested. - Plan next feature iterations or custom integrations as requested by user. diff --git a/src/app/api/tasks/route.ts b/src/app/api/tasks/route.ts index 7409d08..2952166 100644 --- a/src/app/api/tasks/route.ts +++ b/src/app/api/tasks/route.ts @@ -11,12 +11,13 @@ export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const listId = searchParams.get("listId"); const showCompleted = searchParams.get("showCompleted") === "true"; + const tagName = searchParams.get("tag"); const where: Record = { userId: session.user.id, - parentId: null, - ...(listId ? { listId } : {}), + ...(listId ? { listId, parentId: null } : tagName ? {} : { parentId: null }), ...(showCompleted ? {} : { completed: false }), + ...(tagName ? { tags: { some: { tag: { name: tagName } } } } : {}), }; const tasks = await prisma.task.findMany({ diff --git a/src/components/layout/AppShell.tsx b/src/components/layout/AppShell.tsx index e1ce0d3..fe2d730 100644 --- a/src/components/layout/AppShell.tsx +++ b/src/components/layout/AppShell.tsx @@ -116,7 +116,7 @@ export function AppShell({ user, isDemo = false }: AppShellProps) { return () => document.removeEventListener("checkflow:openCommandPalette", handler); }, []); - // Demo tasks filter & reconstruct hierarchical structure + // Task filter & reconstruct hierarchical structure for Demo and API modes useEffect(() => { if (isDemo) { const store = getDemoStore(); @@ -130,6 +130,23 @@ export function AppShell({ user, isDemo = false }: AppShellProps) { (t) => !t.isDeleted && t.listId === selectedListId && (showCompleted ? true : !t.completed) ); setTasks(listTasks); + } else { + setTasks([]); + } + } else { + // Authenticated API mode + if (isTrashActive) { + setTasks([]); + } else if (selectedTag) { + fetch(`/api/tasks?tag=${encodeURIComponent(selectedTag)}&showCompleted=${showCompleted}`) + .then((r) => (r.ok ? r.json() : [])) + .then((data) => setTasks(Array.isArray(data) ? data : [])) + .catch((err) => console.error("Failed to load tag tasks", err)); + } else if (selectedListId) { + fetch(`/api/tasks?listId=${selectedListId}&showCompleted=${showCompleted}`) + .then((r) => (r.ok ? r.json() : [])) + .then((data) => setTasks(Array.isArray(data) ? data : [])) + .catch((err) => console.error("Failed to load list tasks", err)); } } }, [isDemo, selectedListId, isTrashActive, selectedTag, showCompleted, refreshKey]); @@ -161,18 +178,25 @@ export function AppShell({ user, isDemo = false }: AppShellProps) { const handleTagSelect = useCallback((tagName: string | null) => { setIsTrashActive(false); + setSelectedListId(null); setSelectedTag(tagName); setSelectedTask(null); setSidebarOpen(false); }, []); const handleTrashSelect = useCallback(() => { - setIsTrashActive(true); - setSelectedTag(null); setSelectedListId(null); + setSelectedTag(null); + setIsTrashActive(true); setSelectedTask(null); setSidebarOpen(false); - }, []); + if (isDemo) { + const store = getDemoStore(); + setTasks(getAllTrashTasks(store.tasks) as Task[]); + } else { + setTasks([]); + } + }, [isDemo]); // Update List Name const handleUpdateListName = async (id: string, newName: string) => { diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index e4ae43e..89646ac 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -573,7 +573,16 @@ export function Sidebar({ {list.name} )} - {list._count?.tasks || ""} + + {isDemo + ? (() => { + if (typeof window === "undefined") return ""; + const store = getDemoStore(); + const cnt = store.tasks.filter((t) => t.listId === list.id && !t.isDeleted && !t.completed).length; + return cnt > 0 ? cnt : ""; + })() + : list._count?.tasks || ""} +