@burdenoff/microfe-assethandler 2026.713.2 → 2026.714.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assethandler/components/AttachmentsPanel.d.ts.map +1 -1
- package/dist/assethandler/components/AttachmentsPanel.js.map +1 -1
- package/dist/assethandler/hooks/useCopy.d.ts +28 -0
- package/dist/assethandler/hooks/useCopy.d.ts.map +1 -0
- package/dist/assethandler/hooks/useCopy.js +33 -0
- package/dist/assethandler/hooks/useCopy.js.map +1 -0
- package/dist/assethandler/index.d.ts +1 -1
- package/dist/assethandler/index.d.ts.map +1 -1
- package/dist/assethandler/pages/AnalyticsPage.d.ts.map +1 -1
- package/dist/assethandler/pages/AnalyticsPage.js +259 -258
- package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCategoriesPage.d.ts.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +1 -2
- package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetListPage.d.ts.map +1 -1
- package/dist/assethandler/pages/AssetsAssetListPage.js +47 -48
- package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
- package/dist/assethandler/pages/DashboardPage.d.ts.map +1 -1
- package/dist/assethandler/pages/DashboardPage.js +236 -236
- package/dist/assethandler/pages/DashboardPage.js.map +1 -1
- package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.d.ts.map +1 -1
- package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js +126 -125
- package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
- package/dist/assethandler/pages/TransactionsTransactionListPage.js +127 -127
- package/dist/assethandler/pages/TransactionsTransactionListPage.js.map +1 -1
- package/dist/assethandler/store/assethandlerStore.d.ts.map +1 -1
- package/dist/assethandler/store/assethandlerStore.js.map +1 -1
- package/dist/assethandler/types/index.d.ts +20 -0
- package/dist/assethandler/types/index.d.ts.map +1 -1
- package/dist/types/assistant.d.ts.map +1 -1
- 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 { 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 { 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 & 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=\"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=\"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=\"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 {\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 & 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsAssetCategoriesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"names":[],"mappings":"AAyGA,wBAAgB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"AssetsAssetCategoriesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"names":[],"mappings":"AAyGA,wBAAgB,yBAAyB,gCAsVxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n Monitor,\n Server,\n Wifi,\n Truck,\n Wrench,\n Code,\n MoreHorizontal,\n Copy,\n ExternalLink,\n ArrowUpDown,\n Layers,\n} from 'lucide-react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetTerminology, useHiddenCategoryIds } from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerStore, type AssetStatus } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n ChartCard,\n StatTile,\n BarsChart,\n formatCompact,\n formatCurrencyCompact,\n} from '../components';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\n// Stable fallback tint for any category outside the known slug map — hashes the\n// key into the semantic palette so every real category is visually differentiated.\nconst FALLBACK_TINTS = [\n 'bg-status-info-bg-subtle text-status-info-text',\n 'bg-status-success-bg-subtle text-status-success-text',\n 'bg-status-warning-bg-subtle text-status-warning-text',\n 'bg-status-error-bg-subtle text-status-error-text',\n 'bg-action-primary-bg/10 text-action-primary-bg',\n];\nfunction tintFor(key: string): string {\n if (categoryColors[key]) return categoryColors[key];\n let hash = 0;\n for (let i = 0; i < key.length; i += 1) hash = (hash * 31 + key.charCodeAt(i)) >>> 0;\n return FALLBACK_TINTS[hash % FALLBACK_TINTS.length];\n}\n\n// Proportional status-bar segments — semantic tokens (CSS vars), theme-aware.\nconst STATUS_SEGMENTS: { key: AssetStatus; label: string; varName: string }[] = [\n { key: 'active', label: 'Active', varName: 'var(--color-status-success-bg)' },\n { key: 'maintenance', label: 'Maintenance', varName: 'var(--color-status-warning-bg)' },\n { key: 'retired', label: 'Retired', varName: 'var(--color-status-error-bg)' },\n { key: 'inactive', label: 'Inactive', varName: 'var(--color-status-info-bg)' },\n { key: 'disposed', label: 'Disposed', varName: 'var(--color-text-muted)' },\n];\n\nconst CONDITION_SCORE: Record<string, number> = {\n excellent: 5,\n good: 4,\n fair: 3,\n poor: 2,\n damaged: 1,\n};\n\ninterface CategoryAggregate {\n key: string;\n count: number;\n totalValue: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n avgCondition: number;\n}\n\nconst money = (value: number): string => `$${value.toLocaleString()}`;\n\nexport function AssetsAssetCategoriesPage() {\n const navigate = useAssetHandlerNavigate();\n const term = useAssetTerminology();\n const hiddenCategories = useHiddenCategoryIds();\n const { assets: allAssets } = useAssetHandlerStore();\n const assets = useMemo(\n () => allAssets.filter((a) => !hiddenCategories.has(a.category)),\n [allAssets, hiddenCategories]\n );\n const [chartMetric, setChartMetric] = useState<'count' | 'value'>('count');\n const [sortKey, setSortKey] = useState<'count' | 'value'>('count');\n\n const aggregates = useMemo<CategoryAggregate[]>(() => {\n const map: Record<string, CategoryAggregate> = {};\n for (const asset of assets) {\n if (!map[asset.category]) {\n map[asset.category] = {\n key: asset.category,\n count: 0,\n totalValue: 0,\n statuses: {},\n conditions: {},\n avgCondition: 0,\n };\n }\n const cat = map[asset.category];\n cat.count += 1;\n cat.totalValue += asset.currentValue ?? asset.purchaseValue ?? 0;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n }\n for (const cat of Object.values(map)) {\n let scoreSum = 0;\n let scoreN = 0;\n for (const [cond, n] of Object.entries(cat.conditions)) {\n const score = CONDITION_SCORE[cond];\n if (score) {\n scoreSum += score * n;\n scoreN += n;\n }\n }\n cat.avgCondition = scoreN > 0 ? scoreSum / scoreN : 0;\n }\n return Object.values(map).sort((a, b) => b.count - a.count);\n }, [assets]);\n\n const totalAssets = assets.length;\n const totalValue = useMemo(\n () => assets.reduce((sum, a) => sum + (a.currentValue ?? a.purchaseValue ?? 0), 0),\n [assets]\n );\n\n const chartData = useMemo(\n () =>\n aggregates.map((cat) => ({\n category: cat.key.replace(/_/g, ' '),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates]\n );\n\n const sortedRows = useMemo(\n () =>\n [...aggregates].sort((a, b) =>\n sortKey === 'count' ? b.count - a.count : b.totalValue - a.totalValue\n ),\n [aggregates, sortKey]\n );\n\n const copyId = (key: string) => {\n void navigator.clipboard?.writeText(key);\n notify.success('Category copied', `\"${key.replace(/_/g, ' ')}\" copied to clipboard.`);\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{term.asset} Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {aggregates.length} categories · {totalAssets} total{' '}\n {term.assets.toLowerCase()}\n </p>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where value and volume are concentrated.\n Each card rolls up how many assets you hold, their total book value and a proportional\n status mix — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {aggregates.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description={`Add ${term.assets.toLowerCase()} and they will be grouped into categories here automatically.`}\n />\n ) : (\n <>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <StatTile\n label=\"Categories\"\n value={aggregates.length}\n format={formatCompact}\n icon={Layers}\n hero\n />\n <StatTile\n label={`${term.assets} tracked`}\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile\n label=\"Total book value\"\n value={totalValue}\n format={formatCurrencyCompact('USD')}\n icon={Box}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {aggregates.map((cat, idx) => {\n const pct = totalAssets > 0 ? Math.round((cat.count / totalAssets) * 100) : 0;\n const segments = STATUS_SEGMENTS.filter((s) => (cat.statuses[s.key] || 0) > 0);\n return (\n <GlassCard\n key={cat.key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <div className=\"space-y-4 p-5\">\n {/* Header: icon + title + overflow */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className={`shrink-0 rounded-lg p-2 ${tintFor(cat.key)}`}>\n {categoryIcons[cat.key] || <Box className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </p>\n <Badge variant=\"outline\" className=\"mt-0.5 text-xs\">\n {pct}% of estate\n </Badge>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0\"\n aria-label={`Actions for ${cat.key.replace(/_/g, ' ')}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => navigate('/assets/asset-list')}>\n <ExternalLink className=\"mr-2 h-4 w-4\" />\n View {term.assets.toLowerCase()}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => copyId(cat.key)}>\n <Copy className=\"mr-2 h-4 w-4\" />\n Copy category ID\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Dual metric row */}\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <p className=\"text-xs text-text-secondary\">{term.asset} count</p>\n <p className=\"text-2xl font-bold tabular-nums\">{cat.count}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary\">Total value</p>\n <p className=\"text-2xl font-bold tabular-nums\">\n {formatCurrencyCompact('USD')(cat.totalValue)}\n </p>\n </div>\n </div>\n\n {/* Proportional status bar */}\n <div>\n <div\n className=\"flex h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"img\"\n aria-label={segments\n .map((s) => `${s.label}: ${cat.statuses[s.key]}`)\n .join(', ')}\n >\n {segments.map((s) => (\n <div\n key={s.key}\n style={{\n width: `${((cat.statuses[s.key] || 0) / cat.count) * 100}%`,\n backgroundColor: s.varName,\n }}\n />\n ))}\n </div>\n <div className=\"mt-2 flex flex-wrap gap-x-3 gap-y-1\">\n {segments.map((s) => (\n <span\n key={s.key}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary\"\n >\n <span\n className=\"h-2.5 w-2.5 rounded-[3px]\"\n style={{ backgroundColor: s.varName }}\n />\n {s.label}\n <span className=\"tabular-nums text-text-muted\">\n {cat.statuses[s.key]}\n </span>\n </span>\n ))}\n </div>\n </div>\n\n {/* Avg condition */}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-sm\">\n <span className=\"text-text-secondary\">Avg. condition</span>\n <span className=\"font-semibold tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n <span className=\"text-xs font-normal text-text-muted\"> / 5</span>\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {/* Cross-category comparison chart */}\n <ChartCard\n title=\"Category comparison\"\n subtitle={\n chartMetric === 'count'\n ? `${term.assets} per category`\n : 'Book value per category'\n }\n actions={\n <div className=\"flex gap-1 rounded-md border border-border-seam p-0.5\">\n <Button\n variant={chartMetric === 'count' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('count')}\n >\n Count\n </Button>\n <Button\n variant={chartMetric === 'value' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('value')}\n >\n Value\n </Button>\n </div>\n }\n >\n <BarsChart\n data={chartData}\n xKey=\"category\"\n orientation=\"horizontal\"\n height={Math.max(200, chartData.length * 44)}\n categoryWidth={120}\n valueFormat={chartMetric === 'value' ? formatCurrencyCompact('USD') : formatCompact}\n series={[\n {\n key: chartMetric,\n label: chartMetric === 'count' ? term.assets : 'Book value',\n },\n ]}\n />\n </ChartCard>\n\n {/* Summary comparison table */}\n <ChartCard\n title=\"Category summary\"\n subtitle=\"Sortable roll-up across every category\"\n icon={Layers}\n >\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[520px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Category</th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('count')}\n >\n Count\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'count' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('value')}\n >\n Value\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'value' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 font-medium\">Avg. condition</th>\n </tr>\n </thead>\n <tbody>\n {sortedRows.map((cat) => (\n <tr key={cat.key} className=\"border-b border-border-seam/60 last:border-0\">\n <td className=\"py-2.5 pr-4 font-medium capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{cat.count}</td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{money(cat.totalValue)}</td>\n <td className=\"py-2.5 tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,IAAM,IAAiD;CACrD,aAAa,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;CAC5C,aAAa,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;CAC5C,WAAW,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;CACtC,UAAU,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;CACvC,WAAW,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;CACzC,UAAU,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;CACtC,YAAY,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;CACxC,SAAS,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;CACxC,EAEK,IAAyC;CAC7C,aAAa;CACb,aAAa;CACb,WAAW;CACX,UAAU;CACV,WAAW;CACX,UAAU;CACV,YAAY;CACZ,SAAS;CACV,EAIK,IAAiB;CACrB;CACA;CACA;CACA;CACA;CACD;AACD,SAAS,EAAQ,GAAqB;AACpC,KAAI,EAAe,GAAM,QAAO,EAAe;CAC/C,IAAI,IAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,EAAI,QAAQ,KAAK,EAAG,KAAQ,IAAO,KAAK,EAAI,WAAW,EAAE,KAAM;AACnF,QAAO,EAAe,IAAO,EAAe;;AAI9C,IAAM,IAA0E;CAC9E;EAAE,KAAK;EAAU,OAAO;EAAU,SAAS;EAAkC;CAC7E;EAAE,KAAK;EAAe,OAAO;EAAe,SAAS;EAAkC;CACvF;EAAE,KAAK;EAAW,OAAO;EAAW,SAAS;EAAgC;CAC7E;EAAE,KAAK;EAAY,OAAO;EAAY,SAAS;EAA+B;CAC9E;EAAE,KAAK;EAAY,OAAO;EAAY,SAAS;EAA2B;CAC3E,EAEK,IAA0C;CAC9C,WAAW;CACX,MAAM;CACN,MAAM;CACN,MAAM;CACN,SAAS;CACV,EAWK,KAAS,MAA0B,IAAI,EAAM,gBAAgB;AAEnE,SAAgB,IAA4B;CAC1C,IAAM,IAAW,GAAyB,EACpC,IAAO,GAAqB,EAC5B,IAAmB,GAAsB,EACzC,EAAE,QAAQ,MAAc,GAAsB,EAC9C,IAAS,QACP,EAAU,QAAQ,MAAM,CAAC,EAAiB,IAAI,EAAE,SAAS,CAAC,EAChE,CAAC,GAAW,EAAiB,CAC9B,EACK,CAAC,GAAa,KAAkB,EAA4B,QAAQ,EACpE,CAAC,GAAS,KAAc,EAA4B,QAAQ,EAE5D,IAAa,QAAmC;EACpD,IAAM,IAAyC,EAAE;AACjD,OAAK,IAAM,KAAS,GAAQ;AAC1B,GAAK,EAAI,EAAM,cACb,EAAI,EAAM,YAAY;IACpB,KAAK,EAAM;IACX,OAAO;IACP,YAAY;IACZ,UAAU,EAAE;IACZ,YAAY,EAAE;IACd,cAAc;IACf;GAEH,IAAM,IAAM,EAAI,EAAM;AAItB,GAHA,EAAI,SAAS,GACb,EAAI,cAAc,EAAM,gBAAgB,EAAM,iBAAiB,GAC/D,EAAI,SAAS,EAAM,WAAW,EAAI,SAAS,EAAM,WAAW,KAAK,GACjE,EAAI,WAAW,EAAM,cAAc,EAAI,WAAW,EAAM,cAAc,KAAK;;AAE7E,OAAK,IAAM,KAAO,OAAO,OAAO,EAAI,EAAE;GACpC,IAAI,IAAW,GACX,IAAS;AACb,QAAK,IAAM,CAAC,GAAM,MAAM,OAAO,QAAQ,EAAI,WAAW,EAAE;IACtD,IAAM,IAAQ,EAAgB;AAC9B,IAAI,MACF,KAAY,IAAQ,GACpB,KAAU;;AAGd,KAAI,eAAe,IAAS,IAAI,IAAW,IAAS;;AAEtD,SAAO,OAAO,OAAO,EAAI,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;IAC1D,CAAC,EAAO,CAAC,EAEN,IAAc,EAAO,QACrB,IAAa,QACX,EAAO,QAAQ,GAAK,MAAM,KAAO,EAAE,gBAAgB,EAAE,iBAAiB,IAAI,EAAE,EAClF,CAAC,EAAO,CACT,EAEK,IAAY,QAEd,EAAW,KAAK,OAAS;EACvB,UAAU,EAAI,IAAI,QAAQ,MAAM,IAAI;EACpC,OAAO,EAAI;EACX,OAAO,EAAI;EACZ,EAAE,EACL,CAAC,EAAW,CACb,EAEK,IAAa,QAEf,CAAC,GAAG,EAAW,CAAC,MAAM,GAAG,MACvB,MAAY,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,WAC5D,EACH,CAAC,GAAY,EAAQ,CACtB,EAEK,KAAU,MAAgB;AAE9B,EADK,UAAU,WAAW,UAAU,EAAI,EACxC,EAAO,QAAQ,mBAAmB,IAAI,EAAI,QAAQ,MAAM,IAAI,CAAC,wBAAwB;;AAGvF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,GAAD,EAAA,UAAA,CAAe,EAAK,OAAM,cAA0B,EAAA,CAAA;IACjD,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cAAb;KACG,EAAW;KAAO;KAAsB;KAAY;KAAO;KAC3D,EAAK,OAAO,aAAa;KACxB;MACA,EAAA,CAAA;GAEN,kBAAC,GAAD,EAAA,UAAa,oRAKC,CAAA;GAEb,EAAW,WAAW,IACrB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAa,OAAO,EAAK,OAAO,aAAa,CAAC;IAC9C,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAW;OAClB,QAAQ;OACR,MAAM;OACN,MAAA;OACA,CAAA;MACF,kBAAC,GAAD;OACE,OAAO,GAAG,EAAK,OAAO;OACtB,OAAO;OACP,QAAQ;OACR,MAAM;OACN,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO;OACP,QAAQ,EAAsB,MAAM;OACpC,MAAM;OACN,CAAA;MACE;;IAEN,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAW,KAAK,GAAK,MAAQ;MAC5B,IAAM,IAAM,IAAc,IAAI,KAAK,MAAO,EAAI,QAAQ,IAAe,IAAI,GAAG,GACtE,IAAW,EAAgB,QAAQ,OAAO,EAAI,SAAS,EAAE,QAAQ,KAAK,EAAE;AAC9E,aACE,kBAAC,GAAD;OAEE,WAAW,MAAQ,IAAI,UAAU;OACjC,MAAM,MAAQ;OACd,WAAU;iBAEV,kBAAC,OAAD;QAAK,WAAU;kBAAf;SAEE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD;YAAK,WAAW,2BAA2B,EAAQ,EAAI,IAAI;sBACxD,EAAc,EAAI,QAAQ,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;YAClD,CAAA,EACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,KAAD;aAAG,WAAU;uBACV,EAAI,IAAI,QAAQ,MAAM,IAAI;aACzB,CAAA,EACJ,kBAAC,GAAD;aAAO,SAAQ;aAAU,WAAU;uBAAnC,CACG,GAAI,cACC;eACJ;cACF;cACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;WAAqB,SAAA;qBACnB,kBAAC,GAAD;YACE,SAAQ;YACR,MAAK;YACL,WAAU;YACV,cAAY,eAAe,EAAI,IAAI,QAAQ,MAAM,IAAI;sBAErD,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;YAC/B,CAAA;WACW,CAAA,EACtB,kBAAC,GAAD;WAAqB,OAAM;qBAA3B,CACE,kBAAC,GAAD;YAAkB,eAAe,EAAS,qBAAqB;sBAA/D;aACE,kBAAC,GAAD,EAAc,WAAU,gBAAiB,CAAA;;aACnC,EAAK,OAAO,aAAa;aACd;eACnB,kBAAC,GAAD;YAAkB,eAAe,EAAO,EAAI,IAAI;sBAAhD,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,mBAEhB;cACC;aACT,EAAA,CAAA,CACX;;SAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAb,CAA4C,EAAK,OAAM,SAAU;cACjE,kBAAC,KAAD;WAAG,WAAU;qBAAmC,EAAI;WAAU,CAAA,CAC1D,EAAA,CAAA,EACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,KAAD;YAAG,WAAU;sBAA8B;YAAe,CAAA,EAC1D,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAsB,MAAM,CAAC,EAAI,WAAW;YAC3C,CAAA,CACA;aACF;;SAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;UACE,WAAU;UACV,MAAK;UACL,cAAY,EACT,KAAK,MAAM,GAAG,EAAE,MAAM,IAAI,EAAI,SAAS,EAAE,OAAO,CAChD,KAAK,KAAK;oBAEZ,EAAS,KAAK,MACb,kBAAC,OAAD,EAEE,OAAO;WACL,OAAO,IAAK,EAAI,SAAS,EAAE,QAAQ,KAAK,EAAI,QAAS,IAAI;WACzD,iBAAiB,EAAE;WACpB,EACD,EALK,EAAE,IAKP,CACF;UACE,CAAA,EACN,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAS,KAAK,MACb,kBAAC,QAAD;WAEE,WAAU;qBAFZ;YAIE,kBAAC,QAAD;aACE,WAAU;aACV,OAAO,EAAE,iBAAiB,EAAE,SAAS;aACrC,CAAA;YACD,EAAE;YACH,kBAAC,QAAD;aAAM,WAAU;uBACb,EAAI,SAAS,EAAE;aACX,CAAA;YACF;aAXA,EAAE,IAWF,CACP;UACE,CAAA,CACF,EAAA,CAAA;SAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAAsB;WAAqB,CAAA,EAC3D,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CACG,EAAI,eAAe,IAAI,EAAI,aAAa,QAAQ,EAAE,GAAG,KACtD,kBAAC,QAAD;YAAM,WAAU;sBAAsC;YAAW,CAAA,CAC5D;aACH;;SACF;;OACI,EA1GL,EAAI,IA0GC;OAEd;KACE,CAAA;IAGN,kBAAC,GAAD;KACE,OAAM;KACN,UACE,MAAgB,UACZ,GAAG,EAAK,OAAO,iBACf;KAEN,SACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,SAAS,MAAgB,UAAU,YAAY;OAC/C,MAAK;OACL,WAAU;OACV,eAAe,EAAe,QAAQ;iBACvC;OAEQ,CAAA,EACT,kBAAC,GAAD;OACE,SAAS,MAAgB,UAAU,YAAY;OAC/C,MAAK;OACL,WAAU;OACV,eAAe,EAAe,QAAQ;iBACvC;OAEQ,CAAA,CACL;;eAGR,kBAAC,GAAD;MACE,MAAM;MACN,MAAK;MACL,aAAY;MACZ,QAAQ,KAAK,IAAI,KAAK,EAAU,SAAS,GAAG;MAC5C,eAAe;MACf,aAAa,MAAgB,UAAU,EAAsB,MAAM,GAAG;MACtE,QAAQ,CACN;OACE,KAAK;OACL,OAAO,MAAgB,UAAU,EAAK,SAAS;OAChD,CACF;MACD,CAAA;KACQ,CAAA;IAGZ,kBAAC,GAAD;KACE,OAAM;KACN,UAAS;KACT,MAAM;eAEN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,MAAD;UAAI,WAAU;oBAAwB;UAAa,CAAA;SACnD,kBAAC,MAAD;UAAI,WAAU;oBACZ,kBAAC,UAAD;WACE,MAAK;WACL,WAAU;WACV,eAAe,EAAW,QAAQ;qBAHpC,CAIC,SAEC,kBAAC,GAAD,EACE,WAAW,WAAW,MAAY,UAAU,2BAA2B,MACvE,CAAA,CACK;;UACN,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACZ,kBAAC,UAAD;WACE,MAAK;WACL,WAAU;WACV,eAAe,EAAW,QAAQ;qBAHpC,CAIC,SAEC,kBAAC,GAAD,EACE,WAAW,WAAW,MAAY,UAAU,2BAA2B,MACvE,CAAA,CACK;;UACN,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBAAmB;UAAmB,CAAA;SACjD;WACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAW,KAAK,MACf,kBAAC,MAAD;QAAkB,WAAU;kBAA5B;SACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAI,IAAI,QAAQ,MAAM,IAAI;UACxB,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBAA4B,EAAI;UAAW,CAAA;SACzD,kBAAC,MAAD;UAAI,WAAU;oBAA4B,EAAM,EAAI,WAAW;UAAM,CAAA;SACrE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAI,eAAe,IAAI,EAAI,aAAa,QAAQ,EAAE,GAAG;UACnD,CAAA;SACF;UATI,EAAI,IASR,CACL,EACI,CAAA,CACF;;MACJ,CAAA;KACI,CAAA;IACX,EAAA,CAAA;GAED"}
|
|
1
|
+
{"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n Monitor,\n Server,\n Wifi,\n Truck,\n Wrench,\n Code,\n MoreHorizontal,\n Copy,\n ExternalLink,\n ArrowUpDown,\n Layers,\n} from 'lucide-react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetTerminology, useHiddenCategoryIds } from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerStore, type AssetStatus } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n ChartCard,\n StatTile,\n BarsChart,\n formatCompact,\n formatCurrencyCompact,\n} from '../components';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\n// Stable fallback tint for any category outside the known slug map — hashes the\n// key into the semantic palette so every real category is visually differentiated.\nconst FALLBACK_TINTS = [\n 'bg-status-info-bg-subtle text-status-info-text',\n 'bg-status-success-bg-subtle text-status-success-text',\n 'bg-status-warning-bg-subtle text-status-warning-text',\n 'bg-status-error-bg-subtle text-status-error-text',\n 'bg-action-primary-bg/10 text-action-primary-bg',\n];\nfunction tintFor(key: string): string {\n if (categoryColors[key]) return categoryColors[key];\n let hash = 0;\n for (let i = 0; i < key.length; i += 1) hash = (hash * 31 + key.charCodeAt(i)) >>> 0;\n return FALLBACK_TINTS[hash % FALLBACK_TINTS.length];\n}\n\n// Proportional status-bar segments — semantic tokens (CSS vars), theme-aware.\nconst STATUS_SEGMENTS: { key: AssetStatus; label: string; varName: string }[] = [\n { key: 'active', label: 'Active', varName: 'var(--color-status-success-bg)' },\n { key: 'maintenance', label: 'Maintenance', varName: 'var(--color-status-warning-bg)' },\n { key: 'retired', label: 'Retired', varName: 'var(--color-status-error-bg)' },\n { key: 'inactive', label: 'Inactive', varName: 'var(--color-status-info-bg)' },\n { key: 'disposed', label: 'Disposed', varName: 'var(--color-text-muted)' },\n];\n\nconst CONDITION_SCORE: Record<string, number> = {\n excellent: 5,\n good: 4,\n fair: 3,\n poor: 2,\n damaged: 1,\n};\n\ninterface CategoryAggregate {\n key: string;\n count: number;\n totalValue: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n avgCondition: number;\n}\n\nconst money = (value: number): string => `$${value.toLocaleString()}`;\n\nexport function AssetsAssetCategoriesPage() {\n const navigate = useAssetHandlerNavigate();\n const term = useAssetTerminology();\n const hiddenCategories = useHiddenCategoryIds();\n const { assets: allAssets } = useAssetHandlerStore();\n const assets = useMemo(\n () => allAssets.filter((a) => !hiddenCategories.has(a.category)),\n [allAssets, hiddenCategories]\n );\n const [chartMetric, setChartMetric] = useState<'count' | 'value'>('count');\n const [sortKey, setSortKey] = useState<'count' | 'value'>('count');\n\n const aggregates = useMemo<CategoryAggregate[]>(() => {\n const map: Record<string, CategoryAggregate> = {};\n for (const asset of assets) {\n if (!map[asset.category]) {\n map[asset.category] = {\n key: asset.category,\n count: 0,\n totalValue: 0,\n statuses: {},\n conditions: {},\n avgCondition: 0,\n };\n }\n const cat = map[asset.category];\n cat.count += 1;\n cat.totalValue += asset.currentValue ?? asset.purchaseValue ?? 0;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n }\n for (const cat of Object.values(map)) {\n let scoreSum = 0;\n let scoreN = 0;\n for (const [cond, n] of Object.entries(cat.conditions)) {\n const score = CONDITION_SCORE[cond];\n if (score) {\n scoreSum += score * n;\n scoreN += n;\n }\n }\n cat.avgCondition = scoreN > 0 ? scoreSum / scoreN : 0;\n }\n return Object.values(map).sort((a, b) => b.count - a.count);\n }, [assets]);\n\n const totalAssets = assets.length;\n const totalValue = useMemo(\n () => assets.reduce((sum, a) => sum + (a.currentValue ?? a.purchaseValue ?? 0), 0),\n [assets]\n );\n\n const chartData = useMemo(\n () =>\n aggregates.map((cat) => ({\n category: cat.key.replace(/_/g, ' '),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates]\n );\n\n const sortedRows = useMemo(\n () =>\n [...aggregates].sort((a, b) =>\n sortKey === 'count' ? b.count - a.count : b.totalValue - a.totalValue\n ),\n [aggregates, sortKey]\n );\n\n const copyId = (key: string) => {\n void navigator.clipboard?.writeText(key);\n notify.success('Category copied', `\"${key.replace(/_/g, ' ')}\" copied to clipboard.`);\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{term.asset} Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {aggregates.length} categories · {totalAssets} total {term.assets.toLowerCase()}\n </p>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where value and volume are concentrated.\n Each card rolls up how many assets you hold, their total book value and a proportional\n status mix — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {aggregates.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description={`Add ${term.assets.toLowerCase()} and they will be grouped into categories here automatically.`}\n />\n ) : (\n <>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <StatTile\n label=\"Categories\"\n value={aggregates.length}\n format={formatCompact}\n icon={Layers}\n hero\n />\n <StatTile\n label={`${term.assets} tracked`}\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile\n label=\"Total book value\"\n value={totalValue}\n format={formatCurrencyCompact('USD')}\n icon={Box}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {aggregates.map((cat, idx) => {\n const pct = totalAssets > 0 ? Math.round((cat.count / totalAssets) * 100) : 0;\n const segments = STATUS_SEGMENTS.filter((s) => (cat.statuses[s.key] || 0) > 0);\n return (\n <GlassCard\n key={cat.key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <div className=\"space-y-4 p-5\">\n {/* Header: icon + title + overflow */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className={`shrink-0 rounded-lg p-2 ${tintFor(cat.key)}`}>\n {categoryIcons[cat.key] || <Box className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </p>\n <Badge variant=\"outline\" className=\"mt-0.5 text-xs\">\n {pct}% of estate\n </Badge>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0\"\n aria-label={`Actions for ${cat.key.replace(/_/g, ' ')}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => navigate('/assets/asset-list')}>\n <ExternalLink className=\"mr-2 h-4 w-4\" />\n View {term.assets.toLowerCase()}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => copyId(cat.key)}>\n <Copy className=\"mr-2 h-4 w-4\" />\n Copy category ID\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Dual metric row */}\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <p className=\"text-xs text-text-secondary\">{term.asset} count</p>\n <p className=\"text-2xl font-bold tabular-nums\">{cat.count}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary\">Total value</p>\n <p className=\"text-2xl font-bold tabular-nums\">\n {formatCurrencyCompact('USD')(cat.totalValue)}\n </p>\n </div>\n </div>\n\n {/* Proportional status bar */}\n <div>\n <div\n className=\"flex h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"img\"\n aria-label={segments\n .map((s) => `${s.label}: ${cat.statuses[s.key]}`)\n .join(', ')}\n >\n {segments.map((s) => (\n <div\n key={s.key}\n style={{\n width: `${((cat.statuses[s.key] || 0) / cat.count) * 100}%`,\n backgroundColor: s.varName,\n }}\n />\n ))}\n </div>\n <div className=\"mt-2 flex flex-wrap gap-x-3 gap-y-1\">\n {segments.map((s) => (\n <span\n key={s.key}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary\"\n >\n <span\n className=\"h-2.5 w-2.5 rounded-[3px]\"\n style={{ backgroundColor: s.varName }}\n />\n {s.label}\n <span className=\"tabular-nums text-text-muted\">\n {cat.statuses[s.key]}\n </span>\n </span>\n ))}\n </div>\n </div>\n\n {/* Avg condition */}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-sm\">\n <span className=\"text-text-secondary\">Avg. condition</span>\n <span className=\"font-semibold tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n <span className=\"text-xs font-normal text-text-muted\"> / 5</span>\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {/* Cross-category comparison chart */}\n <ChartCard\n title=\"Category comparison\"\n subtitle={\n chartMetric === 'count' ? `${term.assets} per category` : 'Book value per category'\n }\n actions={\n <div className=\"flex gap-1 rounded-md border border-border-seam p-0.5\">\n <Button\n variant={chartMetric === 'count' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('count')}\n >\n Count\n </Button>\n <Button\n variant={chartMetric === 'value' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('value')}\n >\n Value\n </Button>\n </div>\n }\n >\n <BarsChart\n data={chartData}\n xKey=\"category\"\n orientation=\"horizontal\"\n height={Math.max(200, chartData.length * 44)}\n categoryWidth={120}\n valueFormat={chartMetric === 'value' ? formatCurrencyCompact('USD') : formatCompact}\n series={[\n {\n key: chartMetric,\n label: chartMetric === 'count' ? term.assets : 'Book value',\n },\n ]}\n />\n </ChartCard>\n\n {/* Summary comparison table */}\n <ChartCard\n title=\"Category summary\"\n subtitle=\"Sortable roll-up across every category\"\n icon={Layers}\n >\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[520px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Category</th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('count')}\n >\n Count\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'count' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('value')}\n >\n Value\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'value' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 font-medium\">Avg. condition</th>\n </tr>\n </thead>\n <tbody>\n {sortedRows.map((cat) => (\n <tr key={cat.key} className=\"border-b border-border-seam/60 last:border-0\">\n <td className=\"py-2.5 pr-4 font-medium capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{cat.count}</td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{money(cat.totalValue)}</td>\n <td className=\"py-2.5 tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsAssetListPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetListPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AssetsAssetListPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetListPage.tsx"],"names":[],"mappings":"AAiJA,wBAAgB,mBAAmB,gCA4flC"}
|