feat: implement todo tracking with collapsible status row and update event handling

This commit is contained in:
Bohdan Triapitsyn
2025-12-16 02:34:29 +02:00
parent ca898cf321
commit 8f6e6db5c5
7 changed files with 421 additions and 51 deletions
+94
View File
@@ -0,0 +1,94 @@
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { opencodeClient } from "@/lib/opencode/client";
export type TodoStatus = "pending" | "in_progress" | "completed" | "cancelled";
export type TodoPriority = "high" | "medium" | "low";
export interface TodoItem {
id: string;
content: string;
status: TodoStatus;
priority: TodoPriority;
}
interface TodoStore {
// Map of sessionId -> todos
sessionTodos: Map<string, TodoItem[]>;
isLoading: boolean;
// Actions
loadTodos: (sessionId: string) => Promise<void>;
updateTodos: (sessionId: string, todos: TodoItem[]) => void;
getTodosForSession: (sessionId: string) => TodoItem[];
clearTodos: (sessionId: string) => void;
}
type RawTodo = { id: string; content: string; status: string; priority: string };
const normalizeTodo = (todo: RawTodo): TodoItem => ({
id: todo.id,
content: todo.content,
status: (todo.status as TodoStatus) || "pending",
priority: (todo.priority as TodoPriority) || "medium",
});
export const useTodoStore = create<TodoStore>()(
devtools(
(set, get) => ({
sessionTodos: new Map(),
isLoading: false,
loadTodos: async (sessionId: string) => {
if (!sessionId) return;
set({ isLoading: true });
try {
const rawTodos = await opencodeClient.getSessionTodos(sessionId);
const todos = rawTodos.map(normalizeTodo);
set((state) => {
const newMap = new Map(state.sessionTodos);
newMap.set(sessionId, todos);
return { sessionTodos: newMap, isLoading: false };
});
} catch (error) {
console.warn("[TodoStore] Failed to load todos:", error);
set({ isLoading: false });
}
},
updateTodos: (sessionId: string, todos: TodoItem[]) => {
set((state) => {
const newMap = new Map(state.sessionTodos);
newMap.set(sessionId, todos);
return { sessionTodos: newMap };
});
},
getTodosForSession: (sessionId: string) => {
return get().sessionTodos.get(sessionId) || [];
},
clearTodos: (sessionId: string) => {
set((state) => {
const newMap = new Map(state.sessionTodos);
newMap.delete(sessionId);
return { sessionTodos: newMap };
});
},
}),
{ name: "todo-store" }
)
);
// Helper to handle SSE todo.updated events
export const handleTodoUpdatedEvent = (
sessionId: string,
todos: RawTodo[]
): void => {
const normalizedTodos = todos.map(normalizeTodo);
useTodoStore.getState().updateTodos(sessionId, normalizedTodos);
};