@burdenoff/microfe-assethandler 2026.625.2 → 2026.703.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.
Files changed (76) hide show
  1. package/dist/assethandler/AssetHandlerRoot.js +0 -24
  2. package/dist/assethandler/AssetHandlerRoot.js.map +1 -1
  3. package/dist/assethandler/AssetHandlerRoutes.js +92 -160
  4. package/dist/assethandler/AssetHandlerRoutes.js.map +1 -1
  5. package/dist/assethandler/pages/AnalyticsPage.js +367 -402
  6. package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
  7. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +29 -26
  8. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  9. package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js +278 -164
  10. package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js.map +1 -1
  11. package/dist/assethandler/pages/AssetsAssetCreatePage.js +200 -242
  12. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  13. package/dist/assethandler/pages/AssetsAssetDetailPage.js +192 -286
  14. package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
  15. package/dist/assethandler/pages/AssetsAssetListPage.js +204 -296
  16. package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
  17. package/dist/assethandler/pages/AssetsAssetTrackingPage.js +38 -42
  18. package/dist/assethandler/pages/AssetsAssetTrackingPage.js.map +1 -1
  19. package/dist/assethandler/pages/DashboardPage.js +212 -236
  20. package/dist/assethandler/pages/DashboardPage.js.map +1 -1
  21. package/dist/assethandler/pages/ItilIncidentDetailPage.js +41 -85
  22. package/dist/assethandler/pages/ItilIncidentDetailPage.js.map +1 -1
  23. package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js +1 -1
  24. package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js.map +1 -1
  25. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js +37 -86
  26. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
  27. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js +82 -98
  28. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
  29. package/dist/assethandler/pages/OperationsOperationsHubPage.js +6 -22
  30. package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
  31. package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js +4 -34
  32. package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js.map +1 -1
  33. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js +105 -150
  34. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
  35. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js +116 -152
  36. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
  37. package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
  38. package/dist/assethandler/store/assethandlerStore.js +68 -2206
  39. package/dist/assethandler/store/assethandlerStore.js.map +1 -1
  40. package/dist/assethandler/utils/toast.js +33 -0
  41. package/dist/assethandler/utils/toast.js.map +1 -0
  42. package/package.json +5 -4
  43. package/dist/assethandler/pages/AssetsAssetEditPage.js +0 -353
  44. package/dist/assethandler/pages/AssetsAssetEditPage.js.map +0 -1
  45. package/dist/assethandler/pages/AuditTrailPage.js +0 -215
  46. package/dist/assethandler/pages/AuditTrailPage.js.map +0 -1
  47. package/dist/assethandler/pages/MarketplaceMarketplaceDetailPage.js +0 -177
  48. package/dist/assethandler/pages/MarketplaceMarketplaceDetailPage.js.map +0 -1
  49. package/dist/assethandler/pages/MarketplaceMarketplaceListPage.js +0 -162
  50. package/dist/assethandler/pages/MarketplaceMarketplaceListPage.js.map +0 -1
  51. package/dist/assethandler/pages/MarketplaceMarketplaceOrdersPage.js +0 -143
  52. package/dist/assethandler/pages/MarketplaceMarketplaceOrdersPage.js.map +0 -1
  53. package/dist/assethandler/pages/MarketplaceMarketplaceSellPage.js +0 -163
  54. package/dist/assethandler/pages/MarketplaceMarketplaceSellPage.js.map +0 -1
  55. package/dist/assethandler/pages/ReportsReportBuilderPage.js +0 -208
  56. package/dist/assethandler/pages/ReportsReportBuilderPage.js.map +0 -1
  57. package/dist/assethandler/pages/ReportsReportsDashboardPage.js +0 -236
  58. package/dist/assethandler/pages/ReportsReportsDashboardPage.js.map +0 -1
  59. package/dist/assethandler/pages/ReportsReportsListPage.js +0 -144
  60. package/dist/assethandler/pages/ReportsReportsListPage.js.map +0 -1
  61. package/dist/assethandler/pages/SecurityScheduleBackupPage.js +0 -149
  62. package/dist/assethandler/pages/SecurityScheduleBackupPage.js.map +0 -1
  63. package/dist/assethandler/pages/UsersPage.js +0 -208
  64. package/dist/assethandler/pages/UsersPage.js.map +0 -1
  65. package/dist/assethandler/pages/UsersUserCreatePage.js +0 -220
  66. package/dist/assethandler/pages/UsersUserCreatePage.js.map +0 -1
  67. package/dist/assethandler/pages/UsersUserManagementHubPage.js +0 -373
  68. package/dist/assethandler/pages/UsersUserManagementHubPage.js.map +0 -1
  69. package/dist/assethandler/pages/WorkflowsWorkflowBuilderPage.js +0 -264
  70. package/dist/assethandler/pages/WorkflowsWorkflowBuilderPage.js.map +0 -1
  71. package/dist/assethandler/pages/WorkflowsWorkflowExecutionsPage.js +0 -206
  72. package/dist/assethandler/pages/WorkflowsWorkflowExecutionsPage.js.map +0 -1
  73. package/dist/assethandler/pages/WorkflowsWorkflowListPage.js +0 -171
  74. package/dist/assethandler/pages/WorkflowsWorkflowListPage.js.map +0 -1
  75. package/dist/assethandler/pages/WorkflowsWorkflowTemplatesPage.js +0 -173
  76. package/dist/assethandler/pages/WorkflowsWorkflowTemplatesPage.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/assethandler/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n GlassCard,\n CTAOverflowMenu,\n EmphasisPanel,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n BarChart3,\n TrendingUp,\n TrendingDown,\n DollarSign,\n Box,\n PieChart,\n Activity,\n Download,\n ArrowUpRight,\n ArrowDownRight,\n} from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\n\nexport function AnalyticsPage() {\n const { assets, portfolio, transactions, incidents, maintenanceRecords, workflows } =\n useAssetHandlerStore();\n const [timeRange, setTimeRange] = useState<'7d' | '30d' | '90d' | '1y'>('30d');\n\n const totalValue = assets.reduce((sum, a) => sum + a.currentValue, 0);\n const totalOriginal = assets.reduce((sum, a) => sum + a.value, 0);\n const depreciationTotal = totalOriginal - totalValue;\n const depreciationPct =\n totalOriginal > 0 ? ((depreciationTotal / totalOriginal) * 100).toFixed(1) : '0';\n const avgAssetValue = assets.length > 0 ? Math.round(totalValue / assets.length) : 0;\n\n const categoryBreakdown = assets.reduce<Record<string, { count: number; value: number }>>(\n (acc, a) => {\n const cat = a.category.replace('_', ' ');\n if (!acc[cat]) acc[cat] = { count: 0, value: 0 };\n acc[cat].count++;\n acc[cat].value += a.currentValue;\n return acc;\n },\n {}\n );\n\n const statusBreakdown = assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {});\n\n const departmentBreakdown = assets.reduce<Record<string, { count: number; value: number }>>(\n (acc, a) => {\n if (!acc[a.department]) acc[a.department] = { count: 0, value: 0 };\n acc[a.department].count++;\n acc[a.department].value += a.currentValue;\n return acc;\n },\n {}\n );\n\n const resolvedIncidents = incidents.filter(\n (i) => i.status === 'resolved' || i.status === 'closed'\n );\n const slaBreached = incidents.filter((i) => i.slaBreached);\n const completedMaintenance = maintenanceRecords.filter((m) => m.status === 'completed');\n const totalMaintenanceCost = maintenanceRecords.reduce((sum, m) => sum + m.cost, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold\">\n <GradientText>Analytics & Business Intelligence</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Comprehensive asset management analytics and insights\n </p>\n </div>\n <div className=\"flex gap-2\">\n <select\n value={timeRange}\n onChange={(e) => setTimeRange(e.target.value as typeof timeRange)}\n className=\"rounded-md border border-border-default bg-bg-surface px-3 py-1.5 text-sm\"\n >\n <option value=\"7d\">Last 7 days</option>\n <option value=\"30d\">Last 30 days</option>\n <option value=\"90d\">Last 90 days</option>\n <option value=\"1y\">Last year</option>\n </select>\n <CTAOverflowMenu\n primary={{ label: 'Generate Report', onSelect: () => {} }}\n actions={[\n { label: 'Export', onSelect: () => {}, icon: <Download className=\"h-4 w-4\" /> },\n ]}\n />\n </div>\n </div>\n\n <PagePurpose>\n Turn raw asset data into decisions. See where value is concentrated, how it is depreciating,\n which categories cost the most to run, and how operations are trending — so you can plan\n budgets, justify replacements and spot risk before it shows up as downtime.\n </PagePurpose>\n\n <Tabs defaultValue=\"overview\">\n <TabsList>\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 {/* Key Metrics */}\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Portfolio Value</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${portfolio.totalValue.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary flex items-center mt-1\">\n <ArrowUpRight className=\"mr-1 h-3 w-3 text-status-success-text\" />\n <span className=\"text-status-success-text\">+5.6%</span> from last quarter\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Depreciation</CardTitle>\n <TrendingDown className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold text-status-error-text\">\n -${depreciationTotal.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">{depreciationPct}% of original value</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Assets</CardTitle>\n <Box className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{portfolio.totalAssets}</div>\n <p className=\"text-xs text-text-secondary\">\n {assets.filter((a) => a.status === 'active').length} currently active\n </p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg. Asset Value</CardTitle>\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${avgAssetValue.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">Across all categories</p>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Category & Status Breakdown */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <PieChart className=\"h-4 w-4\" />\n Category Distribution\n </CardTitle>\n <CardDescription>Assets by category with values</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {Object.entries(categoryBreakdown)\n .sort((a, b) => b[1].value - a[1].value)\n .map(([cat, data]) => {\n const pct = Math.round((data.count / assets.length) * 100);\n return (\n <div key={cat} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium capitalize\">{cat}</span>\n <span className=\"text-text-secondary\">\n {data.count} &middot; ${(data.value / 1000).toFixed(1)}K\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"h-4 w-4\" />\n Status Breakdown\n </CardTitle>\n <CardDescription>Current asset status distribution</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {Object.entries(statusBreakdown).map(([status, count]) => {\n const pct = Math.round((count / assets.length) * 100);\n return (\n <div key={status} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Badge\n variant={\n status === 'active'\n ? 'default'\n : status === 'under_maintenance'\n ? 'secondary'\n : 'outline'\n }\n >\n {status.replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right\">{count}</span>\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator className=\"my-4\" />\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">By Department</h4>\n <div className=\"space-y-2\">\n {Object.entries(departmentBreakdown)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 5)\n .map(([dept, data]) => (\n <div key={dept} className=\"flex items-center justify-between text-sm\">\n <span>{dept}</span>\n <span className=\"text-text-secondary\">{data.count} assets</span>\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Depreciation Trend */}\n <Card>\n <CardHeader>\n <CardTitle>Depreciation Trend</CardTitle>\n <CardDescription>Portfolio depreciation over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex items-end gap-2 h-40\">\n {portfolio.depreciationTrend.map((point) => {\n const maxVal = Math.max(...portfolio.depreciationTrend.map((p) => p.value));\n const height = maxVal > 0 ? (point.value / maxVal) * 100 : 0;\n return (\n <div key={point.month} className=\"flex-1 flex flex-col items-center gap-1\">\n <span className=\"text-[10px] text-text-secondary\">\n ${(point.value / 1000000).toFixed(2)}M\n </span>\n <div\n className=\"w-full bg-action-primary-bg/80 rounded-t\"\n style={{ height: `${height}%` }}\n />\n <span className=\"text-[10px] text-text-secondary\">\n {point.month.replace(' 20', \"'\")}\n </span>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Financial Tab */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{transactions.length}</div>\n <p className=\"text-xs text-text-secondary\">\n ${transactions.reduce((s, t) => s + Math.abs(t.amount), 0).toLocaleString()} total\n value\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Cost</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${totalMaintenanceCost.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {maintenanceRecords.length} maintenance records\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Acquisitions (6mo)</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {portfolio.acquisitionTrend.reduce((s, a) => s + a.count, 0)}\n </div>\n <p className=\"text-xs text-text-secondary\">\n ${(portfolio.acquisitionTrend.reduce((s, a) => s + a.value, 0) / 1000).toFixed(0)}\n K total spend\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Acquisition Trend */}\n <Card>\n <CardHeader>\n <CardTitle>Acquisition Trend</CardTitle>\n <CardDescription>New assets acquired over the last 6 months</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex items-end gap-2 h-40\">\n {portfolio.acquisitionTrend.map((point) => {\n const maxVal = Math.max(...portfolio.acquisitionTrend.map((p) => p.count));\n const height = maxVal > 0 ? (point.count / maxVal) * 100 : 0;\n return (\n <div key={point.month} className=\"flex-1 flex flex-col items-center gap-1\">\n <span className=\"text-[10px] text-text-secondary\">{point.count} units</span>\n <div\n className=\"w-full bg-status-success-bg rounded-t\"\n style={{ height: `${height}%` }}\n />\n <span className=\"text-[10px] text-text-secondary\">\n {point.month.replace(' 20', \"'\")}\n </span>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n\n {/* Recent Transactions */}\n <Card>\n <CardHeader>\n <CardTitle>Recent Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b\">\n <th className=\"text-left py-2 font-medium\">ID</th>\n <th className=\"text-left py-2 font-medium\">Type</th>\n <th className=\"text-left py-2 font-medium\">Description</th>\n <th className=\"text-right py-2 font-medium\">Amount</th>\n <th className=\"text-left py-2 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 5).map((txn) => (\n <tr key={txn.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"py-2 font-mono text-xs\">{txn.id}</td>\n <td className=\"py-2 capitalize\">{txn.type}</td>\n <td className=\"py-2 truncate max-w-xs\">{txn.description}</td>\n <td\n className={`py-2 text-right font-medium ${txn.amount < 0 ? 'text-status-error-text' : ''}`}\n >\n ${Math.abs(txn.amount).toLocaleString()}\n </td>\n <td className=\"py-2\">\n <Badge variant=\"outline\">{txn.status}</Badge>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Operational Tab */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Incidents</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{incidents.length}</div>\n <p className=\"text-xs text-text-secondary\">{resolvedIncidents.length} resolved</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">SLA Breaches</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold text-status-error-text\">\n {slaBreached.length}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {((1 - slaBreached.length / Math.max(incidents.length, 1)) * 100).toFixed(1)}%\n compliance\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Done</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedMaintenance.length}</div>\n <p className=\"text-xs text-text-secondary\">of {maintenanceRecords.length} total</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Workflow Success</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {(\n workflows.reduce((s, w) => s + w.successRate, 0) / Math.max(workflows.length, 1)\n ).toFixed(1)}\n %\n </div>\n <p className=\"text-xs text-text-secondary\">{workflows.length} active workflows</p>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Insights Tab */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md: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 portfolio spans {Object.keys(categoryBreakdown).length} categories across{' '}\n {Object.keys(departmentBreakdown).length} departments. IT Hardware represents the\n largest segment at {portfolio.byCategory[0]?.count || 0} assets.\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\">Performance</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-success-text\">\n Portfolio depreciated value stands at $\n {(portfolio.depreciatedValue / 1000000).toFixed(2)}M. Depreciation rate is within\n expected range at {depreciationPct}% of original cost.\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 {maintenanceRecords.filter((m) => m.status === 'scheduled').length} maintenance\n tasks are scheduled. Total maintenance spend is $\n {totalMaintenanceCost.toLocaleString()}. Stay on top of preventive maintenance.\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Top Assets by Value */}\n <EmphasisPanel>\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Top Assets by Value</CardTitle>\n <CardDescription>Highest value assets in your portfolio</CardDescription>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n <div className=\"space-y-3\">\n {[...assets]\n .sort((a, b) => b.currentValue - a.currentValue)\n .slice(0, 5)\n .map((asset, i) => (\n <div\n key={asset.id}\n className=\"flex items-center justify-between p-3 rounded-lg border border-border-seam bg-gloss-card\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div>\n <p className=\"text-sm font-medium\">{asset.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {asset.department} &middot; {asset.category.replace('_', ' ')}\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-medium\">\n ${asset.currentValue.toLocaleString()}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {asset.currentValue < asset.value ? (\n <span className=\"text-status-error-text\">\n -{((1 - asset.currentValue / asset.value) * 100).toFixed(1)}%\n </span>\n ) : (\n <span className=\"text-status-success-text\">No depreciation</span>\n )}\n </p>\n </div>\n </div>\n ))}\n </div>\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 Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Box, PieChart, Activity, Wrench, AlertTriangle, Wallet } from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\n\nconst statusVariant = (status: string): 'default' | 'secondary' | 'outline' =>\n status === 'active' ? 'default' : status === 'maintenance' ? 'secondary' : 'outline';\n\nexport function AnalyticsPage() {\n const { assets, transactions, incidents, maintenanceRecords, budgets, invoices, workOrders } =\n useAssetHandlerStore();\n\n // ─── Asset aggregations (count-based only — no monetary asset value exists) ──\n const categoryBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n const cat = a.category.replace('_', ' ');\n acc[cat] = (acc[cat] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const statusBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const conditionBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.condition] = (acc[a.condition] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const activeAssets = assets.filter((a) => a.status === 'active').length;\n const attentionAssets = assets.filter(\n (a) => a.condition === 'poor' || a.condition === 'damaged' || a.status === 'maintenance'\n );\n\n // ─── Financial aggregations (from real transactions / budgets / invoices) ────\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((i) => i.status !== 'paid' && i.status !== 'cancelled');\n const outstandingInvoiceTotal = outstandingInvoices.reduce((s, i) => s + i.amount, 0);\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 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 return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold\">\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 </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>\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 {/* Key Metrics */}\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Assets</CardTitle>\n <Box className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{assets.length}</div>\n <p className=\"text-xs text-text-secondary\">{activeAssets} currently active</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Categories</CardTitle>\n <PieChart className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {Object.keys(categoryBreakdown).length}\n </div>\n <p className=\"text-xs text-text-secondary\">Distinct asset categories</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Needs Attention</CardTitle>\n <AlertTriangle className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{attentionAssets.length}</div>\n <p className=\"text-xs text-text-secondary\">Poor / damaged / in maintenance</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Incidents</CardTitle>\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {incidents.length - resolvedIncidents.length}\n </div>\n <p className=\"text-xs text-text-secondary\">{incidents.length} total logged</p>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Category & Status Breakdown */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <PieChart className=\"h-4 w-4\" />\n Category Distribution\n </CardTitle>\n <CardDescription>Assets grouped by category</CardDescription>\n </CardHeader>\n <CardContent>\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets yet\"\n description=\"Add assets to see how your estate is distributed.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {Object.entries(categoryBreakdown)\n .sort((a, b) => b[1] - a[1])\n .map(([cat, count]) => {\n const pct = Math.round((count / assets.length) * 100);\n return (\n <div key={cat} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium capitalize\">{cat}</span>\n <span className=\"text-text-secondary\">\n {count} &middot; {pct}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"h-4 w-4\" />\n Status &amp; Condition\n </CardTitle>\n <CardDescription>Current asset status and condition mix</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {Object.entries(statusBreakdown).map(([status, count]) => {\n const pct = assets.length > 0 ? Math.round((count / assets.length) * 100) : 0;\n return (\n <div key={status} className=\"flex items-center justify-between\">\n <Badge variant={statusVariant(status)}>{status.replace('_', ' ')}</Badge>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right\">{count}</span>\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator className=\"my-4\" />\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">By Condition</h4>\n <div className=\"flex flex-wrap gap-2\">\n {Object.entries(conditionBreakdown).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"capitalize\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Financial Tab */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{transactions.length}</div>\n <p className=\"text-xs text-text-secondary\">\n ${transactionsTotal.toLocaleString()} total value\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Budget Allocated</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${budgetAllocated.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n ${budgetSpent.toLocaleString()} spent\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Cost</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${maintenanceCost.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {maintenanceRecords.length} maintenance records\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Outstanding Invoices</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${outstandingInvoiceTotal.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {outstandingInvoices.length} of {invoices.length} unpaid\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Budget Utilization */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Wallet className=\"h-4 w-4\" />\n Budget Utilization\n </CardTitle>\n <CardDescription>Allocated vs. spent per budget</CardDescription>\n </CardHeader>\n <CardContent>\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-3\">\n {budgets.map((budget) => {\n const pct =\n budget.allocated > 0\n ? Math.min(100, Math.round((budget.spent / budget.allocated) * 100))\n : 0;\n return (\n <div key={budget.id} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{budget.name}</span>\n <span className=\"text-text-secondary\">\n ${budget.spent.toLocaleString()} / ${budget.allocated.toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className={`h-full rounded-full ${pct >= 100 ? 'bg-status-error-text' : 'bg-action-primary-bg'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\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>${budgetRemaining.toLocaleString()}</span>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Recent Transactions */}\n <Card>\n <CardHeader>\n <CardTitle>Recent Transactions</CardTitle>\n </CardHeader>\n <CardContent>\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\">\n <th className=\"text-left py-2 font-medium\">Type</th>\n <th className=\"text-left py-2 font-medium\">Description</th>\n <th className=\"text-right py-2 font-medium\">Amount</th>\n <th className=\"text-left py-2 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 5).map((txn) => (\n <tr key={txn.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"py-2 capitalize\">{txn.type}</td>\n <td className=\"py-2 truncate max-w-xs\">{txn.description}</td>\n <td\n className={`py-2 text-right font-medium ${txn.amount < 0 ? 'text-status-error-text' : ''}`}\n >\n ${Math.abs(txn.amount).toLocaleString()}\n </td>\n <td className=\"py-2\">\n <Badge variant=\"outline\">{txn.status}</Badge>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Operational Tab */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Incidents</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{incidents.length}</div>\n <p className=\"text-xs text-text-secondary\">{resolvedIncidents.length} resolved</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">SLA Breaches</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold text-status-error-text\">\n {slaBreached.length}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {((1 - slaBreached.length / Math.max(incidents.length, 1)) * 100).toFixed(1)}%\n within SLA\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Done</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedMaintenance.length}</div>\n <p className=\"text-xs text-text-secondary\">of {maintenanceRecords.length} total</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Work Orders</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{openWorkOrders.length}</div>\n <p className=\"text-xs text-text-secondary\">of {workOrders.length} total</p>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Insights Tab */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md: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 {Object.keys(categoryBreakdown).length} categories across{' '}\n {assets.length} assets. {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} maintenance tasks are scheduled. Total maintenance\n spend is ${maintenanceCost.toLocaleString()}. Stay on top of preventive\n maintenance.\n </p>\n </CardContent>\n </Card>\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 <CardDescription>\n Assets in poor or damaged condition, or currently under maintenance\n </CardDescription>\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 p-3 rounded-lg border border-border-seam bg-gloss-card\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div>\n <p className=\"text-sm font-medium\">{asset.name}</p>\n <p className=\"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 items-center gap-2\">\n <Badge variant=\"outline\" className=\"capitalize\">\n {asset.condition}\n </Badge>\n <Badge variant={statusVariant(asset.status)}>\n {asset.status.replace('_', ' ')}\n </Badge>\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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"}
