mirror of
https://github.com/temetro/temetro.git
synced 2026-08-09 10:09:39 +00:00
feat(tasks): kanban board with To Do / In Progress / Done columns
- add a `status` field to tasks (backend schema/validation/service/types + frontend types), kept in sync with the legacy `done` flag - migration 0017 adds the column and backfills done tasks to "done" - rewrite the tasks page as a three-column board: per-column add buttons, draggable cards, and a status mover in the detail sheet Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
import type { TaskPriority } from "../../types/task.js";
|
||||
import type { TaskPriority, TaskStatus } from "../../types/task.js";
|
||||
import { organization, user } from "./auth.js";
|
||||
|
||||
// One row per care-team to-do, scoped to a clinic (organization). Shared across
|
||||
@@ -27,6 +27,9 @@ export const tasks = pgTable(
|
||||
assigneeRole: text("assignee_role"),
|
||||
due: text("due").notNull().default("No due date"),
|
||||
priority: text("priority").$type<TaskPriority>().notNull(),
|
||||
// Board column: todo | in_progress | done. Kept in sync with `done`
|
||||
// (done === status === "done") so the legacy toggle still works.
|
||||
status: text("status").$type<TaskStatus>().notNull().default("todo"),
|
||||
patient: text("patient"),
|
||||
notes: text("notes"),
|
||||
done: boolean("done").notNull().default(false),
|
||||
|
||||
@@ -17,6 +17,7 @@ export const taskInputSchema = z.object({
|
||||
assigneeRole: z.enum(TASK_DEPARTMENTS).nullish(),
|
||||
due: z.string().trim().max(120).default("No due date"),
|
||||
priority: z.enum(["high", "medium", "low"]).default("medium"),
|
||||
status: z.enum(["todo", "in_progress", "done"]).default("todo"),
|
||||
patient: z.string().trim().max(200).nullish(),
|
||||
notes: z.string().max(5000).nullish(),
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ function toTask(row: TaskRow): Task {
|
||||
assigneeRole: row.assigneeRole,
|
||||
due: row.due,
|
||||
priority: row.priority,
|
||||
status: row.status,
|
||||
patient: row.patient,
|
||||
notes: row.notes,
|
||||
done: row.done,
|
||||
@@ -74,6 +75,9 @@ export async function createTask(
|
||||
assigneeRole: input.assigneeRole ?? null,
|
||||
due: input.due,
|
||||
priority: input.priority,
|
||||
status: input.status,
|
||||
// Keep the legacy `done` flag in lock-step with the board column.
|
||||
done: input.status === "done",
|
||||
patient: input.patient ?? null,
|
||||
notes: input.notes ?? null,
|
||||
createdBy: creator.id,
|
||||
@@ -100,7 +104,15 @@ export async function updateTask(
|
||||
if (patch.priority !== undefined) set.priority = patch.priority;
|
||||
if (patch.patient !== undefined) set.patient = patch.patient ?? null;
|
||||
if (patch.notes !== undefined) set.notes = patch.notes ?? null;
|
||||
if (patch.done !== undefined) set.done = patch.done;
|
||||
// Keep `status` and the legacy `done` flag in sync: a status patch wins and
|
||||
// sets done; a bare done toggle maps to done/todo.
|
||||
if (patch.status !== undefined) {
|
||||
set.status = patch.status;
|
||||
set.done = patch.status === "done";
|
||||
} else if (patch.done !== undefined) {
|
||||
set.done = patch.done;
|
||||
set.status = patch.done ? "done" : "todo";
|
||||
}
|
||||
|
||||
if (Object.keys(set).length === 0) {
|
||||
const [row] = await db
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// `lib/tasks.ts` Task type. Scoped to the active clinic (a shared care-team
|
||||
// to-do board). `patient` is an optional free-text reference for context.
|
||||
export type TaskPriority = "high" | "medium" | "low";
|
||||
// Board column the task sits in. `done` mirrors `status === "done"`.
|
||||
export type TaskStatus = "todo" | "in_progress" | "done";
|
||||
|
||||
export type Task = {
|
||||
id: string;
|
||||
@@ -11,6 +13,7 @@ export type Task = {
|
||||
assigneeRole: string | null;
|
||||
due: string;
|
||||
priority: TaskPriority;
|
||||
status: TaskStatus;
|
||||
patient: string | null;
|
||||
notes: string | null;
|
||||
done: boolean;
|
||||
|
||||
Reference in New Issue
Block a user