@burdenoff/microfe-assethandler 2026.813.1 → 2026.813.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/assethandler/components/charts/BarsChart.js +25 -25
  2. package/dist/assethandler/components/charts/ChartParts.js +27 -27
  3. package/dist/assethandler/components/charts/DonutChart.js +17 -17
  4. package/dist/assethandler/components/charts/ProgressMeter.js +5 -5
  5. package/dist/assethandler/components/charts/RadialGauge.js +13 -13
  6. package/dist/assethandler/components/charts/Sparkline.js +27 -27
  7. package/dist/assethandler/components/charts/TrendChart.js +47 -47
  8. package/dist/assethandler/components/index.js +1 -1
  9. package/dist/assethandler/pages/AiPredictionsPage.js +20 -20
  10. package/dist/assethandler/pages/AnalyticsPage.d.ts.map +1 -1
  11. package/dist/assethandler/pages/AnalyticsPage.js +133 -133
  12. package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
  13. package/dist/assethandler/pages/AssetsAssetCategoriesPage.d.ts.map +1 -1
  14. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +55 -55
  15. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  16. package/dist/assethandler/pages/AssetsAssetCreatePage.d.ts.map +1 -1
  17. package/dist/assethandler/pages/AssetsAssetCreatePage.js +77 -78
  18. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  19. package/dist/assethandler/pages/AssetsAssetDetailPage.d.ts.map +1 -1
  20. package/dist/assethandler/pages/AssetsAssetDetailPage.js +140 -141
  21. package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
  22. package/dist/assethandler/pages/AssetsAssetListPage.js +249 -249
  23. package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
  24. package/dist/assethandler/pages/AssetsAssetTrackingPage.js +8 -8
  25. package/dist/assethandler/pages/CmdbBrowserPage.js +34 -34
  26. package/dist/assethandler/pages/DashboardPage.d.ts.map +1 -1
  27. package/dist/assethandler/pages/DashboardPage.js +256 -255
  28. package/dist/assethandler/pages/DashboardPage.js.map +1 -1
  29. package/dist/assethandler/pages/DiscoveryJobsPage.js +27 -27
  30. package/dist/assethandler/pages/EsgDashboardPage.js +53 -53
  31. package/dist/assethandler/pages/FinanceBudgetDetailPage.d.ts.map +1 -1
  32. package/dist/assethandler/pages/FinanceBudgetDetailPage.js +156 -155
  33. package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
  34. package/dist/assethandler/pages/FinanceFinanceHubPage.d.ts.map +1 -1
  35. package/dist/assethandler/pages/FinanceFinanceHubPage.js +475 -475
  36. package/dist/assethandler/pages/FinanceFinanceHubPage.js.map +1 -1
  37. package/dist/assethandler/pages/FinanceInvoiceDetailPage.d.ts.map +1 -1
  38. package/dist/assethandler/pages/FinanceInvoiceDetailPage.js +141 -140
  39. package/dist/assethandler/pages/FinanceInvoiceDetailPage.js.map +1 -1
  40. package/dist/assethandler/pages/ItilChangeManagementPage.js +21 -21
  41. package/dist/assethandler/pages/ItilIncidentManagementPage.js +34 -34
  42. package/dist/assethandler/pages/ItilProblemManagementPage.js +17 -17
  43. package/dist/assethandler/pages/KnowledgeBasePage.js +39 -39
  44. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.d.ts.map +1 -1
  45. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js +114 -114
  46. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
  47. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.d.ts.map +1 -1
  48. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js +158 -157
  49. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
  50. package/dist/assethandler/pages/OperationsOperationsHubPage.d.ts.map +1 -1
  51. package/dist/assethandler/pages/OperationsOperationsHubPage.js +253 -252
  52. package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
  53. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.d.ts.map +1 -1
  54. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js +96 -95
  55. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
  56. package/dist/assethandler/pages/ProvenancePage.js +21 -21
  57. package/dist/assethandler/pages/ReportsDashboardPage.d.ts.map +1 -1
  58. package/dist/assethandler/pages/ReportsDashboardPage.js +301 -301
  59. package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
  60. package/dist/assethandler/pages/SecuritySecurityHubPage.js +126 -126
  61. package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
  62. package/dist/assethandler/pages/TransactionsTransactionDetailPage.d.ts.map +1 -1
  63. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js +112 -113
  64. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
  65. package/dist/assethandler/pages/TransactionsTransactionListPage.d.ts.map +1 -1
  66. package/dist/assethandler/pages/TransactionsTransactionListPage.js +136 -137
  67. package/dist/assethandler/pages/TransactionsTransactionListPage.js.map +1 -1
  68. package/dist/assethandler/providers/AssetHandlerProvider.d.ts +14 -0
  69. package/dist/assethandler/providers/AssetHandlerProvider.d.ts.map +1 -1
  70. package/dist/assethandler/providers/AssetHandlerProvider.js +44 -34
  71. package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
  72. package/dist/assethandler/types/index.d.ts +10 -0
  73. package/dist/assethandler/types/index.d.ts.map +1 -1
  74. package/dist/assethandler/utils/money.d.ts +34 -0
  75. package/dist/assethandler/utils/money.d.ts.map +1 -0
  76. package/dist/assethandler/utils/money.js +27 -0
  77. package/dist/assethandler/utils/money.js.map +1 -0
  78. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"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 {\n formatAssetCategory,\n useAssetCategoryPresentation,\n useAssetTerminology,\n useHiddenCategoryIds,\n} 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 { labels: categoryLabels } = useAssetCategoryPresentation();\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: formatAssetCategory(cat.key, categoryLabels),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates, categoryLabels]\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(\n 'Category copied',\n `\"${formatAssetCategory(key, categoryLabels)}\" copied to clipboard.`\n );\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 &middot; {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 {formatAssetCategory(cat.key, categoryLabels)}\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 ${formatAssetCategory(cat.key, categoryLabels)}`}\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 {formatAssetCategory(cat.key, categoryLabels)}\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
