Files
edr-platform/apps/edr-freight-web/backoffice/src/components/overview/summary/OverviewHeroKpis.tsx
2026-08-13 18:13:42 +00:00

84 lines
2.9 KiB
TypeScript

import { Banknote, FileSignature, FileText, Package } from "lucide-react";
import { KpiStrip, type KpiItem } from "@/components/page";
import type { IOverviewKpis, IOverviewPeriodTotals } from "@/types/overview";
import { CountUp } from "./CountUp";
function formatCurrency(amount: number, currency: "ETB" | "USD") {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
maximumFractionDigits: 0,
}).format(amount);
}
/** Compact form ("ETB 58.6M") — the hero cell is too narrow for nine digits. */
function formatCompactCurrency(amount: number, currency: "ETB" | "USD") {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
notation: "compact",
maximumFractionDigits: 1,
}).format(amount);
}
/** Period-over-period % change, or undefined when there's no prior baseline to compare against. */
function pctDelta(current: number, previous: number): number | undefined {
if (previous === 0) return undefined;
return Math.round(((current - previous) / previous) * 100);
}
interface OverviewHeroKpisProps {
kpis: IOverviewKpis;
current: IOverviewPeriodTotals;
previous: IOverviewPeriodTotals;
rangeLabel: string;
}
/**
* The four numbers an executive reads first: money and volume for the
* selected range, plus what's currently in flight. Revenue and cargo carry a
* real vs-prior-period delta; the two workflow snapshots don't, because
* "active bookings/contracts" is a point-in-time gauge, not a period total —
* showing a delta for it would mean inventing a comparison that isn't real.
*/
export function OverviewHeroKpis({ kpis, current, previous, rangeLabel }: OverviewHeroKpisProps) {
const items: KpiItem[] = [
// An API deployed before the overview revamp omits the period totals —
// drop the two tiles that need them rather than crash (or hide the strip).
...(current
? [
{
label: `Revenue (${rangeLabel})`,
value: <CountUp value={current.revenueEtb} format={(n) => formatCompactCurrency(n, "ETB")} />,
hint: formatCurrency(current.revenueUsd, "USD"),
icon: Banknote,
color: "yellow",
delta: pctDelta(current.revenueEtb, previous?.revenueEtb ?? 0),
},
{
label: "Cargo moved",
value: <CountUp value={current.tons} format={(n) => `${Math.round(n).toLocaleString()} t`} />,
icon: Package,
color: "edr-green",
delta: pctDelta(current.tons, previous?.tons ?? 0),
},
]
: []),
{
label: "Active bookings",
value: <CountUp value={kpis.bookings.totalActive} />,
icon: FileText,
color: "edr-green",
},
{
label: "Active contracts",
value: <CountUp value={kpis.contracts.totalActive} />,
icon: FileSignature,
color: "edr-green",
},
];
return <KpiStrip items={items} />;
}