From 661a1bfab695d5a07c52e008cd41394aa1eae39f Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Mon, 21 Sep 2026 16:18:09 -0600 Subject: [PATCH] Collapse completed to-dos in group session dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move completed drafts out of the active list to reduce clutter. - Add a collapsible “Completed” section with per-row expansion controls. --- components/board/group-session-dialog.tsx | 85 ++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/components/board/group-session-dialog.tsx b/components/board/group-session-dialog.tsx index 8d3d2d1..d43f01c 100644 --- a/components/board/group-session-dialog.tsx +++ b/components/board/group-session-dialog.tsx @@ -1,7 +1,7 @@ "use client"; import { useCallback, useEffect, useRef, useState } from "react"; -import { Pencil, Plus, Sparkles, Trash2, X, XIcon } from "lucide-react"; +import { ChevronDown, ChevronRight, Pencil, Plus, Sparkles, Trash2, X, XIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; @@ -108,6 +108,8 @@ export function GroupSessionDialog({ const [aiOpen, setAiOpen] = useState(false); const [todoAiOpen, setTodoAiOpen] = useState(false); const [savingAll, setSavingAll] = useState(false); + const [completedOpen, setCompletedOpen] = useState(false); + const [expandedCompletedIds, setExpandedCompletedIds] = useState>(new Set()); const rowFlushersRef = useRef(new Map Promise>()); const dirtyRowIdsRef = useRef(new Set()); @@ -150,6 +152,9 @@ export function GroupSessionDialog({ setTodoDrafts((prev) => reconcileTodoDrafts(prev, group.todos, dirtyRowIdsRef.current)); }, [group.todos, open]); + const activeTodoDrafts = todoDrafts.filter((d) => !d.completed); + const completedTodoDrafts = todoDrafts.filter((d) => d.completed); + function updateTodoDraft(id: string, patch: Partial) { setTodoDrafts((prev) => prev.map((d) => (d.id === id ? { ...d, ...patch } : d))); } @@ -176,6 +181,15 @@ export function GroupSessionDialog({ removeTodoDraftLocalOnly(tempIdToRemove); } + function toggleCompletedExpanded(id: string) { + setExpandedCompletedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + } + function addEmptyTodoDraft() { setTodoDrafts((prev) => [ ...prev, @@ -231,7 +245,7 @@ export function GroupSessionDialog({
- {todoDrafts.map((draft) => ( + {activeTodoDrafts.map((draft) => ( )}
+ + {completedTodoDrafts.length > 0 && ( +
+ + {completedOpen && ( +
+ {completedTodoDrafts.map((draft) => + expandedCompletedIds.has(draft.id) ? ( + updateTodoDraft(draft.id, patch)} + onRemoveLocalOnly={() => removeTodoDraftLocalOnly(draft.id)} + onCreated={() => handleAddTodoCreated(draft.id)} + onDirtyChange={(dirty) => setRowDirty(draft.id, dirty)} + registerFlush={registerRowFlush} + addTodo={addTodo} + editTodo={editTodo} + toggleTodoDone={toggleTodoDone} + removeTodo={removeTodo} + onCollapse={() => toggleCompletedExpanded(draft.id)} + /> + ) : ( + + ) + )} +
+ )} +
+ )}
@@ -304,6 +372,7 @@ function SessionTodoRow({ editTodo, toggleTodoDone, removeTodo, + onCollapse, }: { draft: TodoDraft; groupId: string; @@ -320,6 +389,7 @@ function SessionTodoRow({ editTodo: ReturnType["editTodo"]; toggleTodoDone: ReturnType["toggleTodoDone"]; removeTodo: ReturnType["removeTodo"]; + onCollapse?: () => void; }) { const [editingDetails, setEditingDetails] = useState(false); // Guards against creating the same new to-do twice if title/details both @@ -435,6 +505,17 @@ function SessionTodoRow({ style={draft.completed ? undefined : { color: todoTextColor }} className={cn("flex-1", draft.completed && "text-muted-foreground line-through")} /> + {onCollapse && ( + + )}