@@ -29,17 +29,16 @@ function T() {
29
29
  for (let t of r) {
30
30
  e[t.category] || (e[t.category] = {
31
31
  count: 0,
32
- totalValue: 0,
33
- currentValue: 0,
34
32
  statuses: {},
33
+ conditions: {},
35
34
  topAssets: []
36
35
  });
37
36
  let n = e[t.category];
38
- n.count++, n.totalValue += t.value, n.currentValue += t.currentValue, n.statuses[t.status] = (n.statuses[t.status] || 0) + 1, n.topAssets.push(t);
37
+ n.count++, n.statuses[t.status] = (n.statuses[t.status] || 0) + 1, n.conditions[t.condition] = (n.conditions[t.condition] || 0) + 1, n.topAssets.push(t);
39
38
  }
40
- for (let t of Object.values(e)) t.topAssets.sort((e, t) => t.currentValue - e.currentValue), t.topAssets = t.topAssets.slice(0, 3);
39
+ for (let t of Object.values(e)) t.topAssets.sort((e, t) => e.name.localeCompare(t.name)), t.topAssets = t.topAssets.slice(0, 3);
41
40
  return e;
42
- }, [r]), o = r.length, s = r.reduce((e, t) => e + t.currentValue, 0);
41
+ }, [r]), o = r.length;
43
42
  return /* @__PURE__ */ S("div", {
44
43
  className: "space-y-6 p-6",
45
44
  children: [
@@ -54,16 +53,14 @@ function T() {
54
53
  Object.keys(i).length,
55
54
  " categories · ",
56
55
  o,
57
- " total assets · $",
58
- s.toLocaleString(),
59
- " total value"
56
+ " total assets"
60
57
  ]
61
58
  })] }), /* @__PURE__ */ S(d, {
62
59
  size: "sm",
63
60
  children: [/* @__PURE__ */ x(a, { className: "mr-2 h-4 w-4" }), "Add Category"]
64
61
  })]
