@burdenoff/microfe-assethandler 2026.813.1 → 2026.813.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 (78) hide show
  1. package/dist/assethandler/components/charts/BarsChart.js +25 -25
  2. package/dist/assethandler/components/charts/ChartParts.js +27 -27
  3. package/dist/assethandler/components/charts/DonutChart.js +17 -17
  4. package/dist/assethandler/components/charts/ProgressMeter.js +5 -5
  5. package/dist/assethandler/components/charts/RadialGauge.js +13 -13
  6. package/dist/assethandler/components/charts/Sparkline.js +27 -27
  7. package/dist/assethandler/components/charts/TrendChart.js +47 -47
  8. package/dist/assethandler/components/index.js +1 -1
  9. package/dist/assethandler/pages/AiPredictionsPage.js +20 -20
  10. package/dist/assethandler/pages/AnalyticsPage.d.ts.map +1 -1
  11. package/dist/assethandler/pages/AnalyticsPage.js +133 -133
  12. package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
  13. package/dist/assethandler/pages/AssetsAssetCategoriesPage.d.ts.map +1 -1
  14. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +55 -55
  15. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  16. package/dist/assethandler/pages/AssetsAssetCreatePage.d.ts.map +1 -1
  17. package/dist/assethandler/pages/AssetsAssetCreatePage.js +77 -78
  18. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  19. package/dist/assethandler/pages/AssetsAssetDetailPage.d.ts.map +1 -1
  20. package/dist/assethandler/pages/AssetsAssetDetailPage.js +140 -141
  21. package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
  22. package/dist/assethandler/pages/AssetsAssetListPage.js +249 -249
  23. package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
  24. package/dist/assethandler/pages/AssetsAssetTrackingPage.js +8 -8
  25. package/dist/assethandler/pages/CmdbBrowserPage.js +34 -34
  26. package/dist/assethandler/pages/DashboardPage.d.ts.map +1 -1
  27. package/dist/assethandler/pages/DashboardPage.js +256 -255
  28. package/dist/assethandler/pages/DashboardPage.js.map +1 -1
  29. package/dist/assethandler/pages/DiscoveryJobsPage.js +27 -27
  30. package/dist/assethandler/pages/EsgDashboardPage.js +53 -53
  31. package/dist/assethandler/pages/FinanceBudgetDetailPage.d.ts.map +1 -1
  32. package/dist/assethandler/pages/FinanceBudgetDetailPage.js +156 -155
  33. package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
  34. package/dist/assethandler/pages/FinanceFinanceHubPage.d.ts.map +1 -1
  35. package/dist/assethandler/pages/FinanceFinanceHubPage.js +475 -475
  36. package/dist/assethandler/pages/FinanceFinanceHubPage.js.map +1 -1
  37. package/dist/assethandler/pages/FinanceInvoiceDetailPage.d.ts.map +1 -1
  38. package/dist/assethandler/pages/FinanceInvoiceDetailPage.js +141 -140
  39. package/dist/assethandler/pages/FinanceInvoiceDetailPage.js.map +1 -1
  40. package/dist/assethandler/pages/ItilChangeManagementPage.js +21 -21
  41. package/dist/assethandler/pages/ItilIncidentManagementPage.js +34 -34
  42. package/dist/assethandler/pages/ItilProblemManagementPage.js +17 -17
  43. package/dist/assethandler/pages/KnowledgeBasePage.js +39 -39
  44. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.d.ts.map +1 -1
  45. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js +114 -114
  46. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
  47. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.d.ts.map +1 -1
  48. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js +158 -157
  49. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
  50. package/dist/assethandler/pages/OperationsOperationsHubPage.d.ts.map +1 -1
  51. package/dist/assethandler/pages/OperationsOperationsHubPage.js +253 -252
  52. package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
  53. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.d.ts.map +1 -1
  54. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js +96 -95
  55. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
  56. package/dist/assethandler/pages/ProvenancePage.js +21 -21
  57. package/dist/assethandler/pages/ReportsDashboardPage.d.ts.map +1 -1
  58. package/dist/assethandler/pages/ReportsDashboardPage.js +301 -301
  59. package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
  60. package/dist/assethandler/pages/SecuritySecurityHubPage.js +126 -126
  61. package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
  62. package/dist/assethandler/pages/TransactionsTransactionDetailPage.d.ts.map +1 -1
  63. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js +112 -113
  64. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
  65. package/dist/assethandler/pages/TransactionsTransactionListPage.d.ts.map +1 -1
  66. package/dist/assethandler/pages/TransactionsTransactionListPage.js +136 -137
  67. package/dist/assethandler/pages/TransactionsTransactionListPage.js.map +1 -1
  68. package/dist/assethandler/providers/AssetHandlerProvider.d.ts +14 -0
  69. package/dist/assethandler/providers/AssetHandlerProvider.d.ts.map +1 -1
  70. package/dist/assethandler/providers/AssetHandlerProvider.js +44 -34
  71. package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
  72. package/dist/assethandler/types/index.d.ts +10 -0
  73. package/dist/assethandler/types/index.d.ts.map +1 -1
  74. package/dist/assethandler/utils/money.d.ts +34 -0
  75. package/dist/assethandler/utils/money.d.ts.map +1 -0
  76. package/dist/assethandler/utils/money.js +27 -0
  77. package/dist/assethandler/utils/money.js.map +1 -0
  78. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/assethandler/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n PieChart,\n Activity,\n Wrench,\n AlertTriangle,\n Wallet,\n DollarSign,\n TrendingDown,\n Users,\n Gauge,\n Receipt,\n ClipboardList,\n ShieldCheck,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { useCopy } from '../hooks/useCopy';\nimport { cn } from '../utils/cn';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n TrendChart,\n RadialGauge,\n ProgressMeter,\n formatCompact,\n formatCurrencyCompact,\n seriesColor,\n type ChartDatum,\n type DonutSlice,\n} from '../components';\n\n// ─── Backend rollups (wspace-assethandler-svc, @rbac-guarded, no args) ────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface AssetHandlerStatusCount {\n status: string;\n count: number;\n}\n\ninterface AssetHandlerUtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: AssetHandlerStatusCount[];\n}\n\nconst ANALYTICS_ROLLUPS_QUERY = gql`\n query AssetHandlerAnalyticsRollups {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst usd = formatCurrencyCompact('USD');\n\nfunction titleCase(value: string): string {\n return value.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\nfunction parseDate(value: string | undefined | null): Date | null {\n if (!value) return null;\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n}\nfunction monthKey(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n}\nfunction monthLabel(key: string): string {\n const [year, month] = key.split('-').map(Number);\n return new Date(year, month - 1, 1).toLocaleString(undefined, { month: 'short' });\n}\n\ntype PillTone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\nconst PILL_CLASS: Record<PillTone, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\nfunction StatusPill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap',\n PILL_CLASS[tone]\n )}\n >\n {children}\n </span>\n );\n}\nconst txStatusTone = (status: string): PillTone =>\n status === 'completed' || status === 'approved'\n ? 'success'\n : status === 'rejected'\n ? 'error'\n : status === 'pending'\n ? 'warning'\n : 'neutral';\n\nexport function AnalyticsPage() {\n const tc = useCopy();\n const { assets, transactions, incidents, maintenanceRecords, budgets, invoices, workOrders } =\n useAssetHandlerStore();\n\n const { data: rollupData } = useQuery<{\n financialSummary: AssetHandlerFinancialSummary;\n utilizationReport: AssetHandlerUtilizationReport;\n }>(ANALYTICS_ROLLUPS_QUERY, { fetchPolicy: 'cache-and-network', errorPolicy: 'all' });\n const financialSummary = rollupData?.financialSummary;\n const utilizationReport = rollupData?.utilizationReport;\n\n // ─── Asset aggregations (count-based) ────────────────────────────────────────\n const categoryDonut = useMemo<DonutSlice[]>(() => {\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n const cat = titleCase(a.category);\n acc[cat] = (acc[cat] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([name, value]) => ({ name, value }));\n }, [assets]);\n\n const categoryBars = useMemo<ChartDatum[]>(\n () => categoryDonut.map((s) => ({ category: s.name, count: s.value })),\n [categoryDonut]\n );\n\n const statusDonut = useMemo<DonutSlice[]>(() => {\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {});\n const toneFor: Record<string, PillTone> = {\n active: 'success',\n maintenance: 'warning',\n retired: 'neutral',\n inactive: 'neutral',\n disposed: 'error',\n };\n const toneToken: Record<PillTone, string> = {\n success: 'var(--color-status-success-text)',\n warning: 'var(--color-status-warning-text)',\n error: 'var(--color-status-error-text)',\n info: 'var(--color-status-info-text)',\n neutral: 'var(--color-text-muted)',\n };\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({\n name: titleCase(status),\n value,\n color: toneToken[toneFor[status] ?? 'neutral'],\n }));\n }, [assets]);\n\n const conditionBars = useMemo<ChartDatum[]>(() => {\n const order = ['excellent', 'good', 'fair', 'poor', 'damaged'];\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.condition] = (acc[a.condition] || 0) + 1;\n return acc;\n }, {});\n return order\n .filter((c) => counts[c])\n .map((c) => ({ condition: titleCase(c), count: counts[c] }))\n .concat(\n Object.entries(counts)\n .filter(([c]) => !order.includes(c))\n .map(([c, count]) => ({ condition: titleCase(c), count }))\n );\n }, [assets]);\n\n const totalAssets = assets.length;\n const activeAssets = assets.filter((a) => a.status === 'active').length;\n const distinctCategories = categoryDonut.length;\n const attentionAssets = assets.filter(\n (a) => a.condition === 'poor' || a.condition === 'damaged' || a.status === 'maintenance'\n );\n\n // ─── Financial aggregations ──────────────────────────────────────────────────\n const transactionsTotal = transactions.reduce((s, t) => s + Math.abs(t.amount), 0);\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const budgetRemaining = budgets.reduce((s, b) => s + b.remaining, 0);\n const maintenanceCost = maintenanceRecords.reduce(\n (s, m) => s + (m.actualCost ?? m.estimatedCost ?? 0),\n 0\n );\n const outstandingInvoices = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n );\n const outstandingInvoiceTotal = outstandingInvoices.reduce((s, i) => s + i.amount, 0);\n\n const budgetBars = useMemo<ChartDatum[]>(\n () =>\n budgets.map((b) => ({\n name: b.name,\n allocated: b.allocated,\n spent: b.spent,\n })),\n [budgets]\n );\n\n const spendTrend = useMemo<ChartDatum[]>(() => {\n const byMonth = new Map<string, number>();\n for (const tx of transactions) {\n const d = parseDate(tx.date);\n if (!d) continue;\n const key = monthKey(d);\n byMonth.set(key, (byMonth.get(key) ?? 0) + Math.abs(tx.amount));\n }\n return [...byMonth.keys()]\n .sort()\n .map((k) => ({ month: monthLabel(k), spend: Math.round(byMonth.get(k) ?? 0) }));\n }, [transactions]);\n\n // ─── Operational aggregations ────────────────────────────────────────────────\n const resolvedIncidents = incidents.filter(\n (i) => i.status === 'resolved' || i.status === 'closed'\n );\n const slaBreached = incidents.filter((i) => i.slaBreached);\n const slaCompliancePct =\n incidents.length > 0 ? Math.round((1 - slaBreached.length / incidents.length) * 100) : 100;\n const completedMaintenance = maintenanceRecords.filter((m) => m.status === 'completed');\n const scheduledMaintenance = maintenanceRecords.filter((m) => m.status === 'scheduled');\n const openWorkOrders = workOrders.filter(\n (w) => w.status !== 'completed' && w.status !== 'cancelled'\n );\n\n const utilStatusBars = useMemo<ChartDatum[]>(\n () =>\n (utilizationReport?.byStatus ?? []).map((s) => ({\n status: titleCase(s.status),\n count: s.count,\n })),\n [utilizationReport]\n );\n\n const workOrderDonut = useMemo<DonutSlice[]>(() => {\n const counts = workOrders.reduce<Record<string, number>>((acc, w) => {\n acc[w.status] = (acc[w.status] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([name, value]) => ({ name: titleCase(name), value }));\n }, [workOrders]);\n\n const maintenanceCostTrend = useMemo<ChartDatum[]>(() => {\n const byMonth = new Map<string, number>();\n for (const m of maintenanceRecords) {\n const d = parseDate(m.completedDate ?? m.scheduledDate);\n const cost = m.actualCost ?? m.estimatedCost;\n if (!d || cost === undefined) continue;\n const key = monthKey(d);\n byMonth.set(key, (byMonth.get(key) ?? 0) + cost);\n }\n return [...byMonth.keys()]\n .sort()\n .map((k) => ({ month: monthLabel(k), cost: Math.round(byMonth.get(k) ?? 0) }));\n }, [maintenanceRecords]);\n\n const financialTiles = financialSummary\n ? [\n { label: 'Total Asset Value', value: financialSummary.totalAssetValue, icon: DollarSign },\n {\n label: 'Total Depreciation',\n value: financialSummary.totalDepreciation,\n icon: TrendingDown,\n },\n {\n label: 'Maintenance Spend',\n value: financialSummary.totalMaintenanceSpend,\n icon: Wrench,\n },\n {\n label: 'Transaction Expense',\n value: financialSummary.transactionExpense,\n icon: Receipt,\n },\n { label: 'Budget Allocated', value: financialSummary.budgetAllocated, icon: Wallet },\n { label: 'Budget Spent', value: financialSummary.budgetSpent, icon: Wallet },\n {\n label: 'Invoices Outstanding',\n value: financialSummary.invoicesOutstanding,\n icon: AlertTriangle,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Analytics &amp; Business Intelligence</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Asset, financial and operational insights across your estate\n </p>\n </div>\n\n <PagePurpose>\n Turn raw asset data into decisions. See how your estate is distributed by category, status\n and condition, where budget is being spent, and how operations are trending — so you can\n plan work, justify replacements and spot risk before it shows up as downtime.\n </PagePurpose>\n\n <Tabs defaultValue=\"overview\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"financial\">Financial</TabsTrigger>\n <TabsTrigger value=\"operational\">Operational</TabsTrigger>\n <TabsTrigger value=\"insights\">Insights</TabsTrigger>\n </TabsList>\n\n {/* ─── Overview Tab ─────────────────────────────────────────────────── */}\n <TabsContent value=\"overview\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n hero\n label={tc('assethandler.analytics.metrics.totalAssets', 'Total {{assets}}')}\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile label=\"Categories\" value={distinctCategories} icon={PieChart} />\n <StatTile label=\"Needs Attention\" value={attentionAssets.length} icon={AlertTriangle} />\n <StatTile\n label=\"Open Incidents\"\n value={incidents.length - resolvedIncidents.length}\n icon={Activity}\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Assets by Category\"\n subtitle=\"Computed from real data\"\n icon={PieChart}\n >\n <BarsChart\n data={categoryBars}\n xKey=\"category\"\n series={[{ key: 'count', label: 'Assets' }]}\n orientation=\"horizontal\"\n height={Math.max(220, categoryBars.length * 40)}\n categoryWidth={120}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by category\"\n />\n </ChartCard>\n\n <ChartCard title=\"Assets by Status\" subtitle=\"Computed from real data\" icon={Activity}>\n <DonutChart\n data={statusDonut}\n centerLabel={tc('assethandler.analytics.donut.centerLabel', '{{assets}}')}\n centerValue={totalAssets}\n height={260}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n </div>\n\n <ChartCard title=\"Asset Condition Mix\" subtitle=\"Computed from real data\" icon={Gauge}>\n <BarsChart\n data={conditionBars}\n xKey=\"condition\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by condition\"\n />\n </ChartCard>\n </TabsContent>\n\n {/* ─── Financial Tab ────────────────────────────────────────────────── */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n {financialSummary ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {financialTiles.slice(0, 4).map((tile, i) => (\n <StatTile\n key={tile.label}\n hero={i === 0}\n label={tile.label}\n value={tile.value}\n format={usd}\n icon={tile.icon}\n />\n ))}\n </div>\n ) : (\n <Card>\n <CardContent className=\"py-8\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Financial summary unavailable\"\n description=\"Portfolio valuation and budget rollups will appear once financial data is recorded.\"\n />\n </CardContent>\n </Card>\n )}\n\n {financialSummary ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {financialTiles.slice(4).map((tile) => (\n <StatTile\n key={tile.label}\n label={tile.label}\n value={tile.value}\n format={usd}\n icon={tile.icon}\n delta={\n tile.label === 'Invoices Outstanding'\n ? `${financialSummary.invoicesOutstandingCount} unpaid`\n : undefined\n }\n deltaTone=\"neutral\"\n />\n ))}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Budget: Allocated vs Spent\"\n subtitle=\"Per budget, from real data\"\n icon={Wallet}\n >\n {budgetBars.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <BarsChart\n data={budgetBars}\n xKey=\"name\"\n series={[\n { key: 'allocated', label: 'Allocated', color: seriesColor(1) },\n { key: 'spent', label: 'Spent' },\n ]}\n height={Math.max(220, budgetBars.length * 56)}\n orientation=\"horizontal\"\n categoryWidth={120}\n valueFormat={usd}\n ariaLabel=\"Budget allocated versus spent\"\n />\n )}\n </ChartCard>\n\n <ChartCard title=\"Spend Over Time\" subtitle=\"Monthly transaction spend\" icon={Receipt}>\n {spendTrend.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions yet\"\n description=\"Recorded purchases, leases and disposals will chart here.\"\n />\n ) : (\n <TrendChart\n data={spendTrend}\n xKey=\"month\"\n series={[{ key: 'spend', label: 'Spend' }]}\n valueFormat={usd}\n height={260}\n ariaLabel=\"Monthly transaction spend\"\n />\n )}\n </ChartCard>\n </div>\n\n {/* Budget utilization meters */}\n <ChartCard\n title=\"Budget Utilization\"\n subtitle=\"Allocated vs. spent per budget\"\n icon={Wallet}\n >\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {budgets.map((budget) => (\n <ProgressMeter\n key={budget.id}\n label={budget.name}\n value={budget.spent}\n max={budget.allocated}\n format={(v) => `$${Math.round(v).toLocaleString()}`}\n />\n ))}\n <Separator />\n <div className=\"flex items-center justify-between text-sm font-medium\">\n <span>Remaining across all budgets</span>\n <span className=\"tabular-nums\">${budgetRemaining.toLocaleString()}</span>\n </div>\n </div>\n )}\n </ChartCard>\n\n {/* Financial quick stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Transactions\"\n value={transactions.length}\n icon={Receipt}\n delta={`$${transactionsTotal.toLocaleString()} value`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Budget Allocated\"\n value={budgetAllocated}\n format={usd}\n icon={Wallet}\n delta={`$${budgetSpent.toLocaleString()} spent`}\n deltaTone=\"neutral\"\n />\n <StatTile label=\"Maintenance Cost\" value={maintenanceCost} format={usd} icon={Wrench} />\n <StatTile\n label=\"Outstanding Invoices\"\n value={outstandingInvoiceTotal}\n format={usd}\n icon={AlertTriangle}\n delta={`${outstandingInvoices.length} of ${invoices.length}`}\n deltaTone=\"neutral\"\n />\n </div>\n\n {/* Recent transactions */}\n <ChartCard title=\"Recent Transactions\" icon={Receipt}>\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions\"\n description=\"Asset purchases, leases and disposals will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-text-secondary\">\n <th className=\"py-2 text-left font-medium\">Type</th>\n <th className=\"py-2 text-left font-medium\">Description</th>\n <th className=\"py-2 text-right font-medium\">Amount</th>\n <th className=\"py-2 text-left font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 6).map((txn) => (\n <tr\n key={txn.id}\n className=\"border-b border-border-seam hover:bg-bg-sunken/50\"\n >\n <td className=\"py-2.5 capitalize\">{txn.type}</td>\n <td className=\"max-w-xs truncate py-2.5\">{txn.description}</td>\n <td\n className={cn(\n 'py-2.5 text-right font-medium tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n ${Math.abs(txn.amount).toLocaleString()}\n </td>\n <td className=\"py-2.5\">\n <StatusPill tone={txStatusTone(txn.status)}>{txn.status}</StatusPill>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </ChartCard>\n </TabsContent>\n\n {/* ─── Operational Tab ──────────────────────────────────────────────── */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n <StatTile\n label={tc('assethandler.analytics.metrics.totalAssets', 'Total {{assets}}')}\n value={utilizationReport?.totalAssets ?? totalAssets}\n icon={Box}\n />\n <StatTile\n label=\"Assigned\"\n value={utilizationReport?.assignedAssets ?? '—'}\n icon={Users}\n />\n <StatTile\n label=\"Unassigned\"\n value={utilizationReport?.unassignedAssets ?? '—'}\n icon={Box}\n />\n <StatTile label=\"Idle\" value={utilizationReport?.idleAssets ?? '—'} icon={Activity} />\n <StatTile\n label=\"Overdue\"\n value={utilizationReport?.overdueAssignments ?? '—'}\n icon={AlertTriangle}\n delta={\n utilizationReport && utilizationReport.overdueAssignments > 0\n ? 'action needed'\n : undefined\n }\n deltaTone=\"negative\"\n deltaDirection=\"up\"\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Status\" subtitle=\"Assignment coverage\" icon={Gauge}>\n <BarsChart\n data={utilStatusBars}\n xKey=\"status\"\n series={[{ key: 'count', label: 'Assets' }]}\n orientation=\"horizontal\"\n height={Math.max(200, utilStatusBars.length * 40)}\n categoryWidth={120}\n emptyLabel=\"No utilization data\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n\n <ChartCard title=\"Work Orders by Status\" subtitle=\"From real data\" icon={ClipboardList}>\n <DonutChart\n data={workOrderDonut}\n centerLabel=\"Work orders\"\n centerValue={workOrders.length}\n height={260}\n emptyLabel=\"No work orders\"\n ariaLabel=\"Work orders by status\"\n />\n </ChartCard>\n </div>\n\n <ChartCard\n title=\"Maintenance Cost Over Time\"\n subtitle=\"Monthly recorded maintenance cost\"\n icon={Wrench}\n >\n {maintenanceCostTrend.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No maintenance cost yet\"\n description=\"Once maintenance records carry cost, monthly spend will chart here.\"\n />\n ) : (\n <TrendChart\n data={maintenanceCostTrend}\n xKey=\"month\"\n series={[{ key: 'cost', label: 'Cost' }]}\n valueFormat={usd}\n height={260}\n ariaLabel=\"Monthly maintenance cost\"\n />\n )}\n </ChartCard>\n\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Incidents\"\n value={incidents.length}\n icon={Activity}\n delta={`${resolvedIncidents.length} resolved`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"SLA Breaches\"\n value={slaBreached.length}\n icon={AlertTriangle}\n delta={slaBreached.length > 0 ? 'over SLA' : 'all within SLA'}\n deltaTone={slaBreached.length > 0 ? 'negative' : 'positive'}\n deltaDirection={slaBreached.length > 0 ? 'up' : 'neutral'}\n />\n <StatTile\n label=\"Maintenance Done\"\n value={completedMaintenance.length}\n icon={Wrench}\n delta={`of ${maintenanceRecords.length}`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Open Work Orders\"\n value={openWorkOrders.length}\n icon={ClipboardList}\n delta={`of ${workOrders.length}`}\n deltaTone=\"neutral\"\n />\n </div>\n </TabsContent>\n\n {/* ─── Insights Tab ─────────────────────────────────────────────────── */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <ChartCard\n title=\"SLA Compliance\"\n subtitle=\"Incidents resolved within SLA\"\n icon={ShieldCheck}\n >\n <RadialGauge\n value={slaCompliancePct}\n label=\"within SLA\"\n sublabel={`${slaBreached.length} of ${incidents.length} breached`}\n format={(v) => `${Math.round(v)}%`}\n height={220}\n ariaLabel=\"SLA compliance percentage\"\n />\n </ChartCard>\n\n <div className=\"lg:col-span-2 grid gap-4 sm:grid-cols-3\">\n <Card className=\"border-status-info-border bg-status-info-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-info-text text-base\">Diversification</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-info-text\">\n Your estate spans {distinctCategories} categories across {totalAssets} assets.{' '}\n {activeAssets} are currently active.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-success-border bg-status-success-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-success-text text-base\">Budget</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-success-text\">\n ${budgetSpent.toLocaleString()} spent of ${budgetAllocated.toLocaleString()}{' '}\n allocated, leaving ${budgetRemaining.toLocaleString()} across {budgets.length}{' '}\n budgets.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-warning-border bg-status-warning-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-warning-text text-base\">Maintenance</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-warning-text\">\n {scheduledMaintenance.length} tasks scheduled. Total maintenance spend is $\n {maintenanceCost.toLocaleString()}. Stay on top of preventive maintenance.\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Assets needing attention */}\n <EmphasisPanel>\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n Assets Needing Attention\n </CardTitle>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n {attentionAssets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-success\"\n title=\"Everything looks healthy\"\n description=\"No assets are flagged as poor, damaged or in maintenance right now.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {attentionAssets.slice(0, 6).map((asset, i) => (\n <div\n key={asset.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-seam bg-gloss-card p-3\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{asset.name}</p>\n <p className=\"truncate text-xs text-text-secondary capitalize\">\n {asset.category.replace('_', ' ')}\n {asset.location ? ` · ${asset.location}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusPill tone={asset.condition === 'damaged' ? 'error' : 'warning'}>\n {asset.condition}\n </StatusPill>\n <StatusPill tone={asset.status === 'active' ? 'success' : 'warning'}>\n {asset.status.replace('_', ' ')}\n </StatusPill>\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </EmphasisPanel>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/assethandler/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useAssetMoney } from '../providers/AssetHandlerProvider';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n PieChart,\n Activity,\n Wrench,\n AlertTriangle,\n Wallet,\n DollarSign,\n TrendingDown,\n Users,\n Gauge,\n Receipt,\n ClipboardList,\n ShieldCheck,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { useCopy } from '../hooks/useCopy';\nimport { cn } from '../utils/cn';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n TrendChart,\n RadialGauge,\n ProgressMeter,\n formatCompact,\n seriesColor,\n type ChartDatum,\n type DonutSlice,\n} from '../components';\n\n// ─── Backend rollups (wspace-assethandler-svc, @rbac-guarded, no args) ────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface AssetHandlerStatusCount {\n status: string;\n count: number;\n}\n\ninterface AssetHandlerUtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: AssetHandlerStatusCount[];\n}\n\nconst ANALYTICS_ROLLUPS_QUERY = gql`\n query AssetHandlerAnalyticsRollups {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nfunction titleCase(value: string): string {\n return value.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\nfunction parseDate(value: string | undefined | null): Date | null {\n if (!value) return null;\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n}\nfunction monthKey(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n}\nfunction monthLabel(key: string): string {\n const [year, month] = key.split('-').map(Number);\n return new Date(year, month - 1, 1).toLocaleString(undefined, { month: 'short' });\n}\n\ntype PillTone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\nconst PILL_CLASS: Record<PillTone, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\nfunction StatusPill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap',\n PILL_CLASS[tone]\n )}\n >\n {children}\n </span>\n );\n}\nconst txStatusTone = (status: string): PillTone =>\n status === 'completed' || status === 'approved'\n ? 'success'\n : status === 'rejected'\n ? 'error'\n : status === 'pending'\n ? 'warning'\n : 'neutral';\n\nexport function AnalyticsPage() {\n const { compact: usd, format: money } = useAssetMoney();\n const tc = useCopy();\n const { assets, transactions, incidents, maintenanceRecords, budgets, invoices, workOrders } =\n useAssetHandlerStore();\n\n const { data: rollupData } = useQuery<{\n financialSummary: AssetHandlerFinancialSummary;\n utilizationReport: AssetHandlerUtilizationReport;\n }>(ANALYTICS_ROLLUPS_QUERY, { fetchPolicy: 'cache-and-network', errorPolicy: 'all' });\n const financialSummary = rollupData?.financialSummary;\n const utilizationReport = rollupData?.utilizationReport;\n\n // ─── Asset aggregations (count-based) ────────────────────────────────────────\n const categoryDonut = useMemo<DonutSlice[]>(() => {\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n const cat = titleCase(a.category);\n acc[cat] = (acc[cat] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([name, value]) => ({ name, value }));\n }, [assets]);\n\n const categoryBars = useMemo<ChartDatum[]>(\n () => categoryDonut.map((s) => ({ category: s.name, count: s.value })),\n [categoryDonut]\n );\n\n const statusDonut = useMemo<DonutSlice[]>(() => {\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {});\n const toneFor: Record<string, PillTone> = {\n active: 'success',\n maintenance: 'warning',\n retired: 'neutral',\n inactive: 'neutral',\n disposed: 'error',\n };\n const toneToken: Record<PillTone, string> = {\n success: 'var(--color-status-success-text)',\n warning: 'var(--color-status-warning-text)',\n error: 'var(--color-status-error-text)',\n info: 'var(--color-status-info-text)',\n neutral: 'var(--color-text-muted)',\n };\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({\n name: titleCase(status),\n value,\n color: toneToken[toneFor[status] ?? 'neutral'],\n }));\n }, [assets]);\n\n const conditionBars = useMemo<ChartDatum[]>(() => {\n const order = ['excellent', 'good', 'fair', 'poor', 'damaged'];\n const counts = assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.condition] = (acc[a.condition] || 0) + 1;\n return acc;\n }, {});\n return order\n .filter((c) => counts[c])\n .map((c) => ({ condition: titleCase(c), count: counts[c] }))\n .concat(\n Object.entries(counts)\n .filter(([c]) => !order.includes(c))\n .map(([c, count]) => ({ condition: titleCase(c), count }))\n );\n }, [assets]);\n\n const totalAssets = assets.length;\n const activeAssets = assets.filter((a) => a.status === 'active').length;\n const distinctCategories = categoryDonut.length;\n const attentionAssets = assets.filter(\n (a) => a.condition === 'poor' || a.condition === 'damaged' || a.status === 'maintenance'\n );\n\n // ─── Financial aggregations ──────────────────────────────────────────────────\n const transactionsTotal = transactions.reduce((s, t) => s + Math.abs(t.amount), 0);\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const budgetRemaining = budgets.reduce((s, b) => s + b.remaining, 0);\n const maintenanceCost = maintenanceRecords.reduce(\n (s, m) => s + (m.actualCost ?? m.estimatedCost ?? 0),\n 0\n );\n const outstandingInvoices = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n );\n const outstandingInvoiceTotal = outstandingInvoices.reduce((s, i) => s + i.amount, 0);\n\n const budgetBars = useMemo<ChartDatum[]>(\n () =>\n budgets.map((b) => ({\n name: b.name,\n allocated: b.allocated,\n spent: b.spent,\n })),\n [budgets]\n );\n\n const spendTrend = useMemo<ChartDatum[]>(() => {\n const byMonth = new Map<string, number>();\n for (const tx of transactions) {\n const d = parseDate(tx.date);\n if (!d) continue;\n const key = monthKey(d);\n byMonth.set(key, (byMonth.get(key) ?? 0) + Math.abs(tx.amount));\n }\n return [...byMonth.keys()]\n .sort()\n .map((k) => ({ month: monthLabel(k), spend: Math.round(byMonth.get(k) ?? 0) }));\n }, [transactions]);\n\n // ─── Operational aggregations ────────────────────────────────────────────────\n const resolvedIncidents = incidents.filter(\n (i) => i.status === 'resolved' || i.status === 'closed'\n );\n const slaBreached = incidents.filter((i) => i.slaBreached);\n const slaCompliancePct =\n incidents.length > 0 ? Math.round((1 - slaBreached.length / incidents.length) * 100) : 100;\n const completedMaintenance = maintenanceRecords.filter((m) => m.status === 'completed');\n const scheduledMaintenance = maintenanceRecords.filter((m) => m.status === 'scheduled');\n const openWorkOrders = workOrders.filter(\n (w) => w.status !== 'completed' && w.status !== 'cancelled'\n );\n\n const utilStatusBars = useMemo<ChartDatum[]>(\n () =>\n (utilizationReport?.byStatus ?? []).map((s) => ({\n status: titleCase(s.status),\n count: s.count,\n })),\n [utilizationReport]\n );\n\n const workOrderDonut = useMemo<DonutSlice[]>(() => {\n const counts = workOrders.reduce<Record<string, number>>((acc, w) => {\n acc[w.status] = (acc[w.status] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([name, value]) => ({ name: titleCase(name), value }));\n }, [workOrders]);\n\n const maintenanceCostTrend = useMemo<ChartDatum[]>(() => {\n const byMonth = new Map<string, number>();\n for (const m of maintenanceRecords) {\n const d = parseDate(m.completedDate ?? m.scheduledDate);\n const cost = m.actualCost ?? m.estimatedCost;\n if (!d || cost === undefined) continue;\n const key = monthKey(d);\n byMonth.set(key, (byMonth.get(key) ?? 0) + cost);\n }\n return [...byMonth.keys()]\n .sort()\n .map((k) => ({ month: monthLabel(k), cost: Math.round(byMonth.get(k) ?? 0) }));\n }, [maintenanceRecords]);\n\n const financialTiles = financialSummary\n ? [\n { label: 'Total Asset Value', value: financialSummary.totalAssetValue, icon: DollarSign },\n {\n label: 'Total Depreciation',\n value: financialSummary.totalDepreciation,\n icon: TrendingDown,\n },\n {\n label: 'Maintenance Spend',\n value: financialSummary.totalMaintenanceSpend,\n icon: Wrench,\n },\n {\n label: 'Transaction Expense',\n value: financialSummary.transactionExpense,\n icon: Receipt,\n },\n { label: 'Budget Allocated', value: financialSummary.budgetAllocated, icon: Wallet },\n { label: 'Budget Spent', value: financialSummary.budgetSpent, icon: Wallet },\n {\n label: 'Invoices Outstanding',\n value: financialSummary.invoicesOutstanding,\n icon: AlertTriangle,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Analytics &amp; Business Intelligence</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Asset, financial and operational insights across your estate\n </p>\n </div>\n\n <PagePurpose>\n Turn raw asset data into decisions. See how your estate is distributed by category, status\n and condition, where budget is being spent, and how operations are trending — so you can\n plan work, justify replacements and spot risk before it shows up as downtime.\n </PagePurpose>\n\n <Tabs defaultValue=\"overview\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"financial\">Financial</TabsTrigger>\n <TabsTrigger value=\"operational\">Operational</TabsTrigger>\n <TabsTrigger value=\"insights\">Insights</TabsTrigger>\n </TabsList>\n\n {/* ─── Overview Tab ─────────────────────────────────────────────────── */}\n <TabsContent value=\"overview\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n hero\n label={tc('assethandler.analytics.metrics.totalAssets', 'Total {{assets}}')}\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile label=\"Categories\" value={distinctCategories} icon={PieChart} />\n <StatTile label=\"Needs Attention\" value={attentionAssets.length} icon={AlertTriangle} />\n <StatTile\n label=\"Open Incidents\"\n value={incidents.length - resolvedIncidents.length}\n icon={Activity}\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Assets by Category\"\n subtitle=\"Computed from real data\"\n icon={PieChart}\n >\n <BarsChart\n data={categoryBars}\n xKey=\"category\"\n series={[{ key: 'count', label: 'Assets' }]}\n orientation=\"horizontal\"\n height={Math.max(220, categoryBars.length * 40)}\n categoryWidth={120}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by category\"\n />\n </ChartCard>\n\n <ChartCard title=\"Assets by Status\" subtitle=\"Computed from real data\" icon={Activity}>\n <DonutChart\n data={statusDonut}\n centerLabel={tc('assethandler.analytics.donut.centerLabel', '{{assets}}')}\n centerValue={totalAssets}\n height={260}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n </div>\n\n <ChartCard title=\"Asset Condition Mix\" subtitle=\"Computed from real data\" icon={Gauge}>\n <BarsChart\n data={conditionBars}\n xKey=\"condition\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n emptyLabel=\"No assets yet\"\n ariaLabel=\"Assets by condition\"\n />\n </ChartCard>\n </TabsContent>\n\n {/* ─── Financial Tab ────────────────────────────────────────────────── */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n {financialSummary ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {financialTiles.slice(0, 4).map((tile, i) => (\n <StatTile\n key={tile.label}\n hero={i === 0}\n label={tile.label}\n value={tile.value}\n format={usd}\n icon={tile.icon}\n />\n ))}\n </div>\n ) : (\n <Card>\n <CardContent className=\"py-8\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Financial summary unavailable\"\n description=\"Portfolio valuation and budget rollups will appear once financial data is recorded.\"\n />\n </CardContent>\n </Card>\n )}\n\n {financialSummary ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {financialTiles.slice(4).map((tile) => (\n <StatTile\n key={tile.label}\n label={tile.label}\n value={tile.value}\n format={usd}\n icon={tile.icon}\n delta={\n tile.label === 'Invoices Outstanding'\n ? `${financialSummary.invoicesOutstandingCount} unpaid`\n : undefined\n }\n deltaTone=\"neutral\"\n />\n ))}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Budget: Allocated vs Spent\"\n subtitle=\"Per budget, from real data\"\n icon={Wallet}\n >\n {budgetBars.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <BarsChart\n data={budgetBars}\n xKey=\"name\"\n series={[\n { key: 'allocated', label: 'Allocated', color: seriesColor(1) },\n { key: 'spent', label: 'Spent' },\n ]}\n height={Math.max(220, budgetBars.length * 56)}\n orientation=\"horizontal\"\n categoryWidth={120}\n valueFormat={usd}\n ariaLabel=\"Budget allocated versus spent\"\n />\n )}\n </ChartCard>\n\n <ChartCard title=\"Spend Over Time\" subtitle=\"Monthly transaction spend\" icon={Receipt}>\n {spendTrend.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions yet\"\n description=\"Recorded purchases, leases and disposals will chart here.\"\n />\n ) : (\n <TrendChart\n data={spendTrend}\n xKey=\"month\"\n series={[{ key: 'spend', label: 'Spend' }]}\n valueFormat={usd}\n height={260}\n ariaLabel=\"Monthly transaction spend\"\n />\n )}\n </ChartCard>\n </div>\n\n {/* Budget utilization meters */}\n <ChartCard\n title=\"Budget Utilization\"\n subtitle=\"Allocated vs. spent per budget\"\n icon={Wallet}\n >\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {budgets.map((budget) => (\n <ProgressMeter\n key={budget.id}\n label={budget.name}\n value={budget.spent}\n max={budget.allocated}\n format={(v) => money(Math.round(v))}\n />\n ))}\n <Separator />\n <div className=\"flex items-center justify-between text-sm font-medium\">\n <span>Remaining across all budgets</span>\n <span className=\"tabular-nums\">{money(budgetRemaining)}</span>\n </div>\n </div>\n )}\n </ChartCard>\n\n {/* Financial quick stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Transactions\"\n value={transactions.length}\n icon={Receipt}\n delta={`${money(transactionsTotal)} value`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Budget Allocated\"\n value={budgetAllocated}\n format={usd}\n icon={Wallet}\n delta={`${money(budgetSpent)} spent`}\n deltaTone=\"neutral\"\n />\n <StatTile label=\"Maintenance Cost\" value={maintenanceCost} format={usd} icon={Wrench} />\n <StatTile\n label=\"Outstanding Invoices\"\n value={outstandingInvoiceTotal}\n format={usd}\n icon={AlertTriangle}\n delta={`${outstandingInvoices.length} of ${invoices.length}`}\n deltaTone=\"neutral\"\n />\n </div>\n\n {/* Recent transactions */}\n <ChartCard title=\"Recent Transactions\" icon={Receipt}>\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions\"\n description=\"Asset purchases, leases and disposals will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-text-secondary\">\n <th className=\"py-2 text-left font-medium\">Type</th>\n <th className=\"py-2 text-left font-medium\">Description</th>\n <th className=\"py-2 text-right font-medium\">Amount</th>\n <th className=\"py-2 text-left font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 6).map((txn) => (\n <tr\n key={txn.id}\n className=\"border-b border-border-seam hover:bg-bg-sunken/50\"\n >\n <td className=\"py-2.5 capitalize\">{txn.type}</td>\n <td className=\"max-w-xs truncate py-2.5\">{txn.description}</td>\n <td\n className={cn(\n 'py-2.5 text-right font-medium tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n {money(Math.abs(txn.amount))}\n </td>\n <td className=\"py-2.5\">\n <StatusPill tone={txStatusTone(txn.status)}>{txn.status}</StatusPill>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </ChartCard>\n </TabsContent>\n\n {/* ─── Operational Tab ──────────────────────────────────────────────── */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n <StatTile\n label={tc('assethandler.analytics.metrics.totalAssets', 'Total {{assets}}')}\n value={utilizationReport?.totalAssets ?? totalAssets}\n icon={Box}\n />\n <StatTile\n label=\"Assigned\"\n value={utilizationReport?.assignedAssets ?? '—'}\n icon={Users}\n />\n <StatTile\n label=\"Unassigned\"\n value={utilizationReport?.unassignedAssets ?? '—'}\n icon={Box}\n />\n <StatTile label=\"Idle\" value={utilizationReport?.idleAssets ?? '—'} icon={Activity} />\n <StatTile\n label=\"Overdue\"\n value={utilizationReport?.overdueAssignments ?? '—'}\n icon={AlertTriangle}\n delta={\n utilizationReport && utilizationReport.overdueAssignments > 0\n ? 'action needed'\n : undefined\n }\n deltaTone=\"negative\"\n deltaDirection=\"up\"\n />\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Status\" subtitle=\"Assignment coverage\" icon={Gauge}>\n <BarsChart\n data={utilStatusBars}\n xKey=\"status\"\n series={[{ key: 'count', label: 'Assets' }]}\n orientation=\"horizontal\"\n height={Math.max(200, utilStatusBars.length * 40)}\n categoryWidth={120}\n emptyLabel=\"No utilization data\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n\n <ChartCard title=\"Work Orders by Status\" subtitle=\"From real data\" icon={ClipboardList}>\n <DonutChart\n data={workOrderDonut}\n centerLabel=\"Work orders\"\n centerValue={workOrders.length}\n height={260}\n emptyLabel=\"No work orders\"\n ariaLabel=\"Work orders by status\"\n />\n </ChartCard>\n </div>\n\n <ChartCard\n title=\"Maintenance Cost Over Time\"\n subtitle=\"Monthly recorded maintenance cost\"\n icon={Wrench}\n >\n {maintenanceCostTrend.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No maintenance cost yet\"\n description=\"Once maintenance records carry cost, monthly spend will chart here.\"\n />\n ) : (\n <TrendChart\n data={maintenanceCostTrend}\n xKey=\"month\"\n series={[{ key: 'cost', label: 'Cost' }]}\n valueFormat={usd}\n height={260}\n ariaLabel=\"Monthly maintenance cost\"\n />\n )}\n </ChartCard>\n\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Incidents\"\n value={incidents.length}\n icon={Activity}\n delta={`${resolvedIncidents.length} resolved`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"SLA Breaches\"\n value={slaBreached.length}\n icon={AlertTriangle}\n delta={slaBreached.length > 0 ? 'over SLA' : 'all within SLA'}\n deltaTone={slaBreached.length > 0 ? 'negative' : 'positive'}\n deltaDirection={slaBreached.length > 0 ? 'up' : 'neutral'}\n />\n <StatTile\n label=\"Maintenance Done\"\n value={completedMaintenance.length}\n icon={Wrench}\n delta={`of ${maintenanceRecords.length}`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Open Work Orders\"\n value={openWorkOrders.length}\n icon={ClipboardList}\n delta={`of ${workOrders.length}`}\n deltaTone=\"neutral\"\n />\n </div>\n </TabsContent>\n\n {/* ─── Insights Tab ─────────────────────────────────────────────────── */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <ChartCard\n title=\"SLA Compliance\"\n subtitle=\"Incidents resolved within SLA\"\n icon={ShieldCheck}\n >\n <RadialGauge\n value={slaCompliancePct}\n label=\"within SLA\"\n sublabel={`${slaBreached.length} of ${incidents.length} breached`}\n format={(v) => `${Math.round(v)}%`}\n height={220}\n ariaLabel=\"SLA compliance percentage\"\n />\n </ChartCard>\n\n <div className=\"lg:col-span-2 grid gap-4 sm:grid-cols-3\">\n <Card className=\"border-status-info-border bg-status-info-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-info-text text-base\">Diversification</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-info-text\">\n Your estate spans {distinctCategories} categories across {totalAssets} assets.{' '}\n {activeAssets} are currently active.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-success-border bg-status-success-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-success-text text-base\">Budget</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-success-text\">\n {money(budgetSpent)} spent of {money(budgetAllocated)} allocated, leaving{' '}\n {money(budgetRemaining)} across {budgets.length} budgets.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-warning-border bg-status-warning-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-warning-text text-base\">Maintenance</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-warning-text\">\n {scheduledMaintenance.length} tasks scheduled. Total maintenance spend is{' '}\n {money(maintenanceCost)}. Stay on top of preventive maintenance.\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Assets needing attention */}\n <EmphasisPanel>\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n Assets Needing Attention\n </CardTitle>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n {attentionAssets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-success\"\n title=\"Everything looks healthy\"\n description=\"No assets are flagged as poor, damaged or in maintenance right now.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {attentionAssets.slice(0, 6).map((asset, i) => (\n <div\n key={asset.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-seam bg-gloss-card p-3\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{asset.name}</p>\n <p className=\"truncate text-xs text-text-secondary capitalize\">\n {asset.category.replace('_', ' ')}\n {asset.location ? ` · ${asset.location}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusPill tone={asset.condition === 'damaged' ? 'error' : 'warning'}>\n {asset.condition}\n </StatusPill>\n <StatusPill tone={asset.status === 'active' ? 'success' : 'warning'}>\n {asset.status.replace('_', ' ')}\n </StatusPill>\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </EmphasisPanel>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsAssetCategoriesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"names":[],"mappings":"AA8GA,wBAAgB,yBAAyB,gCA0VxC"}
