"use client"; import { useState, useCallback, useEffect, useRef } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { fetchInvoices as fetchInvoicesAction, type InvoiceRow, type InvoiceListFilter } from "@/lib/supabase/invoicing-actions"; import { getOrgName } from "@/lib/supabase/invoicing-actions"; import InvoicesListView from "./InvoicesListView"; import InvoicesNewView from "./InvoicesNewView"; import InvoicesDetailView from "./InvoicesDetailView"; type ViewType = "list" | "new" | "detail"; export default function InvoicesShell({ initialInvoices, }: { initialInvoices: InvoiceRow[]; }) { const router = useRouter(); const searchParams = useSearchParams(); // Determine initial view from URL const urlInvoiceId = searchParams.get("id"); const urlView = searchParams.get("view"); const [view, setView] = useState(urlView === "new" ? "new" : urlInvoiceId ? "detail" : "list"); const [selectedInvoiceId, setSelectedInvoiceId] = useState(urlInvoiceId); const [invoices, setInvoices] = useState(initialInvoices); const [filter, setFilter] = useState((searchParams.get("filter") as InvoiceListFilter) || "all"); const [orgName, setOrgName] = useState("Vela"); const [filterLoading, setFilterLoading] = useState(false); const filterLockRef = useRef(false); useEffect(() => { getOrgName().then(setOrgName).catch(() => {}); }, []); // Sync URL with state — updates URL without full navigation const syncUrl = useCallback( (v: ViewType, invoiceId?: string | null) => { const params = new URLSearchParams(); if (v === "new") params.set("view", "new"); if (v === "detail" && invoiceId) params.set("id", invoiceId); if (v === "list" && filter !== "all") params.set("filter", filter); const qs = params.toString(); router.push(qs ? `/invoices?${qs}` : "/invoices", { scroll: false }); }, [router, filter] ); // Navigate to list view const goToList = useCallback(() => { setView("list"); setSelectedInvoiceId(null); syncUrl("list"); }, [syncUrl]); // Navigate to new invoice view const goToNew = useCallback(() => { setView("new"); setSelectedInvoiceId(null); syncUrl("new"); }, [syncUrl]); // Navigate to detail view const goToDetail = useCallback( (id: string) => { setSelectedInvoiceId(id); setView("detail"); syncUrl("detail", id); }, [syncUrl] ); // Handle filter change — fetch fresh data without page reload const handleFilterChange = useCallback( async (newFilter: InvoiceListFilter) => { if (filterLockRef.current) return; filterLockRef.current = true; setFilter(newFilter); setFilterLoading(true); try { const { fetchInvoices } = await import("@/lib/supabase/invoicing-actions"); const data = await fetchInvoices(newFilter); setInvoices(data); } finally { setFilterLoading(false); filterLockRef.current = false; syncUrl(view, selectedInvoiceId); } }, [syncUrl, view, selectedInvoiceId] ); // Handle newly created invoice const handleCreated = useCallback( (invoiceId: string) => { // Refresh list and go to detail goToList(); // Re-fetch list to include new invoice fetchInvoicesAction(filter).then((data) => setInvoices(data)); // Navigate to detail after a tick so state updates settle requestAnimationFrame(() => goToDetail(invoiceId)); }, [goToList, goToDetail, filter] ); return (
{view === "list" && (
{filterLoading && (
)}
)} {view === "new" && ( )} {view === "detail" && selectedInvoiceId && ( )}
); }