Files
edr-platform/apps/edr-freight-web/backoffice/src/pages/invoices/FinanceHubPage.tsx

121 lines
4.2 KiB
TypeScript

import { Tabs } from "@mantine/core";
import { Banknote, DollarSign, Receipt } from "lucide-react";
import { useSearchParams } from "react-router-dom";
import { useAuth } from "@/auth/useAuth";
import { useManualPaymentSettingsQuery } from "@/hooks/useManualPaymentSettings";
import { PageContainer, PageHeader } from "@/components/page";
import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions";
import InvoicesPanel from "./InvoicesPage";
import UsdPaymentsPanel from "./UsdPaymentsPage";
/**
* Invoices and USD Payments used to be separate routes/pages with
* near-identical chrome. They're merged here as URL-linkable tabs (`?tab=`)
* on one page — each tab keeps the permission it was individually gated on
* before, and just doesn't render if the user lacks it.
*
* The Payments tab was removed; its summary (total collected, ETB/USD) now
* lives as a card at the top of the Invoices tab instead. Manual payments are
* split into one tab per currency — ETB keeps the original `?tab=manual-payments`
* key so existing links and the old redirect still land somewhere valid.
*/
const TABS = [
{
key: "invoices",
label: "Invoices",
icon: Receipt,
permission: FREIGHT_PERMS.invoices.view,
subtitle:
"Every invoice issued across bookings, warehouse fees and clearance charges.",
Panel: InvoicesPanel,
},
{
key: "manual-payments",
label: "Manual Payments (ETB)",
icon: Banknote,
// Same gate as Invoices, not a dedicated key — mirrors the old route.
permission: FREIGHT_PERMS.invoices.view,
/** Hidden unless manual settlement is switched on for this currency. */
manualCurrency: "ETB",
subtitle:
"Import and export invoices in ETB that Finance settles by hand (bank transfer or counter). Upload the customer's slip and confirm the payment before the pay window closes.",
Panel: () => <UsdPaymentsPanel currency="ETB" />,
},
{
key: "manual-payments-usd",
label: "Manual Payments (USD)",
icon: DollarSign,
permission: FREIGHT_PERMS.invoices.view,
manualCurrency: "USD",
subtitle:
"Import and export invoices in USD that Finance settles by hand (bank transfer or counter). Upload the customer's slip and confirm the payment before the pay window closes.",
Panel: () => <UsdPaymentsPanel currency="USD" />,
},
] as const;
type TabKey = (typeof TABS)[number]["key"];
export default function FinanceHubPage() {
const { user } = useAuth();
const [searchParams, setSearchParams] = useSearchParams();
// A currency whose manual-payment channel is switched off has no tab at all
// — the list would be empty and every confirmation refused.
const { data: manualSettings } = useManualPaymentSettingsQuery();
const manualEnabled = (currency: "ETB" | "USD") =>
!manualSettings ||
(currency === "ETB" ? manualSettings.etbEnabled : manualSettings.usdEnabled);
const visibleTabs = TABS.filter(
(tab) =>
hasPermission(user, tab.permission) &&
(!("manualCurrency" in tab) || manualEnabled(tab.manualCurrency)),
);
const requested = searchParams.get("tab");
const active: TabKey =
visibleTabs.find((tab) => tab.key === requested)?.key ??
visibleTabs[0]?.key ??
"invoices";
const activeTab = visibleTabs.find((tab) => tab.key === active);
const handleChange = (value: string | null) => {
if (!value) return;
setSearchParams(
(prev) => {
const next = new URLSearchParams(prev);
next.set("tab", value);
return next;
},
{ replace: true },
);
};
return (
<PageContainer>
<PageHeader title={activeTab?.label ?? "Invoices"} subtitle={activeTab?.subtitle} />
<Tabs value={active} onChange={handleChange} keepMounted={false}>
<Tabs.List>
{visibleTabs.map((tab) => (
<Tabs.Tab
key={tab.key}
value={tab.key}
leftSection={<tab.icon size={16} />}
>
{tab.label}
</Tabs.Tab>
))}
</Tabs.List>
{visibleTabs.map((tab) => (
<Tabs.Panel key={tab.key} value={tab.key} pt="lg">
<tab.Panel />
</Tabs.Panel>
))}
</Tabs>
</PageContainer>
);
}