feat: implement todo tracking with collapsible status row and update event handling
This commit is contained in:
@@ -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);
|
||||
};
|
||||
Reference in New Issue
Block a user