Add sprint support to data model and API
- Added Sprint type and interface - Updated Task to include sprintId - Added sprint actions to task store (addSprint, updateSprint, deleteSprint, selectSprint) - Updated API to handle sprints - Updated all sync calls to include sprints
This commit is contained in:
parent
584cabaf07
commit
7710167eb2
BIN
comps/01-statuses.png
Executable file
BIN
comps/01-statuses.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
comps/02-workflow.png
Executable file
BIN
comps/02-workflow.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
BIN
comps/03-gannt-board.png
Executable file
BIN
comps/03-gannt-board.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
BIN
comps/04-backlog-screen.png
Executable file
BIN
comps/04-backlog-screen.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
@ -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);
|
||||
|
||||
@ -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,8 +51,10 @@ 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
|
||||
|
||||
@ -59,6 +74,13 @@ interface TaskStore {
|
||||
deleteTask: (id: string) => void
|
||||
selectTask: (id: string | null) => void
|
||||
|
||||
// Sprint actions
|
||||
addSprint: (sprint: Omit<Sprint, 'id' | 'createdAt'>) => void
|
||||
updateSprint: (id: string, updates: Partial<Sprint>) => 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
|
||||
@ -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<TaskStore>()(
|
||||
(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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
},
|
||||
|
||||
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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
}
|
||||
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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
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<TaskStore>()(
|
||||
|
||||
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<TaskStore>()(
|
||||
? { ...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<TaskStore>()(
|
||||
? { ...t, comments: t.comments.filter((c) => c.id !== commentId) }
|
||||
: t
|
||||
)
|
||||
syncToServer(state.projects, newTasks)
|
||||
syncToServer(state.projects, newTasks, state.sprints)
|
||||
return { tasks: newTasks }
|
||||
})
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user