@burdenoff/microfe-theglobalfuel 2026.716.1 → 2026.720.2

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.
Files changed (67) hide show
  1. package/dist/theglobalfuel/TheGlobalFuelRoutes.d.ts.map +1 -1
  2. package/dist/theglobalfuel/TheGlobalFuelRoutes.js +34 -22
  3. package/dist/theglobalfuel/TheGlobalFuelRoutes.js.map +1 -1
  4. package/dist/theglobalfuel/components/DataTable.js +1 -1
  5. package/dist/theglobalfuel/components/DataTable.js.map +1 -1
  6. package/dist/theglobalfuel/components/MiniBar.d.ts.map +1 -1
  7. package/dist/theglobalfuel/components/MiniBar.js +1 -1
  8. package/dist/theglobalfuel/components/MiniBar.js.map +1 -1
  9. package/dist/theglobalfuel/components/Tabs.js +1 -1
  10. package/dist/theglobalfuel/components/Tabs.js.map +1 -1
  11. package/dist/theglobalfuel/components/index.js +2 -0
  12. package/dist/theglobalfuel/components/viz/BarChart.js +83 -4
  13. package/dist/theglobalfuel/components/viz/BarChart.js.map +1 -0
  14. package/dist/theglobalfuel/components/viz/StatCard.js +60 -3
  15. package/dist/theglobalfuel/components/viz/StatCard.js.map +1 -0
  16. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts +10 -1
  17. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts.map +1 -1
  18. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js +1 -1
  19. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js.map +1 -1
  20. package/dist/theglobalfuel/pages/alerts/AlertsPage.d.ts +16 -0
  21. package/dist/theglobalfuel/pages/alerts/AlertsPage.d.ts.map +1 -0
  22. package/dist/theglobalfuel/pages/alerts/AlertsPage.js +295 -0
  23. package/dist/theglobalfuel/pages/alerts/AlertsPage.js.map +1 -0
  24. package/dist/theglobalfuel/pages/analytics/AnalyticsPage.d.ts +13 -0
  25. package/dist/theglobalfuel/pages/analytics/AnalyticsPage.d.ts.map +1 -0
  26. package/dist/theglobalfuel/pages/analytics/AnalyticsPage.js +358 -0
  27. package/dist/theglobalfuel/pages/analytics/AnalyticsPage.js.map +1 -0
  28. package/dist/theglobalfuel/pages/assets/AssetDetailPage.d.ts.map +1 -1
  29. package/dist/theglobalfuel/pages/assets/AssetDetailPage.js +386 -342
  30. package/dist/theglobalfuel/pages/assets/AssetDetailPage.js.map +1 -1
  31. package/dist/theglobalfuel/pages/assets/AssetsPage.d.ts.map +1 -1
  32. package/dist/theglobalfuel/pages/assets/AssetsPage.js +277 -199
  33. package/dist/theglobalfuel/pages/assets/AssetsPage.js.map +1 -1
  34. package/dist/theglobalfuel/pages/contracts/ContractsPage.js +1 -1
  35. package/dist/theglobalfuel/pages/contracts/ContractsPage.js.map +1 -1
  36. package/dist/theglobalfuel/pages/dashboard/DashboardPage.js +6 -6
  37. package/dist/theglobalfuel/pages/dashboard/DashboardPage.js.map +1 -1
  38. package/dist/theglobalfuel/pages/emissions/EmissionsPage.js +1 -1
  39. package/dist/theglobalfuel/pages/emissions/EmissionsPage.js.map +1 -1
  40. package/dist/theglobalfuel/pages/inventory/InventoryPage.js +1 -1
  41. package/dist/theglobalfuel/pages/inventory/InventoryPage.js.map +1 -1
  42. package/dist/theglobalfuel/pages/provenance/ProvenancePage.d.ts.map +1 -1
  43. package/dist/theglobalfuel/pages/provenance/ProvenancePage.js +372 -216
  44. package/dist/theglobalfuel/pages/provenance/ProvenancePage.js.map +1 -1
  45. package/dist/theglobalfuel/pages/reports/ReportsPage.d.ts +16 -0
  46. package/dist/theglobalfuel/pages/reports/ReportsPage.d.ts.map +1 -0
  47. package/dist/theglobalfuel/pages/reports/ReportsPage.js +578 -0
  48. package/dist/theglobalfuel/pages/reports/ReportsPage.js.map +1 -0
  49. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.d.ts.map +1 -1
  50. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js +43 -12
  51. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js.map +1 -1
  52. package/dist/theglobalfuel/types/index.d.ts +37 -8
  53. package/dist/theglobalfuel/types/index.d.ts.map +1 -1
  54. package/dist/theglobalfuel/utils/csv.d.ts +16 -0
  55. package/dist/theglobalfuel/utils/csv.d.ts.map +1 -0
  56. package/dist/theglobalfuel/utils/csv.js +14 -0
  57. package/dist/theglobalfuel/utils/csv.js.map +1 -0
  58. package/dist/theglobalfuel/utils/index.d.ts +2 -0
  59. package/dist/theglobalfuel/utils/index.d.ts.map +1 -1
  60. package/dist/theglobalfuel/utils/pageContext.d.ts.map +1 -1
  61. package/dist/theglobalfuel/utils/pageContext.js +32 -0
  62. package/dist/theglobalfuel/utils/pageContext.js.map +1 -1
  63. package/dist/theglobalfuel/utils/provenance.d.ts +135 -0
  64. package/dist/theglobalfuel/utils/provenance.d.ts.map +1 -0
  65. package/dist/theglobalfuel/utils/provenance.js +57 -0
  66. package/dist/theglobalfuel/utils/provenance.js.map +1 -0
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — TheGlobalFuel resources operating-system overview.\n * @module theglobalfuel/pages/dashboard\n *\n * The hero landing (eager-loaded). Every figure is aggregated client-side from\n * the real `the*` list endpoints — no mock data. The page opens with the domain\n * KPI grid (registry / commerce / sustainability), then a rich analytics band:\n * - a PRODUCTION TREND area chart (metered output bucketed by month),\n * - a COMMODITY MIX donut (share of assets by commodity), and\n * - an EMISSIONS-BY-SCOPE donut (tonnes CO₂e per GHG scope);\n * a COMMODITY PORTFOLIO grid (one glass card per commodity present, with real\n * asset counts, summed capacity and a production sparkline); an ASSETS-BY-\n * LIFECYCLE distribution; and recent-activity tables. All charts derive from\n * the fetched arrays and degrade to pre-launch empty states.\n */\n\nimport { useMemo, type ReactNode } from 'react';\nimport {\n Layers,\n ScrollText,\n Ship,\n Factory,\n Boxes,\n ShieldCheck,\n ArrowRight,\n ArrowUpRight,\n Fuel,\n GitBranch,\n Gauge,\n Activity,\n ClipboardList,\n} from 'lucide-react';\nimport { GlassCard, Spinner } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { MiniBar } from '../../components/MiniBar';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport {\n AreaChart,\n DonutChart,\n Sparkline,\n chartPalette,\n type LabeledValue,\n type Segment,\n} from '../../components/viz';\nimport { cn } from '../../utils/cn';\nimport { useTheGlobalFuel } from '../../context';\nimport {\n useTheAssets,\n useTheContracts,\n useTheShipments,\n useTheEmissionRecords,\n useTheInventoryLots,\n useTheComplianceCerts,\n useTheProductionRecords,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport {\n formatNumber,\n formatPercent,\n formatDate,\n formatRelativeTime,\n formatEnumLabel,\n formatVolume,\n formatTonnesCo2e,\n isExpired,\n} from '../../utils/formatters';\nimport type {\n TheAssetStatus,\n TheCommodityType,\n TheEmissionScope,\n TheOfftakeContract,\n TheProductionRecord,\n} from '../../types';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst EXPIRY_WINDOW_DAYS = 30;\n\n/** True when a cert expires within the next 30 days (but hasn't yet). */\nconst isExpiringSoon = (date: string | null | undefined): boolean => {\n if (!date) return false;\n const t = new Date(date).getTime();\n if (Number.isNaN(t)) return false;\n const diff = t - Date.now();\n return diff >= 0 && diff <= EXPIRY_WINDOW_DAYS * DAY_MS;\n};\n\n/** Asset lifecycle → MiniBar fill token (semantic, maturity-ordered). */\nconst ASSET_STATUS_BAR: Record<TheAssetStatus, string> = {\n EXPLORATION: 'bg-[var(--color-accent-indigo)]',\n APPRAISAL: 'bg-[var(--color-accent-blue)]',\n DEVELOPMENT: 'bg-[var(--color-accent-teal)]',\n PRODUCING: 'bg-status-success-text',\n SUSPENDED: 'bg-status-warning-text',\n DECOMMISSIONING: 'bg-status-error-text',\n RETIRED: 'bg-[var(--color-text-tertiary)]',\n};\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// ── Month bucketing (UTC-stable, gap-free) ───────────────────────────────────\n\n/** UTC year-month key (e.g. \"2026-07\"). */\nconst monthKey = (d: Date): string =>\n `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n\nconst MONTH_FMT = new Intl.DateTimeFormat(undefined, {\n month: 'short',\n year: '2-digit',\n timeZone: 'UTC',\n});\n\nconst monthLabel = (year: number, month1: number): string =>\n MONTH_FMT.format(new Date(Date.UTC(year, month1 - 1, 1)));\n\n/** Sum `valueOf` into UTC-month buckets keyed by the `dateOf` field. */\nfunction bucketByMonth<T>(\n rows: readonly T[],\n dateOf: (row: T) => string | null | undefined,\n valueOf: (row: T) => number\n): Map<string, number> {\n const buckets = new Map<string, number>();\n for (const row of rows) {\n const raw = dateOf(row);\n if (!raw) continue;\n const d = new Date(raw);\n if (Number.isNaN(d.getTime())) continue;\n const key = monthKey(d);\n buckets.set(key, (buckets.get(key) ?? 0) + valueOf(row));\n }\n return buckets;\n}\n\n/**\n * Expand month buckets into a gap-free ordered series from the earliest to the\n * latest populated month (so a trend line stays continuous), then keep only the\n * most recent `maxMonths` so a stray old record can't blow out the axis.\n */\nfunction monthlySeries(buckets: Map<string, number>, maxMonths = 18): LabeledValue[] {\n const keys = [...buckets.keys()].sort();\n if (keys.length === 0) return [];\n const parse = (k: string): [number, number] => {\n const [y, m] = k.split('-');\n return [Number(y), Number(m)];\n };\n const [maxY, maxM] = parse(keys[keys.length - 1]);\n let [y, m] = parse(keys[0]);\n const series: LabeledValue[] = [];\n let guard = 0;\n while ((y < maxY || (y === maxY && m <= maxM)) && guard < 240) {\n series.push({\n label: monthLabel(y, m),\n value: buckets.get(`${y}-${String(m).padStart(2, '0')}`) ?? 0,\n });\n m += 1;\n if (m > 12) {\n m = 1;\n y += 1;\n }\n guard += 1;\n }\n return series.length > maxMonths ? series.slice(series.length - maxMonths) : series;\n}\n\n/** Group rows by a discriminator, sorted by descending count, with % of total. */\nfunction distribution<T>(\n rows: T[],\n keyOf: (row: T) => string\n): { key: string; count: number; pct: number }[] {\n const counts = new Map<string, number>();\n for (const row of rows) {\n const k = keyOf(row);\n counts.set(k, (counts.get(k) ?? 0) + 1);\n }\n const total = rows.length;\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([key, count]) => ({ key, count, pct: total ? (count / total) * 100 : 0 }));\n}\n\n// ── Distribution card (MiniBar rows) ─────────────────────────────────────────\n\ninterface DistBar {\n key: string;\n label: ReactNode;\n /** Pre-formatted primary figure shown right of the bar. */\n value: string;\n /** 0–100 — drives the bar width + the muted percentage. */\n pct: number;\n barClassName: string;\n}\n\ninterface DistributionCardProps {\n title: string;\n description: string;\n icon: ReactNode;\n linkLabel: string;\n onLink: () => void;\n isLoading: boolean;\n isError: boolean;\n onRetry: () => void;\n emptyLabel: string;\n bars: DistBar[];\n footer?: ReactNode;\n className?: string;\n}\n\n/** A titled glass card that renders a compact set of MiniBar distribution rows. */\nfunction DistributionCard({\n title,\n description,\n icon,\n linkLabel,\n onLink,\n isLoading,\n isError,\n onRetry,\n emptyLabel,\n bars,\n footer,\n className,\n}: DistributionCardProps) {\n return (\n <GlassCard treatment=\"glass\" className={cn('flex flex-col p-5', className)}>\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n {icon}\n </span>\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">{description}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onLink}\n className=\"inline-flex shrink-0 items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n {linkLabel} <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n\n {isLoading ? (\n <div className=\"flex items-center gap-2 py-8 text-sm text-text-secondary\">\n <Spinner size=\"sm\" /> Loading…\n </div>\n ) : isError ? (\n <div className=\"flex flex-col items-start gap-2 py-6\">\n <p className=\"text-sm text-status-error-text\">\n Couldn&apos;t load {title.toLowerCase()}.\n </p>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Retry\n </button>\n </div>\n ) : bars.length === 0 ? (\n <div className=\"flex flex-1 flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-8 text-center\">\n <p className=\"text-sm text-text-secondary\">{emptyLabel}</p>\n </div>\n ) : (\n <div className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"space-y-3\">\n {bars.map((bar) => (\n <div key={bar.key} className=\"flex items-center gap-3\">\n <div className=\"w-24 shrink-0 truncate text-sm text-text-secondary sm:w-28\">\n {bar.label}\n </div>\n <MiniBar\n value={bar.pct}\n barClassName={bar.barClassName}\n className=\"flex-1\"\n height={8}\n />\n <span className=\"w-16 shrink-0 whitespace-nowrap text-right text-sm font-medium tabular-nums text-text-primary\">\n {bar.value}\n </span>\n <span className=\"hidden w-11 shrink-0 text-right text-xs tabular-nums text-text-tertiary sm:inline\">\n {formatPercent(bar.pct)}\n </span>\n </div>\n ))}\n </div>\n {footer && (\n <p className=\"border-t border-border-seam/70 pt-3 text-xs text-text-tertiary\">\n {footer}\n </p>\n )}\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ── Chart card (titled glass panel for a chart body) ─────────────────────────\n\ninterface ChartCardProps {\n title: string;\n caption: string;\n icon: ReactNode;\n link?: { label: string; onClick: () => void };\n isLoading: boolean;\n isError: boolean;\n onRetry: () => void;\n isEmpty: boolean;\n emptyLabel: string;\n footer?: ReactNode;\n className?: string;\n children: ReactNode;\n}\n\n/** A titled glass card wrapping a viz-kit chart, with loading/error/empty states. */\nfunction ChartCard({\n title,\n caption,\n icon,\n link,\n isLoading,\n isError,\n onRetry,\n isEmpty,\n emptyLabel,\n footer,\n className,\n children,\n}: ChartCardProps) {\n return (\n <GlassCard treatment=\"glass\" className={cn('flex flex-col p-5', className)}>\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n {icon}\n </span>\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">{caption}</p>\n </div>\n </div>\n {link ? (\n <button\n type=\"button\"\n onClick={link.onClick}\n className=\"inline-flex shrink-0 items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n {link.label} <ArrowRight className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n\n {isLoading ? (\n <div className=\"flex flex-1 items-center gap-2 py-8 text-sm text-text-secondary\">\n <Spinner size=\"sm\" /> Loading…\n </div>\n ) : isError ? (\n <div className=\"flex flex-1 flex-col items-start gap-2 py-6\">\n <p className=\"text-sm text-status-error-text\">\n Couldn&apos;t load {title.toLowerCase()}.\n </p>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Retry\n </button>\n </div>\n ) : isEmpty ? (\n <div className=\"flex flex-1 flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-10 text-center\">\n <p className=\"text-sm text-text-secondary\">{emptyLabel}</p>\n </div>\n ) : (\n <div className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"flex-1\">{children}</div>\n {footer ? (\n <p className=\"border-t border-border-seam/70 pt-3 text-xs text-text-tertiary\">\n {footer}\n </p>\n ) : null}\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ── Commodity portfolio card ─────────────────────────────────────────────────\n\ninterface CommodityPortfolioEntry {\n key: TheCommodityType;\n name: string;\n /** Tokenized commodity color, shared with the commodity-mix donut. */\n color: string;\n assetCount: number;\n producing: number;\n capacity: number;\n /** Dominant `TheUnit` token across the commodity's assets. */\n unit: string;\n /** Monthly production volumes (oldest first) — empty when none logged. */\n spark: number[];\n}\n\nfunction CommodityCard({ card, onOpen }: { card: CommodityPortfolioEntry; onOpen: () => void }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-0\">\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${card.name} assets`}\n className=\"flex w-full flex-col gap-4 rounded-[var(--radius-card)] p-5 text-left outline-none transition-transform duration-200 hover:-translate-y-0.5 focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2.5\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: card.color }}\n />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{card.name}</p>\n <p className=\"text-xs text-text-tertiary\">\n {card.assetCount} {card.assetCount === 1 ? 'asset' : 'assets'}\n </p>\n </div>\n </div>\n <ArrowUpRight className=\"h-4 w-4 shrink-0 text-text-tertiary\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-[11px] uppercase tracking-wide text-text-tertiary\">Capacity/day</p>\n <p className=\"mt-0.5 truncate text-sm font-semibold tabular-nums text-text-primary\">\n {card.capacity > 0 ? formatVolume(card.capacity, card.unit) : '—'}\n </p>\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] uppercase tracking-wide text-text-tertiary\">Producing</p>\n <p className=\"mt-0.5 text-sm font-semibold tabular-nums text-text-primary\">\n {card.producing}{' '}\n <span className=\"text-xs font-normal text-text-tertiary\">of {card.assetCount}</span>\n </p>\n </div>\n </div>\n\n {card.spark.length >= 2 ? (\n <div>\n <Sparkline\n data={card.spark}\n variant=\"area\"\n height={40}\n label={`${card.name} monthly production`}\n />\n <p className=\"mt-1 text-[11px] text-text-tertiary\">Monthly output</p>\n </div>\n ) : (\n <p className=\"text-xs text-text-tertiary\">No production logged yet.</p>\n )}\n </button>\n </GlassCard>\n );\n}\n\nexport function DashboardPage() {\n useThePageView('dashboard');\n const { navigateTo, currentUser } = useTheGlobalFuel();\n\n // Real backend queries. Aggregate domains fetch a generous page (200) so the\n // charts + portfolio reflect the whole book; commerce lists stay at 50.\n const assets = useTheAssets({ pagination: { limit: 200 } });\n const contracts = useTheContracts({ pagination: { limit: 50 } });\n const shipments = useTheShipments(undefined, 'IN_TRANSIT');\n const emissions = useTheEmissionRecords({ pagination: { limit: 200 } });\n const inventory = useTheInventoryLots({ pagination: { limit: 50 } });\n const compliance = useTheComplianceCerts();\n const production = useTheProductionRecords({ pagination: { limit: 200 } });\n\n const assetRows = useMemo(() => assets.data?.items ?? [], [assets.data]);\n const contractRows = useMemo(() => contracts.data?.items ?? [], [contracts.data]);\n const inTransitRows = useMemo(() => shipments.data?.items ?? [], [shipments.data]);\n const emissionRows = useMemo(() => emissions.data?.items ?? [], [emissions.data]);\n const inventoryRows = useMemo(() => inventory.data?.items ?? [], [inventory.data]);\n const certRows = useMemo(() => compliance.data?.items ?? [], [compliance.data]);\n const productionRows = useMemo(() => production.data?.items ?? [], [production.data]);\n\n // ── KPI aggregates ─────────────────────────────────────────────────────────\n const producingCount = assetRows.filter((a) => a.status === 'PRODUCING').length;\n const activeContracts = contractRows.filter((c) => c.status === 'ACTIVE').length;\n const totalTonnes = emissionRows.reduce((sum, e) => sum + (e.co2eTonnes ?? 0), 0);\n const verifiedEmissions = emissionRows.filter((e) => e.verified).length;\n const availableLots = inventoryRows.filter((l) => l.status === 'AVAILABLE').length;\n const totalProductionVolume = productionRows.reduce((sum, p) => sum + (p.volume ?? 0), 0);\n\n const pendingCerts = certRows.filter((c) => c.status === 'PENDING').length;\n const expiringCerts = certRows.filter(\n (c) => c.expiresAt != null && (isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt))\n ).length;\n const attentionCerts = certRows.filter(\n (c) =>\n c.status === 'PENDING' ||\n (c.expiresAt != null && (isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt)))\n ).length;\n\n const withPlus = (rows: unknown[], hasMore: boolean | undefined): string =>\n hasMore ? `${rows.length}+` : formatNumber(rows.length);\n\n // ── Production trend (metered output by month) ───────────────────────────────\n const productionSeries = useMemo<LabeledValue[]>(\n () =>\n monthlySeries(\n bucketByMonth(\n productionRows,\n (p) => p.periodEnd,\n (p) => p.volume ?? 0\n )\n ),\n [productionRows]\n );\n\n // ── Commodity distribution + shared per-commodity colors ─────────────────────\n const commodityDist = useMemo(() => distribution(assetRows, (a) => a.commodity), [assetRows]);\n\n const commodityColor = useMemo(() => {\n const palette = chartPalette(commodityDist.length);\n const map = new Map<string, string>();\n commodityDist.forEach((d, i) => map.set(d.key, palette[i]));\n return map;\n }, [commodityDist]);\n\n const commodityMix = useMemo<Segment[]>(\n () =>\n commodityDist.map((d) => ({\n label: formatEnumLabel(d.key),\n value: d.count,\n color: commodityColor.get(d.key),\n })),\n [commodityDist, commodityColor]\n );\n\n // ── Emissions by scope (tonnes CO₂e) ─────────────────────────────────────────\n const scopeMix = 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((s) => totals[s] > 0).map((s) => ({\n label: formatEnumLabel(s),\n value: totals[s],\n color: SCOPE_COLOR[s],\n }));\n }, [emissionRows]);\n\n // ── Assets by lifecycle (MiniBar rows) ───────────────────────────────────────\n const lifecycleBars = useMemo<DistBar[]>(\n () =>\n distribution(assetRows, (a) => a.status).map((d) => ({\n key: d.key,\n label: <StatusBadge status={d.key} />,\n value: formatNumber(d.count),\n pct: d.pct,\n barClassName: ASSET_STATUS_BAR[d.key as TheAssetStatus] ?? 'bg-action-primary-bg',\n })),\n [assetRows]\n );\n\n // ── Commodity portfolio (one entry per commodity with assets) ────────────────\n const assetCommodity = useMemo(() => {\n const m = new Map<string, TheCommodityType>();\n for (const a of assetRows) m.set(a.id, a.commodity);\n return m;\n }, [assetRows]);\n\n const commodityCards = useMemo<CommodityPortfolioEntry[]>(() => {\n if (commodityDist.length === 0) return [];\n\n // Per-commodity asset aggregates.\n interface Agg {\n count: number;\n producing: number;\n capacity: number;\n units: Map<string, number>;\n }\n const agg = new Map<string, Agg>();\n for (const a of assetRows) {\n const cur = agg.get(a.commodity) ?? {\n count: 0,\n producing: 0,\n capacity: 0,\n units: new Map<string, number>(),\n };\n cur.count += 1;\n if (a.status === 'PRODUCING') cur.producing += 1;\n cur.capacity += a.capacityPerDay ?? 0;\n if (a.capacityUnit) cur.units.set(a.capacityUnit, (cur.units.get(a.capacityUnit) ?? 0) + 1);\n agg.set(a.commodity, cur);\n }\n\n // Per-commodity monthly production buckets → sparkline series.\n const prodBuckets = new Map<string, Map<string, number>>();\n for (const p of productionRows) {\n const commodity = assetCommodity.get(p.assetId);\n if (!commodity) continue;\n const d = new Date(p.periodEnd);\n if (Number.isNaN(d.getTime())) continue;\n const mk = monthKey(d);\n let bucket = prodBuckets.get(commodity);\n if (!bucket) {\n bucket = new Map<string, number>();\n prodBuckets.set(commodity, bucket);\n }\n bucket.set(mk, (bucket.get(mk) ?? 0) + (p.volume ?? 0));\n }\n\n const dominantUnit = (units: Map<string, number>): string => {\n let best = '';\n let bestN = -1;\n for (const [unit, n] of units) {\n if (n > bestN) {\n best = unit;\n bestN = n;\n }\n }\n return best;\n };\n\n return commodityDist.map((d) => {\n const a = agg.get(d.key) ?? {\n count: 0,\n producing: 0,\n capacity: 0,\n units: new Map<string, number>(),\n };\n const bucket = prodBuckets.get(d.key);\n const spark = bucket ? monthlySeries(bucket, 12).map((s) => s.value) : [];\n return {\n key: d.key as TheCommodityType,\n name: formatEnumLabel(d.key),\n color: commodityColor.get(d.key) ?? 'var(--color-accent-primary)',\n assetCount: a.count,\n producing: a.producing,\n capacity: a.capacity,\n unit: dominantUnit(a.units),\n spark,\n };\n });\n }, [assetRows, productionRows, assetCommodity, commodityDist, commodityColor]);\n\n // ── Recent activity ────────────────────────────────────────────────────────\n const assetName = useMemo(() => {\n const m = new Map<string, string>();\n for (const a of assetRows) m.set(a.id, a.name);\n return m;\n }, [assetRows]);\n\n const recentContracts = useMemo(\n () =>\n [...contractRows]\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())\n .slice(0, 6),\n [contractRows]\n );\n\n const recentProduction = useMemo(\n () =>\n [...productionRows]\n .sort((a, b) => new Date(b.periodEnd).getTime() - new Date(a.periodEnd).getTime())\n .slice(0, 6),\n [productionRows]\n );\n\n const contractColumns: Column<TheOfftakeContract>[] = [\n {\n key: 'reference',\n header: 'Reference',\n render: (c) => <span className=\"font-medium text-text-primary\">{c.reference}</span>,\n },\n {\n key: 'counterparty',\n header: 'Counterparty',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{c.counterparty}</span>,\n },\n {\n key: 'commodity',\n header: 'Commodity',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.commodity)}</span>,\n },\n { key: 'side', header: 'Side', render: (c) => <StatusBadge status={c.side} /> },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n hideOnMobile: true,\n render: (c) => (\n <span className=\"tabular-nums text-text-secondary\">\n {formatVolume(c.contractedVolume, c.unit)}\n </span>\n ),\n },\n { key: 'status', header: 'Status', render: (c) => <StatusBadge status={c.status} /> },\n {\n key: 'created',\n header: 'Created',\n align: 'right',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-tertiary\">{formatRelativeTime(c.createdAt)}</span>,\n },\n ];\n\n const productionColumns: Column<TheProductionRecord>[] = [\n {\n key: 'asset',\n header: 'Asset',\n render: (p) => (\n <span className=\"font-medium text-text-primary\">\n {assetName.get(p.assetId) ?? 'Unknown asset'}\n </span>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n hideOnMobile: true,\n render: (p) => (\n <span className=\"text-text-secondary\">\n {formatDate(p.periodStart)} – {formatDate(p.periodEnd)}\n </span>\n ),\n },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n render: (p) => (\n <span className=\"tabular-nums font-medium text-text-primary\">\n {formatVolume(p.volume, p.unit)}\n </span>\n ),\n },\n {\n key: 'downtime',\n header: 'Downtime',\n align: 'right',\n hideOnMobile: true,\n render: (p) => (\n <span className=\"tabular-nums text-text-secondary\">{formatNumber(p.downtimeHours)} h</span>\n ),\n },\n {\n key: 'logged',\n header: 'Logged',\n align: 'right',\n hideOnMobile: true,\n render: (p) => <span className=\"text-text-tertiary\">{formatRelativeTime(p.createdAt)}</span>,\n },\n ];\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n const t = useTheTranslation();\n const description = t(\n 'theglobalfuel.pages.dashboard.subtitle',\n 'Welcome back, {name} — your energy, mineral & commodity resource control room.',\n { name: greetingName }\n );\n\n // Full-page loader only while every primary domain is still loading its first page.\n const initialLoading =\n assets.isLoading &&\n contracts.isLoading &&\n emissions.isLoading &&\n inventory.isLoading &&\n compliance.isLoading;\n\n const pageTitle = t('theglobalfuel.pages.dashboard.title', 'TheGlobalFuel');\n\n if (initialLoading) {\n return (\n <PageLayout\n title={pageTitle}\n titleGradient\n aurora\n description={description}\n dataTour=\"theglobalfuel-dashboard-page\"\n >\n <LoadingView label=\"Loading your resource portfolio…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={pageTitle}\n titleGradient\n aurora\n description={description}\n dataTour=\"theglobalfuel-dashboard-page\"\n >\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n icon={<Layers className=\"h-5 w-5\" />}\n value={withPlus(assetRows, assets.data?.pageInfo?.hasMore)}\n label=\"Resource assets\"\n featured\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={assetRows.length ? (producingCount / assetRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{producingCount}</span> producing\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<ScrollText className=\"h-5 w-5\" />}\n value={withPlus(contractRows, contracts.data?.pageInfo?.hasMore)}\n label=\"Offtake contracts\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={contractRows.length ? (activeContracts / contractRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{activeContracts}</span> active\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<Ship className=\"h-5 w-5\" />}\n value={shipments.isError ? '—' : formatNumber(inTransitRows.length)}\n label=\"Shipments in transit\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n {inTransitRows.length > 0 ? 'In motion across carriers' : 'No active movements'}\n </p>\n }\n />\n\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={emissions.isError ? '—' : formatTonnesCo2e(totalTonnes)}\n label=\"Emissions logged\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={emissionRows.length ? (verifiedEmissions / emissionRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{verifiedEmissions}</span>{' '}\n verified\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<Boxes className=\"h-5 w-5\" />}\n value={inventory.isError ? '—' : formatNumber(availableLots)}\n label=\"Inventory available\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={inventoryRows.length ? (availableLots / inventoryRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {withPlus(inventoryRows, inventory.data?.pageInfo?.hasMore)}\n </span>{' '}\n lots total\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<ShieldCheck className=\"h-5 w-5\" />}\n value={compliance.isError ? '—' : formatNumber(attentionCerts)}\n label=\"Certs need attention\"\n accent={attentionCerts > 0 ? 'warning' : certRows.length > 0 ? 'success' : undefined}\n footer={\n <p\n className={\n attentionCerts > 0\n ? 'text-xs font-medium text-status-warning-text'\n : 'text-xs text-text-tertiary'\n }\n >\n {certRows.length === 0\n ? 'No certifications tracked yet'\n : attentionCerts > 0\n ? `${pendingCerts} pending · ${expiringCerts} expiring`\n : 'All certifications current'}\n </p>\n }\n />\n </div>\n\n {/* Analytics — production trend + commodity mix (real aggregates) */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <ChartCard\n className=\"lg:col-span-2\"\n title=\"Production trend\"\n caption=\"Metered output aggregated by month across your producing assets.\"\n icon={<Activity className=\"h-4 w-4\" />}\n link={{ label: 'Production', onClick: () => navigateTo('production') }}\n isLoading={production.isLoading}\n isError={production.isError}\n onRetry={() => production.refetch()}\n isEmpty={productionSeries.length === 0}\n emptyLabel=\"No production logged yet — record output to build the trend.\"\n footer={\n productionSeries.length > 0\n ? `${formatNumber(totalProductionVolume, { maximumFractionDigits: 0 })} total volume across ${withPlus(productionRows, production.data?.pageInfo?.hasMore)} records`\n : undefined\n }\n >\n <AreaChart series={productionSeries} height={220} title=\"Monthly production volume\" />\n </ChartCard>\n\n <ChartCard\n title=\"Commodity mix\"\n caption=\"Share of assets across your commodity streams.\"\n icon={<Fuel className=\"h-4 w-4\" />}\n link={{ label: 'Assets', onClick: () => navigateTo('assets') }}\n isLoading={assets.isLoading}\n isError={assets.isError}\n onRetry={() => assets.refetch()}\n isEmpty={commodityMix.length === 0}\n emptyLabel=\"No assets registered yet.\"\n footer={\n commodityMix.length > 0\n ? `${withPlus(assetRows, assets.data?.pageInfo?.hasMore)} assets · ${commodityMix.length} ${commodityMix.length === 1 ? 'commodity' : 'commodities'}`\n : undefined\n }\n >\n <div className=\"flex items-center justify-center py-2\">\n <DonutChart\n segments={commodityMix}\n size={168}\n centerLabel={formatNumber(assetRows.length)}\n centerCaption=\"assets\"\n valueFormatter={(v) => formatNumber(v)}\n title=\"Assets by commodity\"\n />\n </div>\n </ChartCard>\n </div>\n\n {/* Commodity portfolio — one model, every commodity */}\n <Section\n title=\"Commodity portfolio\"\n description=\"One model, every commodity — assets, capacity and output across your book.\"\n >\n {assets.isLoading ? (\n <LoadingView label=\"Loading commodities…\" />\n ) : assets.isError ? (\n <ErrorView\n message=\"Couldn't load your commodity portfolio.\"\n onRetry={() => assets.refetch()}\n />\n ) : commodityCards.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Layers className=\"h-10 w-10\" />}\n title=\"No commodities yet\"\n description=\"Register your first resource asset to build out your commodity portfolio.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n {commodityCards.map((card) => (\n <CommodityCard key={card.key} card={card} onOpen={() => navigateTo('assets')} />\n ))}\n </div>\n )}\n </Section>\n\n {/* Lifecycle distribution + emissions by scope */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <DistributionCard\n title=\"Assets by lifecycle\"\n description=\"Where the portfolio sits from exploration to retirement.\"\n icon={<GitBranch className=\"h-4 w-4\" />}\n linkLabel=\"Assets\"\n onLink={() => navigateTo('assets')}\n isLoading={assets.isLoading}\n isError={assets.isError}\n onRetry={() => assets.refetch()}\n emptyLabel=\"No assets to profile yet.\"\n bars={lifecycleBars}\n footer={\n assetRows.length > 0\n ? `${producingCount} of ${withPlus(assetRows, assets.data?.pageInfo?.hasMore)} currently producing`\n : undefined\n }\n />\n\n <ChartCard\n title=\"Emissions by scope\"\n caption=\"GHG Protocol Scope 1 / 2 / 3 — tonnes CO₂e logged.\"\n icon={<Gauge className=\"h-4 w-4\" />}\n link={{ label: 'Emissions', onClick: () => navigateTo('emissions') }}\n isLoading={emissions.isLoading}\n isError={emissions.isError}\n onRetry={() => emissions.refetch()}\n isEmpty={scopeMix.length === 0}\n emptyLabel=\"No emission records logged yet.\"\n footer={\n emissionRows.length > 0\n ? `${formatTonnesCo2e(totalTonnes)} across ${withPlus(emissionRows, emissions.data?.pageInfo?.hasMore)} records`\n : undefined\n }\n >\n <div className=\"flex items-center justify-center py-2\">\n <DonutChart\n segments={scopeMix}\n size={168}\n centerLabel={formatNumber(totalTonnes, {\n notation: 'compact',\n maximumFractionDigits: 1,\n })}\n centerCaption=\"t CO₂e\"\n valueFormatter={(v) =>\n `${formatNumber(v, { maximumFractionDigits: v >= 100 ? 0 : 1 })} t`\n }\n title=\"Emissions by scope\"\n />\n </div>\n </ChartCard>\n </div>\n\n {/* Recent offtake contracts */}\n <Section\n title=\"Recent offtake contracts\"\n description=\"Latest buy/sell agreements across your commodity book.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('contracts')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All contracts <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {contracts.isLoading ? (\n <LoadingView label=\"Loading contracts…\" />\n ) : contracts.isError ? (\n <ErrorView message=\"Couldn't load contracts.\" onRetry={() => contracts.refetch()} />\n ) : recentContracts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ScrollText className=\"h-10 w-10\" />}\n title=\"No contracts yet\"\n description=\"Draft your first offtake contract to start tracking commodity trade.\"\n />\n ) : (\n <DataTable\n columns={contractColumns}\n rows={recentContracts}\n rowKey={(c) => c.id}\n onRowClick={(c) => navigateTo(`contracts/${c.id}`)}\n rowLabel={(c) => `Open contract ${c.reference}`}\n />\n )}\n </Section>\n\n {/* Recent production */}\n <Section\n title=\"Recent production\"\n description=\"Latest metered output captured against your producing assets.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('production')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All production <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {production.isLoading ? (\n <LoadingView label=\"Loading production…\" />\n ) : production.isError ? (\n <ErrorView\n message=\"Couldn't load production records.\"\n onRetry={() => production.refetch()}\n />\n ) : recentProduction.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title=\"No production logged yet\"\n description=\"Record production against an asset to build its output history.\"\n />\n ) : (\n <DataTable\n columns={productionColumns}\n rows={recentProduction}\n rowKey={(p) => p.id}\n onRowClick={(p) => navigateTo(`assets/${p.assetId}`)}\n rowLabel={(p) => `Open ${assetName.get(p.assetId) ?? 'asset'}`}\n />\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — TheGlobalFuel resources operating-system overview.\n * @module theglobalfuel/pages/dashboard\n *\n * The hero landing (eager-loaded). Every figure is aggregated client-side from\n * the real `the*` list endpoints — no mock data. The page opens with the domain\n * KPI grid (registry / commerce / sustainability), then a rich analytics band:\n * - a PRODUCTION TREND area chart (metered output bucketed by month),\n * - a COMMODITY MIX donut (share of assets by commodity), and\n * - an EMISSIONS-BY-SCOPE donut (tonnes CO₂e per GHG scope);\n * a COMMODITY PORTFOLIO grid (one glass card per commodity present, with real\n * asset counts, summed capacity and a production sparkline); an ASSETS-BY-\n * LIFECYCLE distribution; and recent-activity tables. All charts derive from\n * the fetched arrays and degrade to pre-launch empty states.\n */\n\nimport { useMemo, type ReactNode } from 'react';\nimport {\n Layers,\n ScrollText,\n Ship,\n Factory,\n Boxes,\n ShieldCheck,\n ArrowRight,\n ArrowUpRight,\n Fuel,\n GitBranch,\n Gauge,\n Activity,\n ClipboardList,\n} from 'lucide-react';\nimport { GlassCard, Spinner } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { MiniBar } from '../../components/MiniBar';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport {\n AreaChart,\n DonutChart,\n Sparkline,\n chartPalette,\n type LabeledValue,\n type Segment,\n} from '../../components/viz';\nimport { cn } from '../../utils/cn';\nimport { useTheGlobalFuel } from '../../context';\nimport {\n useTheAssets,\n useTheContracts,\n useTheShipments,\n useTheEmissionRecords,\n useTheInventoryLots,\n useTheComplianceCerts,\n useTheProductionRecords,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport {\n formatNumber,\n formatPercent,\n formatDate,\n formatRelativeTime,\n formatEnumLabel,\n formatVolume,\n formatTonnesCo2e,\n isExpired,\n} from '../../utils/formatters';\nimport type {\n TheAssetStatus,\n TheCommodityType,\n TheEmissionScope,\n TheOfftakeContract,\n TheProductionRecord,\n} from '../../types';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst EXPIRY_WINDOW_DAYS = 30;\n\n/** True when a cert expires within the next 30 days (but hasn't yet). */\nconst isExpiringSoon = (date: string | null | undefined): boolean => {\n if (!date) return false;\n const t = new Date(date).getTime();\n if (Number.isNaN(t)) return false;\n const diff = t - Date.now();\n return diff >= 0 && diff <= EXPIRY_WINDOW_DAYS * DAY_MS;\n};\n\n/** Asset lifecycle → MiniBar fill token (semantic, maturity-ordered). */\nconst ASSET_STATUS_BAR: Record<TheAssetStatus, string> = {\n EXPLORATION: 'bg-[var(--color-accent-indigo)]',\n APPRAISAL: 'bg-[var(--color-accent-blue)]',\n DEVELOPMENT: 'bg-[var(--color-accent-teal)]',\n PRODUCING: 'bg-status-success-text',\n SUSPENDED: 'bg-status-warning-text',\n DECOMMISSIONING: 'bg-status-error-text',\n RETIRED: 'bg-[var(--color-text-tertiary)]',\n};\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// ── Month bucketing (UTC-stable, gap-free) ───────────────────────────────────\n\n/** UTC year-month key (e.g. \"2026-07\"). */\nconst monthKey = (d: Date): string =>\n `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n\nconst MONTH_FMT = new Intl.DateTimeFormat(undefined, {\n month: 'short',\n year: '2-digit',\n timeZone: 'UTC',\n});\n\nconst monthLabel = (year: number, month1: number): string =>\n MONTH_FMT.format(new Date(Date.UTC(year, month1 - 1, 1)));\n\n/** Sum `valueOf` into UTC-month buckets keyed by the `dateOf` field. */\nfunction bucketByMonth<T>(\n rows: readonly T[],\n dateOf: (row: T) => string | null | undefined,\n valueOf: (row: T) => number\n): Map<string, number> {\n const buckets = new Map<string, number>();\n for (const row of rows) {\n const raw = dateOf(row);\n if (!raw) continue;\n const d = new Date(raw);\n if (Number.isNaN(d.getTime())) continue;\n const key = monthKey(d);\n buckets.set(key, (buckets.get(key) ?? 0) + valueOf(row));\n }\n return buckets;\n}\n\n/**\n * Expand month buckets into a gap-free ordered series from the earliest to the\n * latest populated month (so a trend line stays continuous), then keep only the\n * most recent `maxMonths` so a stray old record can't blow out the axis.\n */\nfunction monthlySeries(buckets: Map<string, number>, maxMonths = 18): LabeledValue[] {\n const keys = [...buckets.keys()].sort();\n if (keys.length === 0) return [];\n const parse = (k: string): [number, number] => {\n const [y, m] = k.split('-');\n return [Number(y), Number(m)];\n };\n const [maxY, maxM] = parse(keys[keys.length - 1]);\n let [y, m] = parse(keys[0]);\n const series: LabeledValue[] = [];\n let guard = 0;\n while ((y < maxY || (y === maxY && m <= maxM)) && guard < 240) {\n series.push({\n label: monthLabel(y, m),\n value: buckets.get(`${y}-${String(m).padStart(2, '0')}`) ?? 0,\n });\n m += 1;\n if (m > 12) {\n m = 1;\n y += 1;\n }\n guard += 1;\n }\n return series.length > maxMonths ? series.slice(series.length - maxMonths) : series;\n}\n\n/** Group rows by a discriminator, sorted by descending count, with % of total. */\nfunction distribution<T>(\n rows: T[],\n keyOf: (row: T) => string\n): { key: string; count: number; pct: number }[] {\n const counts = new Map<string, number>();\n for (const row of rows) {\n const k = keyOf(row);\n counts.set(k, (counts.get(k) ?? 0) + 1);\n }\n const total = rows.length;\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([key, count]) => ({ key, count, pct: total ? (count / total) * 100 : 0 }));\n}\n\n// ── Distribution card (MiniBar rows) ─────────────────────────────────────────\n\ninterface DistBar {\n key: string;\n label: ReactNode;\n /** Pre-formatted primary figure shown right of the bar. */\n value: string;\n /** 0–100 — drives the bar width + the muted percentage. */\n pct: number;\n barClassName: string;\n}\n\ninterface DistributionCardProps {\n title: string;\n description: string;\n icon: ReactNode;\n linkLabel: string;\n onLink: () => void;\n isLoading: boolean;\n isError: boolean;\n onRetry: () => void;\n emptyLabel: string;\n bars: DistBar[];\n footer?: ReactNode;\n className?: string;\n}\n\n/** A titled glass card that renders a compact set of MiniBar distribution rows. */\nfunction DistributionCard({\n title,\n description,\n icon,\n linkLabel,\n onLink,\n isLoading,\n isError,\n onRetry,\n emptyLabel,\n bars,\n footer,\n className,\n}: DistributionCardProps) {\n return (\n <GlassCard treatment=\"glass\" className={cn('flex flex-col p-5', className)}>\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-action-primary-bg\">\n {icon}\n </span>\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">{description}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onLink}\n className=\"inline-flex shrink-0 items-center gap-1 text-sm font-medium text-action-primary-bg hover:underline\"\n >\n {linkLabel} <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n\n {isLoading ? (\n <div className=\"flex items-center gap-2 py-8 text-sm text-text-secondary\">\n <Spinner size=\"sm\" /> Loading…\n </div>\n ) : isError ? (\n <div className=\"flex flex-col items-start gap-2 py-6\">\n <p className=\"text-sm text-status-error-text\">\n Couldn&apos;t load {title.toLowerCase()}.\n </p>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Retry\n </button>\n </div>\n ) : bars.length === 0 ? (\n <div className=\"flex flex-1 flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-8 text-center\">\n <p className=\"text-sm text-text-secondary\">{emptyLabel}</p>\n </div>\n ) : (\n <div className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"space-y-3\">\n {bars.map((bar) => (\n <div key={bar.key} className=\"flex items-center gap-3\">\n <div className=\"w-24 shrink-0 truncate text-sm text-text-secondary sm:w-28\">\n {bar.label}\n </div>\n <MiniBar\n value={bar.pct}\n barClassName={bar.barClassName}\n className=\"flex-1\"\n height={8}\n />\n <span className=\"w-16 shrink-0 whitespace-nowrap text-right text-sm font-medium tabular-nums text-text-primary\">\n {bar.value}\n </span>\n <span className=\"hidden w-11 shrink-0 text-right text-xs tabular-nums text-text-tertiary sm:inline\">\n {formatPercent(bar.pct)}\n </span>\n </div>\n ))}\n </div>\n {footer && (\n <p className=\"border-t border-border-seam/70 pt-3 text-xs text-text-tertiary\">\n {footer}\n </p>\n )}\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ── Chart card (titled glass panel for a chart body) ─────────────────────────\n\ninterface ChartCardProps {\n title: string;\n caption: string;\n icon: ReactNode;\n link?: { label: string; onClick: () => void };\n isLoading: boolean;\n isError: boolean;\n onRetry: () => void;\n isEmpty: boolean;\n emptyLabel: string;\n footer?: ReactNode;\n className?: string;\n children: ReactNode;\n}\n\n/** A titled glass card wrapping a viz-kit chart, with loading/error/empty states. */\nfunction ChartCard({\n title,\n caption,\n icon,\n link,\n isLoading,\n isError,\n onRetry,\n isEmpty,\n emptyLabel,\n footer,\n className,\n children,\n}: ChartCardProps) {\n return (\n <GlassCard treatment=\"glass\" className={cn('flex flex-col p-5', className)}>\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-action-primary-bg\">\n {icon}\n </span>\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">{caption}</p>\n </div>\n </div>\n {link ? (\n <button\n type=\"button\"\n onClick={link.onClick}\n className=\"inline-flex shrink-0 items-center gap-1 text-sm font-medium text-action-primary-bg hover:underline\"\n >\n {link.label} <ArrowRight className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n\n {isLoading ? (\n <div className=\"flex flex-1 items-center gap-2 py-8 text-sm text-text-secondary\">\n <Spinner size=\"sm\" /> Loading…\n </div>\n ) : isError ? (\n <div className=\"flex flex-1 flex-col items-start gap-2 py-6\">\n <p className=\"text-sm text-status-error-text\">\n Couldn&apos;t load {title.toLowerCase()}.\n </p>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Retry\n </button>\n </div>\n ) : isEmpty ? (\n <div className=\"flex flex-1 flex-col items-center justify-center rounded-xl border border-dashed border-border-seam py-10 text-center\">\n <p className=\"text-sm text-text-secondary\">{emptyLabel}</p>\n </div>\n ) : (\n <div className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"flex-1\">{children}</div>\n {footer ? (\n <p className=\"border-t border-border-seam/70 pt-3 text-xs text-text-tertiary\">\n {footer}\n </p>\n ) : null}\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ── Commodity portfolio card ─────────────────────────────────────────────────\n\ninterface CommodityPortfolioEntry {\n key: TheCommodityType;\n name: string;\n /** Tokenized commodity color, shared with the commodity-mix donut. */\n color: string;\n assetCount: number;\n producing: number;\n capacity: number;\n /** Dominant `TheUnit` token across the commodity's assets. */\n unit: string;\n /** Monthly production volumes (oldest first) — empty when none logged. */\n spark: number[];\n}\n\nfunction CommodityCard({ card, onOpen }: { card: CommodityPortfolioEntry; onOpen: () => void }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-0\">\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${card.name} assets`}\n className=\"flex w-full flex-col gap-4 rounded-[var(--radius-card)] p-5 text-left outline-none transition-transform duration-200 hover:-translate-y-0.5 focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2.5\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: card.color }}\n />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{card.name}</p>\n <p className=\"text-xs text-text-tertiary\">\n {card.assetCount} {card.assetCount === 1 ? 'asset' : 'assets'}\n </p>\n </div>\n </div>\n <ArrowUpRight className=\"h-4 w-4 shrink-0 text-text-tertiary\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-[11px] uppercase tracking-wide text-text-tertiary\">Capacity/day</p>\n <p className=\"mt-0.5 truncate text-sm font-semibold tabular-nums text-text-primary\">\n {card.capacity > 0 ? formatVolume(card.capacity, card.unit) : '—'}\n </p>\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] uppercase tracking-wide text-text-tertiary\">Producing</p>\n <p className=\"mt-0.5 text-sm font-semibold tabular-nums text-text-primary\">\n {card.producing}{' '}\n <span className=\"text-xs font-normal text-text-tertiary\">of {card.assetCount}</span>\n </p>\n </div>\n </div>\n\n {card.spark.length >= 2 ? (\n <div>\n <Sparkline\n data={card.spark}\n variant=\"area\"\n height={40}\n label={`${card.name} monthly production`}\n />\n <p className=\"mt-1 text-[11px] text-text-tertiary\">Monthly output</p>\n </div>\n ) : (\n <p className=\"text-xs text-text-tertiary\">No production logged yet.</p>\n )}\n </button>\n </GlassCard>\n );\n}\n\nexport function DashboardPage() {\n useThePageView('dashboard');\n const { navigateTo, currentUser } = useTheGlobalFuel();\n\n // Real backend queries. Aggregate domains fetch a generous page (200) so the\n // charts + portfolio reflect the whole book; commerce lists stay at 50.\n const assets = useTheAssets({ pagination: { limit: 200 } });\n const contracts = useTheContracts({ pagination: { limit: 50 } });\n const shipments = useTheShipments(undefined, 'IN_TRANSIT');\n const emissions = useTheEmissionRecords({ pagination: { limit: 200 } });\n const inventory = useTheInventoryLots({ pagination: { limit: 50 } });\n const compliance = useTheComplianceCerts();\n const production = useTheProductionRecords({ pagination: { limit: 200 } });\n\n const assetRows = useMemo(() => assets.data?.items ?? [], [assets.data]);\n const contractRows = useMemo(() => contracts.data?.items ?? [], [contracts.data]);\n const inTransitRows = useMemo(() => shipments.data?.items ?? [], [shipments.data]);\n const emissionRows = useMemo(() => emissions.data?.items ?? [], [emissions.data]);\n const inventoryRows = useMemo(() => inventory.data?.items ?? [], [inventory.data]);\n const certRows = useMemo(() => compliance.data?.items ?? [], [compliance.data]);\n const productionRows = useMemo(() => production.data?.items ?? [], [production.data]);\n\n // ── KPI aggregates ─────────────────────────────────────────────────────────\n const producingCount = assetRows.filter((a) => a.status === 'PRODUCING').length;\n const activeContracts = contractRows.filter((c) => c.status === 'ACTIVE').length;\n const totalTonnes = emissionRows.reduce((sum, e) => sum + (e.co2eTonnes ?? 0), 0);\n const verifiedEmissions = emissionRows.filter((e) => e.verified).length;\n const availableLots = inventoryRows.filter((l) => l.status === 'AVAILABLE').length;\n const totalProductionVolume = productionRows.reduce((sum, p) => sum + (p.volume ?? 0), 0);\n\n const pendingCerts = certRows.filter((c) => c.status === 'PENDING').length;\n const expiringCerts = certRows.filter(\n (c) => c.expiresAt != null && (isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt))\n ).length;\n const attentionCerts = certRows.filter(\n (c) =>\n c.status === 'PENDING' ||\n (c.expiresAt != null && (isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt)))\n ).length;\n\n const withPlus = (rows: unknown[], hasMore: boolean | undefined): string =>\n hasMore ? `${rows.length}+` : formatNumber(rows.length);\n\n // ── Production trend (metered output by month) ───────────────────────────────\n const productionSeries = useMemo<LabeledValue[]>(\n () =>\n monthlySeries(\n bucketByMonth(\n productionRows,\n (p) => p.periodEnd,\n (p) => p.volume ?? 0\n )\n ),\n [productionRows]\n );\n\n // ── Commodity distribution + shared per-commodity colors ─────────────────────\n const commodityDist = useMemo(() => distribution(assetRows, (a) => a.commodity), [assetRows]);\n\n const commodityColor = useMemo(() => {\n const palette = chartPalette(commodityDist.length);\n const map = new Map<string, string>();\n commodityDist.forEach((d, i) => map.set(d.key, palette[i]));\n return map;\n }, [commodityDist]);\n\n const commodityMix = useMemo<Segment[]>(\n () =>\n commodityDist.map((d) => ({\n label: formatEnumLabel(d.key),\n value: d.count,\n color: commodityColor.get(d.key),\n })),\n [commodityDist, commodityColor]\n );\n\n // ── Emissions by scope (tonnes CO₂e) ─────────────────────────────────────────\n const scopeMix = 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((s) => totals[s] > 0).map((s) => ({\n label: formatEnumLabel(s),\n value: totals[s],\n color: SCOPE_COLOR[s],\n }));\n }, [emissionRows]);\n\n // ── Assets by lifecycle (MiniBar rows) ───────────────────────────────────────\n const lifecycleBars = useMemo<DistBar[]>(\n () =>\n distribution(assetRows, (a) => a.status).map((d) => ({\n key: d.key,\n label: <StatusBadge status={d.key} />,\n value: formatNumber(d.count),\n pct: d.pct,\n barClassName: ASSET_STATUS_BAR[d.key as TheAssetStatus] ?? 'bg-action-primary-bg',\n })),\n [assetRows]\n );\n\n // ── Commodity portfolio (one entry per commodity with assets) ────────────────\n const assetCommodity = useMemo(() => {\n const m = new Map<string, TheCommodityType>();\n for (const a of assetRows) m.set(a.id, a.commodity);\n return m;\n }, [assetRows]);\n\n const commodityCards = useMemo<CommodityPortfolioEntry[]>(() => {\n if (commodityDist.length === 0) return [];\n\n // Per-commodity asset aggregates.\n interface Agg {\n count: number;\n producing: number;\n capacity: number;\n units: Map<string, number>;\n }\n const agg = new Map<string, Agg>();\n for (const a of assetRows) {\n const cur = agg.get(a.commodity) ?? {\n count: 0,\n producing: 0,\n capacity: 0,\n units: new Map<string, number>(),\n };\n cur.count += 1;\n if (a.status === 'PRODUCING') cur.producing += 1;\n cur.capacity += a.capacityPerDay ?? 0;\n if (a.capacityUnit) cur.units.set(a.capacityUnit, (cur.units.get(a.capacityUnit) ?? 0) + 1);\n agg.set(a.commodity, cur);\n }\n\n // Per-commodity monthly production buckets → sparkline series.\n const prodBuckets = new Map<string, Map<string, number>>();\n for (const p of productionRows) {\n const commodity = assetCommodity.get(p.assetId);\n if (!commodity) continue;\n const d = new Date(p.periodEnd);\n if (Number.isNaN(d.getTime())) continue;\n const mk = monthKey(d);\n let bucket = prodBuckets.get(commodity);\n if (!bucket) {\n bucket = new Map<string, number>();\n prodBuckets.set(commodity, bucket);\n }\n bucket.set(mk, (bucket.get(mk) ?? 0) + (p.volume ?? 0));\n }\n\n const dominantUnit = (units: Map<string, number>): string => {\n let best = '';\n let bestN = -1;\n for (const [unit, n] of units) {\n if (n > bestN) {\n best = unit;\n bestN = n;\n }\n }\n return best;\n };\n\n return commodityDist.map((d) => {\n const a = agg.get(d.key) ?? {\n count: 0,\n producing: 0,\n capacity: 0,\n units: new Map<string, number>(),\n };\n const bucket = prodBuckets.get(d.key);\n const spark = bucket ? monthlySeries(bucket, 12).map((s) => s.value) : [];\n return {\n key: d.key as TheCommodityType,\n name: formatEnumLabel(d.key),\n color: commodityColor.get(d.key) ?? 'var(--color-accent-primary)',\n assetCount: a.count,\n producing: a.producing,\n capacity: a.capacity,\n unit: dominantUnit(a.units),\n spark,\n };\n });\n }, [assetRows, productionRows, assetCommodity, commodityDist, commodityColor]);\n\n // ── Recent activity ────────────────────────────────────────────────────────\n const assetName = useMemo(() => {\n const m = new Map<string, string>();\n for (const a of assetRows) m.set(a.id, a.name);\n return m;\n }, [assetRows]);\n\n const recentContracts = useMemo(\n () =>\n [...contractRows]\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())\n .slice(0, 6),\n [contractRows]\n );\n\n const recentProduction = useMemo(\n () =>\n [...productionRows]\n .sort((a, b) => new Date(b.periodEnd).getTime() - new Date(a.periodEnd).getTime())\n .slice(0, 6),\n [productionRows]\n );\n\n const contractColumns: Column<TheOfftakeContract>[] = [\n {\n key: 'reference',\n header: 'Reference',\n render: (c) => <span className=\"font-medium text-text-primary\">{c.reference}</span>,\n },\n {\n key: 'counterparty',\n header: 'Counterparty',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{c.counterparty}</span>,\n },\n {\n key: 'commodity',\n header: 'Commodity',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.commodity)}</span>,\n },\n { key: 'side', header: 'Side', render: (c) => <StatusBadge status={c.side} /> },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n hideOnMobile: true,\n render: (c) => (\n <span className=\"tabular-nums text-text-secondary\">\n {formatVolume(c.contractedVolume, c.unit)}\n </span>\n ),\n },\n { key: 'status', header: 'Status', render: (c) => <StatusBadge status={c.status} /> },\n {\n key: 'created',\n header: 'Created',\n align: 'right',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-tertiary\">{formatRelativeTime(c.createdAt)}</span>,\n },\n ];\n\n const productionColumns: Column<TheProductionRecord>[] = [\n {\n key: 'asset',\n header: 'Asset',\n render: (p) => (\n <span className=\"font-medium text-text-primary\">\n {assetName.get(p.assetId) ?? 'Unknown asset'}\n </span>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n hideOnMobile: true,\n render: (p) => (\n <span className=\"text-text-secondary\">\n {formatDate(p.periodStart)} – {formatDate(p.periodEnd)}\n </span>\n ),\n },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n render: (p) => (\n <span className=\"tabular-nums font-medium text-text-primary\">\n {formatVolume(p.volume, p.unit)}\n </span>\n ),\n },\n {\n key: 'downtime',\n header: 'Downtime',\n align: 'right',\n hideOnMobile: true,\n render: (p) => (\n <span className=\"tabular-nums text-text-secondary\">{formatNumber(p.downtimeHours)} h</span>\n ),\n },\n {\n key: 'logged',\n header: 'Logged',\n align: 'right',\n hideOnMobile: true,\n render: (p) => <span className=\"text-text-tertiary\">{formatRelativeTime(p.createdAt)}</span>,\n },\n ];\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n const t = useTheTranslation();\n const description = t(\n 'theglobalfuel.pages.dashboard.subtitle',\n 'Welcome back, {name} — your energy, mineral & commodity resource control room.',\n { name: greetingName }\n );\n\n // Full-page loader only while every primary domain is still loading its first page.\n const initialLoading =\n assets.isLoading &&\n contracts.isLoading &&\n emissions.isLoading &&\n inventory.isLoading &&\n compliance.isLoading;\n\n const pageTitle = t('theglobalfuel.pages.dashboard.title', 'TheGlobalFuel');\n\n if (initialLoading) {\n return (\n <PageLayout\n title={pageTitle}\n titleGradient\n aurora\n description={description}\n dataTour=\"theglobalfuel-dashboard-page\"\n >\n <LoadingView label=\"Loading your resource portfolio…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={pageTitle}\n titleGradient\n aurora\n description={description}\n dataTour=\"theglobalfuel-dashboard-page\"\n >\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n <MetricsCard\n icon={<Layers className=\"h-5 w-5\" />}\n value={withPlus(assetRows, assets.data?.pageInfo?.hasMore)}\n label=\"Resource assets\"\n featured\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={assetRows.length ? (producingCount / assetRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{producingCount}</span> producing\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<ScrollText className=\"h-5 w-5\" />}\n value={withPlus(contractRows, contracts.data?.pageInfo?.hasMore)}\n label=\"Offtake contracts\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={contractRows.length ? (activeContracts / contractRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{activeContracts}</span> active\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<Ship className=\"h-5 w-5\" />}\n value={shipments.isError ? '—' : formatNumber(inTransitRows.length)}\n label=\"Shipments in transit\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n {inTransitRows.length > 0 ? 'In motion across carriers' : 'No active movements'}\n </p>\n }\n />\n\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={emissions.isError ? '—' : formatTonnesCo2e(totalTonnes)}\n label=\"Emissions logged\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={emissionRows.length ? (verifiedEmissions / emissionRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-status-success-text\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{verifiedEmissions}</span>{' '}\n verified\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<Boxes className=\"h-5 w-5\" />}\n value={inventory.isError ? '—' : formatNumber(availableLots)}\n label=\"Inventory available\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar\n value={inventoryRows.length ? (availableLots / inventoryRows.length) * 100 : 0}\n height={4}\n barClassName=\"bg-action-primary-bg\"\n />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {withPlus(inventoryRows, inventory.data?.pageInfo?.hasMore)}\n </span>{' '}\n lots total\n </p>\n </div>\n }\n />\n\n <MetricsCard\n icon={<ShieldCheck className=\"h-5 w-5\" />}\n value={compliance.isError ? '—' : formatNumber(attentionCerts)}\n label=\"Certs need attention\"\n accent={attentionCerts > 0 ? 'warning' : certRows.length > 0 ? 'success' : undefined}\n footer={\n <p\n className={\n attentionCerts > 0\n ? 'text-xs font-medium text-status-warning-text'\n : 'text-xs text-text-tertiary'\n }\n >\n {certRows.length === 0\n ? 'No certifications tracked yet'\n : attentionCerts > 0\n ? `${pendingCerts} pending · ${expiringCerts} expiring`\n : 'All certifications current'}\n </p>\n }\n />\n </div>\n\n {/* Analytics — production trend + commodity mix (real aggregates) */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <ChartCard\n className=\"lg:col-span-2\"\n title=\"Production trend\"\n caption=\"Metered output aggregated by month across your producing assets.\"\n icon={<Activity className=\"h-4 w-4\" />}\n link={{ label: 'Production', onClick: () => navigateTo('production') }}\n isLoading={production.isLoading}\n isError={production.isError}\n onRetry={() => production.refetch()}\n isEmpty={productionSeries.length === 0}\n emptyLabel=\"No production logged yet — record output to build the trend.\"\n footer={\n productionSeries.length > 0\n ? `${formatNumber(totalProductionVolume, { maximumFractionDigits: 0 })} total volume across ${withPlus(productionRows, production.data?.pageInfo?.hasMore)} records`\n : undefined\n }\n >\n <AreaChart series={productionSeries} height={220} title=\"Monthly production volume\" />\n </ChartCard>\n\n <ChartCard\n title=\"Commodity mix\"\n caption=\"Share of assets across your commodity streams.\"\n icon={<Fuel className=\"h-4 w-4\" />}\n link={{ label: 'Assets', onClick: () => navigateTo('assets') }}\n isLoading={assets.isLoading}\n isError={assets.isError}\n onRetry={() => assets.refetch()}\n isEmpty={commodityMix.length === 0}\n emptyLabel=\"No assets registered yet.\"\n footer={\n commodityMix.length > 0\n ? `${withPlus(assetRows, assets.data?.pageInfo?.hasMore)} assets · ${commodityMix.length} ${commodityMix.length === 1 ? 'commodity' : 'commodities'}`\n : undefined\n }\n >\n <div className=\"flex items-center justify-center py-2\">\n <DonutChart\n segments={commodityMix}\n size={168}\n centerLabel={formatNumber(assetRows.length)}\n centerCaption=\"assets\"\n valueFormatter={(v) => formatNumber(v)}\n title=\"Assets by commodity\"\n />\n </div>\n </ChartCard>\n </div>\n\n {/* Commodity portfolio — one model, every commodity */}\n <Section\n title=\"Commodity portfolio\"\n description=\"One model, every commodity — assets, capacity and output across your book.\"\n >\n {assets.isLoading ? (\n <LoadingView label=\"Loading commodities…\" />\n ) : assets.isError ? (\n <ErrorView\n message=\"Couldn't load your commodity portfolio.\"\n onRetry={() => assets.refetch()}\n />\n ) : commodityCards.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Layers className=\"h-10 w-10\" />}\n title=\"No commodities yet\"\n description=\"Register your first resource asset to build out your commodity portfolio.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n {commodityCards.map((card) => (\n <CommodityCard key={card.key} card={card} onOpen={() => navigateTo('assets')} />\n ))}\n </div>\n )}\n </Section>\n\n {/* Lifecycle distribution + emissions by scope */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <DistributionCard\n title=\"Assets by lifecycle\"\n description=\"Where the portfolio sits from exploration to retirement.\"\n icon={<GitBranch className=\"h-4 w-4\" />}\n linkLabel=\"Assets\"\n onLink={() => navigateTo('assets')}\n isLoading={assets.isLoading}\n isError={assets.isError}\n onRetry={() => assets.refetch()}\n emptyLabel=\"No assets to profile yet.\"\n bars={lifecycleBars}\n footer={\n assetRows.length > 0\n ? `${producingCount} of ${withPlus(assetRows, assets.data?.pageInfo?.hasMore)} currently producing`\n : undefined\n }\n />\n\n <ChartCard\n title=\"Emissions by scope\"\n caption=\"GHG Protocol Scope 1 / 2 / 3 — tonnes CO₂e logged.\"\n icon={<Gauge className=\"h-4 w-4\" />}\n link={{ label: 'Emissions', onClick: () => navigateTo('emissions') }}\n isLoading={emissions.isLoading}\n isError={emissions.isError}\n onRetry={() => emissions.refetch()}\n isEmpty={scopeMix.length === 0}\n emptyLabel=\"No emission records logged yet.\"\n footer={\n emissionRows.length > 0\n ? `${formatTonnesCo2e(totalTonnes)} across ${withPlus(emissionRows, emissions.data?.pageInfo?.hasMore)} records`\n : undefined\n }\n >\n <div className=\"flex items-center justify-center py-2\">\n <DonutChart\n segments={scopeMix}\n size={168}\n centerLabel={formatNumber(totalTonnes, {\n notation: 'compact',\n maximumFractionDigits: 1,\n })}\n centerCaption=\"t CO₂e\"\n valueFormatter={(v) =>\n `${formatNumber(v, { maximumFractionDigits: v >= 100 ? 0 : 1 })} t`\n }\n title=\"Emissions by scope\"\n />\n </div>\n </ChartCard>\n </div>\n\n {/* Recent offtake contracts */}\n <Section\n title=\"Recent offtake contracts\"\n description=\"Latest buy/sell agreements across your commodity book.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('contracts')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-action-primary-bg hover:underline\"\n >\n All contracts <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {contracts.isLoading ? (\n <LoadingView label=\"Loading contracts…\" />\n ) : contracts.isError ? (\n <ErrorView message=\"Couldn't load contracts.\" onRetry={() => contracts.refetch()} />\n ) : recentContracts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ScrollText className=\"h-10 w-10\" />}\n title=\"No contracts yet\"\n description=\"Draft your first offtake contract to start tracking commodity trade.\"\n />\n ) : (\n <DataTable\n columns={contractColumns}\n rows={recentContracts}\n rowKey={(c) => c.id}\n onRowClick={(c) => navigateTo(`contracts/${c.id}`)}\n rowLabel={(c) => `Open contract ${c.reference}`}\n />\n )}\n </Section>\n\n {/* Recent production */}\n <Section\n title=\"Recent production\"\n description=\"Latest metered output captured against your producing assets.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('production')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-action-primary-bg hover:underline\"\n >\n All production <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {production.isLoading ? (\n <LoadingView label=\"Loading production…\" />\n ) : production.isError ? (\n <ErrorView\n message=\"Couldn't load production records.\"\n onRetry={() => production.refetch()}\n />\n ) : recentProduction.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title=\"No production logged yet\"\n description=\"Record production against an asset to build its output history.\"\n />\n ) : (\n <DataTable\n columns={productionColumns}\n rows={recentProduction}\n rowKey={(p) => p.id}\n onRowClick={(p) => navigateTo(`assets/${p.assetId}`)}\n rowLabel={(p) => `Open ${assetName.get(p.assetId) ?? 'asset'}`}\n />\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -45,7 +45,7 @@ var T = [
45
45
  ], D = {
46
46
  SCOPE_1: "bg-status-error-text",
47
47
  SCOPE_2: "bg-status-warning-text",
48
- SCOPE_3: "bg-accent"
48
+ SCOPE_3: "bg-accent-blue"
49
49
  }, Oe = {
50
50
  SCOPE_1: "Scope 1",
51
51
  SCOPE_2: "Scope 2",
@@ -1 +1 @@
1
- {"version":3,"file":"EmissionsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/emissions/EmissionsPage.tsx"],"sourcesContent":["/**\n * Emissions — the ESG greenhouse-gas ledger.\n * @module theglobalfuel/pages/emissions\n *\n * Reads `theListEmissionRecords` (optionally filtered by GHG-Protocol scope and\n * by asset); writes `theRecordEmission` and `theVerifyEmission`. Asset names are\n * resolved from `theListAssets`. The header pairs a scope-breakdown donut with a\n * KPI strip — both computed from a broad, asset-scoped pull of the ledger (not\n * just the current table page), split by Scope 1 / 2 / 3. No mock data — every\n * number comes from the real `wspace-theglobalfuel-svc` contract.\n */\n\nimport { useMemo, useState, type ReactNode } from 'react';\nimport { Plus, Factory, Flame, Zap, Truck, Cloud, CheckCircle2, Circle } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { MiniBar } from '../../components/MiniBar';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Pagination } from '../../components/Pagination';\nimport { DonutChart, type Segment } from '../../components';\nimport {\n useTheAssets,\n useTheEmissionRecords,\n useTheRecordEmission,\n useTheVerifyEmission,\n useCursorPager,\n useNotify,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport {\n formatDate,\n formatRelativeTime,\n formatPercent,\n formatTonnesCo2e,\n formatEnumLabel,\n toBackendDate,\n} from '../../utils/formatters';\nimport type { TheEmissionRecord, TheEmissionScope } from '../../types';\n\nconst SCOPES: TheEmissionScope[] = ['SCOPE_1', 'SCOPE_2', 'SCOPE_3'];\n\nconst SCOPE_FILTERS: FilterOption<TheEmissionScope | 'ALL'>[] = [\n { value: 'ALL', label: 'All scopes' },\n { value: 'SCOPE_1', label: 'Scope 1' },\n { value: 'SCOPE_2', label: 'Scope 2' },\n { value: 'SCOPE_3', label: 'Scope 3' },\n];\n\n/** MiniBar fill token per GHG scope — mirrors the StatusBadge scope palette. */\nconst SCOPE_BAR: Record<TheEmissionScope, string> = {\n SCOPE_1: 'bg-status-error-text',\n SCOPE_2: 'bg-status-warning-text',\n SCOPE_3: 'bg-accent',\n};\n\n/** Short legend label per scope for the breakdown donut. */\nconst SCOPE_LABEL: Record<TheEmissionScope, string> = {\n SCOPE_1: 'Scope 1',\n SCOPE_2: 'Scope 2',\n SCOPE_3: 'Scope 3',\n};\n\n/**\n * Tokenized donut-segment color per scope — aligned to the StatusBadge scope\n * palette (Scope 1 = danger, Scope 2 = warning, Scope 3 = info/blue). All are\n * semantic CSS variables, so the chart themes automatically.\n */\nconst SCOPE_DONUT_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\n/** Compact CO₂e for the donut center (e.g. 1.24M) — full figures live in the KPIs. */\nconst compactTonnes = (n: number): string =>\n new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 }).format(n);\n\nconst PAGE_SIZE = 25;\n\nconst today = (): string => new Date().toISOString().slice(0, 10);\n\ninterface RecordFormState {\n assetId: string;\n scope: TheEmissionScope;\n co2eTonnes: string;\n periodStart: string;\n periodEnd: string;\n source: string;\n methodology: string;\n notes: string;\n}\n\nconst emptyForm = (): RecordFormState => ({\n assetId: '',\n scope: 'SCOPE_1',\n co2eTonnes: '',\n periodStart: today(),\n periodEnd: today(),\n source: '',\n methodology: '',\n notes: '',\n});\n\n/** KPI-card footer: this scope's share of the page total, tinted by scope. */\nconst renderScopeShare = (value: number, total: number, scope: TheEmissionScope): ReactNode => {\n if (total <= 0) return undefined;\n const pct = (value / total) * 100;\n return (\n <div className=\"space-y-2\">\n <MiniBar value={pct} barClassName={SCOPE_BAR[scope]} height={4} />\n <p className=\"text-xs tabular-nums text-text-tertiary\">{formatPercent(pct)} of total</p>\n </div>\n );\n};\n\nexport function EmissionsPage() {\n useThePageView('emissions');\n const t = useTheTranslation();\n const notify = useNotify();\n const [scope, setScope] = useState<TheEmissionScope | 'ALL'>('ALL');\n const [assetId, setAssetId] = useState<string>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<RecordFormState>(emptyForm);\n const [formError, setFormError] = useState<string | null>(null);\n // Track the row whose verification is in flight, so only that row's Verify\n // button disables (verifyEmission.isPending is shared across all rows).\n const [verifyingId, setVerifyingId] = useState<string | null>(null);\n\n // Assets feed the filter Select, the create picker, and the id→name map.\n const assetsQuery = useTheAssets({ pagination: { limit: 100 } });\n const assetMap = useMemo<Record<string, string>>(() => {\n const map: Record<string, string> = {};\n for (const a of assetsQuery.data?.items ?? []) map[a.id] = a.name;\n return map;\n }, [assetsQuery.data]);\n const assetOptions = assetsQuery.data?.items ?? [];\n\n const { data, isLoading, isError, refetch } = useTheEmissionRecords({\n scope: scope === 'ALL' ? undefined : scope,\n assetId: assetId === 'ALL' ? undefined : assetId,\n pagination: { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE },\n });\n const recordEmission = useTheRecordEmission();\n const verifyEmission = useTheVerifyEmission();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n\n // Broad, scope-agnostic pull (respects the asset filter only) so the header\n // donut + KPI totals reflect the whole ledger — stable as the scope tabs and\n // pagination narrow the table below.\n const metricsQuery = useTheEmissionRecords({\n assetId: assetId === 'ALL' ? undefined : assetId,\n pagination: { limit: 200 },\n });\n const metricsRows = useMemo(() => metricsQuery.data?.items ?? [], [metricsQuery.data]);\n const metricsCount = metricsRows.length;\n\n // KPI + donut summary — CO₂e total plus per-scope breakdown across the ledger.\n const { total, byScope } = useMemo(() => {\n const byScope: Record<TheEmissionScope, number> = {\n SCOPE_1: 0,\n SCOPE_2: 0,\n SCOPE_3: 0,\n };\n let total = 0;\n for (const r of metricsRows) {\n total += r.co2eTonnes;\n byScope[r.scope] += r.co2eTonnes;\n }\n return { total, byScope };\n }, [metricsRows]);\n\n // Donut segments — one per scope that actually carries CO₂e.\n const scopeSegments = useMemo<Segment[]>(\n () =>\n SCOPES.filter((s) => byScope[s] > 0).map((s) => ({\n label: SCOPE_LABEL[s],\n value: byScope[s],\n color: SCOPE_DONUT_COLOR[s],\n })),\n [byScope]\n );\n\n const columns: Column<TheEmissionRecord>[] = [\n {\n key: 'scope',\n header: 'Scope',\n render: (r) => <StatusBadge status={r.scope} />,\n },\n {\n key: 'asset',\n header: 'Asset',\n render: (r) => (\n <span className=\"text-text-secondary\">\n {r.assetId ? (assetMap[r.assetId] ?? `${r.assetId.slice(0, 8)}…`) : '—'}\n </span>\n ),\n },\n {\n key: 'co2e',\n header: 'CO₂e',\n align: 'right',\n sortValue: (r) => r.co2eTonnes,\n render: (r) => (\n <span className=\"tabular-nums font-medium text-text-primary\">\n {formatTonnesCo2e(r.co2eTonnes)}\n </span>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n hideOnMobile: true,\n sortValue: (r) => new Date(r.periodStart).getTime(),\n render: (r) => (\n <span className=\"text-text-secondary\">\n {formatDate(r.periodStart)} – {formatDate(r.periodEnd)}\n </span>\n ),\n },\n {\n key: 'source',\n header: 'Source',\n hideOnMobile: true,\n render: (r) => <span className=\"text-text-secondary\">{r.source ?? '—'}</span>,\n },\n {\n key: 'verified',\n header: 'Verified',\n render: (r) =>\n r.verified ? (\n <span className=\"inline-flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 className=\"h-4 w-4\" />\n <span className=\"text-xs font-medium\">Verified</span>\n {r.verifiedAt && (\n <span className=\"text-xs font-normal tabular-nums text-text-tertiary\">\n {formatDate(r.verifiedAt)}\n </span>\n )}\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 text-text-tertiary\">\n <Circle className=\"h-3.5 w-3.5\" />\n <span className=\"text-xs\">Unverified</span>\n </span>\n ),\n },\n {\n key: 'created',\n header: 'Logged',\n align: 'right',\n hideOnMobile: true,\n render: (r) => <span className=\"text-text-tertiary\">{formatRelativeTime(r.createdAt)}</span>,\n },\n {\n key: 'verify',\n header: '',\n align: 'right',\n render: (r) =>\n !r.verified ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setVerifyingId(r.id);\n verifyEmission.mutate(r.id, {\n onSuccess: () => notify.success('Emission verified'),\n onError: (e) => notify.fromError(e),\n onSettled: () => setVerifyingId(null),\n });\n }}\n disabled={verifyingId === r.id}\n >\n {verifyingId === r.id ? 'Verifying…' : 'Verify'}\n </Button>\n ) : null,\n },\n ];\n\n const openDialog = () => {\n setForm(emptyForm());\n setFormError(null);\n setDialogOpen(true);\n };\n\n const handleRecord = async () => {\n const co2e = Number(form.co2eTonnes);\n if (!form.co2eTonnes || Number.isNaN(co2e) || co2e <= 0) {\n const message = 'CO₂e (tonnes) must be greater than zero.';\n setFormError(message);\n notify.error(message);\n return;\n }\n if (!form.periodStart || !form.periodEnd) {\n setFormError('Period start and end are required.');\n return;\n }\n const start = toBackendDate(form.periodStart);\n const end = toBackendDate(form.periodEnd);\n if (!start || !end) {\n setFormError('Period start and end are required.');\n return;\n }\n if (new Date(end).getTime() < new Date(start).getTime()) {\n setFormError('Period end must be on or after the period start.');\n return;\n }\n setFormError(null);\n try {\n await recordEmission.mutateAsync({\n assetId: form.assetId || null,\n scope: form.scope,\n co2eTonnes: co2e,\n periodStart: start,\n periodEnd: end,\n source: form.source.trim() || null,\n methodology: form.methodology.trim() || null,\n notes: form.notes.trim() || null,\n });\n notify.success('Emission recorded');\n setDialogOpen(false);\n setForm(emptyForm());\n } catch (err) {\n notify.fromError(err);\n setFormError(err instanceof Error ? err.message : 'Failed to record emission.');\n }\n };\n\n return (\n <PageLayout\n title={t('theglobalfuel.pages.emissions.title', 'Emissions')}\n description={t(\n 'theglobalfuel.pages.emissions.description',\n 'ESG greenhouse-gas ledger across every asset — GHG Protocol Scope 1 / 2 / 3, with third-party verification.'\n )}\n dataTour=\"theglobalfuel-emissions-page\"\n actions={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record emission\n </Button>\n }\n >\n {/* Header — scope-breakdown donut beside the CO₂e KPI cards (both broad). */}\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"flex flex-col rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"mb-1 flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Emissions by scope</h3>\n <Cloud className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n {metricsQuery.isLoading ? (\n <p className=\"flex flex-1 items-center py-8 text-sm text-text-tertiary\">\n Loading breakdown…\n </p>\n ) : scopeSegments.length > 0 ? (\n <DonutChart\n className=\"mt-3\"\n segments={scopeSegments}\n size={148}\n thickness={0.32}\n centerLabel={compactTonnes(total)}\n centerCaption=\"t CO₂e\"\n valueFormatter={formatTonnesCo2e}\n />\n ) : (\n <p className=\"flex flex-1 items-center py-8 text-sm text-text-tertiary\">\n No emissions recorded yet.\n </p>\n )}\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2\">\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(total) : '—'}\n label=\"Total CO₂e\"\n featured\n footer={\n <p className=\"text-xs tabular-nums text-text-tertiary\">\n {metricsCount} record{metricsCount === 1 ? '' : 's'} · Scope 1 · 2 · 3\n </p>\n }\n />\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_1) : '—'}\n label=\"Scope 1 · Direct\"\n footer={renderScopeShare(byScope.SCOPE_1, total, 'SCOPE_1')}\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_2) : '—'}\n label=\"Scope 2 · Purchased energy\"\n footer={renderScopeShare(byScope.SCOPE_2, total, 'SCOPE_2')}\n />\n <MetricsCard\n icon={<Truck className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_3) : '—'}\n label=\"Scope 3 · Value chain\"\n footer={renderScopeShare(byScope.SCOPE_3, total, 'SCOPE_3')}\n />\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"mt-6 mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={SCOPE_FILTERS}\n value={scope}\n onChange={(v) => {\n setScope(v);\n pager.reset();\n }}\n />\n <Select\n value={assetId}\n onValueChange={(v) => {\n setAssetId(v);\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-56\" aria-label=\"Filter by asset\">\n <SelectValue placeholder=\"All assets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All assets</SelectItem>\n {assetOptions.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading emission records…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load emission records.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Cloud className=\"h-10 w-10\" />}\n title=\"No emissions recorded\"\n description={\n scope === 'ALL' && assetId === 'ALL'\n ? 'Record a CO₂e figure to start building your Scope 1–3 emissions ledger.'\n : 'No emission records match these filters.'\n }\n action={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record emission\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'period', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n {/* Record emission dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) {\n setForm(emptyForm());\n setFormError(null);\n }\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record emission</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a CO₂e figure for a reporting period — optionally attributed to an asset.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleRecord();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emScope\">Scope</Label>\n <Select\n value={form.scope}\n onValueChange={(v) => setForm({ ...form, scope: v as TheEmissionScope })}\n >\n <SelectTrigger id=\"emScope\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SCOPES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emCo2e\">CO₂e (tonnes) *</Label>\n <Input\n id=\"emCo2e\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.co2eTonnes}\n onChange={(e) => setForm({ ...form, co2eTonnes: e.target.value })}\n placeholder=\"1250\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emAsset\">Asset</Label>\n <Select\n value={form.assetId}\n onValueChange={(v) => setForm({ ...form, assetId: v })}\n >\n <SelectTrigger id=\"emAsset\">\n <SelectValue placeholder=\"Optional — org-wide if unset\" />\n </SelectTrigger>\n <SelectContent>\n {assetOptions.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emStart\">Period start *</Label>\n <Input\n id=\"emStart\"\n type=\"date\"\n value={form.periodStart}\n onChange={(e) => setForm({ ...form, periodStart: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emEnd\">Period end *</Label>\n <Input\n id=\"emEnd\"\n type=\"date\"\n value={form.periodEnd}\n onChange={(e) => setForm({ ...form, periodEnd: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emSource\">Source</Label>\n <Input\n id=\"emSource\"\n value={form.source}\n onChange={(e) => setForm({ ...form, source: e.target.value })}\n placeholder=\"Flaring, grid electricity…\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emMethod\">Methodology</Label>\n <Input\n id=\"emMethod\"\n value={form.methodology}\n onChange={(e) => setForm({ ...form, methodology: e.target.value })}\n placeholder=\"GHG Protocol / IPCC / API\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emNotes\">Notes</Label>\n <Textarea\n id=\"emNotes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about this figure.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDialogOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={recordEmission.isPending}>\n {recordEmission.isPending ? 'Recording…' : 'Record emission'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EmissionsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/emissions/EmissionsPage.tsx"],"sourcesContent":["/**\n * Emissions — the ESG greenhouse-gas ledger.\n * @module theglobalfuel/pages/emissions\n *\n * Reads `theListEmissionRecords` (optionally filtered by GHG-Protocol scope and\n * by asset); writes `theRecordEmission` and `theVerifyEmission`. Asset names are\n * resolved from `theListAssets`. The header pairs a scope-breakdown donut with a\n * KPI strip — both computed from a broad, asset-scoped pull of the ledger (not\n * just the current table page), split by Scope 1 / 2 / 3. No mock data — every\n * number comes from the real `wspace-theglobalfuel-svc` contract.\n */\n\nimport { useMemo, useState, type ReactNode } from 'react';\nimport { Plus, Factory, Flame, Zap, Truck, Cloud, CheckCircle2, Circle } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { MiniBar } from '../../components/MiniBar';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Pagination } from '../../components/Pagination';\nimport { DonutChart, type Segment } from '../../components';\nimport {\n useTheAssets,\n useTheEmissionRecords,\n useTheRecordEmission,\n useTheVerifyEmission,\n useCursorPager,\n useNotify,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport {\n formatDate,\n formatRelativeTime,\n formatPercent,\n formatTonnesCo2e,\n formatEnumLabel,\n toBackendDate,\n} from '../../utils/formatters';\nimport type { TheEmissionRecord, TheEmissionScope } from '../../types';\n\nconst SCOPES: TheEmissionScope[] = ['SCOPE_1', 'SCOPE_2', 'SCOPE_3'];\n\nconst SCOPE_FILTERS: FilterOption<TheEmissionScope | 'ALL'>[] = [\n { value: 'ALL', label: 'All scopes' },\n { value: 'SCOPE_1', label: 'Scope 1' },\n { value: 'SCOPE_2', label: 'Scope 2' },\n { value: 'SCOPE_3', label: 'Scope 3' },\n];\n\n/** MiniBar fill token per GHG scope — mirrors the StatusBadge scope palette. */\nconst SCOPE_BAR: Record<TheEmissionScope, string> = {\n SCOPE_1: 'bg-status-error-text',\n SCOPE_2: 'bg-status-warning-text',\n SCOPE_3: 'bg-accent-blue',\n};\n\n/** Short legend label per scope for the breakdown donut. */\nconst SCOPE_LABEL: Record<TheEmissionScope, string> = {\n SCOPE_1: 'Scope 1',\n SCOPE_2: 'Scope 2',\n SCOPE_3: 'Scope 3',\n};\n\n/**\n * Tokenized donut-segment color per scope — aligned to the StatusBadge scope\n * palette (Scope 1 = danger, Scope 2 = warning, Scope 3 = info/blue). All are\n * semantic CSS variables, so the chart themes automatically.\n */\nconst SCOPE_DONUT_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\n/** Compact CO₂e for the donut center (e.g. 1.24M) — full figures live in the KPIs. */\nconst compactTonnes = (n: number): string =>\n new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 }).format(n);\n\nconst PAGE_SIZE = 25;\n\nconst today = (): string => new Date().toISOString().slice(0, 10);\n\ninterface RecordFormState {\n assetId: string;\n scope: TheEmissionScope;\n co2eTonnes: string;\n periodStart: string;\n periodEnd: string;\n source: string;\n methodology: string;\n notes: string;\n}\n\nconst emptyForm = (): RecordFormState => ({\n assetId: '',\n scope: 'SCOPE_1',\n co2eTonnes: '',\n periodStart: today(),\n periodEnd: today(),\n source: '',\n methodology: '',\n notes: '',\n});\n\n/** KPI-card footer: this scope's share of the page total, tinted by scope. */\nconst renderScopeShare = (value: number, total: number, scope: TheEmissionScope): ReactNode => {\n if (total <= 0) return undefined;\n const pct = (value / total) * 100;\n return (\n <div className=\"space-y-2\">\n <MiniBar value={pct} barClassName={SCOPE_BAR[scope]} height={4} />\n <p className=\"text-xs tabular-nums text-text-tertiary\">{formatPercent(pct)} of total</p>\n </div>\n );\n};\n\nexport function EmissionsPage() {\n useThePageView('emissions');\n const t = useTheTranslation();\n const notify = useNotify();\n const [scope, setScope] = useState<TheEmissionScope | 'ALL'>('ALL');\n const [assetId, setAssetId] = useState<string>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<RecordFormState>(emptyForm);\n const [formError, setFormError] = useState<string | null>(null);\n // Track the row whose verification is in flight, so only that row's Verify\n // button disables (verifyEmission.isPending is shared across all rows).\n const [verifyingId, setVerifyingId] = useState<string | null>(null);\n\n // Assets feed the filter Select, the create picker, and the id→name map.\n const assetsQuery = useTheAssets({ pagination: { limit: 100 } });\n const assetMap = useMemo<Record<string, string>>(() => {\n const map: Record<string, string> = {};\n for (const a of assetsQuery.data?.items ?? []) map[a.id] = a.name;\n return map;\n }, [assetsQuery.data]);\n const assetOptions = assetsQuery.data?.items ?? [];\n\n const { data, isLoading, isError, refetch } = useTheEmissionRecords({\n scope: scope === 'ALL' ? undefined : scope,\n assetId: assetId === 'ALL' ? undefined : assetId,\n pagination: { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE },\n });\n const recordEmission = useTheRecordEmission();\n const verifyEmission = useTheVerifyEmission();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n\n // Broad, scope-agnostic pull (respects the asset filter only) so the header\n // donut + KPI totals reflect the whole ledger — stable as the scope tabs and\n // pagination narrow the table below.\n const metricsQuery = useTheEmissionRecords({\n assetId: assetId === 'ALL' ? undefined : assetId,\n pagination: { limit: 200 },\n });\n const metricsRows = useMemo(() => metricsQuery.data?.items ?? [], [metricsQuery.data]);\n const metricsCount = metricsRows.length;\n\n // KPI + donut summary — CO₂e total plus per-scope breakdown across the ledger.\n const { total, byScope } = useMemo(() => {\n const byScope: Record<TheEmissionScope, number> = {\n SCOPE_1: 0,\n SCOPE_2: 0,\n SCOPE_3: 0,\n };\n let total = 0;\n for (const r of metricsRows) {\n total += r.co2eTonnes;\n byScope[r.scope] += r.co2eTonnes;\n }\n return { total, byScope };\n }, [metricsRows]);\n\n // Donut segments — one per scope that actually carries CO₂e.\n const scopeSegments = useMemo<Segment[]>(\n () =>\n SCOPES.filter((s) => byScope[s] > 0).map((s) => ({\n label: SCOPE_LABEL[s],\n value: byScope[s],\n color: SCOPE_DONUT_COLOR[s],\n })),\n [byScope]\n );\n\n const columns: Column<TheEmissionRecord>[] = [\n {\n key: 'scope',\n header: 'Scope',\n render: (r) => <StatusBadge status={r.scope} />,\n },\n {\n key: 'asset',\n header: 'Asset',\n render: (r) => (\n <span className=\"text-text-secondary\">\n {r.assetId ? (assetMap[r.assetId] ?? `${r.assetId.slice(0, 8)}…`) : '—'}\n </span>\n ),\n },\n {\n key: 'co2e',\n header: 'CO₂e',\n align: 'right',\n sortValue: (r) => r.co2eTonnes,\n render: (r) => (\n <span className=\"tabular-nums font-medium text-text-primary\">\n {formatTonnesCo2e(r.co2eTonnes)}\n </span>\n ),\n },\n {\n key: 'period',\n header: 'Period',\n hideOnMobile: true,\n sortValue: (r) => new Date(r.periodStart).getTime(),\n render: (r) => (\n <span className=\"text-text-secondary\">\n {formatDate(r.periodStart)} – {formatDate(r.periodEnd)}\n </span>\n ),\n },\n {\n key: 'source',\n header: 'Source',\n hideOnMobile: true,\n render: (r) => <span className=\"text-text-secondary\">{r.source ?? '—'}</span>,\n },\n {\n key: 'verified',\n header: 'Verified',\n render: (r) =>\n r.verified ? (\n <span className=\"inline-flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 className=\"h-4 w-4\" />\n <span className=\"text-xs font-medium\">Verified</span>\n {r.verifiedAt && (\n <span className=\"text-xs font-normal tabular-nums text-text-tertiary\">\n {formatDate(r.verifiedAt)}\n </span>\n )}\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 text-text-tertiary\">\n <Circle className=\"h-3.5 w-3.5\" />\n <span className=\"text-xs\">Unverified</span>\n </span>\n ),\n },\n {\n key: 'created',\n header: 'Logged',\n align: 'right',\n hideOnMobile: true,\n render: (r) => <span className=\"text-text-tertiary\">{formatRelativeTime(r.createdAt)}</span>,\n },\n {\n key: 'verify',\n header: '',\n align: 'right',\n render: (r) =>\n !r.verified ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setVerifyingId(r.id);\n verifyEmission.mutate(r.id, {\n onSuccess: () => notify.success('Emission verified'),\n onError: (e) => notify.fromError(e),\n onSettled: () => setVerifyingId(null),\n });\n }}\n disabled={verifyingId === r.id}\n >\n {verifyingId === r.id ? 'Verifying…' : 'Verify'}\n </Button>\n ) : null,\n },\n ];\n\n const openDialog = () => {\n setForm(emptyForm());\n setFormError(null);\n setDialogOpen(true);\n };\n\n const handleRecord = async () => {\n const co2e = Number(form.co2eTonnes);\n if (!form.co2eTonnes || Number.isNaN(co2e) || co2e <= 0) {\n const message = 'CO₂e (tonnes) must be greater than zero.';\n setFormError(message);\n notify.error(message);\n return;\n }\n if (!form.periodStart || !form.periodEnd) {\n setFormError('Period start and end are required.');\n return;\n }\n const start = toBackendDate(form.periodStart);\n const end = toBackendDate(form.periodEnd);\n if (!start || !end) {\n setFormError('Period start and end are required.');\n return;\n }\n if (new Date(end).getTime() < new Date(start).getTime()) {\n setFormError('Period end must be on or after the period start.');\n return;\n }\n setFormError(null);\n try {\n await recordEmission.mutateAsync({\n assetId: form.assetId || null,\n scope: form.scope,\n co2eTonnes: co2e,\n periodStart: start,\n periodEnd: end,\n source: form.source.trim() || null,\n methodology: form.methodology.trim() || null,\n notes: form.notes.trim() || null,\n });\n notify.success('Emission recorded');\n setDialogOpen(false);\n setForm(emptyForm());\n } catch (err) {\n notify.fromError(err);\n setFormError(err instanceof Error ? err.message : 'Failed to record emission.');\n }\n };\n\n return (\n <PageLayout\n title={t('theglobalfuel.pages.emissions.title', 'Emissions')}\n description={t(\n 'theglobalfuel.pages.emissions.description',\n 'ESG greenhouse-gas ledger across every asset — GHG Protocol Scope 1 / 2 / 3, with third-party verification.'\n )}\n dataTour=\"theglobalfuel-emissions-page\"\n actions={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record emission\n </Button>\n }\n >\n {/* Header — scope-breakdown donut beside the CO₂e KPI cards (both broad). */}\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"flex flex-col rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"mb-1 flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Emissions by scope</h3>\n <Cloud className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n {metricsQuery.isLoading ? (\n <p className=\"flex flex-1 items-center py-8 text-sm text-text-tertiary\">\n Loading breakdown…\n </p>\n ) : scopeSegments.length > 0 ? (\n <DonutChart\n className=\"mt-3\"\n segments={scopeSegments}\n size={148}\n thickness={0.32}\n centerLabel={compactTonnes(total)}\n centerCaption=\"t CO₂e\"\n valueFormatter={formatTonnesCo2e}\n />\n ) : (\n <p className=\"flex flex-1 items-center py-8 text-sm text-text-tertiary\">\n No emissions recorded yet.\n </p>\n )}\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2\">\n <MetricsCard\n icon={<Factory className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(total) : '—'}\n label=\"Total CO₂e\"\n featured\n footer={\n <p className=\"text-xs tabular-nums text-text-tertiary\">\n {metricsCount} record{metricsCount === 1 ? '' : 's'} · Scope 1 · 2 · 3\n </p>\n }\n />\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_1) : '—'}\n label=\"Scope 1 · Direct\"\n footer={renderScopeShare(byScope.SCOPE_1, total, 'SCOPE_1')}\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_2) : '—'}\n label=\"Scope 2 · Purchased energy\"\n footer={renderScopeShare(byScope.SCOPE_2, total, 'SCOPE_2')}\n />\n <MetricsCard\n icon={<Truck className=\"h-5 w-5\" />}\n value={metricsCount > 0 ? formatTonnesCo2e(byScope.SCOPE_3) : '—'}\n label=\"Scope 3 · Value chain\"\n footer={renderScopeShare(byScope.SCOPE_3, total, 'SCOPE_3')}\n />\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"mt-6 mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={SCOPE_FILTERS}\n value={scope}\n onChange={(v) => {\n setScope(v);\n pager.reset();\n }}\n />\n <Select\n value={assetId}\n onValueChange={(v) => {\n setAssetId(v);\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-56\" aria-label=\"Filter by asset\">\n <SelectValue placeholder=\"All assets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All assets</SelectItem>\n {assetOptions.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading emission records…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load emission records.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Cloud className=\"h-10 w-10\" />}\n title=\"No emissions recorded\"\n description={\n scope === 'ALL' && assetId === 'ALL'\n ? 'Record a CO₂e figure to start building your Scope 1–3 emissions ledger.'\n : 'No emission records match these filters.'\n }\n action={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record emission\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'period', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n {/* Record emission dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) {\n setForm(emptyForm());\n setFormError(null);\n }\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record emission</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a CO₂e figure for a reporting period — optionally attributed to an asset.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleRecord();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emScope\">Scope</Label>\n <Select\n value={form.scope}\n onValueChange={(v) => setForm({ ...form, scope: v as TheEmissionScope })}\n >\n <SelectTrigger id=\"emScope\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SCOPES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emCo2e\">CO₂e (tonnes) *</Label>\n <Input\n id=\"emCo2e\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.co2eTonnes}\n onChange={(e) => setForm({ ...form, co2eTonnes: e.target.value })}\n placeholder=\"1250\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emAsset\">Asset</Label>\n <Select\n value={form.assetId}\n onValueChange={(v) => setForm({ ...form, assetId: v })}\n >\n <SelectTrigger id=\"emAsset\">\n <SelectValue placeholder=\"Optional — org-wide if unset\" />\n </SelectTrigger>\n <SelectContent>\n {assetOptions.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emStart\">Period start *</Label>\n <Input\n id=\"emStart\"\n type=\"date\"\n value={form.periodStart}\n onChange={(e) => setForm({ ...form, periodStart: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emEnd\">Period end *</Label>\n <Input\n id=\"emEnd\"\n type=\"date\"\n value={form.periodEnd}\n onChange={(e) => setForm({ ...form, periodEnd: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emSource\">Source</Label>\n <Input\n id=\"emSource\"\n value={form.source}\n onChange={(e) => setForm({ ...form, source: e.target.value })}\n placeholder=\"Flaring, grid electricity…\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emMethod\">Methodology</Label>\n <Input\n id=\"emMethod\"\n value={form.methodology}\n onChange={(e) => setForm({ ...form, methodology: e.target.value })}\n placeholder=\"GHG Protocol / IPCC / API\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"emNotes\">Notes</Label>\n <Textarea\n id=\"emNotes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about this figure.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDialogOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={recordEmission.isPending}>\n {recordEmission.isPending ? 'Recording…' : 'Record emission'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -121,7 +121,7 @@ function P() {
121
121
  render: (e) => /* @__PURE__ */ h("div", {
122
122
  className: "flex items-center gap-3",
123
123
  children: [/* @__PURE__ */ m("div", {
124
- className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent",
124
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-action-primary-bg",
125
125
  children: /* @__PURE__ */ m(f, { className: "h-4 w-4" })
126
126
  }), /* @__PURE__ */ h("div", {
127
127
  className: "min-w-0",