diff --git a/comps/01-statuses.png b/comps/01-statuses.png new file mode 100755 index 0000000..4af956f Binary files /dev/null and b/comps/01-statuses.png differ diff --git a/comps/02-workflow.png b/comps/02-workflow.png new file mode 100755 index 0000000..b9ba075 Binary files /dev/null and b/comps/02-workflow.png differ diff --git a/comps/03-gannt-board.png b/comps/03-gannt-board.png new file mode 100755 index 0000000..8f9a6fe Binary files /dev/null and b/comps/03-gannt-board.png differ diff --git a/comps/04-backlog-screen.png b/comps/04-backlog-screen.png new file mode 100755 index 0000000..706d1bb Binary files /dev/null and b/comps/04-backlog-screen.png differ diff --git a/src/app/api/tasks/route.ts b/src/app/api/tasks/route.ts index a03dd11..9257ae9 100644 --- a/src/app/api/tasks/route.ts +++ b/src/app/api/tasks/route.ts @@ -12,6 +12,7 @@ interface Task { status: 'backlog' | 'in-progress' | 'review' | 'done' | 'archived'; priority: 'low' | 'medium' | 'high' | 'urgent'; projectId: string; + sprintId?: string; createdAt: string; updatedAt: string; dueDate?: string; @@ -27,9 +28,21 @@ interface Project { createdAt: string; } +interface Sprint { + id: string; + name: string; + goal?: string; + startDate: string; + endDate: string; + status: 'planning' | 'active' | 'completed'; + projectId: string; + createdAt: string; +} + interface DataStore { projects: Project[]; tasks: Task[]; + sprints: Sprint[]; lastUpdated: number; } @@ -40,6 +53,7 @@ const defaultData: DataStore = { { id: '3', name: 'Research', description: 'Experiments and learning', color: '#10b981', createdAt: new Date().toISOString() }, ], tasks: [], + sprints: [], lastUpdated: Date.now(), }; @@ -64,16 +78,16 @@ function saveData(data: DataStore) { writeFileSync(DATA_FILE, JSON.stringify(data, null, 2)); } -// GET - fetch all tasks and projects +// GET - fetch all tasks, projects, and sprints export async function GET() { const data = getData(); return NextResponse.json(data); } -// POST - create or update a task +// POST - create or update tasks, projects, or sprints export async function POST(request: Request) { try { - const { task, projects } = await request.json(); + const { task, projects, sprints } = await request.json(); const data = getData(); // Update projects if provided @@ -81,6 +95,11 @@ export async function POST(request: Request) { data.projects = projects; } + // Update sprints if provided + if (sprints) { + data.sprints = sprints; + } + // Update or add task if (task) { const existingIndex = data.tasks.findIndex((t) => t.id === task.id); diff --git a/src/stores/useTaskStore.ts b/src/stores/useTaskStore.ts index 1b8ca15..bb1b49a 100644 --- a/src/stores/useTaskStore.ts +++ b/src/stores/useTaskStore.ts @@ -4,6 +4,18 @@ import { persist } from 'zustand/middleware' export type TaskType = 'idea' | 'task' | 'bug' | 'research' | 'plan' export type TaskStatus = 'backlog' | 'in-progress' | 'review' | 'done' | 'archived' export type Priority = 'low' | 'medium' | 'high' | 'urgent' +export type SprintStatus = 'planning' | 'active' | 'completed' + +export interface Sprint { + id: string + name: string + goal?: string + startDate: string + endDate: string + status: SprintStatus + projectId: string + createdAt: string +} export interface Comment { id: string @@ -20,6 +32,7 @@ export interface Task { status: TaskStatus priority: Priority projectId: string + sprintId?: string createdAt: string updatedAt: string dueDate?: string @@ -38,31 +51,40 @@ export interface Project { interface TaskStore { projects: Project[] tasks: Task[] + sprints: Sprint[] selectedProjectId: string | null selectedTaskId: string | null + selectedSprintId: string | null isLoading: boolean lastSynced: number | null - + // Sync actions syncFromServer: () => Promise syncToServer: () => Promise - + // Project actions addProject: (name: string, description?: string) => void updateProject: (id: string, updates: Partial) => void deleteProject: (id: string) => void selectProject: (id: string | null) => void - + // Task actions addTask: (task: Omit) => void updateTask: (id: string, updates: Partial) => void deleteTask: (id: string) => void selectTask: (id: string | null) => void - + + // Sprint actions + addSprint: (sprint: Omit) => void + updateSprint: (id: string, updates: Partial) => void + deleteSprint: (id: string) => void + selectSprint: (id: string | null) => void + getTasksBySprint: (sprintId: string) => Task[] + // Comment actions addComment: (taskId: string, text: string, author: 'user' | 'assistant') => void deleteComment: (taskId: string, commentId: string) => void - + // Filters getTasksByProject: (projectId: string) => Task[] getTasksByStatus: (status: TaskStatus) => Task[] @@ -325,12 +347,12 @@ const defaultTasks: Task[] = [ ] // Helper to sync to server -async function syncToServer(projects: Project[], tasks: Task[]) { +async function syncToServer(projects: Project[], tasks: Task[], sprints: Sprint[]) { try { await fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ projects, tasks }), + body: JSON.stringify({ projects, tasks, sprints }), }) } catch (error) { console.error('Failed to sync to server:', error) @@ -342,8 +364,10 @@ export const useTaskStore = create()( (set, get) => ({ projects: defaultProjects, tasks: defaultTasks, + sprints: [], selectedProjectId: '1', selectedTaskId: null, + selectedSprintId: null, isLoading: false, lastSynced: null, @@ -353,10 +377,11 @@ export const useTaskStore = create()( const res = await fetch('/api/tasks') if (res.ok) { const data = await res.json() - if (data.tasks?.length > 0 || data.projects?.length > 0) { + if (data.tasks?.length > 0 || data.projects?.length > 0 || data.sprints?.length > 0) { set({ projects: data.projects || get().projects, tasks: data.tasks || get().tasks, + sprints: data.sprints || get().sprints, lastSynced: Date.now(), }) } @@ -370,8 +395,8 @@ export const useTaskStore = create()( }, syncToServer: async () => { - const { projects, tasks } = get() - await syncToServer(projects, tasks) + const { projects, tasks, sprints } = get() + await syncToServer(projects, tasks, sprints) set({ lastSynced: Date.now() }) }, @@ -387,7 +412,7 @@ export const useTaskStore = create()( set((state) => { const newState = { projects: [...state.projects, newProject] } // Sync to server - syncToServer(newState.projects, state.tasks) + syncToServer(newState.projects, state.tasks, state.sprints) return newState }) }, @@ -395,7 +420,7 @@ export const useTaskStore = create()( updateProject: (id, updates) => { set((state) => { const newProjects = state.projects.map((p) => (p.id === id ? { ...p, ...updates } : p)) - syncToServer(newProjects, state.tasks) + syncToServer(newProjects, state.tasks, state.sprints) return { projects: newProjects } }) }, @@ -404,16 +429,18 @@ export const useTaskStore = create()( set((state) => { const newProjects = state.projects.filter((p) => p.id !== id) const newTasks = state.tasks.filter((t) => t.projectId !== id) - syncToServer(newProjects, newTasks) + const newSprints = state.sprints.filter((s) => s.projectId !== id) + syncToServer(newProjects, newTasks, newSprints) return { projects: newProjects, tasks: newTasks, + sprints: newSprints, selectedProjectId: state.selectedProjectId === id ? null : state.selectedProjectId, } }) }, - selectProject: (id) => set({ selectedProjectId: id, selectedTaskId: null }), + selectProject: (id) => set({ selectedProjectId: id, selectedTaskId: null, selectedSprintId: null }), addTask: (task) => { const newTask: Task = { @@ -425,7 +452,7 @@ export const useTaskStore = create()( } set((state) => { const newTasks = [...state.tasks, newTask] - syncToServer(state.projects, newTasks) + syncToServer(state.projects, newTasks, state.sprints) return { tasks: newTasks } }) }, @@ -435,7 +462,7 @@ export const useTaskStore = create()( const newTasks = state.tasks.map((t) => t.id === id ? { ...t, ...updates, updatedAt: new Date().toISOString() } : t ) - syncToServer(state.projects, newTasks) + syncToServer(state.projects, newTasks, state.sprints) return { tasks: newTasks } }) }, @@ -443,7 +470,7 @@ export const useTaskStore = create()( deleteTask: (id) => { set((state) => { const newTasks = state.tasks.filter((t) => t.id !== id) - syncToServer(state.projects, newTasks) + syncToServer(state.projects, newTasks, state.sprints) return { tasks: newTasks, selectedTaskId: state.selectedTaskId === id ? null : state.selectedTaskId, @@ -453,6 +480,51 @@ export const useTaskStore = create()( selectTask: (id) => set({ selectedTaskId: id }), + // Sprint actions + addSprint: (sprint) => { + const newSprint: Sprint = { + ...sprint, + id: Date.now().toString(), + createdAt: new Date().toISOString(), + } + set((state) => { + const newSprints = [...state.sprints, newSprint] + syncToServer(state.projects, state.tasks, newSprints) + return { sprints: newSprints } + }) + }, + + updateSprint: (id, updates) => { + set((state) => { + const newSprints = state.sprints.map((s) => + s.id === id ? { ...s, ...updates } : s + ) + syncToServer(state.projects, state.tasks, newSprints) + return { sprints: newSprints } + }) + }, + + deleteSprint: (id) => { + set((state) => { + const newSprints = state.sprints.filter((s) => s.id !== id) + const newTasks = state.tasks.map((t) => + t.sprintId === id ? { ...t, sprintId: undefined } : t + ) + syncToServer(state.projects, newTasks, newSprints) + return { + sprints: newSprints, + tasks: newTasks, + selectedSprintId: state.selectedSprintId === id ? null : state.selectedSprintId, + } + }) + }, + + selectSprint: (id) => set({ selectedSprintId: id }), + + getTasksBySprint: (sprintId) => { + return get().tasks.filter((t) => t.sprintId === sprintId) + }, + addComment: (taskId, text, author) => { const newComment: Comment = { id: Date.now().toString(), @@ -466,7 +538,7 @@ export const useTaskStore = create()( ? { ...t, comments: [...t.comments, newComment], updatedAt: new Date().toISOString() } : t ) - syncToServer(state.projects, newTasks) + syncToServer(state.projects, newTasks, state.sprints) return { tasks: newTasks } }) }, @@ -478,7 +550,7 @@ export const useTaskStore = create()( ? { ...t, comments: t.comments.filter((c) => c.id !== commentId) } : t ) - syncToServer(state.projects, newTasks) + syncToServer(state.projects, newTasks, state.sprints) return { tasks: newTasks } }) },