feat: task tags, sort controls, and tag filtering
- Added tags (JSONB array) to tasks schema with full CRUD support - Tag editor in TaskDetailPanel with chip UI, Enter/comma to add, Backspace to remove - Tag badges on TaskCard, KanbanBoard cards, and DashboardPage - Sort controls on QueuePage: sort by priority, due date, created, updated, name - Sort direction toggle (asc/desc) with persistence to localStorage - Tag filter dropdown in QueuePage header (populated from existing tags) - Search now matches tags - Backend: tags in create/update, progressNotes in PATCH body
This commit is contained in:
@@ -82,6 +82,7 @@ export const tasks = pgTable("tasks", {
|
||||
assigneeName: text("assignee_name"),
|
||||
projectId: uuid("project_id").references(() => projects.id, { onDelete: "set null" }),
|
||||
dueDate: timestamp("due_date", { withTimezone: true }),
|
||||
tags: jsonb("tags").$type<string[]>().default([]),
|
||||
subtasks: jsonb("subtasks").$type<Subtask[]>().default([]),
|
||||
progressNotes: jsonb("progress_notes").$type<ProgressNote[]>().default([]),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
||||
|
||||
@@ -154,6 +154,7 @@ export const taskRoutes = new Elysia({ prefix: "/api/tasks" })
|
||||
taskNumber: nextNumber,
|
||||
projectId: body.projectId || null,
|
||||
dueDate: body.dueDate ? new Date(body.dueDate) : null,
|
||||
tags: body.tags || [],
|
||||
subtasks: [],
|
||||
progressNotes: [],
|
||||
})
|
||||
@@ -193,6 +194,7 @@ export const taskRoutes = new Elysia({ prefix: "/api/tasks" })
|
||||
),
|
||||
projectId: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
dueDate: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
tags: t.Optional(t.Array(t.String())),
|
||||
}),
|
||||
}
|
||||
)
|
||||
@@ -240,7 +242,9 @@ export const taskRoutes = new Elysia({ prefix: "/api/tasks" })
|
||||
if (body.assigneeName !== undefined) updates.assigneeName = body.assigneeName;
|
||||
if (body.projectId !== undefined) updates.projectId = body.projectId;
|
||||
if (body.dueDate !== undefined) updates.dueDate = body.dueDate ? new Date(body.dueDate) : null;
|
||||
if (body.tags !== undefined) updates.tags = body.tags;
|
||||
if (body.subtasks !== undefined) updates.subtasks = body.subtasks;
|
||||
if (body.progressNotes !== undefined) updates.progressNotes = body.progressNotes;
|
||||
|
||||
const updated = await db
|
||||
.update(tasks)
|
||||
@@ -269,6 +273,7 @@ export const taskRoutes = new Elysia({ prefix: "/api/tasks" })
|
||||
assigneeName: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
projectId: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
dueDate: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
tags: t.Optional(t.Array(t.String())),
|
||||
subtasks: t.Optional(t.Array(t.Object({
|
||||
id: t.String(),
|
||||
title: t.String(),
|
||||
@@ -276,6 +281,10 @@ export const taskRoutes = new Elysia({ prefix: "/api/tasks" })
|
||||
completedAt: t.Optional(t.String()),
|
||||
createdAt: t.String(),
|
||||
}))),
|
||||
progressNotes: t.Optional(t.Array(t.Object({
|
||||
timestamp: t.String(),
|
||||
note: t.String(),
|
||||
}))),
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user