@burdenoff/microfe-assethandler 2026.826.10 → 2026.826.12

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.
@@ -146,7 +146,7 @@ function w() {
146
146
  className: "mt-4",
147
147
  variant: "outline",
148
148
  size: "sm",
149
- onClick: () => o(`/assethandler/assets/${e.assetId}`),
149
+ onClick: () => o(`/assets/${e.assetId}`),
150
150
  children: "View asset"
151
151
  }) : null]
152
152
  }, e.id))
@@ -1 +1 @@
1
- {"version":3,"file":"ForecastingAlertsPage.js","names":[],"sources":["../../../src/assethandler/pages/ForecastingAlertsPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { useMemo, useState } from 'react';\n\nimport {\n Badge,\n Button,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { SectionHeader } from '../components';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\n\nconst DEADLINE_ALERTS_QUERY = gql`\n query AssetHandlerDeadlineAlerts($withinDays: Int!) {\n assetDeadlineAlerts(withinDays: $withinDays) {\n id\n kind\n subjectId\n subjectName\n assetId\n dueAt\n daysRemaining\n status\n headline\n recommendedAction\n }\n }\n`;\n\ntype AlertKind = 'WARRANTY_EXPIRY' | 'LICENSE_RENEWAL' | 'END_OF_LIFE' | 'MAINTENANCE_DUE';\n\ninterface DeadlineAlert {\n id: string;\n kind: AlertKind;\n subjectId: string;\n subjectName: string;\n assetId: string | null;\n dueAt: string;\n daysRemaining: number;\n status: 'OVERDUE' | 'DUE_SOON' | 'UPCOMING';\n headline: string;\n recommendedAction: string;\n}\n\ninterface DeadlineAlertsResult {\n assetDeadlineAlerts: DeadlineAlert[];\n}\n\nconst KIND_LABELS: Record<AlertKind, string> = {\n WARRANTY_EXPIRY: 'Warranty expiry',\n LICENSE_RENEWAL: 'Licence renewal',\n END_OF_LIFE: 'Replacement planning',\n MAINTENANCE_DUE: 'Maintenance due',\n};\n\nconst STATUS_CLASS: Record<DeadlineAlert['status'], string> = {\n OVERDUE: 'bg-status-error-bg text-status-error-text',\n DUE_SOON: 'bg-status-warning-bg text-status-warning-text',\n UPCOMING: 'bg-status-info-bg text-status-info-text',\n};\n\nfunction dueLabel(alert: DeadlineAlert): string {\n if (alert.daysRemaining < 0) {\n const overdueDays = Math.abs(alert.daysRemaining);\n return `${overdueDays} ${overdueDays === 1 ? 'day' : 'days'} overdue`;\n }\n if (alert.daysRemaining === 0) return 'Due today';\n return `Due in ${alert.daysRemaining} days`;\n}\n\nexport function ForecastingAlertsPage() {\n const navigate = useAssetHandlerNavigate();\n const [withinDays, setWithinDays] = useState(90);\n const { data, loading, error, refetch } = useQuery<DeadlineAlertsResult>(DEADLINE_ALERTS_QUERY, {\n variables: { withinDays },\n fetchPolicy: 'network-only',\n });\n const alerts = useMemo(() => data?.assetDeadlineAlerts ?? [], [data?.assetDeadlineAlerts]);\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <SectionHeader\n title=\"Deadline Alerts\"\n description=\"Warranty, licence, replacement, and maintenance deadlines derived from your saved registry facts.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <Select\n value={String(withinDays)}\n onValueChange={(value) => setWithinDays(Number(value))}\n >\n <SelectTrigger className=\"w-36\" aria-label=\"Planning window\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"30\">Next 30 days</SelectItem>\n <SelectItem value=\"90\">Next 90 days</SelectItem>\n <SelectItem value=\"180\">Next 180 days</SelectItem>\n <SelectItem value=\"365\">Next 365 days</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={() => void refetch()} disabled={loading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n Refresh\n </Button>\n </div>\n }\n />\n <PagePurpose>\n Alerts remain stable across reloads because each one is calculated from the persisted source\n record and carries a deterministic source-backed ID.\n </PagePurpose>\n\n {error ? (\n <GlassCard treatment=\"glass\" className=\"border-status-error-border p-4\">\n <p className=\"font-medium text-status-error-text\">Could not load deadline alerts</p>\n <p className=\"mt-1 text-sm text-text-muted\">{error.message}</p>\n </GlassCard>\n ) : null}\n\n {!loading && !error && alerts.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No deadlines in this window\"\n description=\"Add warranty, useful-life, licence-expiry, or scheduled-maintenance dates to raise planning alerts.\"\n />\n ) : null}\n\n <div className=\"grid gap-3 lg:grid-cols-2\" aria-busy={loading}>\n {alerts.map((alert) => (\n <GlassCard key={alert.id} treatment=\"glass\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\">{KIND_LABELS[alert.kind]}</Badge>\n <span\n className={`rounded-full px-2 py-0.5 text-xs ${STATUS_CLASS[alert.status]}`}\n >\n {dueLabel(alert)}\n </span>\n </div>\n <h2 className=\"mt-3 font-semibold text-text-primary\">{alert.headline}</h2>\n <p className=\"mt-1 break-words text-sm text-text-secondary\">{alert.subjectName}</p>\n <p className=\"mt-2 text-sm text-text-muted\">{alert.recommendedAction}</p>\n <p className=\"mt-3 text-xs text-text-muted\">\n Deadline {new Date(alert.dueAt).toLocaleDateString()}\n </p>\n </div>\n <AlertTriangle className=\"h-5 w-5 shrink-0 text-status-warning-text\" />\n </div>\n {alert.assetId ? (\n <Button\n className=\"mt-4\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(`/assethandler/assets/${alert.assetId}`)}\n >\n View asset\n </Button>\n ) : null}\n </GlassCard>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ForecastingAlertsPage.js","names":[],"sources":["../../../src/assethandler/pages/ForecastingAlertsPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { useMemo, useState } from 'react';\n\nimport {\n Badge,\n Button,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { SectionHeader } from '../components';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\n\nconst DEADLINE_ALERTS_QUERY = gql`\n query AssetHandlerDeadlineAlerts($withinDays: Int!) {\n assetDeadlineAlerts(withinDays: $withinDays) {\n id\n kind\n subjectId\n subjectName\n assetId\n dueAt\n daysRemaining\n status\n headline\n recommendedAction\n }\n }\n`;\n\ntype AlertKind = 'WARRANTY_EXPIRY' | 'LICENSE_RENEWAL' | 'END_OF_LIFE' | 'MAINTENANCE_DUE';\n\ninterface DeadlineAlert {\n id: string;\n kind: AlertKind;\n subjectId: string;\n subjectName: string;\n assetId: string | null;\n dueAt: string;\n daysRemaining: number;\n status: 'OVERDUE' | 'DUE_SOON' | 'UPCOMING';\n headline: string;\n recommendedAction: string;\n}\n\ninterface DeadlineAlertsResult {\n assetDeadlineAlerts: DeadlineAlert[];\n}\n\nconst KIND_LABELS: Record<AlertKind, string> = {\n WARRANTY_EXPIRY: 'Warranty expiry',\n LICENSE_RENEWAL: 'Licence renewal',\n END_OF_LIFE: 'Replacement planning',\n MAINTENANCE_DUE: 'Maintenance due',\n};\n\nconst STATUS_CLASS: Record<DeadlineAlert['status'], string> = {\n OVERDUE: 'bg-status-error-bg text-status-error-text',\n DUE_SOON: 'bg-status-warning-bg text-status-warning-text',\n UPCOMING: 'bg-status-info-bg text-status-info-text',\n};\n\nfunction dueLabel(alert: DeadlineAlert): string {\n if (alert.daysRemaining < 0) {\n const overdueDays = Math.abs(alert.daysRemaining);\n return `${overdueDays} ${overdueDays === 1 ? 'day' : 'days'} overdue`;\n }\n if (alert.daysRemaining === 0) return 'Due today';\n return `Due in ${alert.daysRemaining} days`;\n}\n\nexport function ForecastingAlertsPage() {\n const navigate = useAssetHandlerNavigate();\n const [withinDays, setWithinDays] = useState(90);\n const { data, loading, error, refetch } = useQuery<DeadlineAlertsResult>(DEADLINE_ALERTS_QUERY, {\n variables: { withinDays },\n fetchPolicy: 'network-only',\n });\n const alerts = useMemo(() => data?.assetDeadlineAlerts ?? [], [data?.assetDeadlineAlerts]);\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <SectionHeader\n title=\"Deadline Alerts\"\n description=\"Warranty, licence, replacement, and maintenance deadlines derived from your saved registry facts.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <Select\n value={String(withinDays)}\n onValueChange={(value) => setWithinDays(Number(value))}\n >\n <SelectTrigger className=\"w-36\" aria-label=\"Planning window\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"30\">Next 30 days</SelectItem>\n <SelectItem value=\"90\">Next 90 days</SelectItem>\n <SelectItem value=\"180\">Next 180 days</SelectItem>\n <SelectItem value=\"365\">Next 365 days</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={() => void refetch()} disabled={loading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n Refresh\n </Button>\n </div>\n }\n />\n <PagePurpose>\n Alerts remain stable across reloads because each one is calculated from the persisted source\n record and carries a deterministic source-backed ID.\n </PagePurpose>\n\n {error ? (\n <GlassCard treatment=\"glass\" className=\"border-status-error-border p-4\">\n <p className=\"font-medium text-status-error-text\">Could not load deadline alerts</p>\n <p className=\"mt-1 text-sm text-text-muted\">{error.message}</p>\n </GlassCard>\n ) : null}\n\n {!loading && !error && alerts.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No deadlines in this window\"\n description=\"Add warranty, useful-life, licence-expiry, or scheduled-maintenance dates to raise planning alerts.\"\n />\n ) : null}\n\n <div className=\"grid gap-3 lg:grid-cols-2\" aria-busy={loading}>\n {alerts.map((alert) => (\n <GlassCard key={alert.id} treatment=\"glass\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\">{KIND_LABELS[alert.kind]}</Badge>\n <span\n className={`rounded-full px-2 py-0.5 text-xs ${STATUS_CLASS[alert.status]}`}\n >\n {dueLabel(alert)}\n </span>\n </div>\n <h2 className=\"mt-3 font-semibold text-text-primary\">{alert.headline}</h2>\n <p className=\"mt-1 break-words text-sm text-text-secondary\">{alert.subjectName}</p>\n <p className=\"mt-2 text-sm text-text-muted\">{alert.recommendedAction}</p>\n <p className=\"mt-3 text-xs text-text-muted\">\n Deadline {new Date(alert.dueAt).toLocaleDateString()}\n </p>\n </div>\n <AlertTriangle className=\"h-5 w-5 shrink-0 text-status-warning-text\" />\n </div>\n {alert.assetId ? (\n <Button\n className=\"mt-4\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(`/assets/${alert.assetId}`)}\n >\n View asset\n </Button>\n ) : null}\n </GlassCard>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReportsDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"names":[],"mappings":"AAiNA,wBAAgB,oBAAoB,gCA8pBnC"}
1
+ {"version":3,"file":"ReportsDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"names":[],"mappings":"AAiNA,wBAAgB,oBAAoB,gCAupBnC"}
@@ -8,13 +8,13 @@ import { StatTile as s } from "../components/StatTile.js";
8
8
  import { ChartCard as c } from "../components/ChartCard.js";
9
9
  import "../components/index.js";
10
10
  import { downloadCsv as l } from "../utils/csv.js";
11
- import { buildAssignmentCoverageSlices as ee } from "../utils/utilization.js";
12
- import { useMemo as u, useState as te } from "react";
13
- import { Activity as ne, DollarSign as re, Download as d, FileBarChart2 as ie, Percent as f, Printer as p, TrendingDown as m, Wallet as h, Wrench as g } from "lucide-react";
11
+ import { buildAssignmentCoverageSlices as ee, buildUtilizationCsvRows as te } from "../utils/utilization.js";
12
+ import { useMemo as u, useState as ne } from "react";
13
+ import { Activity as d, DollarSign as re, Download as f, FileBarChart2 as ie, Percent as p, Printer as m, TrendingDown as h, Wallet as g, Wrench as ae } from "lucide-react";
14
14
  import { gql as _ } from "@apollo/client";
15
- import { useQuery as ae } from "@apollo/client/react";
15
+ import { useQuery as oe } from "@apollo/client/react";
16
16
  import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
17
- import { Button as x, GlassCard as S, GradientText as C, IllustratedEmptyState as w, PagePurpose as oe, Select as se, SelectContent as ce, SelectItem as T, SelectTrigger as E, SelectValue as le, Tabs as ue, TabsContent as D, TabsList as de, TabsTrigger as O } from "@burdenoff/fe-libs/ui";
17
+ import { Button as x, GlassCard as S, GradientText as se, IllustratedEmptyState as C, PagePurpose as ce, Select as w, SelectContent as T, SelectItem as E, SelectTrigger as le, SelectValue as ue, Tabs as D, TabsContent as O, TabsList as de, TabsTrigger as k } from "@burdenoff/fe-libs/ui";
18
18
  //#region src/assethandler/pages/ReportsDashboardPage.tsx
19
19
  var fe = _`
20
20
  query AssetHandlerReports($registerFilter: AssetRegisterFilterInput) {
@@ -71,27 +71,27 @@ var fe = _`
71
71
  "MAINTENANCE",
72
72
  "RETIRED",
73
73
  "DISPOSED"
74
- ], k = {
74
+ ], A = {
75
75
  success: "bg-status-success-subtle text-status-success-text",
76
76
  warning: "bg-status-warning-subtle text-status-warning-text",
77
77
  error: "bg-status-error-subtle text-status-error-text",
78
78
  info: "bg-status-info-subtle text-status-info-text",
79
79
  neutral: "bg-bg-sunken text-text-secondary"
80
80
  };
81
- function A({ tone: e, children: t }) {
81
+ function j({ tone: e, children: t }) {
82
82
  return /* @__PURE__ */ y("span", {
83
- className: n("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize", k[e]),
83
+ className: n("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize", A[e]),
84
84
  children: t
85
85
  });
86
86
  }
87
- var j = (e) => {
87
+ var M = (e) => {
88
88
  let t = e.toLowerCase();
89
89
  return t === "active" ? "success" : t === "maintenance" ? "warning" : t === "retired" || t === "disposed" ? "error" : "neutral";
90
- }, M = (e) => {
90
+ }, N = (e) => {
91
91
  let t = e.toLowerCase();
92
92
  return t === "completed" ? "success" : t === "in_progress" || t === "scheduled" ? "info" : t === "overdue" || t === "cancelled" ? "error" : "neutral";
93
93
  };
94
- function N({ rows: e = 6, cols: t }) {
94
+ function P({ rows: e = 6, cols: t }) {
95
95
  return /* @__PURE__ */ y(v, { children: Array.from({ length: e }).map((e, n) => /* @__PURE__ */ y("tr", {
96
96
  className: "border-b border-border-seam/60",
97
97
  children: Array.from({ length: t }).map((e, t) => /* @__PURE__ */ y("td", {
@@ -100,21 +100,21 @@ function N({ rows: e = 6, cols: t }) {
100
100
  }, t))
101
101
  }, n)) });
102
102
  }
103
- function P() {
104
- let { format: n, compact: _ } = t(), { assets: k } = e(), [P, F] = te("all"), { data: I, loading: L } = ae(fe, {
105
- variables: { registerFilter: P === "all" ? null : { status: P } },
103
+ function F() {
104
+ let { format: n, compact: _ } = t(), { assets: A } = e(), [F, I] = ne("all"), { data: L, loading: R } = oe(fe, {
105
+ variables: { registerFilter: F === "all" ? null : { status: F } },
106
106
  fetchPolicy: "cache-and-network",
107
107
  errorPolicy: "all"
108
- }), R = u(() => I?.assetRegister ?? [], [I]), z = u(() => I?.maintenanceLog ?? [], [I]), B = I?.financialSummary, V = I?.utilizationReport, H = u(() => {
108
+ }), z = u(() => L?.assetRegister ?? [], [L]), B = u(() => L?.maintenanceLog ?? [], [L]), V = L?.financialSummary, H = L?.utilizationReport, U = u(() => {
109
109
  let e = /* @__PURE__ */ new Map();
110
- for (let t of k) e.set(t.id, t.name);
110
+ for (let t of A) e.set(t.id, t.name);
111
111
  return e;
112
- }, [k]), U = o(R, {
112
+ }, [A]), W = o(z, {
113
113
  pageSize: 12,
114
- resetKey: P
115
- }), W = o(z, { pageSize: 12 }), G = B?.totalAssetValue ?? 0, K = B?.totalDepreciation ?? 0, me = G + K > 0 ? K / (G + K) * 100 : 0, q = u(() => {
114
+ resetKey: F
115
+ }), G = o(B, { pageSize: 12 }), K = V?.totalAssetValue ?? 0, q = V?.totalDepreciation ?? 0, me = K + q > 0 ? q / (K + q) * 100 : 0, J = u(() => {
116
116
  let e = /* @__PURE__ */ new Map();
117
- for (let t of R) {
117
+ for (let t of z) {
118
118
  let n = t.currentValue ?? t.purchaseValue ?? 0;
119
119
  if (n > 0) {
120
120
  let r = t.category.replace(/_/g, " ");
@@ -125,24 +125,24 @@ function P() {
125
125
  category: e,
126
126
  value: t
127
127
  })).sort((e, t) => t.value - e.value).slice(0, 12);
128
- }, [R]), J = u(() => (V?.byStatus ?? []).map((e) => ({
128
+ }, [z]), he = u(() => (H?.byStatus ?? []).map((e) => ({
129
129
  name: e.status.toLowerCase(),
130
130
  value: e.count
131
- })).filter((e) => e.value > 0), [V]), he = u(() => V ? ee(V.assignedAssets, V.unassignedAssets, V.idleAssets) : [], [V]), Y = u(() => [{
131
+ })).filter((e) => e.value > 0), [H]), ge = u(() => H ? ee(H.assignedAssets, H.unassignedAssets, H.idleAssets) : [], [H]), Y = u(() => [{
132
132
  name: "Current value",
133
- value: G
133
+ value: K
134
134
  }, {
135
135
  name: "Accumulated depreciation",
136
- value: K
137
- }].filter((e) => e.value > 0), [G, K]), X = u(() => B ? [{
136
+ value: q
137
+ }].filter((e) => e.value > 0), [K, q]), X = u(() => V ? [{
138
138
  metric: "Allocated",
139
- value: B.budgetAllocated
139
+ value: V.budgetAllocated
140
140
  }, {
141
141
  metric: "Spent",
142
- value: B.budgetSpent
143
- }] : [], [B]), Z = u(() => {
142
+ value: V.budgetSpent
143
+ }] : [], [V]), Z = u(() => {
144
144
  let e = /* @__PURE__ */ new Map();
145
- for (let t of z) {
145
+ for (let t of B) {
146
146
  let n = t.actualCost ?? t.estimatedCost ?? 0;
147
147
  if (n > 0) {
148
148
  let r = t.type.replace(/_/g, " ");
@@ -153,7 +153,7 @@ function P() {
153
153
  type: e[0],
154
154
  cost: e[1]
155
155
  })).sort((e, t) => t.cost - e.cost);
156
- }, [z]), ge = () => {
156
+ }, [B]), _e = () => {
157
157
  l(`asset-register-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, [
158
158
  "Asset Number",
159
159
  "Name",
@@ -165,7 +165,7 @@ function P() {
165
165
  "Accumulated Depreciation",
166
166
  "Total Cost of Ownership",
167
167
  "Purchase Date"
168
- ], R.map((e) => [
168
+ ], z.map((e) => [
169
169
  e.assetNumber,
170
170
  e.name,
171
171
  e.category,
@@ -177,7 +177,7 @@ function P() {
177
177
  e.totalCostOfOwnership ?? "",
178
178
  e.purchaseDate ? new Date(e.purchaseDate).toISOString().slice(0, 10) : ""
179
179
  ]));
180
- }, _e = () => {
180
+ }, ve = () => {
181
181
  l(`maintenance-log-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, [
182
182
  "Title",
183
183
  "Asset",
@@ -188,9 +188,9 @@ function P() {
188
188
  "Completed",
189
189
  "Estimated Cost",
190
190
  "Actual Cost"
191
- ], z.map((e) => [
191
+ ], B.map((e) => [
192
192
  e.title,
193
- H.get(e.assetId) ?? e.assetId,
193
+ U.get(e.assetId) ?? e.assetId,
194
194
  e.type,
195
195
  e.status,
196
196
  e.priority,
@@ -199,36 +199,29 @@ function P() {
199
199
  e.estimatedCost ?? "",
200
200
  e.actualCost ?? ""
201
201
  ]));
202
- }, ve = () => {
203
- B && l(`financial-summary-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], [
204
- ["Total asset value", B.totalAssetValue],
205
- ["Total depreciation", B.totalDepreciation],
206
- ["Total maintenance spend", B.totalMaintenanceSpend],
207
- ["Budget allocated", B.budgetAllocated],
208
- ["Budget spent", B.budgetSpent],
209
- ["Invoices outstanding", B.invoicesOutstanding],
210
- ["Invoices outstanding (count)", B.invoicesOutstandingCount],
211
- ["Transaction expense", B.transactionExpense]
212
- ]);
213
202
  }, ye = () => {
214
- V && l(`utilization-report-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], [
215
- ["Total assets", V.totalAssets],
216
- ["Assigned assets", V.assignedAssets],
217
- ["Unassigned assets", V.unassignedAssets],
218
- ["Idle assets", V.idleAssets],
219
- ["Overdue assignments", V.overdueAssignments],
220
- ...V.byStatus.map((e) => [`Status: ${e.status}`, e.count])
203
+ V && l(`financial-summary-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], [
204
+ ["Total asset value", V.totalAssetValue],
205
+ ["Total depreciation", V.totalDepreciation],
206
+ ["Total maintenance spend", V.totalMaintenanceSpend],
207
+ ["Budget allocated", V.budgetAllocated],
208
+ ["Budget spent", V.budgetSpent],
209
+ ["Invoices outstanding", V.invoicesOutstanding],
210
+ ["Invoices outstanding (count)", V.invoicesOutstandingCount],
211
+ ["Transaction expense", V.transactionExpense]
221
212
  ]);
222
- }, Q = L && R.length === 0, $ = L && z.length === 0, be = /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ b(x, {
213
+ }, be = () => {
214
+ H && l(`utilization-report-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], te(H));
215
+ }, Q = R && z.length === 0, $ = R && B.length === 0, xe = /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ b(x, {
223
216
  size: "sm",
224
- onClick: ge,
225
- disabled: R.length === 0,
226
- children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
217
+ onClick: _e,
218
+ disabled: z.length === 0,
219
+ children: [/* @__PURE__ */ y(f, { className: "mr-2 h-4 w-4" }), "Export CSV"]
227
220
  }), /* @__PURE__ */ b(x, {
228
221
  size: "sm",
229
222
  variant: "outline",
230
223
  onClick: () => window.print(),
231
- children: [/* @__PURE__ */ y(p, { className: "mr-2 h-4 w-4" }), "Print"]
224
+ children: [/* @__PURE__ */ y(m, { className: "mr-2 h-4 w-4" }), "Print"]
232
225
  })] });
233
226
  return /* @__PURE__ */ b("div", {
234
227
  className: "space-y-6 p-4 sm:p-6",
@@ -237,7 +230,7 @@ function P() {
237
230
  className: "flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between",
238
231
  children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
239
232
  className: "text-2xl font-bold sm:text-3xl",
240
- children: /* @__PURE__ */ y(C, { children: "Reports" })
233
+ children: /* @__PURE__ */ y(se, { children: "Reports" })
241
234
  }), /* @__PURE__ */ y("p", {
242
235
  className: "mt-1 max-w-xl text-sm text-text-secondary",
243
236
  children: "Registers and summaries built from live asset, finance and maintenance data."
@@ -246,66 +239,66 @@ function P() {
246
239
  children: [
247
240
  /* @__PURE__ */ y(s, {
248
241
  label: "Total asset value",
249
- value: G,
242
+ value: K,
250
243
  format: (e) => n(e),
251
244
  icon: re,
252
245
  hero: !0
253
246
  }),
254
247
  /* @__PURE__ */ y(s, {
255
248
  label: "Total depreciation",
256
- value: K,
249
+ value: q,
257
250
  format: (e) => n(e),
258
- icon: m
251
+ icon: h
259
252
  }),
260
253
  /* @__PURE__ */ y(s, {
261
254
  label: "Depreciated",
262
255
  value: me,
263
256
  format: (e) => `${e.toFixed(2)}%`,
264
- icon: f
257
+ icon: p
265
258
  })
266
259
  ]
267
260
  })]
268
261
  }),
269
- /* @__PURE__ */ y(oe, { children: "Pull the numbers you need for audits, finance reviews and planning. Every register below is generated from live records — export any of them to CSV or print the page for sign-off." }),
270
- /* @__PURE__ */ b(ue, {
262
+ /* @__PURE__ */ y(ce, { children: "Pull the numbers you need for audits, finance reviews and planning. Every register below is generated from live records — export any of them to CSV or print the page for sign-off." }),
263
+ /* @__PURE__ */ b(D, {
271
264
  defaultValue: "register",
272
265
  children: [
273
266
  /* @__PURE__ */ b(de, {
274
267
  className: "max-w-full justify-start overflow-x-auto",
275
268
  children: [
276
- /* @__PURE__ */ b(O, {
269
+ /* @__PURE__ */ b(k, {
277
270
  value: "register",
278
271
  children: [/* @__PURE__ */ y(ie, { className: "mr-1.5 h-4 w-4" }), "Asset Register"]
279
272
  }),
280
- /* @__PURE__ */ b(O, {
273
+ /* @__PURE__ */ b(k, {
281
274
  value: "financial",
282
- children: [/* @__PURE__ */ y(h, { className: "mr-1.5 h-4 w-4" }), "Financial Summary"]
275
+ children: [/* @__PURE__ */ y(g, { className: "mr-1.5 h-4 w-4" }), "Financial Summary"]
283
276
  }),
284
- /* @__PURE__ */ b(O, {
277
+ /* @__PURE__ */ b(k, {
285
278
  value: "maintenance",
286
- children: [/* @__PURE__ */ y(g, { className: "mr-1.5 h-4 w-4" }), "Maintenance Log"]
279
+ children: [/* @__PURE__ */ y(ae, { className: "mr-1.5 h-4 w-4" }), "Maintenance Log"]
287
280
  }),
288
- /* @__PURE__ */ b(O, {
281
+ /* @__PURE__ */ b(k, {
289
282
  value: "utilization",
290
- children: [/* @__PURE__ */ y(ne, { className: "mr-1.5 h-4 w-4" }), "Utilization"]
283
+ children: [/* @__PURE__ */ y(d, { className: "mr-1.5 h-4 w-4" }), "Utilization"]
291
284
  })
292
285
  ]
293
286
  }),
294
- /* @__PURE__ */ b(D, {
287
+ /* @__PURE__ */ b(O, {
295
288
  value: "register",
296
289
  className: "mt-4 space-y-4",
297
- children: [q.length > 0 ? /* @__PURE__ */ y(c, {
290
+ children: [J.length > 0 ? /* @__PURE__ */ y(c, {
298
291
  title: "Asset value by category",
299
292
  subtitle: "Current book value aggregated from the register",
300
293
  children: /* @__PURE__ */ y(r, {
301
- data: q,
294
+ data: J,
302
295
  xKey: "category",
303
296
  series: [{
304
297
  key: "value",
305
298
  label: "Current value"
306
299
  }],
307
300
  orientation: "horizontal",
308
- height: Math.max(200, q.length * 34),
301
+ height: Math.max(200, J.length * 34),
309
302
  valueFormat: _,
310
303
  categoryWidth: 128,
311
304
  ariaLabel: "Current asset value by category"
@@ -321,16 +314,16 @@ function P() {
321
314
  children: [/* @__PURE__ */ y("h3", {
322
315
  className: "text-base font-semibold text-text-primary",
323
316
  children: "Asset Register"
324
- }), /* @__PURE__ */ b(se, {
325
- value: P,
326
- onValueChange: F,
327
- children: [/* @__PURE__ */ y(E, {
317
+ }), /* @__PURE__ */ b(w, {
318
+ value: F,
319
+ onValueChange: I,
320
+ children: [/* @__PURE__ */ y(le, {
328
321
  className: "h-8 w-40",
329
- children: /* @__PURE__ */ y(le, { placeholder: "Filter by status" })
330
- }), /* @__PURE__ */ b(ce, { children: [/* @__PURE__ */ y(T, {
322
+ children: /* @__PURE__ */ y(ue, { placeholder: "Filter by status" })
323
+ }), /* @__PURE__ */ b(T, { children: [/* @__PURE__ */ y(E, {
331
324
  value: "all",
332
325
  children: "All statuses"
333
- }), pe.map((e) => /* @__PURE__ */ y(T, {
326
+ }), pe.map((e) => /* @__PURE__ */ y(E, {
334
327
  value: e,
335
328
  className: "capitalize",
336
329
  children: e.toLowerCase()
@@ -338,7 +331,7 @@ function P() {
338
331
  })]
339
332
  }), /* @__PURE__ */ y("div", {
340
333
  className: "flex flex-wrap gap-2",
341
- children: be
334
+ children: xe
342
335
  })]
343
336
  }),
344
337
  /* @__PURE__ */ y("div", {
@@ -381,7 +374,7 @@ function P() {
381
374
  children: "TCO"
382
375
  })
383
376
  ]
384
- }) }), /* @__PURE__ */ y("tbody", { children: Q ? /* @__PURE__ */ y(N, { cols: 8 }) : U.pageItems.map((e) => /* @__PURE__ */ b("tr", {
377
+ }) }), /* @__PURE__ */ y("tbody", { children: Q ? /* @__PURE__ */ y(P, { cols: 8 }) : W.pageItems.map((e) => /* @__PURE__ */ b("tr", {
385
378
  className: "border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40",
386
379
  children: [
387
380
  /* @__PURE__ */ b("td", {
@@ -400,8 +393,8 @@ function P() {
400
393
  }),
401
394
  /* @__PURE__ */ y("td", {
402
395
  className: "p-3",
403
- children: /* @__PURE__ */ y(A, {
404
- tone: j(e.status),
396
+ children: /* @__PURE__ */ y(j, {
397
+ tone: M(e.status),
405
398
  children: e.status.toLowerCase()
406
399
  })
407
400
  }),
@@ -434,7 +427,7 @@ function P() {
434
427
  children: Q ? /* @__PURE__ */ y("div", {
435
428
  className: "space-y-3 p-4",
436
429
  children: Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ y("div", { className: "h-16 animate-pulse rounded bg-bg-sunken" }, t))
437
- }) : U.pageItems.map((e) => /* @__PURE__ */ b("div", {
430
+ }) : W.pageItems.map((e) => /* @__PURE__ */ b("div", {
438
431
  className: "p-4",
439
432
  children: [/* @__PURE__ */ b("div", {
440
433
  className: "flex items-start justify-between gap-2",
@@ -447,8 +440,8 @@ function P() {
447
440
  className: "text-xs text-text-secondary",
448
441
  children: e.assetNumber
449
442
  })]
450
- }), /* @__PURE__ */ y(A, {
451
- tone: j(e.status),
443
+ }), /* @__PURE__ */ y(j, {
444
+ tone: M(e.status),
452
445
  children: e.status.toLowerCase()
453
446
  })]
454
447
  }), /* @__PURE__ */ b("div", {
@@ -473,29 +466,29 @@ function P() {
473
466
  })]
474
467
  }, e.id))
475
468
  }),
476
- R.length === 0 && !L ? /* @__PURE__ */ y(w, {
469
+ z.length === 0 && !R ? /* @__PURE__ */ y(C, {
477
470
  illustration: "empty-data",
478
471
  title: "No assets in the register",
479
472
  description: "Register assets with financial details and they will appear here."
480
473
  }) : /* @__PURE__ */ y("div", {
481
474
  className: "px-3",
482
475
  children: /* @__PURE__ */ y(a, {
483
- page: U.page,
484
- pageCount: U.pageCount,
485
- rangeStart: U.rangeStart,
486
- rangeEnd: U.rangeEnd,
487
- totalItems: U.totalItems,
488
- canPrev: U.canPrev,
489
- canNext: U.canNext,
490
- onPrev: U.prevPage,
491
- onNext: U.nextPage,
476
+ page: W.page,
477
+ pageCount: W.pageCount,
478
+ rangeStart: W.rangeStart,
479
+ rangeEnd: W.rangeEnd,
480
+ totalItems: W.totalItems,
481
+ canPrev: W.canPrev,
482
+ canNext: W.canNext,
483
+ onPrev: W.prevPage,
484
+ onNext: W.nextPage,
492
485
  itemLabel: "assets"
493
486
  })
494
487
  })
495
488
  ]
496
489
  })]
497
490
  }),
498
- /* @__PURE__ */ b(D, {
491
+ /* @__PURE__ */ b(O, {
499
492
  value: "financial",
500
493
  className: "mt-4 space-y-4",
501
494
  children: [
@@ -503,9 +496,9 @@ function P() {
503
496
  className: "flex justify-end",
504
497
  children: /* @__PURE__ */ b(x, {
505
498
  size: "sm",
506
- onClick: ve,
507
- disabled: !B,
508
- children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
499
+ onClick: ye,
500
+ disabled: !V,
501
+ children: [/* @__PURE__ */ y(f, { className: "mr-2 h-4 w-4" }), "Export CSV"]
509
502
  })
510
503
  }),
511
504
  /* @__PURE__ */ b("div", {
@@ -513,48 +506,48 @@ function P() {
513
506
  children: [
514
507
  /* @__PURE__ */ y(s, {
515
508
  label: "Total asset value",
516
- value: G,
509
+ value: K,
517
510
  format: (e) => n(e)
518
511
  }),
519
512
  /* @__PURE__ */ y(s, {
520
513
  label: "Total depreciation",
521
- value: K,
514
+ value: q,
522
515
  format: (e) => n(e)
523
516
  }),
524
517
  /* @__PURE__ */ y(s, {
525
518
  label: "Maintenance spend",
526
- value: B?.totalMaintenanceSpend ?? 0,
519
+ value: V?.totalMaintenanceSpend ?? 0,
527
520
  format: (e) => n(e)
528
521
  }),
529
522
  /* @__PURE__ */ y(s, {
530
523
  label: "Transaction expense",
531
- value: B?.transactionExpense ?? 0,
524
+ value: V?.transactionExpense ?? 0,
532
525
  format: (e) => n(e)
533
526
  }),
534
527
  /* @__PURE__ */ y(s, {
535
528
  label: "Budget allocated",
536
- value: B?.budgetAllocated ?? 0,
529
+ value: V?.budgetAllocated ?? 0,
537
530
  format: (e) => n(e)
538
531
  }),
539
532
  /* @__PURE__ */ y(s, {
540
533
  label: "Budget spent",
541
- value: B?.budgetSpent ?? 0,
534
+ value: V?.budgetSpent ?? 0,
542
535
  format: (e) => n(e)
543
536
  }),
544
537
  /* @__PURE__ */ y(s, {
545
538
  label: "Invoices outstanding",
546
- value: B?.invoicesOutstanding ?? 0,
539
+ value: V?.invoicesOutstanding ?? 0,
547
540
  format: (e) => n(e)
548
541
  }),
549
542
  /* @__PURE__ */ y(s, {
550
543
  label: "Outstanding invoices",
551
- value: B?.invoicesOutstandingCount ?? 0
544
+ value: V?.invoicesOutstandingCount ?? 0
552
545
  })
553
546
  ]
554
547
  }),
555
548
  /* @__PURE__ */ b("div", {
556
549
  className: "grid gap-4 lg:grid-cols-2",
557
- children: [X.length > 0 && (B?.budgetAllocated || B?.budgetSpent) ? /* @__PURE__ */ y(c, {
550
+ children: [X.length > 0 && (V?.budgetAllocated || V?.budgetSpent) ? /* @__PURE__ */ y(c, {
558
551
  title: "Budget utilization",
559
552
  subtitle: "Allocated against spend",
560
553
  children: /* @__PURE__ */ y(r, {
@@ -576,7 +569,7 @@ function P() {
576
569
  children: /* @__PURE__ */ y(i, {
577
570
  data: Y,
578
571
  centerLabel: "Original cost",
579
- centerValue: G + K,
572
+ centerValue: K + q,
580
573
  valueFormat: (e) => n(e),
581
574
  height: 220,
582
575
  ariaLabel: "Current value versus accumulated depreciation"
@@ -585,7 +578,7 @@ function P() {
585
578
  })
586
579
  ]
587
580
  }),
588
- /* @__PURE__ */ b(D, {
581
+ /* @__PURE__ */ b(O, {
589
582
  value: "maintenance",
590
583
  className: "mt-4 space-y-4",
591
584
  children: [
@@ -593,9 +586,9 @@ function P() {
593
586
  className: "flex justify-end",
594
587
  children: /* @__PURE__ */ b(x, {
595
588
  size: "sm",
596
- onClick: _e,
597
- disabled: z.length === 0,
598
- children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
589
+ onClick: ve,
590
+ disabled: B.length === 0,
591
+ children: [/* @__PURE__ */ y(f, { className: "mr-2 h-4 w-4" }), "Export CSV"]
599
592
  })
600
593
  }),
601
594
  Z.length > 0 ? /* @__PURE__ */ y(c, {
@@ -651,7 +644,7 @@ function P() {
651
644
  children: "Actual Cost"
652
645
  })
653
646
  ]
654
- }) }), /* @__PURE__ */ y("tbody", { children: $ ? /* @__PURE__ */ y(N, { cols: 6 }) : W.pageItems.map((e) => /* @__PURE__ */ b("tr", {
647
+ }) }), /* @__PURE__ */ y("tbody", { children: $ ? /* @__PURE__ */ y(P, { cols: 6 }) : G.pageItems.map((e) => /* @__PURE__ */ b("tr", {
655
648
  className: "border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40",
656
649
  children: [
657
650
  /* @__PURE__ */ y("td", {
@@ -660,7 +653,7 @@ function P() {
660
653
  }),
661
654
  /* @__PURE__ */ y("td", {
662
655
  className: "p-3",
663
- children: H.get(e.assetId) ?? e.assetId
656
+ children: U.get(e.assetId) ?? e.assetId
664
657
  }),
665
658
  /* @__PURE__ */ y("td", {
666
659
  className: "p-3 capitalize",
@@ -668,8 +661,8 @@ function P() {
668
661
  }),
669
662
  /* @__PURE__ */ y("td", {
670
663
  className: "p-3",
671
- children: /* @__PURE__ */ y(A, {
672
- tone: M(e.status),
664
+ children: /* @__PURE__ */ y(j, {
665
+ tone: N(e.status),
673
666
  children: e.status.replace(/_/g, " ").toLowerCase()
674
667
  })
675
668
  }),
@@ -690,15 +683,15 @@ function P() {
690
683
  children: $ ? /* @__PURE__ */ y("div", {
691
684
  className: "space-y-3 p-4",
692
685
  children: Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ y("div", { className: "h-16 animate-pulse rounded bg-bg-sunken" }, t))
693
- }) : W.pageItems.map((e) => /* @__PURE__ */ b("div", {
686
+ }) : G.pageItems.map((e) => /* @__PURE__ */ b("div", {
694
687
  className: "p-4",
695
688
  children: [/* @__PURE__ */ b("div", {
696
689
  className: "flex items-start justify-between gap-2",
697
690
  children: [/* @__PURE__ */ y("p", {
698
691
  className: "min-w-0 flex-1 truncate font-medium",
699
692
  children: e.title
700
- }), /* @__PURE__ */ y(A, {
701
- tone: M(e.status),
693
+ }), /* @__PURE__ */ y(j, {
694
+ tone: N(e.status),
702
695
  children: e.status.replace(/_/g, " ").toLowerCase()
703
696
  })]
704
697
  }), /* @__PURE__ */ b("div", {
@@ -708,7 +701,7 @@ function P() {
708
701
  className: "capitalize",
709
702
  children: e.type.replace(/_/g, " ")
710
703
  }),
711
- /* @__PURE__ */ b("span", { children: ["· ", H.get(e.assetId) ?? e.assetId] }),
704
+ /* @__PURE__ */ b("span", { children: ["· ", U.get(e.assetId) ?? e.assetId] }),
712
705
  e.actualCost == null ? null : /* @__PURE__ */ y("span", {
713
706
  className: "ml-auto tabular-nums text-text-primary",
714
707
  children: n(e.actualCost)
@@ -717,22 +710,22 @@ function P() {
717
710
  })]
718
711
  }, e.id))
719
712
  }),
720
- z.length === 0 && !L ? /* @__PURE__ */ y(w, {
713
+ B.length === 0 && !R ? /* @__PURE__ */ y(C, {
721
714
  illustration: "empty-data",
722
715
  title: "No maintenance records",
723
716
  description: "Logged maintenance work will appear here once scheduled or completed."
724
717
  }) : /* @__PURE__ */ y("div", {
725
718
  className: "px-3",
726
719
  children: /* @__PURE__ */ y(a, {
727
- page: W.page,
728
- pageCount: W.pageCount,
729
- rangeStart: W.rangeStart,
730
- rangeEnd: W.rangeEnd,
731
- totalItems: W.totalItems,
732
- canPrev: W.canPrev,
733
- canNext: W.canNext,
734
- onPrev: W.prevPage,
735
- onNext: W.nextPage,
720
+ page: G.page,
721
+ pageCount: G.pageCount,
722
+ rangeStart: G.rangeStart,
723
+ rangeEnd: G.rangeEnd,
724
+ totalItems: G.totalItems,
725
+ canPrev: G.canPrev,
726
+ canNext: G.canNext,
727
+ onPrev: G.prevPage,
728
+ onNext: G.nextPage,
736
729
  itemLabel: "records"
737
730
  })
738
731
  })
@@ -740,7 +733,7 @@ function P() {
740
733
  })
741
734
  ]
742
735
  }),
743
- /* @__PURE__ */ b(D, {
736
+ /* @__PURE__ */ b(O, {
744
737
  value: "utilization",
745
738
  className: "mt-4 space-y-4",
746
739
  children: [
@@ -748,9 +741,9 @@ function P() {
748
741
  className: "flex justify-end",
749
742
  children: /* @__PURE__ */ b(x, {
750
743
  size: "sm",
751
- onClick: ye,
752
- disabled: !V,
753
- children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
744
+ onClick: be,
745
+ disabled: !H,
746
+ children: [/* @__PURE__ */ y(f, { className: "mr-2 h-4 w-4" }), "Export CSV"]
754
747
  })
755
748
  }),
756
749
  /* @__PURE__ */ b("div", {
@@ -758,24 +751,24 @@ function P() {
758
751
  children: [
759
752
  /* @__PURE__ */ y(s, {
760
753
  label: "Total assets",
761
- value: V?.totalAssets ?? 0
754
+ value: H?.totalAssets ?? 0
762
755
  }),
763
756
  /* @__PURE__ */ y(s, {
764
757
  label: "Assigned",
765
- value: V?.assignedAssets ?? 0
758
+ value: H?.assignedAssets ?? 0
766
759
  }),
767
760
  /* @__PURE__ */ y(s, {
768
761
  label: "Unassigned",
769
- value: V?.unassignedAssets ?? 0
762
+ value: H?.unassignedAssets ?? 0
770
763
  }),
771
764
  /* @__PURE__ */ y(s, {
772
765
  label: "Active unassigned",
773
- value: V?.idleAssets ?? 0
766
+ value: H?.idleAssets ?? 0
774
767
  }),
775
768
  /* @__PURE__ */ y(s, {
776
769
  label: "Overdue assignments",
777
- value: V?.overdueAssignments ?? 0,
778
- delta: V && V.overdueAssignments > 0 ? "Attention" : void 0,
770
+ value: H?.overdueAssignments ?? 0,
771
+ delta: H && H.overdueAssignments > 0 ? "Attention" : void 0,
779
772
  deltaTone: "negative"
780
773
  })
781
774
  ]
@@ -786,9 +779,9 @@ function P() {
786
779
  title: "Assets by status",
787
780
  subtitle: "Distribution across lifecycle states",
788
781
  children: /* @__PURE__ */ y(i, {
789
- data: J,
782
+ data: he,
790
783
  centerLabel: "Assets",
791
- centerValue: V?.totalAssets ?? 0,
784
+ centerValue: H?.totalAssets ?? 0,
792
785
  height: 240,
793
786
  emptyLabel: "No status data available.",
794
787
  ariaLabel: "Assets by status"
@@ -797,9 +790,9 @@ function P() {
797
790
  title: "Assignment coverage",
798
791
  subtitle: "Mutually exclusive assigned, active-unassigned and other-unassigned assets",
799
792
  children: /* @__PURE__ */ y(i, {
800
- data: he,
793
+ data: ge,
801
794
  centerLabel: "Assets",
802
- centerValue: V?.totalAssets ?? 0,
795
+ centerValue: H?.totalAssets ?? 0,
803
796
  height: 240,
804
797
  emptyLabel: "No coverage data available.",
805
798
  ariaLabel: "Assignment coverage"
@@ -814,6 +807,6 @@ function P() {
814
807
  });
815
808
  }
816
809
  //#endregion
817
- export { P as ReportsDashboardPage };
810
+ export { F as ReportsDashboardPage };
818
811
 
819
812
  //# sourceMappingURL=ReportsDashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReportsDashboardPage.js","names":[],"sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport { useAssetMoney } from '../providers/AssetHandlerProvider';\nimport {\n Button,\n GlassCard,\n GradientText,\n PagePurpose,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Download,\n Printer,\n FileBarChart2,\n Wallet,\n Wrench,\n Activity,\n DollarSign,\n TrendingDown,\n Percent,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport {\n Pagination,\n usePagination,\n StatTile,\n ChartCard,\n BarsChart,\n DonutChart,\n type DonutSlice,\n} from '../components';\nimport { downloadCsv } from '../utils/csv';\nimport { cn } from '../utils/cn';\nimport { buildAssignmentCoverageSlices } from '../utils/utilization';\n\n// ─── Query result shapes ──────────────────────────────────────────\ninterface RegisterAsset {\n id: string;\n assetNumber: string;\n name: string;\n category: string;\n status: string;\n department?: string | null;\n purchaseValue?: number | null;\n currentValue?: number | null;\n accumulatedDepreciation?: number | null;\n totalCostOfOwnership?: number | null;\n purchaseDate?: string | null;\n}\n\ninterface FinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface MaintenanceLogRecord {\n id: string;\n assetId: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n estimatedCost?: number | null;\n actualCost?: number | null;\n}\n\ninterface UtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: { status: string; count: number }[];\n}\n\nconst REPORTS_QUERY = gql`\n query AssetHandlerReports($registerFilter: AssetRegisterFilterInput) {\n assetRegister(filter: $registerFilter, pagination: { limit: 1000 }) {\n id\n assetNumber\n name\n category\n status\n department\n purchaseValue\n currentValue\n accumulatedDepreciation\n totalCostOfOwnership\n purchaseDate\n }\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n maintenanceLog(pagination: { limit: 1000 }) {\n id\n assetId\n title\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\ninterface ReportsResult {\n assetRegister: RegisterAsset[];\n financialSummary: FinancialSummary;\n maintenanceLog: MaintenanceLogRecord[];\n utilizationReport: UtilizationReport;\n}\n\nconst ASSET_STATUSES = ['ACTIVE', 'INACTIVE', 'MAINTENANCE', 'RETIRED', 'DISPOSED'] as const;\n\n// ─── Presentation helpers ─────────────────────────────────────────\ntype Tone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\n\nconst PILL_TONE: Record<Tone, string> = {\n success: 'bg-status-success-subtle text-status-success-text',\n warning: 'bg-status-warning-subtle text-status-warning-text',\n error: 'bg-status-error-subtle text-status-error-text',\n info: 'bg-status-info-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction StatusPill({ tone, children }: { tone: Tone; children: ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n PILL_TONE[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nconst assetStatusTone = (status: string): Tone => {\n const s = status.toLowerCase();\n if (s === 'active') return 'success';\n if (s === 'maintenance') return 'warning';\n if (s === 'retired' || s === 'disposed') return 'error';\n return 'neutral';\n};\n\nconst maintenanceStatusTone = (status: string): Tone => {\n const s = status.toLowerCase();\n if (s === 'completed') return 'success';\n if (s === 'in_progress' || s === 'scheduled') return 'info';\n if (s === 'overdue' || s === 'cancelled') return 'error';\n return 'neutral';\n};\n\nfunction SkeletonRows({ rows = 6, cols }: { rows?: number; cols: number }) {\n return (\n <>\n {Array.from({ length: rows }).map((_, r) => (\n <tr key={r} className=\"border-b border-border-seam/60\">\n {Array.from({ length: cols }).map((_, c) => (\n <td key={c} className=\"p-3\">\n <div className=\"h-4 w-full max-w-[120px] animate-pulse rounded bg-bg-sunken\" />\n </td>\n ))}\n </tr>\n ))}\n </>\n );\n}\n\nexport function ReportsDashboardPage() {\n const { format: money, compact: compactUsd } = useAssetMoney();\n const { assets } = useAssetHandlerStore();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n\n const { data, loading } = useQuery<ReportsResult>(REPORTS_QUERY, {\n variables: {\n registerFilter: statusFilter === 'all' ? null : { status: statusFilter },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const register = useMemo(() => data?.assetRegister ?? [], [data]);\n const maintenance = useMemo(() => data?.maintenanceLog ?? [], [data]);\n const summary = data?.financialSummary;\n const utilization = data?.utilizationReport;\n\n const assetNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const asset of assets) map.set(asset.id, asset.name);\n return map;\n }, [assets]);\n\n const registerPage = usePagination(register, { pageSize: 12, resetKey: statusFilter });\n const maintenancePage = usePagination(maintenance, { pageSize: 12 });\n\n // ─── Header KPI figures (real, from financialSummary) ───\n const totalAssetValue = summary?.totalAssetValue ?? 0;\n const totalDepreciation = summary?.totalDepreciation ?? 0;\n const depreciationPct =\n totalAssetValue + totalDepreciation > 0\n ? (totalDepreciation / (totalAssetValue + totalDepreciation)) * 100\n : 0;\n\n // ─── Chart aggregations (real data only) ───\n const categoryValue = useMemo(() => {\n const map = new Map<string, number>();\n for (const a of register) {\n const value = a.currentValue ?? a.purchaseValue ?? 0;\n if (value > 0) {\n const key = a.category.replace(/_/g, ' ');\n map.set(key, (map.get(key) ?? 0) + value);\n }\n }\n return Array.from(map, ([category, value]) => ({ category, value }))\n .sort((a, b) => b.value - a.value)\n .slice(0, 12);\n }, [register]);\n\n const statusSlices = useMemo<DonutSlice[]>(\n () =>\n (utilization?.byStatus ?? [])\n .map((s) => ({ name: s.status.toLowerCase(), value: s.count }))\n .filter((s) => s.value > 0),\n [utilization]\n );\n\n const coverageSlices = useMemo<DonutSlice[]>(() => {\n if (!utilization) return [];\n return buildAssignmentCoverageSlices(\n utilization.assignedAssets,\n utilization.unassignedAssets,\n utilization.idleAssets\n );\n }, [utilization]);\n\n const valueSlices = useMemo<DonutSlice[]>(() => {\n const slices = [\n { name: 'Current value', value: totalAssetValue },\n { name: 'Accumulated depreciation', value: totalDepreciation },\n ].filter((s) => s.value > 0);\n return slices;\n }, [totalAssetValue, totalDepreciation]);\n\n const budgetBars = useMemo(() => {\n if (!summary) return [];\n return [\n { metric: 'Allocated', value: summary.budgetAllocated },\n { metric: 'Spent', value: summary.budgetSpent },\n ];\n }, [summary]);\n\n const maintenanceSpendByType = useMemo(() => {\n const map = new Map<string, number>();\n for (const m of maintenance) {\n const cost = m.actualCost ?? m.estimatedCost ?? 0;\n if (cost > 0) {\n const key = m.type.replace(/_/g, ' ');\n map.set(key, (map.get(key) ?? 0) + cost);\n }\n }\n return Array.from(map, (entry) => ({ type: entry[0], cost: entry[1] })).sort(\n (a, b) => b.cost - a.cost\n );\n }, [maintenance]);\n\n // ─── Exports (unchanged behaviour) ───\n const exportRegister = () => {\n downloadCsv(\n `asset-register-${new Date().toISOString().slice(0, 10)}`,\n [\n 'Asset Number',\n 'Name',\n 'Category',\n 'Status',\n 'Department',\n 'Purchase Value',\n 'Current Value',\n 'Accumulated Depreciation',\n 'Total Cost of Ownership',\n 'Purchase Date',\n ],\n register.map((a) => [\n a.assetNumber,\n a.name,\n a.category,\n a.status,\n a.department ?? '',\n a.purchaseValue ?? '',\n a.currentValue ?? '',\n a.accumulatedDepreciation ?? '',\n a.totalCostOfOwnership ?? '',\n a.purchaseDate ? new Date(a.purchaseDate).toISOString().slice(0, 10) : '',\n ])\n );\n };\n\n const exportMaintenance = () => {\n downloadCsv(\n `maintenance-log-${new Date().toISOString().slice(0, 10)}`,\n [\n 'Title',\n 'Asset',\n 'Type',\n 'Status',\n 'Priority',\n 'Scheduled',\n 'Completed',\n 'Estimated Cost',\n 'Actual Cost',\n ],\n maintenance.map((m) => [\n m.title,\n assetNameById.get(m.assetId) ?? m.assetId,\n m.type,\n m.status,\n m.priority,\n m.scheduledAt ? new Date(m.scheduledAt).toISOString().slice(0, 10) : '',\n m.completedAt ? new Date(m.completedAt).toISOString().slice(0, 10) : '',\n m.estimatedCost ?? '',\n m.actualCost ?? '',\n ])\n );\n };\n\n const exportFinancial = () => {\n if (!summary) return;\n downloadCsv(\n `financial-summary-${new Date().toISOString().slice(0, 10)}`,\n ['Metric', 'Value'],\n [\n ['Total asset value', summary.totalAssetValue],\n ['Total depreciation', summary.totalDepreciation],\n ['Total maintenance spend', summary.totalMaintenanceSpend],\n ['Budget allocated', summary.budgetAllocated],\n ['Budget spent', summary.budgetSpent],\n ['Invoices outstanding', summary.invoicesOutstanding],\n ['Invoices outstanding (count)', summary.invoicesOutstandingCount],\n ['Transaction expense', summary.transactionExpense],\n ]\n );\n };\n\n const exportUtilization = () => {\n if (!utilization) return;\n downloadCsv(\n `utilization-report-${new Date().toISOString().slice(0, 10)}`,\n ['Metric', 'Value'],\n [\n ['Total assets', utilization.totalAssets],\n ['Assigned assets', utilization.assignedAssets],\n ['Unassigned assets', utilization.unassignedAssets],\n ['Idle assets', utilization.idleAssets],\n ['Overdue assignments', utilization.overdueAssignments],\n ...utilization.byStatus.map((s): [string, number] => [`Status: ${s.status}`, s.count]),\n ]\n );\n };\n\n const registerLoading = loading && register.length === 0;\n const maintenanceLoading = loading && maintenance.length === 0;\n\n const registerActions = (\n <>\n <Button size=\"sm\" onClick={exportRegister} disabled={register.length === 0}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => window.print()}>\n <Printer className=\"mr-2 h-4 w-4\" />\n Print\n </Button>\n </>\n );\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n {/* ─── Header with persistent finance KPI strip ─── */}\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Reports</GradientText>\n </h1>\n <p className=\"mt-1 max-w-xl text-sm text-text-secondary\">\n Registers and summaries built from live asset, finance and maintenance data.\n </p>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatTile\n label=\"Total asset value\"\n value={totalAssetValue}\n format={(v) => money(v)}\n icon={DollarSign}\n hero\n />\n <StatTile\n label=\"Total depreciation\"\n value={totalDepreciation}\n format={(v) => money(v)}\n icon={TrendingDown}\n />\n <StatTile\n label=\"Depreciated\"\n value={depreciationPct}\n format={(v) => `${v.toFixed(2)}%`}\n icon={Percent}\n />\n </div>\n </div>\n\n <PagePurpose>\n Pull the numbers you need for audits, finance reviews and planning. Every register below is\n generated from live records — export any of them to CSV or print the page for sign-off.\n </PagePurpose>\n\n <Tabs defaultValue=\"register\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"register\">\n <FileBarChart2 className=\"mr-1.5 h-4 w-4\" />\n Asset Register\n </TabsTrigger>\n <TabsTrigger value=\"financial\">\n <Wallet className=\"mr-1.5 h-4 w-4\" />\n Financial Summary\n </TabsTrigger>\n <TabsTrigger value=\"maintenance\">\n <Wrench className=\"mr-1.5 h-4 w-4\" />\n Maintenance Log\n </TabsTrigger>\n <TabsTrigger value=\"utilization\">\n <Activity className=\"mr-1.5 h-4 w-4\" />\n Utilization\n </TabsTrigger>\n </TabsList>\n\n {/* ─── Asset Register ─── */}\n <TabsContent value=\"register\" className=\"mt-4 space-y-4\">\n {categoryValue.length > 0 ? (\n <ChartCard\n title=\"Asset value by category\"\n subtitle=\"Current book value aggregated from the register\"\n >\n <BarsChart\n data={categoryValue}\n xKey=\"category\"\n series={[{ key: 'value', label: 'Current value' }]}\n orientation=\"horizontal\"\n height={Math.max(200, categoryValue.length * 34)}\n valueFormat={compactUsd}\n categoryWidth={128}\n ariaLabel=\"Current asset value by category\"\n />\n </ChartCard>\n ) : null}\n\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n <div className=\"flex flex-col gap-3 border-b border-border-seam p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-base font-semibold text-text-primary\">Asset Register</h3>\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger className=\"h-8 w-40\">\n <SelectValue placeholder=\"Filter by status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {ASSET_STATUSES.map((status) => (\n <SelectItem key={status} value={status} className=\"capitalize\">\n {status.toLowerCase()}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex flex-wrap gap-2\">{registerActions}</div>\n </div>\n\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[860px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/50 text-left\">\n <th className=\"p-3 font-medium\">Asset</th>\n <th className=\"p-3 font-medium\">Category</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Department</th>\n <th className=\"p-3 text-right font-medium\">Purchase</th>\n <th className=\"p-3 text-right font-medium\">Current</th>\n <th className=\"p-3 text-right font-medium\">Depreciation</th>\n <th className=\"p-3 text-right font-medium\">TCO</th>\n </tr>\n </thead>\n <tbody>\n {registerLoading ? (\n <SkeletonRows cols={8} />\n ) : (\n registerPage.pageItems.map((a) => (\n <tr\n key={a.id}\n className=\"border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n >\n <td className=\"p-3\">\n <p className=\"font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetNumber}</p>\n </td>\n <td className=\"p-3 capitalize\">{a.category.replace(/_/g, ' ')}</td>\n <td className=\"p-3\">\n <StatusPill tone={assetStatusTone(a.status)}>\n {a.status.toLowerCase()}\n </StatusPill>\n </td>\n <td className=\"p-3\">{a.department || '—'}</td>\n <td className=\"p-3 text-right tabular-nums\">{money(a.purchaseValue)}</td>\n <td className=\"p-3 text-right tabular-nums\">{money(a.currentValue)}</td>\n <td className=\"p-3 text-right tabular-nums\">\n {money(a.accumulatedDepreciation)}\n </td>\n <td className=\"p-3 text-right tabular-nums\">\n {money(a.totalCostOfOwnership)}\n </td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {registerLoading ? (\n <div className=\"space-y-3 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"h-16 animate-pulse rounded bg-bg-sunken\" />\n ))}\n </div>\n ) : (\n registerPage.pageItems.map((a) => (\n <div key={a.id} className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetNumber}</p>\n </div>\n <StatusPill tone={assetStatusTone(a.status)}>\n {a.status.toLowerCase()}\n </StatusPill>\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs text-text-secondary\">\n <span>Category</span>\n <span className=\"text-right capitalize text-text-primary\">\n {a.category.replace(/_/g, ' ')}\n </span>\n <span>Current value</span>\n <span className=\"text-right tabular-nums text-text-primary\">\n {money(a.currentValue)}\n </span>\n <span>Depreciation</span>\n <span className=\"text-right tabular-nums text-text-primary\">\n {money(a.accumulatedDepreciation)}\n </span>\n </div>\n </div>\n ))\n )}\n </div>\n\n {register.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets in the register\"\n description=\"Register assets with financial details and they will appear here.\"\n />\n ) : (\n <div className=\"px-3\">\n <Pagination\n page={registerPage.page}\n pageCount={registerPage.pageCount}\n rangeStart={registerPage.rangeStart}\n rangeEnd={registerPage.rangeEnd}\n totalItems={registerPage.totalItems}\n canPrev={registerPage.canPrev}\n canNext={registerPage.canNext}\n onPrev={registerPage.prevPage}\n onNext={registerPage.nextPage}\n itemLabel=\"assets\"\n />\n </div>\n )}\n </GlassCard>\n </TabsContent>\n\n {/* ─── Financial Summary ─── */}\n <TabsContent value=\"financial\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportFinancial} disabled={!summary}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile label=\"Total asset value\" value={totalAssetValue} format={(v) => money(v)} />\n <StatTile\n label=\"Total depreciation\"\n value={totalDepreciation}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Maintenance spend\"\n value={summary?.totalMaintenanceSpend ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Transaction expense\"\n value={summary?.transactionExpense ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Budget allocated\"\n value={summary?.budgetAllocated ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Budget spent\"\n value={summary?.budgetSpent ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Invoices outstanding\"\n value={summary?.invoicesOutstanding ?? 0}\n format={(v) => money(v)}\n />\n <StatTile label=\"Outstanding invoices\" value={summary?.invoicesOutstandingCount ?? 0} />\n </div>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n {budgetBars.length > 0 && (summary?.budgetAllocated || summary?.budgetSpent) ? (\n <ChartCard title=\"Budget utilization\" subtitle=\"Allocated against spend\">\n <BarsChart\n data={budgetBars}\n xKey=\"metric\"\n series={[{ key: 'value', label: 'Amount' }]}\n orientation=\"horizontal\"\n height={200}\n valueFormat={compactUsd}\n categoryWidth={96}\n ariaLabel=\"Budget allocated versus spent\"\n />\n </ChartCard>\n ) : null}\n {valueSlices.length > 0 ? (\n <ChartCard\n title=\"Value vs depreciation\"\n subtitle=\"Current book value against accumulated depreciation\"\n >\n <DonutChart\n data={valueSlices}\n centerLabel=\"Original cost\"\n centerValue={totalAssetValue + totalDepreciation}\n valueFormat={(v) => money(v)}\n height={220}\n ariaLabel=\"Current value versus accumulated depreciation\"\n />\n </ChartCard>\n ) : null}\n </div>\n </TabsContent>\n\n {/* ─── Maintenance Log ─── */}\n <TabsContent value=\"maintenance\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportMaintenance} disabled={maintenance.length === 0}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n\n {maintenanceSpendByType.length > 0 ? (\n <ChartCard\n title=\"Maintenance spend by type\"\n subtitle=\"Actual cost, falling back to estimates\"\n >\n <BarsChart\n data={maintenanceSpendByType}\n xKey=\"type\"\n series={[{ key: 'cost', label: 'Spend' }]}\n orientation=\"horizontal\"\n height={Math.max(180, maintenanceSpendByType.length * 34)}\n valueFormat={compactUsd}\n categoryWidth={128}\n ariaLabel=\"Maintenance spend by type\"\n />\n </ChartCard>\n ) : null}\n\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/50 text-left\">\n <th className=\"p-3 font-medium\">Work</th>\n <th className=\"p-3 font-medium\">Asset</th>\n <th className=\"p-3 font-medium\">Type</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Completed</th>\n <th className=\"p-3 text-right font-medium\">Actual Cost</th>\n </tr>\n </thead>\n <tbody>\n {maintenanceLoading ? (\n <SkeletonRows cols={6} />\n ) : (\n maintenancePage.pageItems.map((m) => (\n <tr\n key={m.id}\n className=\"border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n >\n <td className=\"p-3 font-medium\">{m.title}</td>\n <td className=\"p-3\">{assetNameById.get(m.assetId) ?? m.assetId}</td>\n <td className=\"p-3 capitalize\">{m.type.replace(/_/g, ' ')}</td>\n <td className=\"p-3\">\n <StatusPill tone={maintenanceStatusTone(m.status)}>\n {m.status.replace(/_/g, ' ').toLowerCase()}\n </StatusPill>\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {m.completedAt ? new Date(m.completedAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-3 text-right tabular-nums\">{money(m.actualCost)}</td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {maintenanceLoading ? (\n <div className=\"space-y-3 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"h-16 animate-pulse rounded bg-bg-sunken\" />\n ))}\n </div>\n ) : (\n maintenancePage.pageItems.map((m) => (\n <div key={m.id} className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 flex-1 truncate font-medium\">{m.title}</p>\n <StatusPill tone={maintenanceStatusTone(m.status)}>\n {m.status.replace(/_/g, ' ').toLowerCase()}\n </StatusPill>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"capitalize\">{m.type.replace(/_/g, ' ')}</span>\n <span>· {assetNameById.get(m.assetId) ?? m.assetId}</span>\n {m.actualCost != null ? (\n <span className=\"ml-auto tabular-nums text-text-primary\">\n {money(m.actualCost)}\n </span>\n ) : null}\n </div>\n </div>\n ))\n )}\n </div>\n\n {maintenance.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No maintenance records\"\n description=\"Logged maintenance work will appear here once scheduled or completed.\"\n />\n ) : (\n <div className=\"px-3\">\n <Pagination\n page={maintenancePage.page}\n pageCount={maintenancePage.pageCount}\n rangeStart={maintenancePage.rangeStart}\n rangeEnd={maintenancePage.rangeEnd}\n totalItems={maintenancePage.totalItems}\n canPrev={maintenancePage.canPrev}\n canNext={maintenancePage.canNext}\n onPrev={maintenancePage.prevPage}\n onNext={maintenancePage.nextPage}\n itemLabel=\"records\"\n />\n </div>\n )}\n </GlassCard>\n </TabsContent>\n\n {/* ─── Utilization ─── */}\n <TabsContent value=\"utilization\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportUtilization} disabled={!utilization}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n <StatTile label=\"Total assets\" value={utilization?.totalAssets ?? 0} />\n <StatTile label=\"Assigned\" value={utilization?.assignedAssets ?? 0} />\n <StatTile label=\"Unassigned\" value={utilization?.unassignedAssets ?? 0} />\n <StatTile label=\"Active unassigned\" value={utilization?.idleAssets ?? 0} />\n <StatTile\n label=\"Overdue assignments\"\n value={utilization?.overdueAssignments ?? 0}\n delta={utilization && utilization.overdueAssignments > 0 ? 'Attention' : undefined}\n deltaTone=\"negative\"\n />\n </div>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Assets by status\" subtitle=\"Distribution across lifecycle states\">\n <DonutChart\n data={statusSlices}\n centerLabel=\"Assets\"\n centerValue={utilization?.totalAssets ?? 0}\n height={240}\n emptyLabel=\"No status data available.\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n <ChartCard\n title=\"Assignment coverage\"\n subtitle=\"Mutually exclusive assigned, active-unassigned and other-unassigned assets\"\n >\n <DonutChart\n data={coverageSlices}\n centerLabel=\"Assets\"\n centerValue={utilization?.totalAssets ?? 0}\n height={240}\n emptyLabel=\"No coverage data available.\"\n ariaLabel=\"Assignment coverage\"\n />\n </ChartCard>\n </div>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReportsDashboardPage.js","names":[],"sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport { useAssetMoney } from '../providers/AssetHandlerProvider';\nimport {\n Button,\n GlassCard,\n GradientText,\n PagePurpose,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Download,\n Printer,\n FileBarChart2,\n Wallet,\n Wrench,\n Activity,\n DollarSign,\n TrendingDown,\n Percent,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport {\n Pagination,\n usePagination,\n StatTile,\n ChartCard,\n BarsChart,\n DonutChart,\n type DonutSlice,\n} from '../components';\nimport { downloadCsv } from '../utils/csv';\nimport { cn } from '../utils/cn';\nimport { buildAssignmentCoverageSlices, buildUtilizationCsvRows } from '../utils/utilization';\n\n// ─── Query result shapes ──────────────────────────────────────────\ninterface RegisterAsset {\n id: string;\n assetNumber: string;\n name: string;\n category: string;\n status: string;\n department?: string | null;\n purchaseValue?: number | null;\n currentValue?: number | null;\n accumulatedDepreciation?: number | null;\n totalCostOfOwnership?: number | null;\n purchaseDate?: string | null;\n}\n\ninterface FinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface MaintenanceLogRecord {\n id: string;\n assetId: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n estimatedCost?: number | null;\n actualCost?: number | null;\n}\n\ninterface UtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: { status: string; count: number }[];\n}\n\nconst REPORTS_QUERY = gql`\n query AssetHandlerReports($registerFilter: AssetRegisterFilterInput) {\n assetRegister(filter: $registerFilter, pagination: { limit: 1000 }) {\n id\n assetNumber\n name\n category\n status\n department\n purchaseValue\n currentValue\n accumulatedDepreciation\n totalCostOfOwnership\n purchaseDate\n }\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n maintenanceLog(pagination: { limit: 1000 }) {\n id\n assetId\n title\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\ninterface ReportsResult {\n assetRegister: RegisterAsset[];\n financialSummary: FinancialSummary;\n maintenanceLog: MaintenanceLogRecord[];\n utilizationReport: UtilizationReport;\n}\n\nconst ASSET_STATUSES = ['ACTIVE', 'INACTIVE', 'MAINTENANCE', 'RETIRED', 'DISPOSED'] as const;\n\n// ─── Presentation helpers ─────────────────────────────────────────\ntype Tone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\n\nconst PILL_TONE: Record<Tone, string> = {\n success: 'bg-status-success-subtle text-status-success-text',\n warning: 'bg-status-warning-subtle text-status-warning-text',\n error: 'bg-status-error-subtle text-status-error-text',\n info: 'bg-status-info-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction StatusPill({ tone, children }: { tone: Tone; children: ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n PILL_TONE[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nconst assetStatusTone = (status: string): Tone => {\n const s = status.toLowerCase();\n if (s === 'active') return 'success';\n if (s === 'maintenance') return 'warning';\n if (s === 'retired' || s === 'disposed') return 'error';\n return 'neutral';\n};\n\nconst maintenanceStatusTone = (status: string): Tone => {\n const s = status.toLowerCase();\n if (s === 'completed') return 'success';\n if (s === 'in_progress' || s === 'scheduled') return 'info';\n if (s === 'overdue' || s === 'cancelled') return 'error';\n return 'neutral';\n};\n\nfunction SkeletonRows({ rows = 6, cols }: { rows?: number; cols: number }) {\n return (\n <>\n {Array.from({ length: rows }).map((_, r) => (\n <tr key={r} className=\"border-b border-border-seam/60\">\n {Array.from({ length: cols }).map((_, c) => (\n <td key={c} className=\"p-3\">\n <div className=\"h-4 w-full max-w-[120px] animate-pulse rounded bg-bg-sunken\" />\n </td>\n ))}\n </tr>\n ))}\n </>\n );\n}\n\nexport function ReportsDashboardPage() {\n const { format: money, compact: compactUsd } = useAssetMoney();\n const { assets } = useAssetHandlerStore();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n\n const { data, loading } = useQuery<ReportsResult>(REPORTS_QUERY, {\n variables: {\n registerFilter: statusFilter === 'all' ? null : { status: statusFilter },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const register = useMemo(() => data?.assetRegister ?? [], [data]);\n const maintenance = useMemo(() => data?.maintenanceLog ?? [], [data]);\n const summary = data?.financialSummary;\n const utilization = data?.utilizationReport;\n\n const assetNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const asset of assets) map.set(asset.id, asset.name);\n return map;\n }, [assets]);\n\n const registerPage = usePagination(register, { pageSize: 12, resetKey: statusFilter });\n const maintenancePage = usePagination(maintenance, { pageSize: 12 });\n\n // ─── Header KPI figures (real, from financialSummary) ───\n const totalAssetValue = summary?.totalAssetValue ?? 0;\n const totalDepreciation = summary?.totalDepreciation ?? 0;\n const depreciationPct =\n totalAssetValue + totalDepreciation > 0\n ? (totalDepreciation / (totalAssetValue + totalDepreciation)) * 100\n : 0;\n\n // ─── Chart aggregations (real data only) ───\n const categoryValue = useMemo(() => {\n const map = new Map<string, number>();\n for (const a of register) {\n const value = a.currentValue ?? a.purchaseValue ?? 0;\n if (value > 0) {\n const key = a.category.replace(/_/g, ' ');\n map.set(key, (map.get(key) ?? 0) + value);\n }\n }\n return Array.from(map, ([category, value]) => ({ category, value }))\n .sort((a, b) => b.value - a.value)\n .slice(0, 12);\n }, [register]);\n\n const statusSlices = useMemo<DonutSlice[]>(\n () =>\n (utilization?.byStatus ?? [])\n .map((s) => ({ name: s.status.toLowerCase(), value: s.count }))\n .filter((s) => s.value > 0),\n [utilization]\n );\n\n const coverageSlices = useMemo<DonutSlice[]>(() => {\n if (!utilization) return [];\n return buildAssignmentCoverageSlices(\n utilization.assignedAssets,\n utilization.unassignedAssets,\n utilization.idleAssets\n );\n }, [utilization]);\n\n const valueSlices = useMemo<DonutSlice[]>(() => {\n const slices = [\n { name: 'Current value', value: totalAssetValue },\n { name: 'Accumulated depreciation', value: totalDepreciation },\n ].filter((s) => s.value > 0);\n return slices;\n }, [totalAssetValue, totalDepreciation]);\n\n const budgetBars = useMemo(() => {\n if (!summary) return [];\n return [\n { metric: 'Allocated', value: summary.budgetAllocated },\n { metric: 'Spent', value: summary.budgetSpent },\n ];\n }, [summary]);\n\n const maintenanceSpendByType = useMemo(() => {\n const map = new Map<string, number>();\n for (const m of maintenance) {\n const cost = m.actualCost ?? m.estimatedCost ?? 0;\n if (cost > 0) {\n const key = m.type.replace(/_/g, ' ');\n map.set(key, (map.get(key) ?? 0) + cost);\n }\n }\n return Array.from(map, (entry) => ({ type: entry[0], cost: entry[1] })).sort(\n (a, b) => b.cost - a.cost\n );\n }, [maintenance]);\n\n // ─── Exports (unchanged behaviour) ───\n const exportRegister = () => {\n downloadCsv(\n `asset-register-${new Date().toISOString().slice(0, 10)}`,\n [\n 'Asset Number',\n 'Name',\n 'Category',\n 'Status',\n 'Department',\n 'Purchase Value',\n 'Current Value',\n 'Accumulated Depreciation',\n 'Total Cost of Ownership',\n 'Purchase Date',\n ],\n register.map((a) => [\n a.assetNumber,\n a.name,\n a.category,\n a.status,\n a.department ?? '',\n a.purchaseValue ?? '',\n a.currentValue ?? '',\n a.accumulatedDepreciation ?? '',\n a.totalCostOfOwnership ?? '',\n a.purchaseDate ? new Date(a.purchaseDate).toISOString().slice(0, 10) : '',\n ])\n );\n };\n\n const exportMaintenance = () => {\n downloadCsv(\n `maintenance-log-${new Date().toISOString().slice(0, 10)}`,\n [\n 'Title',\n 'Asset',\n 'Type',\n 'Status',\n 'Priority',\n 'Scheduled',\n 'Completed',\n 'Estimated Cost',\n 'Actual Cost',\n ],\n maintenance.map((m) => [\n m.title,\n assetNameById.get(m.assetId) ?? m.assetId,\n m.type,\n m.status,\n m.priority,\n m.scheduledAt ? new Date(m.scheduledAt).toISOString().slice(0, 10) : '',\n m.completedAt ? new Date(m.completedAt).toISOString().slice(0, 10) : '',\n m.estimatedCost ?? '',\n m.actualCost ?? '',\n ])\n );\n };\n\n const exportFinancial = () => {\n if (!summary) return;\n downloadCsv(\n `financial-summary-${new Date().toISOString().slice(0, 10)}`,\n ['Metric', 'Value'],\n [\n ['Total asset value', summary.totalAssetValue],\n ['Total depreciation', summary.totalDepreciation],\n ['Total maintenance spend', summary.totalMaintenanceSpend],\n ['Budget allocated', summary.budgetAllocated],\n ['Budget spent', summary.budgetSpent],\n ['Invoices outstanding', summary.invoicesOutstanding],\n ['Invoices outstanding (count)', summary.invoicesOutstandingCount],\n ['Transaction expense', summary.transactionExpense],\n ]\n );\n };\n\n const exportUtilization = () => {\n if (!utilization) return;\n downloadCsv(\n `utilization-report-${new Date().toISOString().slice(0, 10)}`,\n ['Metric', 'Value'],\n buildUtilizationCsvRows(utilization)\n );\n };\n\n const registerLoading = loading && register.length === 0;\n const maintenanceLoading = loading && maintenance.length === 0;\n\n const registerActions = (\n <>\n <Button size=\"sm\" onClick={exportRegister} disabled={register.length === 0}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => window.print()}>\n <Printer className=\"mr-2 h-4 w-4\" />\n Print\n </Button>\n </>\n );\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n {/* ─── Header with persistent finance KPI strip ─── */}\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Reports</GradientText>\n </h1>\n <p className=\"mt-1 max-w-xl text-sm text-text-secondary\">\n Registers and summaries built from live asset, finance and maintenance data.\n </p>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatTile\n label=\"Total asset value\"\n value={totalAssetValue}\n format={(v) => money(v)}\n icon={DollarSign}\n hero\n />\n <StatTile\n label=\"Total depreciation\"\n value={totalDepreciation}\n format={(v) => money(v)}\n icon={TrendingDown}\n />\n <StatTile\n label=\"Depreciated\"\n value={depreciationPct}\n format={(v) => `${v.toFixed(2)}%`}\n icon={Percent}\n />\n </div>\n </div>\n\n <PagePurpose>\n Pull the numbers you need for audits, finance reviews and planning. Every register below is\n generated from live records — export any of them to CSV or print the page for sign-off.\n </PagePurpose>\n\n <Tabs defaultValue=\"register\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"register\">\n <FileBarChart2 className=\"mr-1.5 h-4 w-4\" />\n Asset Register\n </TabsTrigger>\n <TabsTrigger value=\"financial\">\n <Wallet className=\"mr-1.5 h-4 w-4\" />\n Financial Summary\n </TabsTrigger>\n <TabsTrigger value=\"maintenance\">\n <Wrench className=\"mr-1.5 h-4 w-4\" />\n Maintenance Log\n </TabsTrigger>\n <TabsTrigger value=\"utilization\">\n <Activity className=\"mr-1.5 h-4 w-4\" />\n Utilization\n </TabsTrigger>\n </TabsList>\n\n {/* ─── Asset Register ─── */}\n <TabsContent value=\"register\" className=\"mt-4 space-y-4\">\n {categoryValue.length > 0 ? (\n <ChartCard\n title=\"Asset value by category\"\n subtitle=\"Current book value aggregated from the register\"\n >\n <BarsChart\n data={categoryValue}\n xKey=\"category\"\n series={[{ key: 'value', label: 'Current value' }]}\n orientation=\"horizontal\"\n height={Math.max(200, categoryValue.length * 34)}\n valueFormat={compactUsd}\n categoryWidth={128}\n ariaLabel=\"Current asset value by category\"\n />\n </ChartCard>\n ) : null}\n\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n <div className=\"flex flex-col gap-3 border-b border-border-seam p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-base font-semibold text-text-primary\">Asset Register</h3>\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger className=\"h-8 w-40\">\n <SelectValue placeholder=\"Filter by status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {ASSET_STATUSES.map((status) => (\n <SelectItem key={status} value={status} className=\"capitalize\">\n {status.toLowerCase()}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex flex-wrap gap-2\">{registerActions}</div>\n </div>\n\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[860px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/50 text-left\">\n <th className=\"p-3 font-medium\">Asset</th>\n <th className=\"p-3 font-medium\">Category</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Department</th>\n <th className=\"p-3 text-right font-medium\">Purchase</th>\n <th className=\"p-3 text-right font-medium\">Current</th>\n <th className=\"p-3 text-right font-medium\">Depreciation</th>\n <th className=\"p-3 text-right font-medium\">TCO</th>\n </tr>\n </thead>\n <tbody>\n {registerLoading ? (\n <SkeletonRows cols={8} />\n ) : (\n registerPage.pageItems.map((a) => (\n <tr\n key={a.id}\n className=\"border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n >\n <td className=\"p-3\">\n <p className=\"font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetNumber}</p>\n </td>\n <td className=\"p-3 capitalize\">{a.category.replace(/_/g, ' ')}</td>\n <td className=\"p-3\">\n <StatusPill tone={assetStatusTone(a.status)}>\n {a.status.toLowerCase()}\n </StatusPill>\n </td>\n <td className=\"p-3\">{a.department || '—'}</td>\n <td className=\"p-3 text-right tabular-nums\">{money(a.purchaseValue)}</td>\n <td className=\"p-3 text-right tabular-nums\">{money(a.currentValue)}</td>\n <td className=\"p-3 text-right tabular-nums\">\n {money(a.accumulatedDepreciation)}\n </td>\n <td className=\"p-3 text-right tabular-nums\">\n {money(a.totalCostOfOwnership)}\n </td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {registerLoading ? (\n <div className=\"space-y-3 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"h-16 animate-pulse rounded bg-bg-sunken\" />\n ))}\n </div>\n ) : (\n registerPage.pageItems.map((a) => (\n <div key={a.id} className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetNumber}</p>\n </div>\n <StatusPill tone={assetStatusTone(a.status)}>\n {a.status.toLowerCase()}\n </StatusPill>\n </div>\n <div className=\"mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs text-text-secondary\">\n <span>Category</span>\n <span className=\"text-right capitalize text-text-primary\">\n {a.category.replace(/_/g, ' ')}\n </span>\n <span>Current value</span>\n <span className=\"text-right tabular-nums text-text-primary\">\n {money(a.currentValue)}\n </span>\n <span>Depreciation</span>\n <span className=\"text-right tabular-nums text-text-primary\">\n {money(a.accumulatedDepreciation)}\n </span>\n </div>\n </div>\n ))\n )}\n </div>\n\n {register.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets in the register\"\n description=\"Register assets with financial details and they will appear here.\"\n />\n ) : (\n <div className=\"px-3\">\n <Pagination\n page={registerPage.page}\n pageCount={registerPage.pageCount}\n rangeStart={registerPage.rangeStart}\n rangeEnd={registerPage.rangeEnd}\n totalItems={registerPage.totalItems}\n canPrev={registerPage.canPrev}\n canNext={registerPage.canNext}\n onPrev={registerPage.prevPage}\n onNext={registerPage.nextPage}\n itemLabel=\"assets\"\n />\n </div>\n )}\n </GlassCard>\n </TabsContent>\n\n {/* ─── Financial Summary ─── */}\n <TabsContent value=\"financial\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportFinancial} disabled={!summary}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile label=\"Total asset value\" value={totalAssetValue} format={(v) => money(v)} />\n <StatTile\n label=\"Total depreciation\"\n value={totalDepreciation}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Maintenance spend\"\n value={summary?.totalMaintenanceSpend ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Transaction expense\"\n value={summary?.transactionExpense ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Budget allocated\"\n value={summary?.budgetAllocated ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Budget spent\"\n value={summary?.budgetSpent ?? 0}\n format={(v) => money(v)}\n />\n <StatTile\n label=\"Invoices outstanding\"\n value={summary?.invoicesOutstanding ?? 0}\n format={(v) => money(v)}\n />\n <StatTile label=\"Outstanding invoices\" value={summary?.invoicesOutstandingCount ?? 0} />\n </div>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n {budgetBars.length > 0 && (summary?.budgetAllocated || summary?.budgetSpent) ? (\n <ChartCard title=\"Budget utilization\" subtitle=\"Allocated against spend\">\n <BarsChart\n data={budgetBars}\n xKey=\"metric\"\n series={[{ key: 'value', label: 'Amount' }]}\n orientation=\"horizontal\"\n height={200}\n valueFormat={compactUsd}\n categoryWidth={96}\n ariaLabel=\"Budget allocated versus spent\"\n />\n </ChartCard>\n ) : null}\n {valueSlices.length > 0 ? (\n <ChartCard\n title=\"Value vs depreciation\"\n subtitle=\"Current book value against accumulated depreciation\"\n >\n <DonutChart\n data={valueSlices}\n centerLabel=\"Original cost\"\n centerValue={totalAssetValue + totalDepreciation}\n valueFormat={(v) => money(v)}\n height={220}\n ariaLabel=\"Current value versus accumulated depreciation\"\n />\n </ChartCard>\n ) : null}\n </div>\n </TabsContent>\n\n {/* ─── Maintenance Log ─── */}\n <TabsContent value=\"maintenance\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportMaintenance} disabled={maintenance.length === 0}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n\n {maintenanceSpendByType.length > 0 ? (\n <ChartCard\n title=\"Maintenance spend by type\"\n subtitle=\"Actual cost, falling back to estimates\"\n >\n <BarsChart\n data={maintenanceSpendByType}\n xKey=\"type\"\n series={[{ key: 'cost', label: 'Spend' }]}\n orientation=\"horizontal\"\n height={Math.max(180, maintenanceSpendByType.length * 34)}\n valueFormat={compactUsd}\n categoryWidth={128}\n ariaLabel=\"Maintenance spend by type\"\n />\n </ChartCard>\n ) : null}\n\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/50 text-left\">\n <th className=\"p-3 font-medium\">Work</th>\n <th className=\"p-3 font-medium\">Asset</th>\n <th className=\"p-3 font-medium\">Type</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Completed</th>\n <th className=\"p-3 text-right font-medium\">Actual Cost</th>\n </tr>\n </thead>\n <tbody>\n {maintenanceLoading ? (\n <SkeletonRows cols={6} />\n ) : (\n maintenancePage.pageItems.map((m) => (\n <tr\n key={m.id}\n className=\"border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n >\n <td className=\"p-3 font-medium\">{m.title}</td>\n <td className=\"p-3\">{assetNameById.get(m.assetId) ?? m.assetId}</td>\n <td className=\"p-3 capitalize\">{m.type.replace(/_/g, ' ')}</td>\n <td className=\"p-3\">\n <StatusPill tone={maintenanceStatusTone(m.status)}>\n {m.status.replace(/_/g, ' ').toLowerCase()}\n </StatusPill>\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {m.completedAt ? new Date(m.completedAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-3 text-right tabular-nums\">{money(m.actualCost)}</td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {maintenanceLoading ? (\n <div className=\"space-y-3 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"h-16 animate-pulse rounded bg-bg-sunken\" />\n ))}\n </div>\n ) : (\n maintenancePage.pageItems.map((m) => (\n <div key={m.id} className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 flex-1 truncate font-medium\">{m.title}</p>\n <StatusPill tone={maintenanceStatusTone(m.status)}>\n {m.status.replace(/_/g, ' ').toLowerCase()}\n </StatusPill>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"capitalize\">{m.type.replace(/_/g, ' ')}</span>\n <span>· {assetNameById.get(m.assetId) ?? m.assetId}</span>\n {m.actualCost != null ? (\n <span className=\"ml-auto tabular-nums text-text-primary\">\n {money(m.actualCost)}\n </span>\n ) : null}\n </div>\n </div>\n ))\n )}\n </div>\n\n {maintenance.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No maintenance records\"\n description=\"Logged maintenance work will appear here once scheduled or completed.\"\n />\n ) : (\n <div className=\"px-3\">\n <Pagination\n page={maintenancePage.page}\n pageCount={maintenancePage.pageCount}\n rangeStart={maintenancePage.rangeStart}\n rangeEnd={maintenancePage.rangeEnd}\n totalItems={maintenancePage.totalItems}\n canPrev={maintenancePage.canPrev}\n canNext={maintenancePage.canNext}\n onPrev={maintenancePage.prevPage}\n onNext={maintenancePage.nextPage}\n itemLabel=\"records\"\n />\n </div>\n )}\n </GlassCard>\n </TabsContent>\n\n {/* ─── Utilization ─── */}\n <TabsContent value=\"utilization\" className=\"mt-4 space-y-4\">\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={exportUtilization} disabled={!utilization}>\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n <StatTile label=\"Total assets\" value={utilization?.totalAssets ?? 0} />\n <StatTile label=\"Assigned\" value={utilization?.assignedAssets ?? 0} />\n <StatTile label=\"Unassigned\" value={utilization?.unassignedAssets ?? 0} />\n <StatTile label=\"Active unassigned\" value={utilization?.idleAssets ?? 0} />\n <StatTile\n label=\"Overdue assignments\"\n value={utilization?.overdueAssignments ?? 0}\n delta={utilization && utilization.overdueAssignments > 0 ? 'Attention' : undefined}\n deltaTone=\"negative\"\n />\n </div>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Assets by status\" subtitle=\"Distribution across lifecycle states\">\n <DonutChart\n data={statusSlices}\n centerLabel=\"Assets\"\n centerValue={utilization?.totalAssets ?? 0}\n height={240}\n emptyLabel=\"No status data available.\"\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n <ChartCard\n title=\"Assignment coverage\"\n subtitle=\"Mutually exclusive assigned, active-unassigned and other-unassigned assets\"\n >\n <DonutChart\n data={coverageSlices}\n centerLabel=\"Assets\"\n centerValue={utilization?.totalAssets ?? 0}\n height={240}\n emptyLabel=\"No coverage data available.\"\n ariaLabel=\"Assignment coverage\"\n />\n </ChartCard>\n </div>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -2,6 +2,18 @@ export interface AssignmentCoverageSlice {
2
2
  name: 'Assigned' | 'Active unassigned' | 'Other unassigned';
3
3
  value: number;
4
4
  }
5
+ interface UtilizationCsvReport {
6
+ totalAssets: number;
7
+ assignedAssets: number;
8
+ unassignedAssets: number;
9
+ idleAssets: number;
10
+ overdueAssignments: number;
11
+ byStatus: ReadonlyArray<{
12
+ status: string;
13
+ count: number;
14
+ }>;
15
+ }
16
+ export declare function buildUtilizationCsvRows(report: UtilizationCsvReport): Array<[string, number]>;
5
17
  /**
6
18
  * Build mutually exclusive assignment-coverage slices.
7
19
  *
@@ -9,4 +21,5 @@ export interface AssignmentCoverageSlice {
9
21
  * so it is a subset of unassigned assets rather than a third coverage state.
10
22
  */
11
23
  export declare function buildAssignmentCoverageSlices(assignedAssets: number, unassignedAssets: number, idleAssets: number): AssignmentCoverageSlice[];
24
+ export {};
12
25
  //# sourceMappingURL=utilization.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilization.d.ts","sourceRoot":"","sources":["../../../src/assethandler/utils/utilization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,uBAAuB,EAAE,CAa3B"}
1
+ {"version":3,"file":"utilization.d.ts","sourceRoot":"","sources":["../../../src/assethandler/utils/utilization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAS7F;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,uBAAuB,EAAE,CAa3B"}
@@ -1,5 +1,15 @@
1
1
  //#region src/assethandler/utils/utilization.ts
2
- function e(e, t, n) {
2
+ function e(e) {
3
+ return [
4
+ ["Total assets", e.totalAssets],
5
+ ["Assigned assets", e.assignedAssets],
6
+ ["Unassigned assets", e.unassignedAssets],
7
+ ["Active unassigned assets", e.idleAssets],
8
+ ["Overdue assignments", e.overdueAssignments],
9
+ ...e.byStatus.map((e) => [`Status: ${e.status}`, e.count])
10
+ ];
11
+ }
12
+ function t(e, t, n) {
3
13
  let r = Math.max(0, e), i = Math.max(0, t), a = Math.min(i, Math.max(0, n)), o = i - a;
4
14
  return [
5
15
  {
@@ -17,6 +27,6 @@ function e(e, t, n) {
17
27
  ].filter((e) => e.value > 0);
18
28
  }
19
29
  //#endregion
20
- export { e as buildAssignmentCoverageSlices };
30
+ export { t as buildAssignmentCoverageSlices, e as buildUtilizationCsvRows };
21
31
 
22
32
  //# sourceMappingURL=utilization.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilization.js","names":[],"sources":["../../../src/assethandler/utils/utilization.ts"],"sourcesContent":["export interface AssignmentCoverageSlice {\n name: 'Assigned' | 'Active unassigned' | 'Other unassigned';\n value: number;\n}\n\n/**\n * Build mutually exclusive assignment-coverage slices.\n *\n * The backend's idle metric means an ACTIVE asset without a current holder,\n * so it is a subset of unassigned assets rather than a third coverage state.\n */\nexport function buildAssignmentCoverageSlices(\n assignedAssets: number,\n unassignedAssets: number,\n idleAssets: number\n): AssignmentCoverageSlice[] {\n const assigned = Math.max(0, assignedAssets);\n const unassigned = Math.max(0, unassignedAssets);\n const activeUnassigned = Math.min(unassigned, Math.max(0, idleAssets));\n const otherUnassigned = unassigned - activeUnassigned;\n\n const slices: AssignmentCoverageSlice[] = [\n { name: 'Assigned', value: assigned },\n { name: 'Active unassigned', value: activeUnassigned },\n { name: 'Other unassigned', value: otherUnassigned },\n ];\n\n return slices.filter((slice) => slice.value > 0);\n}\n"],"mappings":";AAWA,SAAgB,EACd,GACA,GACA,GAC2B;CAC3B,IAAM,IAAW,KAAK,IAAI,GAAG,CAAc,GACrC,IAAa,KAAK,IAAI,GAAG,CAAgB,GACzC,IAAmB,KAAK,IAAI,GAAY,KAAK,IAAI,GAAG,CAAU,CAAC,GAC/D,IAAkB,IAAa;CAQrC,OAAO;EALL;GAAE,MAAM;GAAY,OAAO;EAAS;EACpC;GAAE,MAAM;GAAqB,OAAO;EAAiB;EACrD;GAAE,MAAM;GAAoB,OAAO;EAAgB;CAG9C,EAAO,QAAQ,MAAU,EAAM,QAAQ,CAAC;AACjD"}
1
+ {"version":3,"file":"utilization.js","names":[],"sources":["../../../src/assethandler/utils/utilization.ts"],"sourcesContent":["export interface AssignmentCoverageSlice {\n name: 'Assigned' | 'Active unassigned' | 'Other unassigned';\n value: number;\n}\n\ninterface UtilizationCsvReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: ReadonlyArray<{ status: string; count: number }>;\n}\n\nexport function buildUtilizationCsvRows(report: UtilizationCsvReport): Array<[string, number]> {\n return [\n ['Total assets', report.totalAssets],\n ['Assigned assets', report.assignedAssets],\n ['Unassigned assets', report.unassignedAssets],\n ['Active unassigned assets', report.idleAssets],\n ['Overdue assignments', report.overdueAssignments],\n ...report.byStatus.map((entry): [string, number] => [`Status: ${entry.status}`, entry.count]),\n ];\n}\n\n/**\n * Build mutually exclusive assignment-coverage slices.\n *\n * The backend's idle metric means an ACTIVE asset without a current holder,\n * so it is a subset of unassigned assets rather than a third coverage state.\n */\nexport function buildAssignmentCoverageSlices(\n assignedAssets: number,\n unassignedAssets: number,\n idleAssets: number\n): AssignmentCoverageSlice[] {\n const assigned = Math.max(0, assignedAssets);\n const unassigned = Math.max(0, unassignedAssets);\n const activeUnassigned = Math.min(unassigned, Math.max(0, idleAssets));\n const otherUnassigned = unassigned - activeUnassigned;\n\n const slices: AssignmentCoverageSlice[] = [\n { name: 'Assigned', value: assigned },\n { name: 'Active unassigned', value: activeUnassigned },\n { name: 'Other unassigned', value: otherUnassigned },\n ];\n\n return slices.filter((slice) => slice.value > 0);\n}\n"],"mappings":";AAcA,SAAgB,EAAwB,GAAuD;CAC7F,OAAO;EACL,CAAC,gBAAgB,EAAO,WAAW;EACnC,CAAC,mBAAmB,EAAO,cAAc;EACzC,CAAC,qBAAqB,EAAO,gBAAgB;EAC7C,CAAC,4BAA4B,EAAO,UAAU;EAC9C,CAAC,uBAAuB,EAAO,kBAAkB;EACjD,GAAG,EAAO,SAAS,KAAK,MAA4B,CAAC,WAAW,EAAM,UAAU,EAAM,KAAK,CAAC;CAC9F;AACF;AAQA,SAAgB,EACd,GACA,GACA,GAC2B;CAC3B,IAAM,IAAW,KAAK,IAAI,GAAG,CAAc,GACrC,IAAa,KAAK,IAAI,GAAG,CAAgB,GACzC,IAAmB,KAAK,IAAI,GAAY,KAAK,IAAI,GAAG,CAAU,CAAC,GAC/D,IAAkB,IAAa;CAQrC,OAAO;EALL;GAAE,MAAM;GAAY,OAAO;EAAS;EACpC;GAAE,MAAM;GAAqB,OAAO;EAAiB;EACrD;GAAE,MAAM;GAAoB,OAAO;EAAgB;CAG9C,EAAO,QAAQ,MAAU,EAAM,QAAQ,CAAC;AACjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-assethandler",
3
- "version": "2026.826.10",
3
+ "version": "2026.826.12",
4
4
  "description": "Burdenoff AssetHandler Microfrontend",
5
5
  "type": "module",
6
6
  "files": [