"use client"; import { CalendarClock, Plus } from "lucide-react"; import { type DragEvent, type FormEvent, type ReactNode, useEffect, useMemo, useState, } from "react"; import { useTranslation } from "react-i18next"; import { TaskDetailSheet } from "@/components/tasks/task-detail-sheet"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPopup, DialogTitle, } from "@/components/ui/dialog"; import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { Input } from "@/components/ui/input"; import { Tabs, TabsList, TabsPanel, TabsTab } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { ROLE_LABELS } from "@/lib/access"; import { DEPARTMENTS } from "@/lib/roles"; import { listProviders, type Provider, specialtyLabel } from "@/lib/staff"; import { type Priority, type Task, type TaskInput, type TaskStatus, createTask, deleteTask, listTasks, updateTask, } from "@/lib/tasks"; import { notify } from "@/lib/toast"; import { cn } from "@/lib/utils"; type AssigneeMode = "self" | "department" | "person"; function deptLabel(role: string): string { return (ROLE_LABELS as Record)[role] ?? role; } export type { Priority, Task } from "@/lib/tasks"; // The board columns, left → right. const COLUMNS: TaskStatus[] = ["todo", "in_progress", "done"]; const priorityVariant: Record = { high: "destructive", medium: "secondary", low: "outline", }; // A subtle accent dot per column so the three are quick to tell apart. const columnDot: Record = { todo: "bg-muted-foreground", in_progress: "bg-primary", done: "bg-success", }; function Field({ label, children }: { label: string; children: ReactNode }) { return ( ); } const controlClass = "h-9 w-full rounded-3xl border border-transparent bg-input/50 px-3 text-sm text-foreground outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30"; function AddTaskDialog({ open, onOpenChange, onAdd, initialStatus, }: { open: boolean; onOpenChange: (open: boolean) => void; onAdd: (task: TaskInput) => void; initialStatus: TaskStatus; }) { const { t } = useTranslation(); const [title, setTitle] = useState(""); const [notes, setNotes] = useState(""); const [assigneeMode, setAssigneeMode] = useState("self"); const [department, setDepartment] = useState("reception"); const [providers, setProviders] = useState([]); const [providerId, setProviderId] = useState(""); const [due, setDue] = useState(""); const [priority, setPriority] = useState("medium"); const [status, setStatus] = useState(initialStatus); // Re-seed the column when the dialog is opened from a specific column. // Adjusted during render rather than in an effect, so the select never paints // with the previous column's value. const [prevSeed, setPrevSeed] = useState(null); const seed = open ? initialStatus : null; if (prevSeed !== seed) { setPrevSeed(seed); if (seed) setStatus(seed); } // Load the clinic's providers so a task can be assigned to a specific person. useEffect(() => { if (!open) return; listProviders() .then((list) => { setProviders(list); setProviderId((id) => id || (list[0]?.userId ?? "")); }) .catch(() => setProviders([])); }, [open]); const reset = () => { setTitle(""); setNotes(""); setAssigneeMode("self"); setDepartment("reception"); setProviderId(""); setDue(""); setPriority("medium"); setStatus(initialStatus); }; const submit = (event: FormEvent) => { event.preventDefault(); if (!title.trim()) { notify.error( t("tasks.toast.needSubjectTitle"), t("tasks.toast.needSubjectBody"), ); return; } if (assigneeMode === "person" && !providerId) { notify.error( t("tasks.toast.needPersonTitle"), t("tasks.toast.needPersonBody"), ); return; } const person = assigneeMode === "person" ? providers.find((p) => p.userId === providerId) : undefined; onAdd({ title: title.trim(), notes: notes.trim() || undefined, // Myself → personal task; Department → a member role; Person → a specific // clinician (their name shows as the assignee, the task surfaces for them). assigneeRole: assigneeMode === "department" ? department : null, assigneeUserId: assigneeMode === "person" ? providerId || null : null, assignee: person?.name, due: due.trim() || "No due date", priority, status, }); notify.success(t("tasks.toast.addedTitle"), title.trim()); reset(); onOpenChange(false); }; return ( { onOpenChange(o); if (!o) reset(); }} open={open} > {t("tasks.dialog.title")} {t("tasks.dialog.description")}
setTitle(e.target.value)} placeholder={t("tasks.dialog.subjectPlaceholder")} value={title} />