diff --git a/src/components/cases/time-tab.tsx b/src/components/cases/time-tab.tsx index eb855e7..83693d6 100644 --- a/src/components/cases/time-tab.tsx +++ b/src/components/cases/time-tab.tsx @@ -7,12 +7,17 @@ import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; -import { Plus, Trash2, Loader2 } from "lucide-react"; +import { Plus, Trash2, Loader2, FilePlus } from "lucide-react"; import { formatCurrency, formatDate } from "@/lib/format"; import { roundToTenth } from "@/lib/timer"; import { toast } from "sonner"; -export function CaseTimeTab({ caseRecord }: { caseRecord: any }) { +interface CaseTimeTabProps { + caseRecord: any; + onInvoice?: () => void; +} + +export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) { const { user } = useAuth(); const [entries, setEntries] = useState([]); const [showForm, setShowForm] = useState(false); @@ -27,12 +32,19 @@ export function CaseTimeTab({ caseRecord }: { caseRecord: any }) { }); const load = async () => { - const { data } = await supabase + const { data, error } = await supabase .from("time_entries") - .select("*, user:profiles!time_entries_user_id_fkey(full_name, email)") + .select("*") .eq("case_id", caseRecord.id) .order("work_date", { ascending: false }); - setEntries(data ?? []); + if (error) { toast.error("Could not load time entries", { description: error.message }); return; } + const ids = Array.from(new Set((data ?? []).map((d: any) => d.user_id).filter(Boolean))); + let profileMap: Record = {}; + if (ids.length) { + const { data: profs } = await supabase.from("profiles").select("id, full_name, email").in("id", ids); + profileMap = Object.fromEntries((profs ?? []).map((p: any) => [p.id, p])); + } + setEntries((data ?? []).map((d: any) => ({ ...d, user: d.user_id ? profileMap[d.user_id] : null }))); }; useEffect(() => { load(); }, [caseRecord.id]); @@ -101,9 +113,16 @@ export function CaseTimeTab({ caseRecord }: { caseRecord: any }) { - +
+ {totals.unbilled > 0 && onInvoice && ( + + )} + +
{showForm && ( diff --git a/src/routes/cases.$caseId.tsx b/src/routes/cases.$caseId.tsx index a04a1a7..fff7e58 100644 --- a/src/routes/cases.$caseId.tsx +++ b/src/routes/cases.$caseId.tsx @@ -137,28 +137,7 @@ function CaseDetail() { )} - - - Litigation - Status log - Documents - Time - Expenses - Invoices - {(data.client?.client_type === "hoa" || data.client?.client_type === "condo") && ( - Collections - )} - - - - - - - - {(data.client?.client_type === "hoa" || data.client?.client_type === "condo") && ( - - )} - +
@@ -170,6 +149,34 @@ function CaseDetail() { ); } +function CaseTabs({ data, caseId, canManage, load }: { data: any; caseId: string; canManage: boolean; load: () => void }) { + const [tab, setTab] = useState("litigation"); + return ( + + + Litigation + Status log + Documents + Time + Expenses + Invoices + {(data.client?.client_type === "hoa" || data.client?.client_type === "condo") && ( + Collections + )} + + + + + setTab("invoices")} /> + + + {(data.client?.client_type === "hoa" || data.client?.client_type === "condo") && ( + + )} + + ); +} + function SmallStat({ label, value }: { label: string; value: string }) { return (