feat(tasks): Kanban board with drag-and-drop (dnd-kit)
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.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
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>;
|
||||
Reference in New Issue
Block a user