@burdenoff/microfe-theglobalfuel 2026.804.2 → 2026.827.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/analytics/AnalyticsPage.tsx"],"sourcesContent":["/**\n * Operational Analytics — utilization, fulfilment and movement performance.\n * @module theglobalfuel/pages/analytics\n *\n * Where the dashboard answers \"what is my portfolio\", this page answers \"how\n * well is it running\". Every figure is aggregated client-side from the same\n * real `the*` list endpoints the rest of the product uses — there is no\n * separate analytics store and no mock data. The window selector re-slices the\n * already-fetched production/shipment rows rather than refetching, so switching\n * periods is instant and costs no extra backend calls.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Activity, Gauge, Ship, Package, TrendingUp, Timer } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n LoadingView,\n ErrorView,\n EmptyState,\n FilterTabs,\n type FilterOption,\n AreaChart,\n BarChart,\n DonutChart,\n StatCard,\n type Segment,\n type LabeledValue,\n} from '../../components';\nimport {\n useTheAssets,\n useTheProductionRecords,\n useTheShipments,\n useTheContracts,\n useTheInventoryLots,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport { formatNumber, formatPercent, formatEnumLabel, formatVolume } from '../../utils';\nimport type { TheProductionRecord, TheUnit } from '../../types';\n\nconst PAGE: { limit: number } = { limit: 200 };\n\n/** Analysis window, in days back from now. */\ntype WindowKey = '30' | '90' | '365';\n\nconst WINDOWS: FilterOption<WindowKey>[] = [\n { value: '30', label: 'Last 30 days' },\n { value: '90', label: 'Last 90 days' },\n { value: '365', label: 'Last 12 months' },\n];\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** Month bucket key (YYYY-MM) for a date string; '' when unparseable. */\nfunction monthKey(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n}\n\n/** Short month label (e.g. \"Mar 26\") for a YYYY-MM key. */\nfunction monthLabel(key: string): string {\n const [year, month] = key.split('-');\n const d = new Date(Date.UTC(Number(year), Number(month) - 1, 1));\n if (Number.isNaN(d.getTime())) return key;\n return d.toLocaleString(undefined, { month: 'short', year: '2-digit', timeZone: 'UTC' });\n}\n\n/**\n * Group volumes by unit. Assets report in BARRELS / TONNES / MCF / MWH …, and\n * summing across them would produce a meaningless number, so every total on\n * this page is either unit-scoped or unitless (hours, counts, percentages).\n */\nfunction volumeByUnit(rows: readonly { volume: number; unit: TheUnit }[]): Map<TheUnit, number> {\n const out = new Map<TheUnit, number>();\n for (const row of rows) {\n out.set(row.unit, (out.get(row.unit) ?? 0) + (row.volume ?? 0));\n }\n return out;\n}\n\n/** The unit carrying the largest share of production — the page's lead unit. */\nfunction dominantUnit(byUnit: Map<TheUnit, number>): TheUnit | null {\n let best: TheUnit | null = null;\n let bestValue = -1;\n for (const [unit, value] of byUnit) {\n if (value > bestValue) {\n best = unit;\n bestValue = value;\n }\n }\n return best;\n}\n\nexport function AnalyticsPage() {\n useThePageView('analytics');\n const t = useTheTranslation();\n\n const [windowKey, setWindowKey] = useState<WindowKey>('90');\n const windowDays = Number(windowKey);\n // Sample the clock exactly once, when the page mounts. Reading Date.now()\n // during render would make every aggregate below re-derive against a moving\n // reference point, so a record could silently drop out of the window on an\n // unrelated re-render. A lazy useState initializer pins it for the visit.\n const [evaluatedAt] = useState(() => Date.now());\n const cutoff = useMemo(() => evaluatedAt - windowDays * DAY_MS, [evaluatedAt, windowDays]);\n\n const assets = useTheAssets({ pagination: PAGE });\n const production = useTheProductionRecords({ pagination: PAGE });\n const shipments = useTheShipments(undefined, undefined, PAGE);\n const contracts = useTheContracts({ pagination: PAGE });\n const inventory = useTheInventoryLots({ pagination: PAGE });\n\n const isLoading =\n assets.isLoading ||\n production.isLoading ||\n shipments.isLoading ||\n contracts.isLoading ||\n inventory.isLoading;\n const isError =\n assets.isError ||\n production.isError ||\n shipments.isError ||\n contracts.isError ||\n inventory.isError;\n\n const assetRows = useMemo(() => assets.data?.items ?? [], [assets.data]);\n const contractRows = useMemo(() => contracts.data?.items ?? [], [contracts.data]);\n const lotRows = useMemo(() => inventory.data?.items ?? [], [inventory.data]);\n\n // ── Window-scoped slices ───────────────────────────────────────────────────\n const productionRows = useMemo<TheProductionRecord[]>(\n () =>\n (production.data?.items ?? []).filter((r) => {\n const ts = new Date(r.periodEnd ?? r.createdAt).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [production.data, cutoff]\n );\n\n const shipmentRows = useMemo(\n () =>\n (shipments.data?.items ?? []).filter((s) => {\n const ts = new Date(\n s.deliveredAt ?? s.departedAt ?? s.scheduledAt ?? s.createdAt\n ).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [shipments.data, cutoff]\n );\n\n // ── Utilization: produced volume vs nameplate capacity over the window ─────\n // Only assets whose reporting unit matches the production record's unit are\n // counted, so utilization never mixes units. Assets with no capacity on\n // record are excluded rather than counted as 0% (which would be a lie).\n const utilization = useMemo(() => {\n const byAsset = new Map<string, { produced: number; unit: TheUnit }>();\n for (const r of productionRows) {\n const prev = byAsset.get(r.assetId);\n if (prev && prev.unit !== r.unit) continue;\n byAsset.set(r.assetId, { produced: (prev?.produced ?? 0) + (r.volume ?? 0), unit: r.unit });\n }\n\n const rows: { name: string; pct: number }[] = [];\n let totalProduced = 0;\n let totalCapacity = 0;\n for (const asset of assetRows) {\n const agg = byAsset.get(asset.id);\n if (!agg || asset.capacityPerDay <= 0 || asset.capacityUnit !== agg.unit) continue;\n const capacity = asset.capacityPerDay * windowDays;\n if (capacity <= 0) continue;\n totalProduced += agg.produced;\n totalCapacity += capacity;\n rows.push({ name: asset.name, pct: (agg.produced / capacity) * 100 });\n }\n return {\n overallPct: totalCapacity > 0 ? (totalProduced / totalCapacity) * 100 : null,\n measuredAssets: rows.length,\n top: rows.sort((a, b) => b.pct - a.pct).slice(0, 8),\n };\n }, [productionRows, assetRows, windowDays]);\n\n // ── Downtime ───────────────────────────────────────────────────────────────\n const downtimeHours = useMemo(\n () => productionRows.reduce((sum, r) => sum + (r.downtimeHours ?? 0), 0),\n [productionRows]\n );\n\n // ── Production trend (monthly, dominant unit only) ─────────────────────────\n const productionByUnit = useMemo(() => volumeByUnit(productionRows), [productionRows]);\n const leadUnit = useMemo(() => dominantUnit(productionByUnit), [productionByUnit]);\n\n const trendSeries = useMemo<LabeledValue[]>(() => {\n if (!leadUnit) return [];\n const buckets = new Map<string, number>();\n for (const r of productionRows) {\n if (r.unit !== leadUnit) continue;\n const key = monthKey(r.periodEnd ?? r.createdAt);\n if (!key) continue;\n buckets.set(key, (buckets.get(key) ?? 0) + (r.volume ?? 0));\n }\n return [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ({ label: monthLabel(key), value }));\n }, [productionRows, leadUnit]);\n\n // ── Contract fulfilment ────────────────────────────────────────────────────\n const fulfilment = useMemo(() => {\n const live = contractRows.filter(\n (c) => c.contractedVolume > 0 && (c.status === 'ACTIVE' || c.status === 'SIGNED')\n );\n const contracted = live.reduce((s, c) => s + c.contractedVolume, 0);\n const delivered = live.reduce((s, c) => s + c.deliveredVolume, 0);\n const atRisk = live.filter((c) => {\n if (!c.endDate) return false;\n const daysLeft = (new Date(c.endDate).getTime() - evaluatedAt) / DAY_MS;\n const pct = c.deliveredVolume / c.contractedVolume;\n return daysLeft > 0 && daysLeft <= 60 && pct < 0.75;\n }).length;\n return {\n pct: contracted > 0 ? (delivered / contracted) * 100 : null,\n liveCount: live.length,\n atRisk,\n top: live\n .map((c) => ({\n label: c.reference,\n value: Math.min(100, (c.deliveredVolume / c.contractedVolume) * 100),\n }))\n .sort((a, b) => a.value - b.value)\n .slice(0, 8),\n };\n }, [contractRows, evaluatedAt]);\n\n // ── Shipment mode mix + delivery rate ──────────────────────────────────────\n const shipmentMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const s of shipmentRows) {\n buckets.set(s.mode, (buckets.get(s.mode) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([mode, value]) => ({ label: formatEnumLabel(mode), value }));\n }, [shipmentRows]);\n\n const deliveryRate = useMemo(() => {\n const terminalOrDone = shipmentRows.filter((s) => s.status !== 'CANCELLED');\n if (terminalOrDone.length === 0) return null;\n const delivered = terminalOrDone.filter((s) => s.status === 'DELIVERED').length;\n return (delivered / terminalOrDone.length) * 100;\n }, [shipmentRows]);\n\n const inTransit = useMemo(\n () => shipmentRows.filter((s) => s.status === 'IN_TRANSIT' || s.status === 'LOADING').length,\n [shipmentRows]\n );\n\n // ── Inventory position ─────────────────────────────────────────────────────\n const inventoryMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const l of lotRows) {\n buckets.set(l.status, (buckets.get(l.status) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({ label: formatEnumLabel(status), value }));\n }, [lotRows]);\n\n const description = t(\n 'theglobalfuel.pages.analytics.description',\n 'Utilization, fulfilment and movement performance across the portfolio.'\n );\n const pageTitle = t('theglobalfuel.pages.analytics.title', 'Operational Analytics');\n\n if (isLoading) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <LoadingView label=\"Aggregating operational data…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <ErrorView\n message=\"Couldn't load the operational data for analytics.\"\n onRetry={() => {\n void assets.refetch();\n void production.refetch();\n void shipments.refetch();\n void contracts.refetch();\n void inventory.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const hasAnything =\n assetRows.length > 0 ||\n productionRows.length > 0 ||\n contractRows.length > 0 ||\n shipmentRows.length > 0;\n\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n actions={<FilterTabs options={WINDOWS} value={windowKey} onChange={setWindowKey} />}\n >\n {!hasAnything ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Activity className=\"h-10 w-10\" />}\n title=\"Nothing to analyse yet\"\n description=\"Register assets and record production, contracts and shipments — analytics is computed from those records.\"\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n label=\"Capacity utilization\"\n value={utilization.overallPct != null ? formatPercent(utilization.overallPct) : '—'}\n icon={<Gauge className=\"h-4 w-4\" />}\n deltaLabel={\n utilization.overallPct != null\n ? `${formatNumber(utilization.measuredAssets)} assets with capacity on record`\n : 'No asset has both capacity and matching-unit production'\n }\n />\n <StatCard\n label=\"Contract fulfilment\"\n value={fulfilment.pct != null ? formatPercent(fulfilment.pct) : '—'}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n deltaLabel={`${formatNumber(fulfilment.liveCount)} live · ${formatNumber(fulfilment.atRisk)} at risk`}\n />\n <StatCard\n label=\"Delivery rate\"\n value={deliveryRate != null ? formatPercent(deliveryRate) : '—'}\n icon={<Ship className=\"h-4 w-4\" />}\n deltaLabel={`${formatNumber(inTransit)} currently moving`}\n />\n <StatCard\n label=\"Recorded downtime\"\n value={`${formatNumber(downtimeHours)} h`}\n icon={<Timer className=\"h-4 w-4\" />}\n invertDelta\n deltaLabel={`Across ${formatNumber(productionRows.length)} production records`}\n />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5 lg:col-span-2\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Production trend</h2>\n <p className=\"text-xs text-text-tertiary\">\n {leadUnit\n ? `Monthly volume in ${formatEnumLabel(leadUnit)} — the unit carrying most of the portfolio. Other units are charted on their own assets.`\n : 'No production recorded in this window.'}\n </p>\n </div>\n {trendSeries.length > 0 ? (\n <AreaChart series={trendSeries} height={220} title=\"Monthly production volume\" />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Movement mix</h2>\n <p className=\"text-xs text-text-tertiary\">Shipments by transport mode.</p>\n </div>\n {shipmentMix.length > 0 ? (\n <DonutChart\n segments={shipmentMix}\n centerLabel={formatNumber(shipmentRows.length)}\n centerCaption=\"shipments\"\n showLegend\n title=\"Shipments by transport mode\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No shipments in this window.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Utilization by asset</h2>\n <p className=\"text-xs text-text-tertiary\">\n Produced volume against nameplate capacity for the window. Assets without a\n capacity figure, or whose production is recorded in a different unit, are omitted.\n </p>\n </div>\n {utilization.top.length > 0 ? (\n <BarChart\n series={utilization.top.map((r) => ({ label: r.name, value: Math.round(r.pct) }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Capacity utilization by asset\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No asset has both a capacity figure and matching-unit production in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Least-fulfilled contracts\n </h2>\n <p className=\"text-xs text-text-tertiary\">\n Delivered against contracted volume for live (signed/active) contracts.\n </p>\n </div>\n {fulfilment.top.length > 0 ? (\n <BarChart\n series={fulfilment.top.map((r) => ({\n label: r.label,\n value: Math.round(r.value),\n }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Contract fulfilment\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No live contracts with a contracted volume.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Package className=\"h-4 w-4 text-text-secondary\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Inventory position</h2>\n </div>\n {inventoryMix.length > 0 ? (\n <DonutChart\n segments={inventoryMix}\n centerLabel={formatNumber(lotRows.length)}\n centerCaption=\"lots\"\n showLegend\n title=\"Inventory lots by status\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No inventory lots recorded.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"p-5 lg:col-span-2\">\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">\n Production volume by unit\n </h2>\n <p className=\"mb-4 text-xs text-text-tertiary\">\n Volumes are never summed across units of measure. Each row is an independent total.\n </p>\n {productionByUnit.size > 0 ? (\n <ul className=\"divide-y divide-border-subtle\">\n {[...productionByUnit.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([unit, value]) => (\n <li key={unit} className=\"flex items-center justify-between py-2.5\">\n <span className=\"text-sm text-text-secondary\">{formatEnumLabel(unit)}</span>\n <span className=\"text-sm font-medium tabular-nums text-text-primary\">\n {formatVolume(value, unit)}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/analytics/AnalyticsPage.tsx"],"sourcesContent":["/**\n * Operational Analytics — utilization, fulfilment and movement performance.\n * @module theglobalfuel/pages/analytics\n *\n * Where the dashboard answers \"what is my portfolio\", this page answers \"how\n * well is it running\". Every figure is aggregated client-side from the same\n * real `the*` list endpoints the rest of the product uses — there is no\n * separate analytics store and no mock data. The window selector re-slices the\n * already-fetched production/shipment rows rather than refetching, so switching\n * periods is instant and costs no extra backend calls.\n *\n * Unit discipline: volumes are NEVER summed across units of measure and money is\n * never summed across currencies. Where a figure cannot be stated honestly for\n * the data on hand (no capacity on record, emissions that carry no asset, lots\n * with no unit value) it renders as \"—\" with the reason, rather than as a zero.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Activity,\n Gauge,\n Ship,\n Package,\n TrendingUp,\n Timer,\n Leaf,\n Coins,\n Download,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n LoadingView,\n ErrorView,\n EmptyState,\n FilterTabs,\n MiniBar,\n type FilterOption,\n AreaChart,\n BarChart,\n DonutChart,\n StatCard,\n type Segment,\n type LabeledValue,\n} from '../../components';\nimport {\n useTheAssets,\n useTheProductionRecords,\n useTheShipments,\n useTheContracts,\n useTheInventoryLots,\n useTheEmissionRecords,\n useNotify,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport {\n downloadTextFile,\n formatEnumLabel,\n formatMoney,\n formatNumber,\n formatPercent,\n formatTonnesCo2e,\n formatUnit,\n formatVolume,\n toCsv,\n} from '../../utils';\nimport type {\n TheEmissionScope,\n TheInventoryStatus,\n TheProductionRecord,\n TheUnit,\n} from '../../types';\n\nconst PAGE: { limit: number } = { limit: 200 };\n\n/** Analysis window, in days back from now. */\ntype WindowKey = '30' | '90' | '365';\n\nconst WINDOWS: FilterOption<WindowKey>[] = [\n { value: '30', label: 'Last 30 days' },\n { value: '90', label: 'Last 90 days' },\n { value: '365', label: 'Last 12 months' },\n];\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** GHG scope → donut segment color token (Scope 1 direct · 2 energy · 3 chain). */\nconst SCOPE_COLOR: Record<TheEmissionScope, string> = {\n SCOPE_1: 'var(--color-status-error-text)',\n SCOPE_2: 'var(--color-status-warning-text)',\n SCOPE_3: 'var(--color-accent-blue)',\n};\n\nconst ALL_SCOPES: TheEmissionScope[] = ['SCOPE_1', 'SCOPE_2', 'SCOPE_3'];\n\n/**\n * Lot states that still represent stock you hold. CONSUMED / WRITTEN_OFF lots\n * have left the book, so counting their value would overstate the position.\n */\nconst ON_HAND_LOT_STATES = new Set<TheInventoryStatus>(['AVAILABLE', 'RESERVED', 'IN_TRANSIT']);\n\n/** Caption used wherever a stat carries a period-over-period comparison. */\nconst PRIOR_WINDOW_LABEL = 'vs the preceding window';\n\n/** Contract states that are live enough for fulfilment to be meaningful. */\nconst LIVE_CONTRACT_STATES = new Set(['ACTIVE', 'SIGNED']);\n\n/** Month bucket key (YYYY-MM) for a date string; '' when unparseable. */\nfunction monthKey(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n}\n\n/** Short month label (e.g. \"Mar 26\") for a YYYY-MM key. */\nfunction monthLabel(key: string): string {\n const [year, month] = key.split('-');\n const d = new Date(Date.UTC(Number(year), Number(month) - 1, 1));\n if (Number.isNaN(d.getTime())) return key;\n return d.toLocaleString(undefined, { month: 'short', year: '2-digit', timeZone: 'UTC' });\n}\n\n/**\n * Group volumes by unit. Assets report in BARRELS / TONNES / MCF / MWH …, and\n * summing across them would produce a meaningless number, so every total on\n * this page is either unit-scoped or unitless (hours, counts, percentages).\n */\nfunction volumeByUnit(rows: readonly { volume: number; unit: TheUnit }[]): Map<TheUnit, number> {\n const out = new Map<TheUnit, number>();\n for (const row of rows) {\n out.set(row.unit, (out.get(row.unit) ?? 0) + (row.volume ?? 0));\n }\n return out;\n}\n\n/** The unit carrying the largest share of production — the page's lead unit. */\nfunction dominantUnit(byUnit: Map<TheUnit, number>): TheUnit | null {\n let best: TheUnit | null = null;\n let bestValue = -1;\n for (const [unit, value] of byUnit) {\n if (value > bestValue) {\n best = unit;\n bestValue = value;\n }\n }\n return best;\n}\n\nexport function AnalyticsPage() {\n useThePageView('analytics');\n const t = useTheTranslation();\n const notify = useNotify();\n\n const [windowKey, setWindowKey] = useState<WindowKey>('90');\n const windowDays = Number(windowKey);\n // Sample the clock exactly once, when the page mounts. Reading Date.now()\n // during render would make every aggregate below re-derive against a moving\n // reference point, so a record could silently drop out of the window on an\n // unrelated re-render. A lazy useState initializer pins it for the visit.\n const [evaluatedAt] = useState(() => Date.now());\n const cutoff = useMemo(() => evaluatedAt - windowDays * DAY_MS, [evaluatedAt, windowDays]);\n // Start of the immediately preceding window of equal length, used for the\n // period-over-period deltas below.\n const priorCutoff = useMemo(\n () => evaluatedAt - 2 * windowDays * DAY_MS,\n [evaluatedAt, windowDays]\n );\n\n const assets = useTheAssets({ pagination: PAGE });\n const production = useTheProductionRecords({ pagination: PAGE });\n const shipments = useTheShipments(undefined, undefined, PAGE);\n const contracts = useTheContracts({ pagination: PAGE });\n const inventory = useTheInventoryLots({ pagination: PAGE });\n const emissions = useTheEmissionRecords({ pagination: PAGE });\n\n const isLoading =\n assets.isLoading ||\n production.isLoading ||\n shipments.isLoading ||\n contracts.isLoading ||\n inventory.isLoading ||\n emissions.isLoading;\n const isError =\n assets.isError ||\n production.isError ||\n shipments.isError ||\n contracts.isError ||\n inventory.isError ||\n emissions.isError;\n\n const assetRows = useMemo(() => assets.data?.items ?? [], [assets.data]);\n const contractRows = useMemo(() => contracts.data?.items ?? [], [contracts.data]);\n const lotRows = useMemo(() => inventory.data?.items ?? [], [inventory.data]);\n\n // ── Window-scoped slices ───────────────────────────────────────────────────\n const productionRows = useMemo<TheProductionRecord[]>(\n () =>\n (production.data?.items ?? []).filter((r) => {\n const ts = new Date(r.periodEnd ?? r.createdAt).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [production.data, cutoff]\n );\n\n const shipmentRows = useMemo(\n () =>\n (shipments.data?.items ?? []).filter((s) => {\n const ts = new Date(\n s.deliveredAt ?? s.departedAt ?? s.scheduledAt ?? s.createdAt\n ).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [shipments.data, cutoff]\n );\n\n const emissionRows = useMemo(\n () =>\n (emissions.data?.items ?? []).filter((e) => {\n const ts = new Date(e.periodEnd ?? e.createdAt).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [emissions.data, cutoff]\n );\n\n /**\n * A period-over-period delta is only honest when the ENTIRE record set is on\n * hand: these lists are fetched with a fixed page size and no date filter, so\n * if the backend has more rows than one page the preceding window may be\n * partly missing and a comparison would understate it. When the connection\n * still reports `hasMore`, no delta is shown at all.\n */\n const productionComplete = production.data?.pageInfo?.hasMore === false;\n const shipmentsComplete = shipments.data?.pageInfo?.hasMore === false;\n\n const priorProductionRows = useMemo(\n () =>\n (production.data?.items ?? []).filter((r) => {\n const ts = new Date(r.periodEnd ?? r.createdAt).getTime();\n return !Number.isNaN(ts) && ts >= priorCutoff && ts < cutoff;\n }),\n [production.data, priorCutoff, cutoff]\n );\n\n const priorShipmentRows = useMemo(\n () =>\n (shipments.data?.items ?? []).filter((s) => {\n const ts = new Date(\n s.deliveredAt ?? s.departedAt ?? s.scheduledAt ?? s.createdAt\n ).getTime();\n return !Number.isNaN(ts) && ts >= priorCutoff && ts < cutoff;\n }),\n [shipments.data, priorCutoff, cutoff]\n );\n\n // ── Utilization: produced volume vs nameplate capacity over the window ─────\n // Only assets whose reporting unit matches the production record's unit are\n // counted, so utilization never mixes units. Assets with no capacity on\n // record are excluded rather than counted as 0% (which would be a lie).\n const utilization = useMemo(() => {\n const byAsset = new Map<string, { produced: number; unit: TheUnit }>();\n for (const r of productionRows) {\n const prev = byAsset.get(r.assetId);\n if (prev && prev.unit !== r.unit) continue;\n byAsset.set(r.assetId, { produced: (prev?.produced ?? 0) + (r.volume ?? 0), unit: r.unit });\n }\n\n const rows: { name: string; pct: number }[] = [];\n let totalProduced = 0;\n let totalCapacity = 0;\n for (const asset of assetRows) {\n const agg = byAsset.get(asset.id);\n if (!agg || asset.capacityPerDay <= 0 || asset.capacityUnit !== agg.unit) continue;\n const capacity = asset.capacityPerDay * windowDays;\n if (capacity <= 0) continue;\n totalProduced += agg.produced;\n totalCapacity += capacity;\n rows.push({ name: asset.name, pct: (agg.produced / capacity) * 100 });\n }\n return {\n overallPct: totalCapacity > 0 ? (totalProduced / totalCapacity) * 100 : null,\n measuredAssets: rows.length,\n top: rows.sort((a, b) => b.pct - a.pct).slice(0, 8),\n };\n }, [productionRows, assetRows, windowDays]);\n\n // ── Downtime ───────────────────────────────────────────────────────────────\n const downtimeHours = useMemo(\n () => productionRows.reduce((sum, r) => sum + (r.downtimeHours ?? 0), 0),\n [productionRows]\n );\n\n /** Percent change in recorded downtime vs the preceding window (null = not stateable). */\n const downtimeDelta = useMemo<number | null>(() => {\n if (!productionComplete) return null;\n const prior = priorProductionRows.reduce((sum, r) => sum + (r.downtimeHours ?? 0), 0);\n // A change \"from zero\" has no percentage — say nothing rather than invent ∞.\n if (prior <= 0) return null;\n return ((downtimeHours - prior) / prior) * 100;\n }, [productionComplete, priorProductionRows, downtimeHours]);\n\n // ── Production trend (monthly, dominant unit only) ─────────────────────────\n const productionByUnit = useMemo(() => volumeByUnit(productionRows), [productionRows]);\n const leadUnit = useMemo(() => dominantUnit(productionByUnit), [productionByUnit]);\n\n const trendSeries = useMemo<LabeledValue[]>(() => {\n if (!leadUnit) return [];\n const buckets = new Map<string, number>();\n for (const r of productionRows) {\n if (r.unit !== leadUnit) continue;\n const key = monthKey(r.periodEnd ?? r.createdAt);\n if (!key) continue;\n buckets.set(key, (buckets.get(key) ?? 0) + (r.volume ?? 0));\n }\n return [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ({ label: monthLabel(key), value }));\n }, [productionRows, leadUnit]);\n\n // ── Carbon intensity: tonnes CO₂e per unit produced ────────────────────────\n // Both sides of the ratio are restricted to the SAME set of assets — those\n // producing in the lead unit inside the window — so the numerator can never\n // include emissions from an asset whose output is missing from the\n // denominator. Emission records that carry no assetId cannot be attributed to\n // production and are excluded (and counted, so the caption can say so).\n const carbonIntensity = useMemo(() => {\n if (!leadUnit) return null;\n const leadAssets = new Set<string>();\n let volume = 0;\n for (const r of productionRows) {\n if (r.unit !== leadUnit) continue;\n leadAssets.add(r.assetId);\n volume += r.volume ?? 0;\n }\n if (volume <= 0 || leadAssets.size === 0) return null;\n\n let tonnes = 0;\n let attributed = 0;\n let unattributed = 0;\n for (const e of emissionRows) {\n if (!e.assetId) {\n unattributed += 1;\n continue;\n }\n if (!leadAssets.has(e.assetId)) continue;\n tonnes += e.co2eTonnes ?? 0;\n attributed += 1;\n }\n if (attributed === 0) return null;\n return {\n perUnit: tonnes / volume,\n tonnes,\n volume,\n unit: leadUnit,\n assets: leadAssets.size,\n attributed,\n unattributed,\n };\n }, [productionRows, emissionRows, leadUnit]);\n\n // ── Emissions by GHG scope (tonnes CO₂e in the window) ─────────────────────\n const emissionsByScope = useMemo<Segment[]>(() => {\n if (emissionRows.length === 0) return [];\n const totals: Record<TheEmissionScope, number> = { SCOPE_1: 0, SCOPE_2: 0, SCOPE_3: 0 };\n for (const e of emissionRows) totals[e.scope] += e.co2eTonnes ?? 0;\n return ALL_SCOPES.filter((scope) => totals[scope] > 0).map((scope) => ({\n label: formatEnumLabel(scope),\n value: totals[scope],\n color: SCOPE_COLOR[scope],\n }));\n }, [emissionRows]);\n\n const emissionsTotal = useMemo(\n () => emissionRows.reduce((sum, e) => sum + (e.co2eTonnes ?? 0), 0),\n [emissionRows]\n );\n\n const verifiedEmissions = useMemo(\n () => emissionRows.filter((e) => e.verified).length,\n [emissionRows]\n );\n\n // ── Inventory value: quantity × unit value, grouped by currency ────────────\n // Money is never summed across currencies. The headline is the currency\n // carrying the largest position; the rest are listed beside it.\n const inventoryValue = useMemo(() => {\n const byCurrency = new Map<string, number>();\n let valuedLots = 0;\n let unvaluedLots = 0;\n for (const lot of lotRows) {\n if (!ON_HAND_LOT_STATES.has(lot.status)) continue;\n if (!(lot.unitValue > 0) || !(lot.quantity > 0)) {\n unvaluedLots += 1;\n continue;\n }\n const currency = lot.currency || 'USD';\n byCurrency.set(currency, (byCurrency.get(currency) ?? 0) + lot.quantity * lot.unitValue);\n valuedLots += 1;\n }\n const ranked = [...byCurrency.entries()].sort((a, b) => b[1] - a[1]);\n return {\n lead: ranked[0] ?? null,\n others: ranked.slice(1),\n valuedLots,\n unvaluedLots,\n };\n }, [lotRows]);\n\n // ── Contract fulfilment ────────────────────────────────────────────────────\n const fulfilment = useMemo(() => {\n const live = contractRows.filter(\n (c) => c.contractedVolume > 0 && (c.status === 'ACTIVE' || c.status === 'SIGNED')\n );\n const contracted = live.reduce((s, c) => s + c.contractedVolume, 0);\n const delivered = live.reduce((s, c) => s + c.deliveredVolume, 0);\n const atRisk = live.filter((c) => {\n if (!c.endDate) return false;\n const daysLeft = (new Date(c.endDate).getTime() - evaluatedAt) / DAY_MS;\n const pct = c.deliveredVolume / c.contractedVolume;\n return daysLeft > 0 && daysLeft <= 60 && pct < 0.75;\n }).length;\n return {\n pct: contracted > 0 ? (delivered / contracted) * 100 : null,\n liveCount: live.length,\n atRisk,\n top: live\n .map((c) => ({\n label: c.reference,\n value: Math.min(100, (c.deliveredVolume / c.contractedVolume) * 100),\n }))\n .sort((a, b) => a.value - b.value)\n .slice(0, 8),\n };\n }, [contractRows, evaluatedAt]);\n\n // ── Fulfilment by counterparty ─────────────────────────────────────────────\n // Per-contract fulfilment is a unitless ratio, so counterparties whose book\n // spans several units of measure can still be compared. Ratios are averaged\n // across a counterparty's live contracts — volumes are deliberately NOT\n // summed, which would mix barrels with tonnes.\n const counterpartyFulfilment = useMemo(() => {\n const byCounterparty = new Map<string, { total: number; count: number }>();\n for (const c of contractRows) {\n if (!LIVE_CONTRACT_STATES.has(c.status) || c.contractedVolume <= 0) continue;\n const name = c.counterparty.trim() || 'Unnamed counterparty';\n const pct = Math.min(100, (c.deliveredVolume / c.contractedVolume) * 100);\n const prev = byCounterparty.get(name) ?? { total: 0, count: 0 };\n byCounterparty.set(name, { total: prev.total + pct, count: prev.count + 1 });\n }\n return [...byCounterparty.entries()]\n .map(([name, agg]) => ({ name, pct: agg.total / agg.count, contracts: agg.count }))\n .sort((a, b) => b.pct - a.pct)\n .slice(0, 8);\n }, [contractRows]);\n\n // ── Shipment mode mix + delivery rate ──────────────────────────────────────\n const shipmentMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const s of shipmentRows) {\n buckets.set(s.mode, (buckets.get(s.mode) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([mode, value]) => ({ label: formatEnumLabel(mode), value }));\n }, [shipmentRows]);\n\n const deliveryRate = useMemo(() => {\n const terminalOrDone = shipmentRows.filter((s) => s.status !== 'CANCELLED');\n if (terminalOrDone.length === 0) return null;\n const delivered = terminalOrDone.filter((s) => s.status === 'DELIVERED').length;\n return (delivered / terminalOrDone.length) * 100;\n }, [shipmentRows]);\n\n /** Change in delivery rate vs the preceding window, in percentage POINTS. */\n const deliveryRateDelta = useMemo<number | null>(() => {\n if (!shipmentsComplete || deliveryRate == null) return null;\n const terminalOrDone = priorShipmentRows.filter((s) => s.status !== 'CANCELLED');\n if (terminalOrDone.length === 0) return null;\n const delivered = terminalOrDone.filter((s) => s.status === 'DELIVERED').length;\n return deliveryRate - (delivered / terminalOrDone.length) * 100;\n }, [shipmentsComplete, priorShipmentRows, deliveryRate]);\n\n const inTransit = useMemo(\n () => shipmentRows.filter((s) => s.status === 'IN_TRANSIT' || s.status === 'LOADING').length,\n [shipmentRows]\n );\n\n // ── Inventory position ─────────────────────────────────────────────────────\n const inventoryMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const l of lotRows) {\n buckets.set(l.status, (buckets.get(l.status) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({ label: formatEnumLabel(status), value }));\n }, [lotRows]);\n\n // ── Export ─────────────────────────────────────────────────────────────────\n /**\n * Download the figures on screen as a metric/value CSV. Every row restates a\n * number this page already computed and displays — nothing is recomputed for\n * the file, so the export can never disagree with the view.\n */\n const handleExport = () => {\n const windowLabel = WINDOWS.find((w) => w.value === windowKey)?.label ?? `${windowDays} days`;\n const rows: { metric: string; value: string; detail: string }[] = [\n {\n metric: 'Window',\n value: windowLabel,\n detail: `Evaluated ${new Date(evaluatedAt).toISOString()}`,\n },\n {\n metric: 'Capacity utilization',\n value: utilization.overallPct != null ? formatPercent(utilization.overallPct) : '',\n detail: `${utilization.measuredAssets} assets with capacity on record`,\n },\n {\n metric: 'Contract fulfilment',\n value: fulfilment.pct != null ? formatPercent(fulfilment.pct) : '',\n detail: `${fulfilment.liveCount} live, ${fulfilment.atRisk} at risk`,\n },\n {\n metric: 'Delivery rate',\n value: deliveryRate != null ? formatPercent(deliveryRate) : '',\n detail:\n deliveryRateDelta != null\n ? `${Math.round(deliveryRateDelta)} pp ${PRIOR_WINDOW_LABEL}; ${inTransit} currently moving`\n : `${inTransit} currently moving`,\n },\n {\n metric: 'Recorded downtime (hours)',\n value: String(downtimeHours),\n detail:\n downtimeDelta != null\n ? `${Math.round(downtimeDelta)}% ${PRIOR_WINDOW_LABEL}; ${productionRows.length} production records`\n : `${productionRows.length} production records`,\n },\n {\n metric: carbonIntensity\n ? `Carbon intensity (t CO2e per ${formatUnit(carbonIntensity.unit)})`\n : 'Carbon intensity',\n value: carbonIntensity ? String(carbonIntensity.perUnit) : '',\n detail: carbonIntensity\n ? `${carbonIntensity.tonnes} t CO2e over ${carbonIntensity.volume} ${formatUnit(carbonIntensity.unit)} across ${carbonIntensity.assets} assets`\n : 'Not computable for this window',\n },\n {\n metric: inventoryValue.lead\n ? `Inventory on hand (${inventoryValue.lead[0]})`\n : 'Inventory on hand',\n value: inventoryValue.lead ? String(inventoryValue.lead[1]) : '',\n detail: `${inventoryValue.valuedLots} valued lots, ${inventoryValue.unvaluedLots} without a unit value`,\n },\n ...inventoryValue.others.map(([currency, total]) => ({\n metric: `Inventory on hand (${currency})`,\n value: String(total),\n detail: '',\n })),\n ...emissionsByScope.map((segment) => ({\n metric: `Emissions ${segment.label} (t CO2e)`,\n value: String(segment.value),\n detail: '',\n })),\n ...counterpartyFulfilment.map((row) => ({\n metric: `Fulfilment: ${row.name}`,\n value: formatPercent(row.pct),\n detail: `${row.contracts} live ${row.contracts === 1 ? 'contract' : 'contracts'}`,\n })),\n ];\n\n const csv = toCsv<{ metric: string; value: string; detail: string }>(\n [\n { header: 'Metric', value: (r) => r.metric },\n { header: 'Value', value: (r) => r.value },\n { header: 'Detail', value: (r) => r.detail },\n ],\n rows\n );\n const ok = downloadTextFile(`theglobalfuel-analytics-${windowDays}d.csv`, 'text/csv', csv);\n if (ok) notify.success('Analytics exported');\n else notify.error(\"This browser can't download the export.\");\n };\n\n const description = t(\n 'theglobalfuel.pages.analytics.description',\n 'Utilization, fulfilment and movement performance across the portfolio.'\n );\n const pageTitle = t('theglobalfuel.pages.analytics.title', 'Operational Analytics');\n\n if (isLoading) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <LoadingView label=\"Aggregating operational data…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <ErrorView\n message=\"Couldn't load the operational data for analytics.\"\n onRetry={() => {\n void assets.refetch();\n void production.refetch();\n void shipments.refetch();\n void contracts.refetch();\n void inventory.refetch();\n void emissions.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const hasAnything =\n assetRows.length > 0 ||\n productionRows.length > 0 ||\n contractRows.length > 0 ||\n shipmentRows.length > 0 ||\n emissionRows.length > 0 ||\n lotRows.length > 0;\n\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <FilterTabs options={WINDOWS} value={windowKey} onChange={setWindowKey} />\n <Button variant=\"outline\" onClick={handleExport} disabled={!hasAnything}>\n <Download className=\"mr-1.5 h-4 w-4\" /> Export\n </Button>\n </div>\n }\n >\n {!hasAnything ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Activity className=\"h-10 w-10\" />}\n title=\"Nothing to analyse yet\"\n description=\"Register assets and record production, contracts and shipments — analytics is computed from those records.\"\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n <StatCard\n label=\"Capacity utilization\"\n value={utilization.overallPct != null ? formatPercent(utilization.overallPct) : '—'}\n icon={<Gauge className=\"h-4 w-4\" />}\n deltaLabel={\n utilization.overallPct != null\n ? `${formatNumber(utilization.measuredAssets)} assets with capacity on record`\n : 'No asset has both capacity and matching-unit production'\n }\n />\n <StatCard\n label=\"Contract fulfilment\"\n value={fulfilment.pct != null ? formatPercent(fulfilment.pct) : '—'}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n deltaLabel={`${formatNumber(fulfilment.liveCount)} live · ${formatNumber(fulfilment.atRisk)} at risk`}\n />\n <StatCard\n label=\"Delivery rate\"\n value={deliveryRate != null ? formatPercent(deliveryRate) : '—'}\n icon={<Ship className=\"h-4 w-4\" />}\n {...(deliveryRateDelta != null\n ? { delta: Math.round(deliveryRateDelta), deltaSuffix: ' pp' }\n : {})}\n deltaLabel={\n deliveryRateDelta != null\n ? `${PRIOR_WINDOW_LABEL} · ${formatNumber(inTransit)} currently moving`\n : `${formatNumber(inTransit)} currently moving`\n }\n />\n <StatCard\n label=\"Recorded downtime\"\n value={`${formatNumber(downtimeHours)} h`}\n icon={<Timer className=\"h-4 w-4\" />}\n invertDelta\n {...(downtimeDelta != null ? { delta: Math.round(downtimeDelta) } : {})}\n deltaLabel={\n downtimeDelta != null\n ? `${PRIOR_WINDOW_LABEL} · ${formatNumber(productionRows.length)} records`\n : `Across ${formatNumber(productionRows.length)} production records`\n }\n />\n <StatCard\n label=\"Carbon intensity\"\n value={\n carbonIntensity\n ? `${formatNumber(carbonIntensity.perUnit, { maximumFractionDigits: 4 })} t CO₂e/${formatUnit(carbonIntensity.unit)}`\n : '—'\n }\n icon={<Leaf className=\"h-4 w-4\" />}\n invertDelta\n deltaLabel={\n carbonIntensity\n ? [\n `${formatTonnesCo2e(carbonIntensity.tonnes)} against ${formatVolume(carbonIntensity.volume, carbonIntensity.unit)} from ${formatNumber(carbonIntensity.assets)} assets`,\n carbonIntensity.unattributed > 0\n ? `${formatNumber(carbonIntensity.unattributed)} unattributed ${carbonIntensity.unattributed === 1 ? 'record' : 'records'} excluded`\n : null,\n ]\n .filter(Boolean)\n .join(' · ')\n : 'Needs emissions logged against an asset that also reports production'\n }\n />\n <StatCard\n label=\"Inventory on hand\"\n value={\n inventoryValue.lead\n ? formatMoney(inventoryValue.lead[1], inventoryValue.lead[0])\n : '—'\n }\n icon={<Coins className=\"h-4 w-4\" />}\n deltaLabel={\n inventoryValue.lead\n ? [\n `${formatNumber(inventoryValue.valuedLots)} valued ${inventoryValue.valuedLots === 1 ? 'lot' : 'lots'}`,\n ...inventoryValue.others.map(\n ([currency, total]) => `${formatMoney(total, currency)}`\n ),\n ].join(' · ')\n : 'No lot on hand carries a unit value'\n }\n />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5 lg:col-span-2\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Production trend</h2>\n <p className=\"text-xs text-text-tertiary\">\n {leadUnit\n ? `Monthly volume in ${formatEnumLabel(leadUnit)} — the unit carrying most of the portfolio. Other units are charted on their own assets.`\n : 'No production recorded in this window.'}\n </p>\n </div>\n {trendSeries.length > 0 ? (\n <AreaChart series={trendSeries} height={220} title=\"Monthly production volume\" />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Movement mix</h2>\n <p className=\"text-xs text-text-tertiary\">Shipments by transport mode.</p>\n </div>\n {shipmentMix.length > 0 ? (\n <DonutChart\n segments={shipmentMix}\n centerLabel={formatNumber(shipmentRows.length)}\n centerCaption=\"shipments\"\n showLegend\n title=\"Shipments by transport mode\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No shipments in this window.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Utilization by asset</h2>\n <p className=\"text-xs text-text-tertiary\">\n Produced volume against nameplate capacity for the window. Assets without a\n capacity figure, or whose production is recorded in a different unit, are omitted.\n </p>\n </div>\n {utilization.top.length > 0 ? (\n <BarChart\n series={utilization.top.map((r) => ({ label: r.name, value: Math.round(r.pct) }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Capacity utilization by asset\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No asset has both a capacity figure and matching-unit production in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Least-fulfilled contracts\n </h2>\n <p className=\"text-xs text-text-tertiary\">\n Delivered against contracted volume for live (signed/active) contracts.\n </p>\n </div>\n {fulfilment.top.length > 0 ? (\n <BarChart\n series={fulfilment.top.map((r) => ({\n label: r.label,\n value: Math.round(r.value),\n }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Contract fulfilment\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No live contracts with a contracted volume.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Emissions by scope</h2>\n <p className=\"text-xs text-text-tertiary\">\n Tonnes CO₂e logged in this window, split by GHG protocol scope.\n {emissionRows.length > 0\n ? ` ${formatNumber(verifiedEmissions)} of ${formatNumber(emissionRows.length)} records verified.`\n : ''}\n </p>\n </div>\n {emissionsByScope.length > 0 ? (\n <DonutChart\n segments={emissionsByScope}\n centerLabel={formatNumber(emissionsTotal, { maximumFractionDigits: 0 })}\n centerCaption=\"t CO₂e\"\n showLegend\n valueFormatter={(v) => formatNumber(v, { maximumFractionDigits: 1 })}\n title=\"Emissions by GHG scope\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No emissions recorded in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Fulfilment by counterparty\n </h2>\n <p className=\"text-xs text-text-tertiary\">\n Mean delivered-against-contracted across each counterparty&apos;s live\n (signed/active) contracts. Ratios are unitless, so no volumes are summed across\n units of measure.\n </p>\n </div>\n {counterpartyFulfilment.length > 0 ? (\n <ul className=\"space-y-3.5\">\n {counterpartyFulfilment.map((row) => (\n <li key={row.name}>\n <div className=\"flex items-baseline justify-between gap-3\">\n <span className=\"truncate text-sm text-text-secondary\">{row.name}</span>\n <span className=\"shrink-0 text-sm font-medium tabular-nums text-text-primary\">\n {formatPercent(row.pct)}\n </span>\n </div>\n <MiniBar\n value={row.pct}\n className=\"mt-1.5\"\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"mt-1 text-[11px] text-text-tertiary\">\n {formatNumber(row.contracts)} live{' '}\n {row.contracts === 1 ? 'contract' : 'contracts'}\n </p>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No live contracts with a contracted volume.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Package className=\"h-4 w-4 text-text-secondary\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Inventory position</h2>\n </div>\n {inventoryMix.length > 0 ? (\n <DonutChart\n segments={inventoryMix}\n centerLabel={formatNumber(lotRows.length)}\n centerCaption=\"lots\"\n showLegend\n title=\"Inventory lots by status\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No inventory lots recorded.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"p-5 lg:col-span-2\">\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">\n Production volume by unit\n </h2>\n <p className=\"mb-4 text-xs text-text-tertiary\">\n Volumes are never summed across units of measure. Each row is an independent total.\n </p>\n {productionByUnit.size > 0 ? (\n <ul className=\"divide-y divide-border-subtle\">\n {[...productionByUnit.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([unit, value]) => (\n <li key={unit} className=\"flex items-center justify-between py-2.5\">\n <span className=\"text-sm text-text-secondary\">{formatEnumLabel(unit)}</span>\n <span className=\"text-sm font-medium tabular-nums text-text-primary\">\n {formatVolume(value, unit)}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContractDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theglobalfuel/pages/contracts/ContractDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8IH,wBAAgB,kBAAkB,gCAo0BjC"}
1
+ {"version":3,"file":"ContractDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theglobalfuel/pages/contracts/ContractDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwJH,wBAAgB,kBAAkB,gCAq5BjC"}