65
62
  }),
66
- /* @__PURE__ */ x(y, { children: "Group and compare your assets by category to see where value and risk are concentrated. Each card rolls up how many assets you have in a category, their current value, depreciation, status mix and top items — so you can plan procurement and replacements by category, not one asset at a time." }),
63
+ /* @__PURE__ */ x(y, { children: "Group and compare your assets by category to see where they are concentrated. Each card rolls up how many assets you have in a category, their status and condition mix, and a few example items — so you can plan procurement and replacements by category, not one asset at a time." }),
67
64
  Object.keys(i).length === 0 ? /* @__PURE__ */ x(v, {
68
65
  illustration: "empty-data",
69
66
  title: "No categories yet",
@@ -71,7 +68,7 @@ function T() {
71
68
  }) : /* @__PURE__ */ x("div", {
72
69
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
73
70
  children: Object.entries(i).map(([e, t], r) => {
74
- let i = o > 0 ? Math.round(t.count / o * 100) : 0, a = t.totalValue - t.currentValue;
71
+ let i = o > 0 ? Math.round(t.count / o * 100) : 0;
75
72
  return /* @__PURE__ */ S(g, {
76
73
  treatment: r === 0 ? "glass" : "plain",
77
74
  glow: r === 0,
@@ -102,20 +99,10 @@ function T() {
102
99
  className: "flex justify-between text-sm",
103
100
  children: [/* @__PURE__ */ x("span", {
104
101
  className: "text-text-secondary",
105
- children: "Current Value"
102
+ children: "Share of estate"
106
103
  }), /* @__PURE__ */ S("span", {
107
104
  className: "font-medium",
108
- children: ["$", t.currentValue.toLocaleString()]
109
- })]
110
- }),
111
- /* @__PURE__ */ S("div", {
112
- className: "flex justify-between text-sm",
113
- children: [/* @__PURE__ */ x("span", {
114
- className: "text-text-secondary",
115
- children: "Depreciation"
116
- }), /* @__PURE__ */ S("span", {
117
- className: "text-status-error-text",
118
- children: ["-$", a.toLocaleString()]
105
+ children: [i, "%"]
119
106
  })]
120
107
  }),
121
108
  /* @__PURE__ */ x("div", {
@@ -144,7 +131,23 @@ function T() {
144
131
  /* @__PURE__ */ x(b, {}),
145
132
  /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("p", {
146
133
  className: "text-xs font-medium text-text-secondary mb-2",
147
- children: "Top Assets"
134
+ children: "Condition breakdown"
135
+ }), /* @__PURE__ */ x("div", {
136
+ className: "flex flex-wrap gap-1",
137
+ children: Object.entries(t.conditions).map(([e, t]) => /* @__PURE__ */ S(u, {
138
+ variant: "outline",
139
+ className: "text-[10px]",
140
+ children: [
141
+ e,
142
+ ": ",
143
+ t
144
+ ]
145
+ }, e))
146
+ })] }),
147
+ /* @__PURE__ */ x(b, {}),
148
+ /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("p", {
149
+ className: "text-xs font-medium text-text-secondary mb-2",
150
+ children: "Example assets"
148
151
  }), /* @__PURE__ */ x("div", {
149
152
  className: "space-y-1.5",
150
153
  children: t.topAssets.map((e) => /* @__PURE__ */ S("div", {
@@ -152,9 +155,9 @@ function T() {
152
155
  children: [/* @__PURE__ */ x("span", {
153
156
  className: "truncate flex-1",
154
157
  children: e.name
155
- }), /* @__PURE__ */ S("span", {
156
- className: "font-medium ml-2",
157
- children: ["$", e.currentValue.toLocaleString()]
158
+ }), /* @__PURE__ */ x("span", {
159
+ className: "font-medium ml-2 capitalize text-text-secondary",
160
+ children: e.condition
158
161
  })]
159
162
  }, e.id))
160
163
  })] })
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Box, Monitor, Server, Wifi, Truck, Wrench, Code, Plus } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\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\nexport function AssetsAssetCategoriesPage() {\n const { assets } = useAssetHandlerStore();\n\n const categories = useMemo(() => {\n const catMap: Record<\n string,\n {\n count: number;\n totalValue: number;\n currentValue: number;\n statuses: Record<string, number>;\n topAssets: Asset[];\n }\n > = {};\n for (const asset of assets) {\n if (!catMap[asset.category])\n catMap[asset.category] = {\n count: 0,\n totalValue: 0,\n currentValue: 0,\n statuses: {},\n topAssets: [],\n };\n const cat = catMap[asset.category];\n cat.count++;\n cat.totalValue += asset.value;\n cat.currentValue += asset.currentValue;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.topAssets.push(asset);\n }\n for (const cat of Object.values(catMap)) {\n cat.topAssets.sort((a, b) => b.currentValue - a.currentValue);\n cat.topAssets = cat.topAssets.slice(0, 3);\n }\n return catMap;\n }, [assets]);\n\n const totalAssets = assets.length;\n const totalValue = assets.reduce((s, a) => s + a.currentValue, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>Asset Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {Object.keys(categories).length} categories &middot; {totalAssets} total assets &middot;\n ${totalValue.toLocaleString()} total value\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n Add Category\n </Button>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where value and risk are concentrated.\n Each card rolls up how many assets you have in a category, their current value,\n depreciation, status mix and top items — so you can plan procurement and replacements by\n category, not one asset at a time.\n </PagePurpose>\n\n {Object.keys(categories).length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description=\"Add assets and they will be grouped into categories here automatically.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {Object.entries(categories).map(([key, data], idx) => {\n const pct = totalAssets > 0 ? Math.round((data.count / totalAssets) * 100) : 0;\n const depreciation = data.totalValue - data.currentValue;\n return (\n <GlassCard\n key={key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-center justify-between\">\n <div className={`p-2 rounded-lg ${categoryColors[key] || 'bg-bg-sunken'}`}>\n {categoryIcons[key] || <Box className=\"h-5 w-5\" />}\n </div>\n <Badge variant=\"outline\">{pct}%</Badge>\n </div>\n <CardTitle className=\"capitalize mt-3\">{key.replace('_', ' ')}</CardTitle>\n <CardDescription>{data.count} assets</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Current Value</span>\n <span className=\"font-medium\">${data.currentValue.toLocaleString()}</span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Depreciation</span>\n <span className=\"text-status-error-text\">\n -${depreciation.toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Status breakdown</p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.statuses).map(([status, count]) => (\n <Badge key={status} variant=\"secondary\" className=\"text-[10px]\">\n {status.replace('_', ' ')}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Top Assets</p>\n <div className=\"space-y-1.5\">\n {data.topAssets.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between text-xs\">\n <span className=\"truncate flex-1\">{a.name}</span>\n <span className=\"font-medium ml-2\">\n ${a.currentValue.toLocaleString()}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Box, Monitor, Server, Wifi, Truck, Wrench, Code, Plus } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\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\nexport function AssetsAssetCategoriesPage() {\n const { assets } = useAssetHandlerStore();\n\n const categories = useMemo(() => {\n const catMap: Record<\n string,\n {\n count: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n topAssets: Asset[];\n }\n > = {};\n for (const asset of assets) {\n if (!catMap[asset.category])\n catMap[asset.category] = {\n count: 0,\n statuses: {},\n conditions: {},\n topAssets: [],\n };\n const cat = catMap[asset.category];\n cat.count++;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n cat.topAssets.push(asset);\n }\n for (const cat of Object.values(catMap)) {\n cat.topAssets.sort((a, b) => a.name.localeCompare(b.name));\n cat.topAssets = cat.topAssets.slice(0, 3);\n }\n return catMap;\n }, [assets]);\n\n const totalAssets = assets.length;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>Asset Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {Object.keys(categories).length} categories &middot; {totalAssets} total assets\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n Add Category\n </Button>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where they are concentrated. Each card\n rolls up how many assets you have in a category, their status and condition mix, and a few\n example items — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {Object.keys(categories).length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description=\"Add assets and they will be grouped into categories here automatically.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {Object.entries(categories).map(([key, data], idx) => {\n const pct = totalAssets > 0 ? Math.round((data.count / totalAssets) * 100) : 0;\n return (\n <GlassCard\n key={key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-center justify-between\">\n <div className={`p-2 rounded-lg ${categoryColors[key] || 'bg-bg-sunken'}`}>\n {categoryIcons[key] || <Box className=\"h-5 w-5\" />}\n </div>\n <Badge variant=\"outline\">{pct}%</Badge>\n </div>\n <CardTitle className=\"capitalize mt-3\">{key.replace('_', ' ')}</CardTitle>\n <CardDescription>{data.count} assets</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Share of estate</span>\n <span className=\"font-medium\">{pct}%</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Status breakdown</p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.statuses).map(([status, count]) => (\n <Badge key={status} variant=\"secondary\" className=\"text-[10px]\">\n {status.replace('_', ' ')}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">\n Condition breakdown\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.conditions).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"text-[10px]\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Example assets</p>\n <div className=\"space-y-1.5\">\n {data.topAssets.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between text-xs\">\n <span className=\"truncate flex-1\">{a.name}</span>\n <span className=\"font-medium ml-2 capitalize text-text-secondary\">\n {a.condition}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}