"use client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; import type { Deadline, Case } from "@/lib/types"; import { format, isPast, isThisWeek, parseISO } from "date-fns"; import { de } from "date-fns/locale"; import { Check, Clock, Filter } from "lucide-react"; import { toast } from "sonner"; import { useState, useMemo } from "react"; import { EmptyState } from "@/components/ui/EmptyState"; type StatusFilter = "all" | "pending" | "completed" | "overdue"; function getUrgency(deadline: Deadline): "red" | "amber" | "green" { if (deadline.status === "completed") return "green"; const due = parseISO(deadline.due_date); if (isPast(due)) return "red"; if (isThisWeek(due, { weekStartsOn: 1 })) return "amber"; return "green"; } const urgencyConfig = { red: { bg: "bg-red-50", border: "border-red-200", badge: "bg-red-100 text-red-700", dot: "bg-red-500", label: "Überfällig", }, amber: { bg: "bg-amber-50", border: "border-amber-200", badge: "bg-amber-100 text-amber-700", dot: "bg-amber-500", label: "Diese Woche", }, green: { bg: "bg-white", border: "border-neutral-200", badge: "bg-green-100 text-green-700", dot: "bg-green-500", label: "OK", }, }; const selectClass = "rounded-md border border-neutral-200 bg-white px-2.5 py-1 text-sm text-neutral-700 transition-colors focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400 outline-none"; export function DeadlineList() { const queryClient = useQueryClient(); const [statusFilter, setStatusFilter] = useState("all"); const [caseFilter, setCaseFilter] = useState("all"); const { data: deadlines, isLoading } = useQuery({ queryKey: ["deadlines"], queryFn: () => api.get("/api/deadlines"), }); const { data: cases } = useQuery({ queryKey: ["cases"], queryFn: () => api.get("/api/cases"), }); const completeMutation = useMutation({ mutationFn: (id: string) => api.patch(`/api/deadlines/${id}/complete`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["deadlines"] }); toast.success("Frist als erledigt markiert"); }, onError: () => { toast.error("Fehler beim Abschließen der Frist"); }, }); const caseMap = useMemo(() => { const map = new Map(); cases?.forEach((c) => map.set(c.id, c)); return map; }, [cases]); const filtered = useMemo(() => { if (!deadlines) return []; return deadlines.filter((d) => { if (statusFilter === "pending" && d.status !== "pending") return false; if (statusFilter === "completed" && d.status !== "completed") return false; if (statusFilter === "overdue") { if (d.status === "completed") return false; if (!isPast(parseISO(d.due_date))) return false; } if (caseFilter !== "all" && d.case_id !== caseFilter) return false; return true; }); }, [deadlines, statusFilter, caseFilter]); const counts = useMemo(() => { if (!deadlines) return { overdue: 0, thisWeek: 0, ok: 0 }; let overdue = 0, thisWeek = 0, ok = 0; for (const d of deadlines) { if (d.status === "completed") continue; const urgency = getUrgency(d); if (urgency === "red") overdue++; else if (urgency === "amber") thisWeek++; else ok++; } return { overdue, thisWeek, ok }; }, [deadlines]); if (isLoading) { return (
{[1, 2, 3, 4, 5].map((i) => (
))}
); } return (
{/* Summary cards */}
{/* Filters */}
Filter:
{cases && cases.length > 0 && ( )}
{/* Deadline list */} {filtered.length === 0 ? ( ) : (
{filtered.map((deadline) => { const urgency = getUrgency(deadline); const config = urgencyConfig[urgency]; const caseInfo = caseMap.get(deadline.case_id); return (
{deadline.title} {config.label} {deadline.status === "completed" && ( Erledigt )}
{format(parseISO(deadline.due_date), "dd. MMM yyyy", { locale: de, })} {caseInfo && ( <> · {caseInfo.case_number} — {caseInfo.title} )} {deadline.source !== "manual" && ( <> · {deadline.source} )}
{deadline.status !== "completed" && ( )}
); })}
)}
); }