import { createFileRoute } from "@tanstack/react-router"; import { useEffect, useMemo, useState } from "react"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; import { ProtectedLayout } from "@/components/protected-layout"; import { PageContainer, PageHeader } from "@/components/app-shell"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Card } from "@/components/ui/card"; import { Calendar } from "@/components/ui/calendar"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Plus, Calendar as CalIcon, Briefcase, CheckSquare } from "lucide-react"; import { format, isSameDay, parseISO, startOfDay } from "date-fns"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Link } from "@tanstack/react-router"; export const Route = createFileRoute("/calendar/")({ component: CalendarPage, }); interface TaskEvt { id: string; title: string; due_date: string; case_id: string | null; kind: "task"; } interface HearingEvt { id: string; // case id title: string; due_date: string; case_id: string; kind: "hearing"; notes: string | null; } type Evt = TaskEvt | HearingEvt; interface CaseRow { id: string; title: string; case_number: string; } function CalendarPage() { const { user } = useAuth(); const [selected, setSelected] = useState(new Date()); const [events, setEvents] = useState([]); const [cases, setCases] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [saving, setSaving] = useState(false); // form const [fTitle, setFTitle] = useState(""); const [fDate, setFDate] = useState(undefined); const [fCase, setFCase] = useState("none"); const [fNotes, setFNotes] = useState(""); const reload = async () => { setLoading(true); const [tasksRes, casesRes] = await Promise.all([ supabase .from("tasks") .select("id,title,due_date,case_id,status") .not("due_date", "is", null) .eq("status", "incomplete"), supabase .from("cases") .select("id,title,case_number,next_hearing_date,next_hearing_notes") .order("title"), ]); const tEvts: Evt[] = (tasksRes.data ?? []).map((t) => ({ id: t.id, title: t.title, due_date: t.due_date as string, case_id: t.case_id, kind: "task" as const, })); const hEvts: Evt[] = (casesRes.data ?? []) .filter((c) => c.next_hearing_date) .map((c) => ({ id: c.id, title: `Hearing: ${c.title}`, due_date: c.next_hearing_date as string, case_id: c.id, kind: "hearing" as const, notes: c.next_hearing_notes, })); setEvents([...tEvts, ...hEvts]); setCases((casesRes.data ?? []).map((c) => ({ id: c.id, title: c.title, case_number: c.case_number }))); setLoading(false); }; useEffect(() => { reload(); }, []); const eventsByDay = useMemo(() => { const m = new Map(); for (const e of events) { const key = e.due_date.slice(0, 10); const arr = m.get(key) ?? []; arr.push(e); m.set(key, arr); } return m; }, [events]); const dayHasEvent = (date: Date) => eventsByDay.has(format(date, "yyyy-MM-dd")); const dayEvents = useMemo(() => { return events.filter((e) => isSameDay(parseISO(e.due_date), selected)); }, [events, selected]); const upcoming = useMemo(() => { const today = startOfDay(new Date()); return [...events] .filter((e) => parseISO(e.due_date) >= today) .sort((a, b) => a.due_date.localeCompare(b.due_date)) .slice(0, 8); }, [events]); const handleCreate = async () => { if (!user || !fTitle.trim() || !fDate) { toast.error("Title and date are required"); return; } setSaving(true); const { error } = await supabase.from("tasks").insert({ title: fTitle.trim(), due_date: format(fDate, "yyyy-MM-dd"), case_id: fCase === "none" ? null : fCase, description: fNotes.trim() || null, created_by: user.id, status: "incomplete", priority: "normal", }); setSaving(false); if (error) { toast.error(error.message); return; } toast.success("Event scheduled"); setDialogOpen(false); setFTitle(""); setFDate(undefined); setFCase("none"); setFNotes(""); reload(); }; return ( Schedule event
setFTitle(e.target.value)} placeholder="Hearing, deadline, meeting…" />
{fDate && (

{format(fDate, "PPP")}

)}