From 4d78923d66e3e14b33e7a7224d4ebc1f55498298 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 14:52:11 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/components/cases/hearing-timeline-tab.tsx | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/components/cases/hearing-timeline-tab.tsx diff --git a/src/components/cases/hearing-timeline-tab.tsx b/src/components/cases/hearing-timeline-tab.tsx new file mode 100644 index 0000000..ee76d66 --- /dev/null +++ b/src/components/cases/hearing-timeline-tab.tsx @@ -0,0 +1,161 @@ +import { useEffect, useState, useCallback } from "react"; +import { Link } from "@tanstack/react-router"; +import { supabase } from "@/integrations/supabase/client"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Gavel, Loader2, MapPin, CalendarPlus, ExternalLink } from "lucide-react"; +import { formatDateTime, formatDate } from "@/lib/format"; + +interface HearingEvent { + id: string; + title: string; + start_at: string; + end_at: string | null; + all_day: boolean; + location: string | null; + description: string | null; + event_type: string; +} + +export function CaseHearingTimelineTab({ caseId, nextHearingDate, nextHearingNotes }: { + caseId: string; + nextHearingDate?: string | null; + nextHearingNotes?: string | null; +}) { + const [hearings, setHearings] = useState([]); + const [loading, setLoading] = useState(true); + + const load = useCallback(async () => { + setLoading(true); + const { data, error } = await supabase + .from("events") + .select("id,title,start_at,end_at,all_day,location,description,event_type") + .eq("case_id", caseId) + .eq("event_type", "hearing") + .order("start_at", { ascending: false }); + if (!error) setHearings((data ?? []) as HearingEvent[]); + setLoading(false); + }, [caseId]); + + useEffect(() => { load(); }, [load]); + + const now = Date.now(); + const upcoming = hearings.filter((h) => new Date(h.start_at).getTime() >= now); + const past = hearings.filter((h) => new Date(h.start_at).getTime() < now); + + if (loading) { + return ( +
+ +
+ ); + } + + if (hearings.length === 0 && !nextHearingDate) { + return ( + + + +

No hearings scheduled for this case yet.

+ +
+
+ ); + } + + return ( +
+
+

+ Hearings are sourced from the calendar. Add or edit them in the{" "} + + Calendar + . +

+ +
+ + {nextHearingDate && ( +
+ + + +
+
Next hearing
+
{formatDate(nextHearingDate)}
+ {nextHearingNotes && ( +
{nextHearingNotes}
+ )} +
+
+
+
+ )} + + {upcoming.length > 0 && ( +
+
+ {upcoming.map((h) => )} +
+
+ )} + + {past.length > 0 && ( +
+
+ {past.map((h) => )} +
+
+ )} +
+ ); +} + +function Section({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
+

{title}

+ {children} +
+ ); +} + +function HearingRow({ h, muted }: { h: HearingEvent; muted?: boolean }) { + return ( + + +
+ + Hearing + +
+
+
{h.title}
+
+ {h.all_day ? formatDate(h.start_at) : formatDateTime(h.start_at)} + {h.end_at && !h.all_day && ` – ${formatDateTime(h.end_at)}`} +
+ {h.location && ( +
+ {h.location} +
+ )} + {h.description && ( +
{h.description}
+ )} +
+ +
+
+ ); +}