+ {"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 {\n formatAssetCategory,\n useAssetCategoryPresentation,\n useAssetMoney,\n useAssetTerminology,\n useHiddenCategoryIds,\n} from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerStore, type AssetStatus } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { ChartCard, StatTile, BarsChart, formatCompact } 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\nexport function AssetsAssetCategoriesPage() {\n const { format: money, compact: moneyCompact } = useAssetMoney();\n const navigate = useAssetHandlerNavigate();\n const term = useAssetTerminology();\n const hiddenCategories = useHiddenCategoryIds();\n const { labels: categoryLabels } = useAssetCategoryPresentation();\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: formatAssetCategory(cat.key, categoryLabels),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates, categoryLabels]\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(\n 'Category copied',\n `\"${formatAssetCategory(key, categoryLabels)}\" copied to clipboard.`\n );\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 &middot; {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={moneyCompact}\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 {formatAssetCategory(cat.key, categoryLabels)}\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 ${formatAssetCategory(cat.key, categoryLabels)}`}\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 {moneyCompact(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' ? moneyCompact : 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 {formatAssetCategory(cat.key, categoryLabels)}\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":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AAiLA,wBAAgB,qBAAqB,gCA6jBpC"}
1
+ {"version":3,"file":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AAkLA,wBAAgB,qBAAqB,gCA8jBpC"}
@@ -1,21 +1,20 @@
1
1
  import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
2
  import { formatAssetCategory as t } from "../utils/categoryPresentation.js";
3
- import { useAssetCategoryPresentation as n, useHiddenCategoryIds as r } from "../providers/AssetHandlerProvider.js";
4
- import { useAssetHandlerNavigate as i } from "../hooks/useAssetHandlerNavigate.js";
5
- import { useCopy as a } from "../hooks/useCopy.js";
6
- import { notify as o } from "../utils/toast.js";
7
- import { formatCurrencyCompact as ee } from "../components/charts/chartTheme.js";
8
- import { TrendChart as te } from "../components/charts/TrendChart.js";
9
- import { ChartCard as s } from "../components/ChartCard.js";
3
+ import { useAssetCategoryPresentation as n, useAssetMoney as r, useHiddenCategoryIds as i } from "../providers/AssetHandlerProvider.js";
4
+ import { useAssetHandlerNavigate as a } from "../hooks/useAssetHandlerNavigate.js";
5
+ import { useCopy as o } from "../hooks/useCopy.js";
6
+ import { notify as s } from "../utils/toast.js";
7
+ import { TrendChart as c } from "../components/charts/TrendChart.js";
8
+ import { ChartCard as ee } from "../components/ChartCard.js";
10
9
  import "../components/index.js";
11
- import { useMemo as c, useRef as l, useState as u } from "react";
12
- import { ArrowLeft as ne, Info as re, Loader2 as d, Save as f, TrendingDown as ie, X as p } from "lucide-react";
13
- import { gql as m } from "@apollo/client";
14
- import { useMutation as h } from "@apollo/client/react";
10
+ import { useMemo as l, useRef as u, useState as d } from "react";
11
+ import { ArrowLeft as te, Info as ne, Loader2 as f, Save as p, TrendingDown as re, X as m } from "lucide-react";
12
+ import { gql as h } from "@apollo/client";
13
+ import { useMutation as ie } from "@apollo/client/react";
15
14
  import { jsx as g, jsxs as _ } from "react/jsx-runtime";
16
- import { Badge as v, Button as y, GlassCard as b, GradientText as ae, Input as x, Label as S, PagePurpose as oe, Select as C, SelectContent as w, SelectItem as T, SelectTrigger as E, SelectValue as D, Textarea as O } from "@burdenoff/fe-libs/ui";
15
+ import { Badge as v, Button as y, GlassCard as b, GradientText as ae, Input as x, Label as S, PagePurpose as C, Select as w, SelectContent as T, SelectItem as E, SelectTrigger as D, SelectValue as O, Textarea as k } from "@burdenoff/fe-libs/ui";
17
16
  //#region src/assethandler/pages/AssetsAssetCreatePage.tsx
18
- var k = m`
17
+ var oe = h`
19
18
  mutation AssetHandlerCreateAsset($input: CreateAssetInput!) {
20
19
  createAsset(input: $input) {
21
20
  id
@@ -23,7 +22,7 @@ var k = m`
23
22
  name
24
23
  }
25
24
  }
26
- `, A = [
25
+ `, se = [
27
26
  "electronics",
28
27
  "it_hardware",
29
28
  "furniture",
@@ -32,7 +31,7 @@ var k = m`
32
31
  "software",
33
32
  "networking",
34
33
  "servers"
35
- ], j = {
34
+ ], A = {
36
35
  name: "",
37
36
  description: "",
38
37
  category: "it_hardware",
@@ -53,7 +52,7 @@ var k = m`
53
52
  supplier: "",
54
53
  department: ""
55
54
  };
56
- function M(e) {
55
+ function j(e) {
57
56
  let t = e.purchaseValue, n = e.usefulLifeMonths;
58
57
  if (!t || t <= 0 || !n || n <= 0) return [];
59
58
  let r = Math.max(0, Math.min(e.salvageValue ?? 0, t)), i = e.method.toLowerCase(), a = Math.max(1, Math.round(n / 12));
@@ -89,7 +88,7 @@ function M(e) {
89
88
  }
90
89
  return o;
91
90
  }
92
- var N = ({ title: e, description: t, children: n, glow: r }) => /* @__PURE__ */ g(b, {
91
+ var M = ({ title: e, description: t, children: n, glow: r }) => /* @__PURE__ */ g(b, {
93
92
  treatment: "glass",
94
93
  glow: r,
95
94
  className: "border-border-seam",
@@ -104,25 +103,25 @@ var N = ({ title: e, description: t, children: n, glow: r }) => /* @__PURE__ */
104
103
  }) : null] }), n]
105
104
  })
106
105
  });
107
- function P() {
108
- let m = a(), b = i(), P = r(), { labels: F, options: I } = n(), { assets: L } = e(), [R, z] = u(j), [B, V] = u([]), [H, U] = u(""), [W, G] = u({}), [K, q] = u(null), J = l(!1), se = c(() => {
109
- let e = new Set(I?.map((e) => e.id) ?? A);
106
+ function N() {
107
+ let h = o(), b = a(), { symbol: N, compact: ce } = r(), P = i(), { labels: F, options: I } = n(), { assets: L } = e(), [R, z] = d(A), [B, V] = d([]), [H, U] = d(""), [W, G] = d({}), [K, q] = d(null), J = u(!1), le = l(() => {
108
+ let e = new Set(I?.map((e) => e.id) ?? se);
110
109
  if (!I) for (let t of L) t.category && e.add(t.category);
111
110
  return [...e].filter((e) => !P.has(e)).sort();
112
111
  }, [
113
112
  L,
114
113
  I,
115
114
  P
116
- ]), [ce, { loading: Y }] = h(k, {
115
+ ]), [ue, { loading: Y }] = ie(oe, {
117
116
  refetchQueries: ["AssetHandlerBootstrap"],
118
117
  onCompleted: (e) => {
119
- o.success(m("assethandler.assets.create.toast.title", "{{asset}} created"), m("assethandler.assets.create.toast.body", "{{name}} was registered in the inventory.", { name: e.createAsset.name })), q(null), J.current ? (J.current = !1, z(j), V([]), U(""), G({}), typeof window < "u" && window.scrollTo({
118
+ s.success(h("assethandler.assets.create.toast.title", "{{asset}} created"), h("assethandler.assets.create.toast.body", "{{name}} was registered in the inventory.", { name: e.createAsset.name })), q(null), J.current ? (J.current = !1, z(A), V([]), U(""), G({}), typeof window < "u" && window.scrollTo({
120
119
  top: 0,
121
120
  behavior: "smooth"
122
121
  })) : b(`/assets/${e.createAsset.id}`);
123
122
  },
124
123
  onError: (e) => {
125
- q(null), o.error("Could not create asset", e.message);
124
+ q(null), s.error("Could not create asset", e.message);
126
125
  }
127
126
  }), X = (e, t) => {
128
127
  z((n) => ({
@@ -136,9 +135,9 @@ function P() {
136
135
  }, Z = () => {
137
136
  let e = H.trim().replace(/,$/, "").trim();
138
137
  e && !B.includes(e) && V((t) => [...t, e]), U("");
139
- }, le = (e) => {
138
+ }, de = (e) => {
140
139
  e.key === "Enter" || e.key === "," ? (e.preventDefault(), Z()) : e.key === "Backspace" && H === "" && B.length > 0 && V((e) => e.slice(0, -1));
141
- }, Q = c(() => M({
140
+ }, Q = l(() => j({
142
141
  purchaseValue: R.purchaseValue.trim() ? Number(R.purchaseValue) : null,
143
142
  salvageValue: R.salvageValue.trim() ? Number(R.salvageValue) : null,
144
143
  usefulLifeMonths: R.usefulLifeMonths.trim() ? Number(R.usefulLifeMonths) : null,
@@ -161,7 +160,7 @@ function P() {
161
160
  (Number.isNaN(i) || i < 0) && (t[r] = `${n} must be a positive number`);
162
161
  };
163
162
  if (n(R.purchaseValue, "Purchase value", "purchaseValue"), n(R.salvageValue, "Salvage value", "salvageValue"), n(R.usefulLifeMonths, "Useful life", "usefulLifeMonths"), G(t), Object.keys(t).length > 0) {
164
- o.warning("Missing required fields", "Fill in the highlighted fields and try again.");
163
+ s.warning("Missing required fields", "Fill in the highlighted fields and try again.");
165
164
  return;
166
165
  }
167
166
  let r = (e) => {
@@ -170,7 +169,7 @@ function P() {
170
169
  let n = Number(t);
171
170
  return Number.isNaN(n) ? null : n;
172
171
  }, i = (e) => e.trim() === "" ? null : new Date(e).toISOString();
173
- J.current = e, q(e ? "add" : "create"), ce({ variables: { input: {
172
+ J.current = e, q(e ? "add" : "create"), ue({ variables: { input: {
174
173
  name: R.name.trim(),
175
174
  assetNumber: R.assetNumber.trim(),
176
175
  category: R.category,
@@ -204,14 +203,14 @@ function P() {
204
203
  "aria-label": "Go back",
205
204
  className: "w-fit",
206
205
  onClick: () => b("/assets/asset-list"),
207
- children: /* @__PURE__ */ g(ne, { className: "h-4 w-4" })
206
+ children: /* @__PURE__ */ g(te, { className: "h-4 w-4" })
208
207
  }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
209
208
  className: "text-2xl font-bold",
210
- children: /* @__PURE__ */ g(ae, { children: m("assethandler.assets.create.title", "Create New {{asset}}") })
209
+ children: /* @__PURE__ */ g(ae, { children: h("assethandler.assets.create.title", "Create New {{asset}}") })
211
210
  }), /* @__PURE__ */ _("p", {
212
211
  className: "text-sm text-text-secondary",
213
212
  children: [
214
- m("assethandler.assets.create.subtitle", "Register a new {{assetLower}} in the inventory"),
213
+ h("assethandler.assets.create.subtitle", "Register a new {{assetLower}} in the inventory"),
215
214
  " ",
216
215
  "· ",
217
216
  /* @__PURE__ */ g("span", {
@@ -222,10 +221,10 @@ function P() {
222
221
  ]
223
222
  })] })]
224
223
  }),
225
- /* @__PURE__ */ g(oe, { children: m("assethandler.assets.create.purpose", "Register a new {{assetLower}} so it can be tracked, maintained and assigned from day one. Capture its identity, category and condition, then its financials and location — the more you fill in now, the better your reports and audits later.") }),
226
- /* @__PURE__ */ _(N, {
224
+ /* @__PURE__ */ g(C, { children: h("assethandler.assets.create.purpose", "Register a new {{assetLower}} so it can be tracked, maintained and assigned from day one. Capture its identity, category and condition, then its financials and location — the more you fill in now, the better your reports and audits later.") }),
225
+ /* @__PURE__ */ _(M, {
227
226
  title: "General Information",
228
- description: m("assethandler.assets.create.general.description", "Basic {{assetLower}} details and identification"),
227
+ description: h("assethandler.assets.create.general.description", "Basic {{assetLower}} details and identification"),
229
228
  glow: !0,
230
229
  children: [
231
230
  /* @__PURE__ */ _("div", {
@@ -234,7 +233,7 @@ function P() {
234
233
  className: "space-y-2",
235
234
  children: [
236
235
  /* @__PURE__ */ _(S, { children: [
237
- m("assethandler.assets.create.field.name", "{{asset}} Name"),
236
+ h("assethandler.assets.create.field.name", "{{asset}} Name"),
238
237
  " ",
239
238
  /* @__PURE__ */ g("span", {
240
239
  className: "text-status-error-text",
@@ -242,7 +241,7 @@ function P() {
242
241
  })
243
242
  ] }),
244
243
  /* @__PURE__ */ g(x, {
245
- placeholder: m("assethandler.assets.create.field.namePlaceholder", "e.g., Dell OptiPlex 7090"),
244
+ placeholder: h("assethandler.assets.create.field.namePlaceholder", "e.g., Dell OptiPlex 7090"),
246
245
  value: R.name,
247
246
  onChange: (e) => X("name", e.target.value),
248
247
  "aria-invalid": !!W.name
@@ -256,7 +255,7 @@ function P() {
256
255
  className: "space-y-2",
257
256
  children: [
258
257
  /* @__PURE__ */ _(S, { children: [
259
- m("assethandler.assets.create.field.tag", "{{asset}} Tag"),
258
+ h("assethandler.assets.create.field.tag", "{{asset}} Tag"),
260
259
  " ",
261
260
  /* @__PURE__ */ g("span", {
262
261
  className: "text-status-error-text",
@@ -264,7 +263,7 @@ function P() {
264
263
  })
265
264
  ] }),
266
265
  /* @__PURE__ */ g(x, {
267
- placeholder: m("assethandler.assets.create.field.tagPlaceholder", "e.g., IT-HW-001"),
266
+ placeholder: h("assethandler.assets.create.field.tagPlaceholder", "e.g., IT-HW-001"),
268
267
  value: R.assetNumber,
269
268
  onChange: (e) => X("assetNumber", e.target.value),
270
269
  "aria-invalid": !!W.assetNumber
@@ -278,8 +277,8 @@ function P() {
278
277
  }),
279
278
  /* @__PURE__ */ _("div", {
280
279
  className: "space-y-2",
281
- children: [/* @__PURE__ */ g(S, { children: "Description" }), /* @__PURE__ */ g(O, {
282
- placeholder: m("assethandler.assets.create.field.descriptionPlaceholder", "Brief description of the {{assetLower}}..."),
280
+ children: [/* @__PURE__ */ g(S, { children: "Description" }), /* @__PURE__ */ g(k, {
281
+ placeholder: h("assethandler.assets.create.field.descriptionPlaceholder", "Brief description of the {{assetLower}}..."),
283
282
  value: R.description,
284
283
  onChange: (e) => X("description", e.target.value),
285
284
  rows: 3
@@ -293,10 +292,10 @@ function P() {
293
292
  children: [/* @__PURE__ */ _(S, { children: ["Category ", /* @__PURE__ */ g("span", {
294
293
  className: "text-status-error-text",
295
294
  children: "*"
296
- })] }), /* @__PURE__ */ _(C, {
295
+ })] }), /* @__PURE__ */ _(w, {
297
296
  value: R.category,
298
297
  onValueChange: (e) => X("category", e),
299
- children: [/* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(D, {}) }), /* @__PURE__ */ g(w, { children: se.map((e) => /* @__PURE__ */ g(T, {
298
+ children: [/* @__PURE__ */ g(D, { children: /* @__PURE__ */ g(O, {}) }), /* @__PURE__ */ g(T, { children: le.map((e) => /* @__PURE__ */ g(E, {
300
299
  value: e,
301
300
  className: "capitalize",
302
301
  children: I?.find((t) => t.id === e)?.label ?? t(e, F)
@@ -305,23 +304,23 @@ function P() {
305
304
  }),
306
305
  /* @__PURE__ */ _("div", {
307
306
  className: "space-y-2",
308
- children: [/* @__PURE__ */ g(S, { children: "Status" }), /* @__PURE__ */ _(C, {
307
+ children: [/* @__PURE__ */ g(S, { children: "Status" }), /* @__PURE__ */ _(w, {
309
308
  value: R.status,
310
309
  onValueChange: (e) => X("status", e),
311
- children: [/* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(D, {}) }), /* @__PURE__ */ _(w, { children: [
312
- /* @__PURE__ */ g(T, {
310
+ children: [/* @__PURE__ */ g(D, { children: /* @__PURE__ */ g(O, {}) }), /* @__PURE__ */ _(T, { children: [
311
+ /* @__PURE__ */ g(E, {
313
312
  value: "ACTIVE",
314
313
  children: "Active"
315
314
  }),
316
- /* @__PURE__ */ g(T, {
315
+ /* @__PURE__ */ g(E, {
317
316
  value: "INACTIVE",
318
317
  children: "Inactive"
319
318
  }),
320
- /* @__PURE__ */ g(T, {
319
+ /* @__PURE__ */ g(E, {
321
320
  value: "MAINTENANCE",
322
321
  children: "Maintenance"
323
322
  }),
324
- /* @__PURE__ */ g(T, {
323
+ /* @__PURE__ */ g(E, {
325
324
  value: "RETIRED",
326
325
  children: "Retired"
327
326
  })
@@ -330,27 +329,27 @@ function P() {
330
329
  }),
331
330
  /* @__PURE__ */ _("div", {
332
331
  className: "space-y-2",
333
- children: [/* @__PURE__ */ g(S, { children: "Condition" }), /* @__PURE__ */ _(C, {
332
+ children: [/* @__PURE__ */ g(S, { children: "Condition" }), /* @__PURE__ */ _(w, {
334
333
  value: R.condition,
335
334
  onValueChange: (e) => X("condition", e),
336
- children: [/* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(D, {}) }), /* @__PURE__ */ _(w, { children: [
337
- /* @__PURE__ */ g(T, {
335
+ children: [/* @__PURE__ */ g(D, { children: /* @__PURE__ */ g(O, {}) }), /* @__PURE__ */ _(T, { children: [
336
+ /* @__PURE__ */ g(E, {
338
337
  value: "EXCELLENT",
339
338
  children: "Excellent"
340
339
  }),
341
- /* @__PURE__ */ g(T, {
340
+ /* @__PURE__ */ g(E, {
342
341
  value: "GOOD",
343
342
  children: "Good"
344
343
  }),
345
- /* @__PURE__ */ g(T, {
344
+ /* @__PURE__ */ g(E, {
346
345
  value: "FAIR",
347
346
  children: "Fair"
348
347
  }),
349
- /* @__PURE__ */ g(T, {
348
+ /* @__PURE__ */ g(E, {
350
349
  value: "POOR",
351
350
  children: "Poor"
352
351
  }),
353
- /* @__PURE__ */ g(T, {
352
+ /* @__PURE__ */ g(E, {
354
353
  value: "DAMAGED",
355
354
  children: "Damaged"
356
355
  })
@@ -369,7 +368,7 @@ function P() {
369
368
  })
370
369
  ]
371
370
  }),
372
- /* @__PURE__ */ _(N, {
371
+ /* @__PURE__ */ _(M, {
373
372
  title: "Financial Details",
374
373
  description: "Purchase and depreciation data powers book value, TCO and finance reports",
375
374
  children: [
@@ -384,7 +383,7 @@ function P() {
384
383
  className: "relative",
385
384
  children: [/* @__PURE__ */ g("span", {
386
385
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary",
387
- children: "$"
386
+ children: N
388
387
  }), /* @__PURE__ */ g(x, {
389
388
  type: "number",
390
389
  min: "0",
@@ -457,7 +456,7 @@ function P() {
457
456
  className: "relative",
458
457
  children: [/* @__PURE__ */ g("span", {
459
458
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-secondary",
460
- children: "$"
459
+ children: N
461
460
  }), /* @__PURE__ */ g(x, {
462
461
  type: "number",
463
462
  min: "0",
@@ -477,31 +476,31 @@ function P() {
477
476
  }),
478
477
  /* @__PURE__ */ _("div", {
479
478
  className: "space-y-2",
480
- children: [/* @__PURE__ */ g(S, { children: "Depreciation Method" }), /* @__PURE__ */ _(C, {
479
+ children: [/* @__PURE__ */ g(S, { children: "Depreciation Method" }), /* @__PURE__ */ _(w, {
481
480
  value: R.depreciationMethod,
482
481
  onValueChange: (e) => X("depreciationMethod", e),
483
- children: [/* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(D, {}) }), /* @__PURE__ */ _(w, { children: [
484
- /* @__PURE__ */ g(T, {
482
+ children: [/* @__PURE__ */ g(D, { children: /* @__PURE__ */ g(O, {}) }), /* @__PURE__ */ _(T, { children: [
483
+ /* @__PURE__ */ g(E, {
485
484
  value: "STRAIGHT_LINE",
486
485
  children: "Straight line"
487
486
  }),
488
- /* @__PURE__ */ g(T, {
487
+ /* @__PURE__ */ g(E, {
489
488
  value: "REDUCING_BALANCE",
490
489
  children: "Reducing balance (200%)"
491
490
  }),
492
- /* @__PURE__ */ g(T, {
491
+ /* @__PURE__ */ g(E, {
493
492
  value: "DECLINING_BALANCE",
494
493
  children: "Declining balance (150%)"
495
494
  }),
496
- /* @__PURE__ */ g(T, {
495
+ /* @__PURE__ */ g(E, {
497
496
  value: "SUM_OF_YEARS_DIGITS",
498
497
  children: "Sum of years' digits"
499
498
  }),
500
- /* @__PURE__ */ g(T, {
499
+ /* @__PURE__ */ g(E, {
501
500
  value: "UNITS_OF_PRODUCTION",
502
501
  children: "Units of production"
503
502
  }),
504
- /* @__PURE__ */ g(T, {
503
+ /* @__PURE__ */ g(E, {
505
504
  value: "NONE",
506
505
  children: "None"
507
506
  })
@@ -542,15 +541,15 @@ function P() {
542
541
  onChange: (e) => X("warrantyExpiry", e.target.value)
543
542
  })]
544
543
  }),
545
- /* @__PURE__ */ g(s, {
544
+ /* @__PURE__ */ g(ee, {
546
545
  title: "Depreciation preview",
547
- subtitle: m("assethandler.assets.create.depreciation.subtitle", "Projected book value over the {{assetLower}}'s useful life"),
548
- icon: ie,
549
- children: Q.length > 0 ? /* @__PURE__ */ g(te, {
546
+ subtitle: h("assethandler.assets.create.depreciation.subtitle", "Projected book value over the {{assetLower}}'s useful life"),
547
+ icon: re,
548
+ children: Q.length > 0 ? /* @__PURE__ */ g(c, {
550
549
  data: Q,
551
550
  xKey: "year",
552
551
  height: 240,
553
- valueFormat: ee("USD"),
552
+ valueFormat: ce,
554
553
  xTickFormat: (e) => `${e}y`,
555
554
  series: [{
556
555
  key: "bookValue",
@@ -558,12 +557,12 @@ function P() {
558
557
  }]
559
558
  }) : /* @__PURE__ */ _("div", {
560
559
  className: "flex flex-col items-center gap-2 py-10 text-center text-sm text-text-secondary",
561
- children: [/* @__PURE__ */ g(re, { className: "h-5 w-5 text-text-muted" }), "Enter a purchase value and useful life to preview the depreciation schedule."]
560
+ children: [/* @__PURE__ */ g(ne, { className: "h-5 w-5 text-text-muted" }), "Enter a purchase value and useful life to preview the depreciation schedule."]
562
561
  })
563
562
  })
564
563
  ]
565
564
  }),
566
- /* @__PURE__ */ g(N, {
565
+ /* @__PURE__ */ g(M, {
567
566
  title: "Assignment & Location",
568
567
  description: "Who holds it and where it lives",
569
568
  children: /* @__PURE__ */ _("div", {
@@ -605,12 +604,12 @@ function P() {
605
604
  "aria-label": `Remove ${e}`,
606
605
  onClick: () => V((t) => t.filter((t) => t !== e)),
607
606
  className: "text-text-secondary hover:text-text-primary",
608
- children: /* @__PURE__ */ g(p, { className: "h-3 w-3" })
607
+ children: /* @__PURE__ */ g(m, { className: "h-3 w-3" })
609
608
  })]
610
609
  }, e)), /* @__PURE__ */ g("input", {
611
610
  value: H,
612
611
  onChange: (e) => U(e.target.value),
613
- onKeyDown: le,
612
+ onKeyDown: de,
614
613
  onBlur: Z,
615
614
  placeholder: B.length === 0 ? "Type a tag and press Enter" : "",
616
615
  className: "min-w-[8rem] flex-1 border-0 bg-transparent text-sm outline-none placeholder:text-text-secondary"
@@ -630,20 +629,20 @@ function P() {
630
629
  className: "w-full sm:w-auto",
631
630
  onClick: () => b("/assets/asset-list"),
632
631
  disabled: Y,
633
- children: [/* @__PURE__ */ g(p, { className: "mr-2 h-4 w-4" }), "Cancel"]
632
+ children: [/* @__PURE__ */ g(m, { className: "mr-2 h-4 w-4" }), "Cancel"]
634
633
  }),
635
634
  /* @__PURE__ */ _(y, {
636
635
  variant: "outline",
637
636
  className: "w-full sm:w-auto",
638
637
  onClick: () => $(!0),
639
638
  disabled: Y,
640
- children: [Y && K === "add" ? /* @__PURE__ */ g(d, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(f, { className: "mr-2 h-4 w-4" }), "Save & add another"]
639
+ children: [Y && K === "add" ? /* @__PURE__ */ g(f, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(p, { className: "mr-2 h-4 w-4" }), "Save & add another"]
641
640
  }),
642
641
  /* @__PURE__ */ _(y, {
643
642
  className: "w-full sm:w-auto",
644
643
  onClick: () => $(!1),
645
644
  disabled: Y,
646
- children: [Y && K === "create" ? /* @__PURE__ */ g(d, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(f, { className: "mr-2 h-4 w-4" }), Y ? "Creating..." : "Create Asset"]
645
+ children: [Y && K === "create" ? /* @__PURE__ */ g(f, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(p, { className: "mr-2 h-4 w-4" }), Y ? "Creating..." : "Create Asset"]
647
646
  })
648
647
  ]
649
648
  })
@@ -652,6 +651,6 @@ function P() {
652
651
  });
653
652
  }
654
653
  //#endregion
655
- export { P as AssetsAssetCreatePage };
654
+ export { N as AssetsAssetCreatePage };
656
655
 
657
656
  //# sourceMappingURL=AssetsAssetCreatePage.js.map