671195fb7d
Replaces the template's /tasks demo (deleted) with a real multi-tenant
Kanban board.
Schema/validation:
- lib/validation/tasks.ts (taskSchema with status/priority enums + dueDate
optional + assignee/customer optional)
- lib/appwrite/task-actions.ts: createTaskAction, updateTaskAction,
deleteTaskAction, moveTaskAction (used by drag-drop). All audit-logged;
moveTaskAction only audits when status actually changes.
- lib/appwrite/task-queries.ts: listTasks ordered by 'order' asc.
UI:
- /tasks server page assembles { tasks, customers, teamMembers } and
passes to TasksBoard. Removed the template's data-table demo files.
- TasksBoard (client): 4 droppable columns. Columns use @dnd-kit/core
useDroppable; cards inside each column are SortableContext+useSortable
for intra-column ordering. closestCorners collision detection.
- Drag-end computes new 'order' as midpoint between adjacent tasks
(no full reindex), updates UI optimistically, then persists via
moveTaskAction. Rolls back on server error with toast.
- TaskCard: priority badge (color-coded), due-date badge (red if
overdue), assignee badge, customer subtitle, dropdown (Edit/Delete)
on hover.
- TaskFormSheet: title/description/status/priority/dueDate/assignee/
customer. Uses sentinel '__none__' for nullable Selects (Radix Select
forbids empty string values), stripped before submit.
- DragOverlay shows the dragged card rotated 3deg with shadow.
19 lines
700 B
TypeScript
19 lines
700 B
TypeScript
import { z } from "zod";
|
||
|
||
export const taskSchema = z.object({
|
||
title: z.string().trim().min(1, "Başlık zorunlu.").max(255),
|
||
description: z
|
||
.string()
|
||
.trim()
|
||
.max(5000)
|
||
.optional()
|
||
.transform((v) => (v ? v : undefined)),
|
||
status: z.enum(["backlog", "todo", "in_progress", "done"]).optional().default("todo"),
|
||
priority: z.enum(["low", "medium", "high", "urgent"]).optional().default("medium"),
|
||
dueDate: z.string().optional().transform((v) => (v ? v : undefined)),
|
||
assigneeId: z.string().optional().transform((v) => (v ? v : undefined)),
|
||
customerId: z.string().optional().transform((v) => (v ? v : undefined)),
|
||
});
|
||
|
||
export type TaskInput = z.infer<typeof taskSchema>;
|