1
+ {"version":3,"file":"AssetsAssetCategoriesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"names":[],"mappings":"AAuGA,wBAAgB,yBAAyB,gCA2VxC"}
@@ -1,9 +1,9 @@
1
1
  import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
- import { formatAssetCategory as t } from "../utils/categoryPresentation.js";
3
- import { useAssetCategoryPresentation as n, useAssetTerminology as r, useHiddenCategoryIds as i } from "../providers/AssetHandlerProvider.js";
4
- import { useAssetHandlerNavigate as a } from "../hooks/useAssetHandlerNavigate.js";
5
- import { notify as o } from "../utils/toast.js";
6
- import { formatCompact as s, formatCurrencyCompact as c } from "../components/charts/chartTheme.js";
2
+ import { formatCompact as t } from "../components/charts/chartTheme.js";
3
+ import { formatAssetCategory as n } from "../utils/categoryPresentation.js";
4
+ import { useAssetCategoryPresentation as r, useAssetMoney as i, useAssetTerminology as a, useHiddenCategoryIds as o } from "../providers/AssetHandlerProvider.js";
5
+ import { useAssetHandlerNavigate as s } from "../hooks/useAssetHandlerNavigate.js";
6
+ import { notify as c } from "../utils/toast.js";
7
7
  import { BarsChart as l } from "../components/charts/BarsChart.js";
8
8
  import { StatTile as u } from "../components/StatTile.js";
9
9
  import { ChartCard as d } from "../components/ChartCard.js";
@@ -76,11 +76,11 @@ var U = [
76
76
  fair: 3,
77
77
  poor: 2,
78
78
  damaged: 1
79
- }, G = (e) => `$${e.toLocaleString()}`;
80
- function K() {
81
- let g = a(), b = r(), S = i(), { labels: C } = n(), { assets: w } = e(), T = f(() => w.filter((e) => !S.has(e.category)), [w, S]), [B, V] = p("count"), [K, q] = p("count"), J = f(() => {
79
+ };
80
+ function G() {
81
+ let { format: g, compact: b } = i(), S = s(), C = a(), w = o(), { labels: T } = r(), { assets: B } = e(), V = f(() => B.filter((e) => !w.has(e.category)), [B, w]), [G, K] = p("count"), [q, J] = p("count"), Y = f(() => {
82
82
  let e = {};
83
- for (let t of T) {
83
+ for (let t of V) {
84
84
  e[t.category] || (e[t.category] = {
85
85
  key: t.category,
86
86
  count: 0,
@@ -101,66 +101,66 @@ function K() {
101
101
  t.avgCondition = n > 0 ? e / n : 0;
102
102
  }
103
103
  return Object.values(e).sort((e, t) => t.count - e.count);
104
- }, [T]), Y = T.length, X = f(() => T.reduce((e, t) => e + (t.currentValue ?? t.purchaseValue ?? 0), 0), [T]), Z = f(() => J.map((e) => ({
105
- category: t(e.key, C),
104
+ }, [V]), X = V.length, Z = f(() => V.reduce((e, t) => e + (t.currentValue ?? t.purchaseValue ?? 0), 0), [V]), Q = f(() => Y.map((e) => ({
105
+ category: n(e.key, T),
106
106
  count: e.count,
107
107
  value: e.totalValue
108
- })), [J, C]), Q = f(() => [...J].sort((e, t) => K === "count" ? t.count - e.count : t.totalValue - e.totalValue), [J, K]), $ = (e) => {
109
- navigator.clipboard?.writeText(e), o.success("Category copied", `"${t(e, C)}" copied to clipboard.`);
108
+ })), [Y, T]), $ = f(() => [...Y].sort((e, t) => q === "count" ? t.count - e.count : t.totalValue - e.totalValue), [Y, q]), ee = (e) => {
109
+ navigator.clipboard?.writeText(e), c.success("Category copied", `"${n(e, T)}" copied to clipboard.`);
110
110
  };
111
111
  return /* @__PURE__ */ O("div", {
112
112
  className: "space-y-6 p-4 sm:p-6",
113
113
  children: [
114
114
  /* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("h1", {
115
115
  className: "text-2xl font-bold",
116
- children: /* @__PURE__ */ O(I, { children: [b.asset, " Categories"] })
116
+ children: /* @__PURE__ */ O(I, { children: [C.asset, " Categories"] })
117
117
  }), /* @__PURE__ */ O("p", {
118
118
  className: "text-sm text-text-secondary",
119
119
  children: [
120
- J.length,
120
+ Y.length,
121
121
  " categories · ",
122
- Y,
122
+ X,
123
123
  " total ",
124
- b.assets.toLowerCase()
124
+ C.assets.toLowerCase()
125
125
  ]
126
126
  })] }),
127
127
  /* @__PURE__ */ D(R, { children: "Group and compare your assets by category to see where value and volume are concentrated. Each card rolls up how many assets you hold, their total book value and a proportional status mix — so you can plan procurement and replacements by category, not one asset at a time." }),
128
- J.length === 0 ? /* @__PURE__ */ D(L, {
128
+ Y.length === 0 ? /* @__PURE__ */ D(L, {
129
129
  illustration: "empty-data",
130
130
  title: "No categories yet",
131
- description: `Add ${b.assets.toLowerCase()} and they will be grouped into categories here automatically.`
131
+ description: `Add ${C.assets.toLowerCase()} and they will be grouped into categories here automatically.`
132
132
  }) : /* @__PURE__ */ O(E, { children: [
133
133
  /* @__PURE__ */ O("div", {
134
134
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
135
135
  children: [
136
136
  /* @__PURE__ */ D(u, {
137
137
  label: "Categories",
138
- value: J.length,
139
- format: s,
138
+ value: Y.length,
139
+ format: t,
140
140
  icon: y,
141
141
  hero: !0
142
142
  }),
143
143
  /* @__PURE__ */ D(u, {
144
- label: `${b.assets} tracked`,
145
- value: Y,
146
- format: s,
144
+ label: `${C.assets} tracked`,
145
+ value: X,
146
+ format: t,
147
147
  icon: h
148
148
  }),
149
149
  /* @__PURE__ */ D(u, {
150
150
  label: "Total book value",
151
- value: X,
152
- format: c("USD"),
151
+ value: Z,
152
+ format: b,
153
153
  icon: h
154
154
  })
155
155
  ]
156
156
  }),
157
157
  /* @__PURE__ */ D("div", {
158
158
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
159
- children: J.map((e, n) => {
160
- let r = Y > 0 ? Math.round(e.count / Y * 100) : 0, i = U.filter((t) => (e.statuses[t.key] || 0) > 0);
159
+ children: Y.map((e, t) => {
160
+ let r = X > 0 ? Math.round(e.count / X * 100) : 0, i = U.filter((t) => (e.statuses[t.key] || 0) > 0);
161
161
  return /* @__PURE__ */ D(F, {
162
- treatment: n === 0 ? "glass" : "plain",
163
- glow: n === 0,
162
+ treatment: t === 0 ? "glass" : "plain",
163
+ glow: t === 0,
164
164
  className: "border-border-seam",
165
165
  children: /* @__PURE__ */ O("div", {
166
166
  className: "space-y-4 p-5",
@@ -176,7 +176,7 @@ function K() {
176
176
  className: "min-w-0",
177
177
  children: [/* @__PURE__ */ D("p", {
178
178
  className: "truncate text-base font-semibold capitalize",
179
- children: t(e.key, C)
179
+ children: n(e.key, T)
180
180
  }), /* @__PURE__ */ O(k, {
181
181
  variant: "outline",
182
182
  className: "mt-0.5 text-xs",
@@ -189,20 +189,20 @@ function K() {
189
189
  variant: "ghost",
190
190
  size: "icon",
191
191
  className: "h-8 w-8 shrink-0",
192
- "aria-label": `Actions for ${t(e.key, C)}`,
192
+ "aria-label": `Actions for ${n(e.key, T)}`,
193
193
  children: /* @__PURE__ */ D(x, { className: "h-4 w-4" })
194
194
  })
195
195
  }), /* @__PURE__ */ O(M, {
196
196
  align: "end",
197
197
  children: [/* @__PURE__ */ O(N, {
198
- onClick: () => g("/assets/asset-list"),
198
+ onClick: () => S("/assets/asset-list"),
199
199
  children: [
200
200
  /* @__PURE__ */ D(v, { className: "mr-2 h-4 w-4" }),
201
201
  "View ",
202
- b.assets.toLowerCase()
202
+ C.assets.toLowerCase()
203
203
  ]
204
204
  }), /* @__PURE__ */ O(N, {
205
- onClick: () => $(e.key),
205
+ onClick: () => ee(e.key),
206
206
  children: [/* @__PURE__ */ D(_, { className: "mr-2 h-4 w-4" }), "Copy category ID"]
207
207
  })]
208
208
  })] })]
@@ -211,7 +211,7 @@ function K() {
211
211
  className: "flex flex-wrap items-end justify-between gap-3",
212
212
  children: [/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ O("p", {
213
213
  className: "text-xs text-text-secondary",
214
- children: [b.asset, " count"]
214
+ children: [C.asset, " count"]
215
215
  }), /* @__PURE__ */ D("p", {
216
216
  className: "text-2xl font-bold tabular-nums",
217
217
  children: e.count
@@ -222,7 +222,7 @@ function K() {
222
222
  children: "Total value"
223
223
  }), /* @__PURE__ */ D("p", {
224
224
  className: "text-2xl font-bold tabular-nums",
225
- children: c("USD")(e.totalValue)
225
+ children: b(e.totalValue)
226
226
  })]
227
227
  })]
228
228
  }),
@@ -271,33 +271,33 @@ function K() {
271
271
  }),
272
272
  /* @__PURE__ */ D(d, {
273
273
  title: "Category comparison",
274
- subtitle: B === "count" ? `${b.assets} per category` : "Book value per category",
274
+ subtitle: G === "count" ? `${C.assets} per category` : "Book value per category",
275
275
  actions: /* @__PURE__ */ O("div", {
276
276
  className: "flex gap-1 rounded-md border border-border-seam p-0.5",
277
277
  children: [/* @__PURE__ */ D(A, {
278
- variant: B === "count" ? "default" : "ghost",
278
+ variant: G === "count" ? "default" : "ghost",
279
279
  size: "sm",
280
280
  className: "h-7 px-2 text-xs",
281
- onClick: () => V("count"),
281
+ onClick: () => K("count"),
282
282
  children: "Count"
283
283
  }), /* @__PURE__ */ D(A, {
284
- variant: B === "value" ? "default" : "ghost",
284
+ variant: G === "value" ? "default" : "ghost",
285
285
  size: "sm",
286
286
  className: "h-7 px-2 text-xs",
287
- onClick: () => V("value"),
287
+ onClick: () => K("value"),
288
288
  children: "Value"
289
289
  })]
290
290
  }),
291
291
  children: /* @__PURE__ */ D(l, {
292
- data: Z,
292
+ data: Q,
293
293
  xKey: "category",
294
294
  orientation: "horizontal",
295
- height: Math.max(200, Z.length * 44),
295
+ height: Math.max(200, Q.length * 44),
296
296
  categoryWidth: 120,
297
- valueFormat: B === "value" ? c("USD") : s,
297
+ valueFormat: G === "value" ? b : t,
298
298
  series: [{
299
- key: B,
300
- label: B === "count" ? b.assets : "Book value"
299
+ key: G,
300
+ label: G === "count" ? C.assets : "Book value"
301
301
  }]
302
302
  })
303
303
  }),
@@ -321,8 +321,8 @@ function K() {
321
321
  children: /* @__PURE__ */ O("button", {
322
322
  type: "button",
323
323
  className: "inline-flex items-center gap-1 hover:text-text-primary",
324
- onClick: () => q("count"),
325
- children: ["Count", /* @__PURE__ */ D(m, { className: `h-3 w-3 ${K === "count" ? "text-action-primary-bg" : ""}` })]
324
+ onClick: () => J("count"),
325
+ children: ["Count", /* @__PURE__ */ D(m, { className: `h-3 w-3 ${q === "count" ? "text-action-primary-bg" : ""}` })]
326
326
  })
327
327
  }),
328
328
  /* @__PURE__ */ D("th", {
@@ -330,8 +330,8 @@ function K() {
330
330
  children: /* @__PURE__ */ O("button", {
331
331
  type: "button",
332
332
  className: "inline-flex items-center gap-1 hover:text-text-primary",
333
- onClick: () => q("value"),
334
- children: ["Value", /* @__PURE__ */ D(m, { className: `h-3 w-3 ${K === "value" ? "text-action-primary-bg" : ""}` })]
333
+ onClick: () => J("value"),
334
+ children: ["Value", /* @__PURE__ */ D(m, { className: `h-3 w-3 ${q === "value" ? "text-action-primary-bg" : ""}` })]
335
335
  })
336
336
  }),
337
337
  /* @__PURE__ */ D("th", {
@@ -339,12 +339,12 @@ function K() {
339
339
  children: "Avg. condition"
340
340
  })
341
341
  ]
342
- }) }), /* @__PURE__ */ D("tbody", { children: Q.map((e) => /* @__PURE__ */ O("tr", {
342
+ }) }), /* @__PURE__ */ D("tbody", { children: $.map((e) => /* @__PURE__ */ O("tr", {
343
343
  className: "border-b border-border-seam/60 last:border-0",
344
344
  children: [
345
345
  /* @__PURE__ */ D("td", {
346
346
  className: "py-2.5 pr-4 font-medium capitalize",
347
- children: t(e.key, C)
347
+ children: n(e.key, T)
348
348
  }),
349
349
  /* @__PURE__ */ D("td", {
350
350
  className: "py-2.5 pr-4 tabular-nums",
@@ -352,7 +352,7 @@ function K() {
352
352
  }),
353
353
  /* @__PURE__ */ D("td", {
354
354
  className: "py-2.5 pr-4 tabular-nums",
355
- children: G(e.totalValue)
355
+ children: g(e.totalValue)
356
356
  }),
357
357
  /* @__PURE__ */ D("td", {
358
358
  className: "py-2.5 tabular-nums",
@@ -368,6 +368,6 @@ function K() {
368
368
  });
369
369
  }
370
370
  //#endregion
371
- export { K as AssetsAssetCategoriesPage };
371
+ export { G as AssetsAssetCategoriesPage };
372
372
 
373
373
  //# sourceMappingURL=AssetsAssetCategoriesPage.js.map