diff --git a/packages/ui/src/components/session/ProjectNotesTodoPanel.tsx b/packages/ui/src/components/session/ProjectNotesTodoPanel.tsx index f8c31127..9d8c0d4f 100644 --- a/packages/ui/src/components/session/ProjectNotesTodoPanel.tsx +++ b/packages/ui/src/components/session/ProjectNotesTodoPanel.tsx @@ -101,6 +101,19 @@ const createTodoId = (): string => { return `todo_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; }; +const sortTodosWithCompletedLast = (items: OpenChamberProjectTodoItem[]): OpenChamberProjectTodoItem[] => [ + ...items.filter((todo) => !todo.completed), + ...items.filter((todo) => todo.completed), +]; + +const insertTodoBeforeCompleted = (items: OpenChamberProjectTodoItem[], item: OpenChamberProjectTodoItem): OpenChamberProjectTodoItem[] => { + const firstCompletedIndex = items.findIndex((todo) => todo.completed); + if (firstCompletedIndex === -1) { + return [...items, item]; + } + return [...items.slice(0, firstCompletedIndex), item, ...items.slice(firstCompletedIndex)]; +}; + type SortableTodoHandleProps = { attributes: ReturnType['attributes']; listeners: ReturnType['listeners']; @@ -216,7 +229,7 @@ export const ProjectNotesTodoPanel: React.FC = ({ return; } setNotes(data.notes); - setTodos(data.todos); + setTodos(sortTodosWithCompletedLast(data.todos)); setPlans(nextPlans); lastSavedNotesRef.current = data.notes; notesHydratedRef.current = true; @@ -341,15 +354,12 @@ export const ProjectNotesTodoPanel: React.FC = ({ return; } - const nextTodos = [ - ...todos, - { - id: createTodoId(), - text: trimmed.slice(0, OPENCHAMBER_PROJECT_TODO_TEXT_MAX_LENGTH), - completed: false, - createdAt: Date.now(), - }, - ]; + const nextTodos = insertTodoBeforeCompleted(todos, { + id: createTodoId(), + text: trimmed.slice(0, OPENCHAMBER_PROJECT_TODO_TEXT_MAX_LENGTH), + completed: false, + createdAt: Date.now(), + }); setTodos(nextTodos); setNewTodoText(''); void persistProjectData(notes, nextTodos); @@ -369,7 +379,15 @@ export const ProjectNotesTodoPanel: React.FC = ({ const handleToggleTodo = React.useCallback( (id: string, completed: boolean) => { - const nextTodos = todos.map((todo) => (todo.id === id ? { ...todo, completed } : todo)); + const todo = todos.find((item) => item.id === id); + if (!todo || todo.completed === completed) { + return; + } + const remainingTodos = todos.filter((item) => item.id !== id); + const updatedTodo = { ...todo, completed }; + const nextTodos = completed + ? [...remainingTodos, updatedTodo] + : insertTodoBeforeCompleted(remainingTodos, updatedTodo); setTodos(nextTodos); void persistProjectData(notes, nextTodos); }, @@ -405,7 +423,7 @@ export const ProjectNotesTodoPanel: React.FC = ({ if (oldIndex === -1 || newIndex === -1) { return; } - const nextTodos = arrayMove(todos, oldIndex, newIndex); + const nextTodos = sortTodosWithCompletedLast(arrayMove(todos, oldIndex, newIndex)); setTodos(nextTodos); void persistProjectData(notes, nextTodos); }, @@ -799,7 +817,7 @@ export const ProjectNotesTodoPanel: React.FC = ({ return ( {(dragHandleProps) => ( -
+