@burdenoff/microfe-assethandler 2026.826.8 → 2026.826.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assethandler/pages/ReportsDashboardPage.d.ts.map +1 -1
- package/dist/assethandler/pages/ReportsDashboardPage.js +190 -202
- package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
- package/dist/assethandler/utils/utilization.d.ts +12 -0
- package/dist/assethandler/utils/utilization.d.ts.map +1 -0
- package/dist/assethandler/utils/utilization.js +22 -0
- package/dist/assethandler/utils/utilization.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportsDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReportsDashboardPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/ReportsDashboardPage.tsx"],"names":[],"mappings":"AAiNA,wBAAgB,oBAAoB,gCA8pBnC"}
|
|
@@ -8,14 +8,15 @@ 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 {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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";
|
|
14
|
+
import { gql as _ } from "@apollo/client";
|
|
15
|
+
import { useQuery as ae } from "@apollo/client/react";
|
|
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
18
|
//#region src/assethandler/pages/ReportsDashboardPage.tsx
|
|
18
|
-
var
|
|
19
|
+
var fe = _`
|
|
19
20
|
query AssetHandlerReports($registerFilter: AssetRegisterFilterInput) {
|
|
20
21
|
assetRegister(filter: $registerFilter, pagination: { limit: 1000 }) {
|
|
21
22
|
id
|
|
@@ -64,7 +65,7 @@ var de = h`
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
|
-
`,
|
|
68
|
+
`, pe = [
|
|
68
69
|
"ACTIVE",
|
|
69
70
|
"INACTIVE",
|
|
70
71
|
"MAINTENANCE",
|
|
@@ -78,7 +79,7 @@ var de = h`
|
|
|
78
79
|
neutral: "bg-bg-sunken text-text-secondary"
|
|
79
80
|
};
|
|
80
81
|
function A({ tone: e, children: t }) {
|
|
81
|
-
return /* @__PURE__ */
|
|
82
|
+
return /* @__PURE__ */ y("span", {
|
|
82
83
|
className: n("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize", k[e]),
|
|
83
84
|
children: t
|
|
84
85
|
});
|
|
@@ -91,16 +92,16 @@ var j = (e) => {
|
|
|
91
92
|
return t === "completed" ? "success" : t === "in_progress" || t === "scheduled" ? "info" : t === "overdue" || t === "cancelled" ? "error" : "neutral";
|
|
92
93
|
};
|
|
93
94
|
function N({ rows: e = 6, cols: t }) {
|
|
94
|
-
return /* @__PURE__ */
|
|
95
|
+
return /* @__PURE__ */ y(v, { children: Array.from({ length: e }).map((e, n) => /* @__PURE__ */ y("tr", {
|
|
95
96
|
className: "border-b border-border-seam/60",
|
|
96
|
-
children: Array.from({ length: t }).map((e, t) => /* @__PURE__ */
|
|
97
|
+
children: Array.from({ length: t }).map((e, t) => /* @__PURE__ */ y("td", {
|
|
97
98
|
className: "p-3",
|
|
98
|
-
children: /* @__PURE__ */
|
|
99
|
+
children: /* @__PURE__ */ y("div", { className: "h-4 w-full max-w-[120px] animate-pulse rounded bg-bg-sunken" })
|
|
99
100
|
}, t))
|
|
100
101
|
}, n)) });
|
|
101
102
|
}
|
|
102
103
|
function P() {
|
|
103
|
-
let { format: n, compact:
|
|
104
|
+
let { format: n, compact: _ } = t(), { assets: k } = e(), [P, F] = te("all"), { data: I, loading: L } = ae(fe, {
|
|
104
105
|
variables: { registerFilter: P === "all" ? null : { status: P } },
|
|
105
106
|
fetchPolicy: "cache-and-network",
|
|
106
107
|
errorPolicy: "all"
|
|
@@ -111,7 +112,7 @@ function P() {
|
|
|
111
112
|
}, [k]), U = o(R, {
|
|
112
113
|
pageSize: 12,
|
|
113
114
|
resetKey: P
|
|
114
|
-
}), W = o(z, { pageSize: 12 }), G = B?.totalAssetValue ?? 0, K = B?.totalDepreciation ?? 0,
|
|
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(() => {
|
|
115
116
|
let e = /* @__PURE__ */ new Map();
|
|
116
117
|
for (let t of R) {
|
|
117
118
|
let n = t.currentValue ?? t.purchaseValue ?? 0;
|
|
@@ -124,35 +125,22 @@ function P() {
|
|
|
124
125
|
category: e,
|
|
125
126
|
value: t
|
|
126
127
|
})).sort((e, t) => t.value - e.value).slice(0, 12);
|
|
127
|
-
}, [R]),
|
|
128
|
+
}, [R]), J = u(() => (V?.byStatus ?? []).map((e) => ({
|
|
128
129
|
name: e.status.toLowerCase(),
|
|
129
130
|
value: e.count
|
|
130
|
-
})).filter((e) => e.value > 0), [V]), he = u(() => V ? [
|
|
131
|
-
{
|
|
132
|
-
name: "Assigned",
|
|
133
|
-
value: V.assignedAssets
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: "Unassigned",
|
|
137
|
-
value: V.unassignedAssets
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: "Idle",
|
|
141
|
-
value: V.idleAssets
|
|
142
|
-
}
|
|
143
|
-
].filter((e) => e.value > 0) : [], [V]), J = u(() => [{
|
|
131
|
+
})).filter((e) => e.value > 0), [V]), he = u(() => V ? ee(V.assignedAssets, V.unassignedAssets, V.idleAssets) : [], [V]), Y = u(() => [{
|
|
144
132
|
name: "Current value",
|
|
145
133
|
value: G
|
|
146
134
|
}, {
|
|
147
135
|
name: "Accumulated depreciation",
|
|
148
136
|
value: K
|
|
149
|
-
}].filter((e) => e.value > 0), [G, K]),
|
|
137
|
+
}].filter((e) => e.value > 0), [G, K]), X = u(() => B ? [{
|
|
150
138
|
metric: "Allocated",
|
|
151
139
|
value: B.budgetAllocated
|
|
152
140
|
}, {
|
|
153
141
|
metric: "Spent",
|
|
154
142
|
value: B.budgetSpent
|
|
155
|
-
}] : [], [B]),
|
|
143
|
+
}] : [], [B]), Z = u(() => {
|
|
156
144
|
let e = /* @__PURE__ */ new Map();
|
|
157
145
|
for (let t of z) {
|
|
158
146
|
let n = t.actualCost ?? t.estimatedCost ?? 0;
|
|
@@ -165,7 +153,7 @@ function P() {
|
|
|
165
153
|
type: e[0],
|
|
166
154
|
cost: e[1]
|
|
167
155
|
})).sort((e, t) => t.cost - e.cost);
|
|
168
|
-
}, [z]),
|
|
156
|
+
}, [z]), ge = () => {
|
|
169
157
|
l(`asset-register-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, [
|
|
170
158
|
"Asset Number",
|
|
171
159
|
"Name",
|
|
@@ -189,7 +177,7 @@ function P() {
|
|
|
189
177
|
e.totalCostOfOwnership ?? "",
|
|
190
178
|
e.purchaseDate ? new Date(e.purchaseDate).toISOString().slice(0, 10) : ""
|
|
191
179
|
]));
|
|
192
|
-
},
|
|
180
|
+
}, _e = () => {
|
|
193
181
|
l(`maintenance-log-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, [
|
|
194
182
|
"Title",
|
|
195
183
|
"Asset",
|
|
@@ -211,7 +199,7 @@ function P() {
|
|
|
211
199
|
e.estimatedCost ?? "",
|
|
212
200
|
e.actualCost ?? ""
|
|
213
201
|
]));
|
|
214
|
-
},
|
|
202
|
+
}, ve = () => {
|
|
215
203
|
B && l(`financial-summary-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], [
|
|
216
204
|
["Total asset value", B.totalAssetValue],
|
|
217
205
|
["Total depreciation", B.totalDepreciation],
|
|
@@ -222,7 +210,7 @@ function P() {
|
|
|
222
210
|
["Invoices outstanding (count)", B.invoicesOutstandingCount],
|
|
223
211
|
["Transaction expense", B.transactionExpense]
|
|
224
212
|
]);
|
|
225
|
-
},
|
|
213
|
+
}, ye = () => {
|
|
226
214
|
V && l(`utilization-report-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ["Metric", "Value"], [
|
|
227
215
|
["Total assets", V.totalAssets],
|
|
228
216
|
["Assigned assets", V.assignedAssets],
|
|
@@ -231,85 +219,85 @@ function P() {
|
|
|
231
219
|
["Overdue assignments", V.overdueAssignments],
|
|
232
220
|
...V.byStatus.map((e) => [`Status: ${e.status}`, e.count])
|
|
233
221
|
]);
|
|
234
|
-
}, Q = L && R.length === 0, $ = L && z.length === 0,
|
|
222
|
+
}, Q = L && R.length === 0, $ = L && z.length === 0, be = /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ b(x, {
|
|
235
223
|
size: "sm",
|
|
236
|
-
onClick:
|
|
224
|
+
onClick: ge,
|
|
237
225
|
disabled: R.length === 0,
|
|
238
|
-
children: [/* @__PURE__ */
|
|
239
|
-
}), /* @__PURE__ */
|
|
226
|
+
children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
|
|
227
|
+
}), /* @__PURE__ */ b(x, {
|
|
240
228
|
size: "sm",
|
|
241
229
|
variant: "outline",
|
|
242
230
|
onClick: () => window.print(),
|
|
243
|
-
children: [/* @__PURE__ */
|
|
231
|
+
children: [/* @__PURE__ */ y(p, { className: "mr-2 h-4 w-4" }), "Print"]
|
|
244
232
|
})] });
|
|
245
|
-
return /* @__PURE__ */
|
|
233
|
+
return /* @__PURE__ */ b("div", {
|
|
246
234
|
className: "space-y-6 p-4 sm:p-6",
|
|
247
235
|
children: [
|
|
248
|
-
/* @__PURE__ */
|
|
236
|
+
/* @__PURE__ */ b("div", {
|
|
249
237
|
className: "flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between",
|
|
250
|
-
children: [/* @__PURE__ */
|
|
238
|
+
children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
|
|
251
239
|
className: "text-2xl font-bold sm:text-3xl",
|
|
252
|
-
children: /* @__PURE__ */
|
|
253
|
-
}), /* @__PURE__ */
|
|
240
|
+
children: /* @__PURE__ */ y(C, { children: "Reports" })
|
|
241
|
+
}), /* @__PURE__ */ y("p", {
|
|
254
242
|
className: "mt-1 max-w-xl text-sm text-text-secondary",
|
|
255
243
|
children: "Registers and summaries built from live asset, finance and maintenance data."
|
|
256
|
-
})] }), /* @__PURE__ */
|
|
244
|
+
})] }), /* @__PURE__ */ b("div", {
|
|
257
245
|
className: "grid grid-cols-1 gap-3 sm:grid-cols-3",
|
|
258
246
|
children: [
|
|
259
|
-
/* @__PURE__ */
|
|
247
|
+
/* @__PURE__ */ y(s, {
|
|
260
248
|
label: "Total asset value",
|
|
261
249
|
value: G,
|
|
262
250
|
format: (e) => n(e),
|
|
263
|
-
icon:
|
|
251
|
+
icon: re,
|
|
264
252
|
hero: !0
|
|
265
253
|
}),
|
|
266
|
-
/* @__PURE__ */
|
|
254
|
+
/* @__PURE__ */ y(s, {
|
|
267
255
|
label: "Total depreciation",
|
|
268
256
|
value: K,
|
|
269
257
|
format: (e) => n(e),
|
|
270
258
|
icon: m
|
|
271
259
|
}),
|
|
272
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ y(s, {
|
|
273
261
|
label: "Depreciated",
|
|
274
|
-
value:
|
|
262
|
+
value: me,
|
|
275
263
|
format: (e) => `${e.toFixed(2)}%`,
|
|
276
264
|
icon: f
|
|
277
265
|
})
|
|
278
266
|
]
|
|
279
267
|
})]
|
|
280
268
|
}),
|
|
281
|
-
/* @__PURE__ */
|
|
282
|
-
/* @__PURE__ */
|
|
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, {
|
|
283
271
|
defaultValue: "register",
|
|
284
272
|
children: [
|
|
285
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ b(de, {
|
|
286
274
|
className: "max-w-full justify-start overflow-x-auto",
|
|
287
275
|
children: [
|
|
288
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ b(O, {
|
|
289
277
|
value: "register",
|
|
290
|
-
children: [/* @__PURE__ */
|
|
278
|
+
children: [/* @__PURE__ */ y(ie, { className: "mr-1.5 h-4 w-4" }), "Asset Register"]
|
|
291
279
|
}),
|
|
292
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ b(O, {
|
|
293
281
|
value: "financial",
|
|
294
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ y(h, { className: "mr-1.5 h-4 w-4" }), "Financial Summary"]
|
|
295
283
|
}),
|
|
296
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ b(O, {
|
|
297
285
|
value: "maintenance",
|
|
298
|
-
children: [/* @__PURE__ */
|
|
286
|
+
children: [/* @__PURE__ */ y(g, { className: "mr-1.5 h-4 w-4" }), "Maintenance Log"]
|
|
299
287
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ b(O, {
|
|
301
289
|
value: "utilization",
|
|
302
|
-
children: [/* @__PURE__ */
|
|
290
|
+
children: [/* @__PURE__ */ y(ne, { className: "mr-1.5 h-4 w-4" }), "Utilization"]
|
|
303
291
|
})
|
|
304
292
|
]
|
|
305
293
|
}),
|
|
306
|
-
/* @__PURE__ */
|
|
294
|
+
/* @__PURE__ */ b(D, {
|
|
307
295
|
value: "register",
|
|
308
296
|
className: "mt-4 space-y-4",
|
|
309
|
-
children: [q.length > 0 ? /* @__PURE__ */
|
|
297
|
+
children: [q.length > 0 ? /* @__PURE__ */ y(c, {
|
|
310
298
|
title: "Asset value by category",
|
|
311
299
|
subtitle: "Current book value aggregated from the register",
|
|
312
|
-
children: /* @__PURE__ */
|
|
300
|
+
children: /* @__PURE__ */ y(r, {
|
|
313
301
|
data: q,
|
|
314
302
|
xKey: "category",
|
|
315
303
|
series: [{
|
|
@@ -318,122 +306,122 @@ function P() {
|
|
|
318
306
|
}],
|
|
319
307
|
orientation: "horizontal",
|
|
320
308
|
height: Math.max(200, q.length * 34),
|
|
321
|
-
valueFormat:
|
|
309
|
+
valueFormat: _,
|
|
322
310
|
categoryWidth: 128,
|
|
323
311
|
ariaLabel: "Current asset value by category"
|
|
324
312
|
})
|
|
325
|
-
}) : null, /* @__PURE__ */
|
|
313
|
+
}) : null, /* @__PURE__ */ b(S, {
|
|
326
314
|
treatment: "glass",
|
|
327
315
|
className: "overflow-hidden border-border-seam",
|
|
328
316
|
children: [
|
|
329
|
-
/* @__PURE__ */
|
|
317
|
+
/* @__PURE__ */ b("div", {
|
|
330
318
|
className: "flex flex-col gap-3 border-b border-border-seam p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
331
|
-
children: [/* @__PURE__ */
|
|
319
|
+
children: [/* @__PURE__ */ b("div", {
|
|
332
320
|
className: "flex items-center gap-3",
|
|
333
|
-
children: [/* @__PURE__ */
|
|
321
|
+
children: [/* @__PURE__ */ y("h3", {
|
|
334
322
|
className: "text-base font-semibold text-text-primary",
|
|
335
323
|
children: "Asset Register"
|
|
336
|
-
}), /* @__PURE__ */
|
|
324
|
+
}), /* @__PURE__ */ b(se, {
|
|
337
325
|
value: P,
|
|
338
326
|
onValueChange: F,
|
|
339
|
-
children: [/* @__PURE__ */
|
|
327
|
+
children: [/* @__PURE__ */ y(E, {
|
|
340
328
|
className: "h-8 w-40",
|
|
341
|
-
children: /* @__PURE__ */
|
|
342
|
-
}), /* @__PURE__ */
|
|
329
|
+
children: /* @__PURE__ */ y(le, { placeholder: "Filter by status" })
|
|
330
|
+
}), /* @__PURE__ */ b(ce, { children: [/* @__PURE__ */ y(T, {
|
|
343
331
|
value: "all",
|
|
344
332
|
children: "All statuses"
|
|
345
|
-
}),
|
|
333
|
+
}), pe.map((e) => /* @__PURE__ */ y(T, {
|
|
346
334
|
value: e,
|
|
347
335
|
className: "capitalize",
|
|
348
336
|
children: e.toLowerCase()
|
|
349
337
|
}, e))] })]
|
|
350
338
|
})]
|
|
351
|
-
}), /* @__PURE__ */
|
|
339
|
+
}), /* @__PURE__ */ y("div", {
|
|
352
340
|
className: "flex flex-wrap gap-2",
|
|
353
|
-
children:
|
|
341
|
+
children: be
|
|
354
342
|
})]
|
|
355
343
|
}),
|
|
356
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ y("div", {
|
|
357
345
|
className: "hidden overflow-x-auto md:block",
|
|
358
|
-
children: /* @__PURE__ */
|
|
346
|
+
children: /* @__PURE__ */ b("table", {
|
|
359
347
|
className: "w-full min-w-[860px] text-sm",
|
|
360
|
-
children: [/* @__PURE__ */
|
|
348
|
+
children: [/* @__PURE__ */ y("thead", { children: /* @__PURE__ */ b("tr", {
|
|
361
349
|
className: "border-b border-border-seam bg-bg-sunken/50 text-left",
|
|
362
350
|
children: [
|
|
363
|
-
/* @__PURE__ */
|
|
351
|
+
/* @__PURE__ */ y("th", {
|
|
364
352
|
className: "p-3 font-medium",
|
|
365
353
|
children: "Asset"
|
|
366
354
|
}),
|
|
367
|
-
/* @__PURE__ */
|
|
355
|
+
/* @__PURE__ */ y("th", {
|
|
368
356
|
className: "p-3 font-medium",
|
|
369
357
|
children: "Category"
|
|
370
358
|
}),
|
|
371
|
-
/* @__PURE__ */
|
|
359
|
+
/* @__PURE__ */ y("th", {
|
|
372
360
|
className: "p-3 font-medium",
|
|
373
361
|
children: "Status"
|
|
374
362
|
}),
|
|
375
|
-
/* @__PURE__ */
|
|
363
|
+
/* @__PURE__ */ y("th", {
|
|
376
364
|
className: "p-3 font-medium",
|
|
377
365
|
children: "Department"
|
|
378
366
|
}),
|
|
379
|
-
/* @__PURE__ */
|
|
367
|
+
/* @__PURE__ */ y("th", {
|
|
380
368
|
className: "p-3 text-right font-medium",
|
|
381
369
|
children: "Purchase"
|
|
382
370
|
}),
|
|
383
|
-
/* @__PURE__ */
|
|
371
|
+
/* @__PURE__ */ y("th", {
|
|
384
372
|
className: "p-3 text-right font-medium",
|
|
385
373
|
children: "Current"
|
|
386
374
|
}),
|
|
387
|
-
/* @__PURE__ */
|
|
375
|
+
/* @__PURE__ */ y("th", {
|
|
388
376
|
className: "p-3 text-right font-medium",
|
|
389
377
|
children: "Depreciation"
|
|
390
378
|
}),
|
|
391
|
-
/* @__PURE__ */
|
|
379
|
+
/* @__PURE__ */ y("th", {
|
|
392
380
|
className: "p-3 text-right font-medium",
|
|
393
381
|
children: "TCO"
|
|
394
382
|
})
|
|
395
383
|
]
|
|
396
|
-
}) }), /* @__PURE__ */
|
|
384
|
+
}) }), /* @__PURE__ */ y("tbody", { children: Q ? /* @__PURE__ */ y(N, { cols: 8 }) : U.pageItems.map((e) => /* @__PURE__ */ b("tr", {
|
|
397
385
|
className: "border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40",
|
|
398
386
|
children: [
|
|
399
|
-
/* @__PURE__ */
|
|
387
|
+
/* @__PURE__ */ b("td", {
|
|
400
388
|
className: "p-3",
|
|
401
|
-
children: [/* @__PURE__ */
|
|
389
|
+
children: [/* @__PURE__ */ y("p", {
|
|
402
390
|
className: "font-medium",
|
|
403
391
|
children: e.name
|
|
404
|
-
}), /* @__PURE__ */
|
|
392
|
+
}), /* @__PURE__ */ y("p", {
|
|
405
393
|
className: "text-xs text-text-secondary",
|
|
406
394
|
children: e.assetNumber
|
|
407
395
|
})]
|
|
408
396
|
}),
|
|
409
|
-
/* @__PURE__ */
|
|
397
|
+
/* @__PURE__ */ y("td", {
|
|
410
398
|
className: "p-3 capitalize",
|
|
411
399
|
children: e.category.replace(/_/g, " ")
|
|
412
400
|
}),
|
|
413
|
-
/* @__PURE__ */
|
|
401
|
+
/* @__PURE__ */ y("td", {
|
|
414
402
|
className: "p-3",
|
|
415
|
-
children: /* @__PURE__ */
|
|
403
|
+
children: /* @__PURE__ */ y(A, {
|
|
416
404
|
tone: j(e.status),
|
|
417
405
|
children: e.status.toLowerCase()
|
|
418
406
|
})
|
|
419
407
|
}),
|
|
420
|
-
/* @__PURE__ */
|
|
408
|
+
/* @__PURE__ */ y("td", {
|
|
421
409
|
className: "p-3",
|
|
422
410
|
children: e.department || "—"
|
|
423
411
|
}),
|
|
424
|
-
/* @__PURE__ */
|
|
412
|
+
/* @__PURE__ */ y("td", {
|
|
425
413
|
className: "p-3 text-right tabular-nums",
|
|
426
414
|
children: n(e.purchaseValue)
|
|
427
415
|
}),
|
|
428
|
-
/* @__PURE__ */
|
|
416
|
+
/* @__PURE__ */ y("td", {
|
|
429
417
|
className: "p-3 text-right tabular-nums",
|
|
430
418
|
children: n(e.currentValue)
|
|
431
419
|
}),
|
|
432
|
-
/* @__PURE__ */
|
|
420
|
+
/* @__PURE__ */ y("td", {
|
|
433
421
|
className: "p-3 text-right tabular-nums",
|
|
434
422
|
children: n(e.accumulatedDepreciation)
|
|
435
423
|
}),
|
|
436
|
-
/* @__PURE__ */
|
|
424
|
+
/* @__PURE__ */ y("td", {
|
|
437
425
|
className: "p-3 text-right tabular-nums",
|
|
438
426
|
children: n(e.totalCostOfOwnership)
|
|
439
427
|
})
|
|
@@ -441,43 +429,43 @@ function P() {
|
|
|
441
429
|
}, e.id)) })]
|
|
442
430
|
})
|
|
443
431
|
}),
|
|
444
|
-
/* @__PURE__ */
|
|
432
|
+
/* @__PURE__ */ y("div", {
|
|
445
433
|
className: "divide-y divide-border-seam/60 md:hidden",
|
|
446
|
-
children: Q ? /* @__PURE__ */
|
|
434
|
+
children: Q ? /* @__PURE__ */ y("div", {
|
|
447
435
|
className: "space-y-3 p-4",
|
|
448
|
-
children: Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */
|
|
449
|
-
}) : U.pageItems.map((e) => /* @__PURE__ */
|
|
436
|
+
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", {
|
|
450
438
|
className: "p-4",
|
|
451
|
-
children: [/* @__PURE__ */
|
|
439
|
+
children: [/* @__PURE__ */ b("div", {
|
|
452
440
|
className: "flex items-start justify-between gap-2",
|
|
453
|
-
children: [/* @__PURE__ */
|
|
441
|
+
children: [/* @__PURE__ */ b("div", {
|
|
454
442
|
className: "min-w-0",
|
|
455
|
-
children: [/* @__PURE__ */
|
|
443
|
+
children: [/* @__PURE__ */ y("p", {
|
|
456
444
|
className: "truncate font-medium",
|
|
457
445
|
children: e.name
|
|
458
|
-
}), /* @__PURE__ */
|
|
446
|
+
}), /* @__PURE__ */ y("p", {
|
|
459
447
|
className: "text-xs text-text-secondary",
|
|
460
448
|
children: e.assetNumber
|
|
461
449
|
})]
|
|
462
|
-
}), /* @__PURE__ */
|
|
450
|
+
}), /* @__PURE__ */ y(A, {
|
|
463
451
|
tone: j(e.status),
|
|
464
452
|
children: e.status.toLowerCase()
|
|
465
453
|
})]
|
|
466
|
-
}), /* @__PURE__ */
|
|
454
|
+
}), /* @__PURE__ */ b("div", {
|
|
467
455
|
className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs text-text-secondary",
|
|
468
456
|
children: [
|
|
469
|
-
/* @__PURE__ */
|
|
470
|
-
/* @__PURE__ */
|
|
457
|
+
/* @__PURE__ */ y("span", { children: "Category" }),
|
|
458
|
+
/* @__PURE__ */ y("span", {
|
|
471
459
|
className: "text-right capitalize text-text-primary",
|
|
472
460
|
children: e.category.replace(/_/g, " ")
|
|
473
461
|
}),
|
|
474
|
-
/* @__PURE__ */
|
|
475
|
-
/* @__PURE__ */
|
|
462
|
+
/* @__PURE__ */ y("span", { children: "Current value" }),
|
|
463
|
+
/* @__PURE__ */ y("span", {
|
|
476
464
|
className: "text-right tabular-nums text-text-primary",
|
|
477
465
|
children: n(e.currentValue)
|
|
478
466
|
}),
|
|
479
|
-
/* @__PURE__ */
|
|
480
|
-
/* @__PURE__ */
|
|
467
|
+
/* @__PURE__ */ y("span", { children: "Depreciation" }),
|
|
468
|
+
/* @__PURE__ */ y("span", {
|
|
481
469
|
className: "text-right tabular-nums text-text-primary",
|
|
482
470
|
children: n(e.accumulatedDepreciation)
|
|
483
471
|
})
|
|
@@ -485,13 +473,13 @@ function P() {
|
|
|
485
473
|
})]
|
|
486
474
|
}, e.id))
|
|
487
475
|
}),
|
|
488
|
-
R.length === 0 && !L ? /* @__PURE__ */
|
|
476
|
+
R.length === 0 && !L ? /* @__PURE__ */ y(w, {
|
|
489
477
|
illustration: "empty-data",
|
|
490
478
|
title: "No assets in the register",
|
|
491
479
|
description: "Register assets with financial details and they will appear here."
|
|
492
|
-
}) : /* @__PURE__ */
|
|
480
|
+
}) : /* @__PURE__ */ y("div", {
|
|
493
481
|
className: "px-3",
|
|
494
|
-
children: /* @__PURE__ */
|
|
482
|
+
children: /* @__PURE__ */ y(a, {
|
|
495
483
|
page: U.page,
|
|
496
484
|
pageCount: U.pageCount,
|
|
497
485
|
rangeStart: U.rangeStart,
|
|
@@ -507,70 +495,70 @@ function P() {
|
|
|
507
495
|
]
|
|
508
496
|
})]
|
|
509
497
|
}),
|
|
510
|
-
/* @__PURE__ */
|
|
498
|
+
/* @__PURE__ */ b(D, {
|
|
511
499
|
value: "financial",
|
|
512
500
|
className: "mt-4 space-y-4",
|
|
513
501
|
children: [
|
|
514
|
-
/* @__PURE__ */
|
|
502
|
+
/* @__PURE__ */ y("div", {
|
|
515
503
|
className: "flex justify-end",
|
|
516
|
-
children: /* @__PURE__ */
|
|
504
|
+
children: /* @__PURE__ */ b(x, {
|
|
517
505
|
size: "sm",
|
|
518
|
-
onClick:
|
|
506
|
+
onClick: ve,
|
|
519
507
|
disabled: !B,
|
|
520
|
-
children: [/* @__PURE__ */
|
|
508
|
+
children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
|
|
521
509
|
})
|
|
522
510
|
}),
|
|
523
|
-
/* @__PURE__ */
|
|
511
|
+
/* @__PURE__ */ b("div", {
|
|
524
512
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
525
513
|
children: [
|
|
526
|
-
/* @__PURE__ */
|
|
514
|
+
/* @__PURE__ */ y(s, {
|
|
527
515
|
label: "Total asset value",
|
|
528
516
|
value: G,
|
|
529
517
|
format: (e) => n(e)
|
|
530
518
|
}),
|
|
531
|
-
/* @__PURE__ */
|
|
519
|
+
/* @__PURE__ */ y(s, {
|
|
532
520
|
label: "Total depreciation",
|
|
533
521
|
value: K,
|
|
534
522
|
format: (e) => n(e)
|
|
535
523
|
}),
|
|
536
|
-
/* @__PURE__ */
|
|
524
|
+
/* @__PURE__ */ y(s, {
|
|
537
525
|
label: "Maintenance spend",
|
|
538
526
|
value: B?.totalMaintenanceSpend ?? 0,
|
|
539
527
|
format: (e) => n(e)
|
|
540
528
|
}),
|
|
541
|
-
/* @__PURE__ */
|
|
529
|
+
/* @__PURE__ */ y(s, {
|
|
542
530
|
label: "Transaction expense",
|
|
543
531
|
value: B?.transactionExpense ?? 0,
|
|
544
532
|
format: (e) => n(e)
|
|
545
533
|
}),
|
|
546
|
-
/* @__PURE__ */
|
|
534
|
+
/* @__PURE__ */ y(s, {
|
|
547
535
|
label: "Budget allocated",
|
|
548
536
|
value: B?.budgetAllocated ?? 0,
|
|
549
537
|
format: (e) => n(e)
|
|
550
538
|
}),
|
|
551
|
-
/* @__PURE__ */
|
|
539
|
+
/* @__PURE__ */ y(s, {
|
|
552
540
|
label: "Budget spent",
|
|
553
541
|
value: B?.budgetSpent ?? 0,
|
|
554
542
|
format: (e) => n(e)
|
|
555
543
|
}),
|
|
556
|
-
/* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ y(s, {
|
|
557
545
|
label: "Invoices outstanding",
|
|
558
546
|
value: B?.invoicesOutstanding ?? 0,
|
|
559
547
|
format: (e) => n(e)
|
|
560
548
|
}),
|
|
561
|
-
/* @__PURE__ */
|
|
549
|
+
/* @__PURE__ */ y(s, {
|
|
562
550
|
label: "Outstanding invoices",
|
|
563
551
|
value: B?.invoicesOutstandingCount ?? 0
|
|
564
552
|
})
|
|
565
553
|
]
|
|
566
554
|
}),
|
|
567
|
-
/* @__PURE__ */
|
|
555
|
+
/* @__PURE__ */ b("div", {
|
|
568
556
|
className: "grid gap-4 lg:grid-cols-2",
|
|
569
|
-
children: [
|
|
557
|
+
children: [X.length > 0 && (B?.budgetAllocated || B?.budgetSpent) ? /* @__PURE__ */ y(c, {
|
|
570
558
|
title: "Budget utilization",
|
|
571
559
|
subtitle: "Allocated against spend",
|
|
572
|
-
children: /* @__PURE__ */
|
|
573
|
-
data:
|
|
560
|
+
children: /* @__PURE__ */ y(r, {
|
|
561
|
+
data: X,
|
|
574
562
|
xKey: "metric",
|
|
575
563
|
series: [{
|
|
576
564
|
key: "value",
|
|
@@ -578,15 +566,15 @@ function P() {
|
|
|
578
566
|
}],
|
|
579
567
|
orientation: "horizontal",
|
|
580
568
|
height: 200,
|
|
581
|
-
valueFormat:
|
|
569
|
+
valueFormat: _,
|
|
582
570
|
categoryWidth: 96,
|
|
583
571
|
ariaLabel: "Budget allocated versus spent"
|
|
584
572
|
})
|
|
585
|
-
}) : null,
|
|
573
|
+
}) : null, Y.length > 0 ? /* @__PURE__ */ y(c, {
|
|
586
574
|
title: "Value vs depreciation",
|
|
587
575
|
subtitle: "Current book value against accumulated depreciation",
|
|
588
|
-
children: /* @__PURE__ */
|
|
589
|
-
data:
|
|
576
|
+
children: /* @__PURE__ */ y(i, {
|
|
577
|
+
data: Y,
|
|
590
578
|
centerLabel: "Original cost",
|
|
591
579
|
centerValue: G + K,
|
|
592
580
|
valueFormat: (e) => n(e),
|
|
@@ -597,99 +585,99 @@ function P() {
|
|
|
597
585
|
})
|
|
598
586
|
]
|
|
599
587
|
}),
|
|
600
|
-
/* @__PURE__ */
|
|
588
|
+
/* @__PURE__ */ b(D, {
|
|
601
589
|
value: "maintenance",
|
|
602
590
|
className: "mt-4 space-y-4",
|
|
603
591
|
children: [
|
|
604
|
-
/* @__PURE__ */
|
|
592
|
+
/* @__PURE__ */ y("div", {
|
|
605
593
|
className: "flex justify-end",
|
|
606
|
-
children: /* @__PURE__ */
|
|
594
|
+
children: /* @__PURE__ */ b(x, {
|
|
607
595
|
size: "sm",
|
|
608
|
-
onClick:
|
|
596
|
+
onClick: _e,
|
|
609
597
|
disabled: z.length === 0,
|
|
610
|
-
children: [/* @__PURE__ */
|
|
598
|
+
children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
|
|
611
599
|
})
|
|
612
600
|
}),
|
|
613
|
-
|
|
601
|
+
Z.length > 0 ? /* @__PURE__ */ y(c, {
|
|
614
602
|
title: "Maintenance spend by type",
|
|
615
603
|
subtitle: "Actual cost, falling back to estimates",
|
|
616
|
-
children: /* @__PURE__ */
|
|
617
|
-
data:
|
|
604
|
+
children: /* @__PURE__ */ y(r, {
|
|
605
|
+
data: Z,
|
|
618
606
|
xKey: "type",
|
|
619
607
|
series: [{
|
|
620
608
|
key: "cost",
|
|
621
609
|
label: "Spend"
|
|
622
610
|
}],
|
|
623
611
|
orientation: "horizontal",
|
|
624
|
-
height: Math.max(180,
|
|
625
|
-
valueFormat:
|
|
612
|
+
height: Math.max(180, Z.length * 34),
|
|
613
|
+
valueFormat: _,
|
|
626
614
|
categoryWidth: 128,
|
|
627
615
|
ariaLabel: "Maintenance spend by type"
|
|
628
616
|
})
|
|
629
617
|
}) : null,
|
|
630
|
-
/* @__PURE__ */
|
|
618
|
+
/* @__PURE__ */ b(S, {
|
|
631
619
|
treatment: "glass",
|
|
632
620
|
className: "overflow-hidden border-border-seam",
|
|
633
621
|
children: [
|
|
634
|
-
/* @__PURE__ */
|
|
622
|
+
/* @__PURE__ */ y("div", {
|
|
635
623
|
className: "hidden overflow-x-auto md:block",
|
|
636
|
-
children: /* @__PURE__ */
|
|
624
|
+
children: /* @__PURE__ */ b("table", {
|
|
637
625
|
className: "w-full min-w-[720px] text-sm",
|
|
638
|
-
children: [/* @__PURE__ */
|
|
626
|
+
children: [/* @__PURE__ */ y("thead", { children: /* @__PURE__ */ b("tr", {
|
|
639
627
|
className: "border-b border-border-seam bg-bg-sunken/50 text-left",
|
|
640
628
|
children: [
|
|
641
|
-
/* @__PURE__ */
|
|
629
|
+
/* @__PURE__ */ y("th", {
|
|
642
630
|
className: "p-3 font-medium",
|
|
643
631
|
children: "Work"
|
|
644
632
|
}),
|
|
645
|
-
/* @__PURE__ */
|
|
633
|
+
/* @__PURE__ */ y("th", {
|
|
646
634
|
className: "p-3 font-medium",
|
|
647
635
|
children: "Asset"
|
|
648
636
|
}),
|
|
649
|
-
/* @__PURE__ */
|
|
637
|
+
/* @__PURE__ */ y("th", {
|
|
650
638
|
className: "p-3 font-medium",
|
|
651
639
|
children: "Type"
|
|
652
640
|
}),
|
|
653
|
-
/* @__PURE__ */
|
|
641
|
+
/* @__PURE__ */ y("th", {
|
|
654
642
|
className: "p-3 font-medium",
|
|
655
643
|
children: "Status"
|
|
656
644
|
}),
|
|
657
|
-
/* @__PURE__ */
|
|
645
|
+
/* @__PURE__ */ y("th", {
|
|
658
646
|
className: "p-3 font-medium",
|
|
659
647
|
children: "Completed"
|
|
660
648
|
}),
|
|
661
|
-
/* @__PURE__ */
|
|
649
|
+
/* @__PURE__ */ y("th", {
|
|
662
650
|
className: "p-3 text-right font-medium",
|
|
663
651
|
children: "Actual Cost"
|
|
664
652
|
})
|
|
665
653
|
]
|
|
666
|
-
}) }), /* @__PURE__ */
|
|
654
|
+
}) }), /* @__PURE__ */ y("tbody", { children: $ ? /* @__PURE__ */ y(N, { cols: 6 }) : W.pageItems.map((e) => /* @__PURE__ */ b("tr", {
|
|
667
655
|
className: "border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40",
|
|
668
656
|
children: [
|
|
669
|
-
/* @__PURE__ */
|
|
657
|
+
/* @__PURE__ */ y("td", {
|
|
670
658
|
className: "p-3 font-medium",
|
|
671
659
|
children: e.title
|
|
672
660
|
}),
|
|
673
|
-
/* @__PURE__ */
|
|
661
|
+
/* @__PURE__ */ y("td", {
|
|
674
662
|
className: "p-3",
|
|
675
663
|
children: H.get(e.assetId) ?? e.assetId
|
|
676
664
|
}),
|
|
677
|
-
/* @__PURE__ */
|
|
665
|
+
/* @__PURE__ */ y("td", {
|
|
678
666
|
className: "p-3 capitalize",
|
|
679
667
|
children: e.type.replace(/_/g, " ")
|
|
680
668
|
}),
|
|
681
|
-
/* @__PURE__ */
|
|
669
|
+
/* @__PURE__ */ y("td", {
|
|
682
670
|
className: "p-3",
|
|
683
|
-
children: /* @__PURE__ */
|
|
671
|
+
children: /* @__PURE__ */ y(A, {
|
|
684
672
|
tone: M(e.status),
|
|
685
673
|
children: e.status.replace(/_/g, " ").toLowerCase()
|
|
686
674
|
})
|
|
687
675
|
}),
|
|
688
|
-
/* @__PURE__ */
|
|
676
|
+
/* @__PURE__ */ y("td", {
|
|
689
677
|
className: "p-3 text-xs text-text-secondary",
|
|
690
678
|
children: e.completedAt ? new Date(e.completedAt).toLocaleDateString() : "—"
|
|
691
679
|
}),
|
|
692
|
-
/* @__PURE__ */
|
|
680
|
+
/* @__PURE__ */ y("td", {
|
|
693
681
|
className: "p-3 text-right tabular-nums",
|
|
694
682
|
children: n(e.actualCost)
|
|
695
683
|
})
|
|
@@ -697,31 +685,31 @@ function P() {
|
|
|
697
685
|
}, e.id)) })]
|
|
698
686
|
})
|
|
699
687
|
}),
|
|
700
|
-
/* @__PURE__ */
|
|
688
|
+
/* @__PURE__ */ y("div", {
|
|
701
689
|
className: "divide-y divide-border-seam/60 md:hidden",
|
|
702
|
-
children: $ ? /* @__PURE__ */
|
|
690
|
+
children: $ ? /* @__PURE__ */ y("div", {
|
|
703
691
|
className: "space-y-3 p-4",
|
|
704
|
-
children: Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */
|
|
705
|
-
}) : W.pageItems.map((e) => /* @__PURE__ */
|
|
692
|
+
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", {
|
|
706
694
|
className: "p-4",
|
|
707
|
-
children: [/* @__PURE__ */
|
|
695
|
+
children: [/* @__PURE__ */ b("div", {
|
|
708
696
|
className: "flex items-start justify-between gap-2",
|
|
709
|
-
children: [/* @__PURE__ */
|
|
697
|
+
children: [/* @__PURE__ */ y("p", {
|
|
710
698
|
className: "min-w-0 flex-1 truncate font-medium",
|
|
711
699
|
children: e.title
|
|
712
|
-
}), /* @__PURE__ */
|
|
700
|
+
}), /* @__PURE__ */ y(A, {
|
|
713
701
|
tone: M(e.status),
|
|
714
702
|
children: e.status.replace(/_/g, " ").toLowerCase()
|
|
715
703
|
})]
|
|
716
|
-
}), /* @__PURE__ */
|
|
704
|
+
}), /* @__PURE__ */ b("div", {
|
|
717
705
|
className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary",
|
|
718
706
|
children: [
|
|
719
|
-
/* @__PURE__ */
|
|
707
|
+
/* @__PURE__ */ y("span", {
|
|
720
708
|
className: "capitalize",
|
|
721
709
|
children: e.type.replace(/_/g, " ")
|
|
722
710
|
}),
|
|
723
|
-
/* @__PURE__ */
|
|
724
|
-
e.actualCost == null ? null : /* @__PURE__ */
|
|
711
|
+
/* @__PURE__ */ b("span", { children: ["· ", H.get(e.assetId) ?? e.assetId] }),
|
|
712
|
+
e.actualCost == null ? null : /* @__PURE__ */ y("span", {
|
|
725
713
|
className: "ml-auto tabular-nums text-text-primary",
|
|
726
714
|
children: n(e.actualCost)
|
|
727
715
|
})
|
|
@@ -729,13 +717,13 @@ function P() {
|
|
|
729
717
|
})]
|
|
730
718
|
}, e.id))
|
|
731
719
|
}),
|
|
732
|
-
z.length === 0 && !L ? /* @__PURE__ */
|
|
720
|
+
z.length === 0 && !L ? /* @__PURE__ */ y(w, {
|
|
733
721
|
illustration: "empty-data",
|
|
734
722
|
title: "No maintenance records",
|
|
735
723
|
description: "Logged maintenance work will appear here once scheduled or completed."
|
|
736
|
-
}) : /* @__PURE__ */
|
|
724
|
+
}) : /* @__PURE__ */ y("div", {
|
|
737
725
|
className: "px-3",
|
|
738
|
-
children: /* @__PURE__ */
|
|
726
|
+
children: /* @__PURE__ */ y(a, {
|
|
739
727
|
page: W.page,
|
|
740
728
|
pageCount: W.pageCount,
|
|
741
729
|
rangeStart: W.rangeStart,
|
|
@@ -752,39 +740,39 @@ function P() {
|
|
|
752
740
|
})
|
|
753
741
|
]
|
|
754
742
|
}),
|
|
755
|
-
/* @__PURE__ */
|
|
743
|
+
/* @__PURE__ */ b(D, {
|
|
756
744
|
value: "utilization",
|
|
757
745
|
className: "mt-4 space-y-4",
|
|
758
746
|
children: [
|
|
759
|
-
/* @__PURE__ */
|
|
747
|
+
/* @__PURE__ */ y("div", {
|
|
760
748
|
className: "flex justify-end",
|
|
761
|
-
children: /* @__PURE__ */
|
|
749
|
+
children: /* @__PURE__ */ b(x, {
|
|
762
750
|
size: "sm",
|
|
763
|
-
onClick:
|
|
751
|
+
onClick: ye,
|
|
764
752
|
disabled: !V,
|
|
765
|
-
children: [/* @__PURE__ */
|
|
753
|
+
children: [/* @__PURE__ */ y(d, { className: "mr-2 h-4 w-4" }), "Export CSV"]
|
|
766
754
|
})
|
|
767
755
|
}),
|
|
768
|
-
/* @__PURE__ */
|
|
756
|
+
/* @__PURE__ */ b("div", {
|
|
769
757
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-5",
|
|
770
758
|
children: [
|
|
771
|
-
/* @__PURE__ */
|
|
759
|
+
/* @__PURE__ */ y(s, {
|
|
772
760
|
label: "Total assets",
|
|
773
761
|
value: V?.totalAssets ?? 0
|
|
774
762
|
}),
|
|
775
|
-
/* @__PURE__ */
|
|
763
|
+
/* @__PURE__ */ y(s, {
|
|
776
764
|
label: "Assigned",
|
|
777
765
|
value: V?.assignedAssets ?? 0
|
|
778
766
|
}),
|
|
779
|
-
/* @__PURE__ */
|
|
767
|
+
/* @__PURE__ */ y(s, {
|
|
780
768
|
label: "Unassigned",
|
|
781
769
|
value: V?.unassignedAssets ?? 0
|
|
782
770
|
}),
|
|
783
|
-
/* @__PURE__ */
|
|
784
|
-
label: "
|
|
771
|
+
/* @__PURE__ */ y(s, {
|
|
772
|
+
label: "Active unassigned",
|
|
785
773
|
value: V?.idleAssets ?? 0
|
|
786
774
|
}),
|
|
787
|
-
/* @__PURE__ */
|
|
775
|
+
/* @__PURE__ */ y(s, {
|
|
788
776
|
label: "Overdue assignments",
|
|
789
777
|
value: V?.overdueAssignments ?? 0,
|
|
790
778
|
delta: V && V.overdueAssignments > 0 ? "Attention" : void 0,
|
|
@@ -792,23 +780,23 @@ function P() {
|
|
|
792
780
|
})
|
|
793
781
|
]
|
|
794
782
|
}),
|
|
795
|
-
/* @__PURE__ */
|
|
783
|
+
/* @__PURE__ */ b("div", {
|
|
796
784
|
className: "grid gap-4 lg:grid-cols-2",
|
|
797
|
-
children: [/* @__PURE__ */
|
|
785
|
+
children: [/* @__PURE__ */ y(c, {
|
|
798
786
|
title: "Assets by status",
|
|
799
787
|
subtitle: "Distribution across lifecycle states",
|
|
800
|
-
children: /* @__PURE__ */
|
|
801
|
-
data:
|
|
788
|
+
children: /* @__PURE__ */ y(i, {
|
|
789
|
+
data: J,
|
|
802
790
|
centerLabel: "Assets",
|
|
803
791
|
centerValue: V?.totalAssets ?? 0,
|
|
804
792
|
height: 240,
|
|
805
793
|
emptyLabel: "No status data available.",
|
|
806
794
|
ariaLabel: "Assets by status"
|
|
807
795
|
})
|
|
808
|
-
}), /* @__PURE__ */
|
|
796
|
+
}), /* @__PURE__ */ y(c, {
|
|
809
797
|
title: "Assignment coverage",
|
|
810
|
-
subtitle: "
|
|
811
|
-
children: /* @__PURE__ */
|
|
798
|
+
subtitle: "Mutually exclusive assigned, active-unassigned and other-unassigned assets",
|
|
799
|
+
children: /* @__PURE__ */ y(i, {
|
|
812
800
|
data: he,
|
|
813
801
|
centerLabel: "Assets",
|
|
814
802
|
centerValue: V?.totalAssets ?? 0,
|
|
@@ -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';\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 [\n { name: 'Assigned', value: utilization.assignedAssets },\n { name: 'Unassigned', value: utilization.unassignedAssets },\n { name: 'Idle', value: utilization.idleAssets },\n ].filter((s) => s.value > 0);\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=\"Idle\" 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 title=\"Assignment coverage\" subtitle=\"Assigned, unassigned and idle assets\">\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 } 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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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface AssignmentCoverageSlice {
|
|
2
|
+
name: 'Assigned' | 'Active unassigned' | 'Other unassigned';
|
|
3
|
+
value: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Build mutually exclusive assignment-coverage slices.
|
|
7
|
+
*
|
|
8
|
+
* The backend's idle metric means an ACTIVE asset without a current holder,
|
|
9
|
+
* so it is a subset of unassigned assets rather than a third coverage state.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildAssignmentCoverageSlices(assignedAssets: number, unassignedAssets: number, idleAssets: number): AssignmentCoverageSlice[];
|
|
12
|
+
//# sourceMappingURL=utilization.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/assethandler/utils/utilization.ts
|
|
2
|
+
function e(e, t, n) {
|
|
3
|
+
let r = Math.max(0, e), i = Math.max(0, t), a = Math.min(i, Math.max(0, n)), o = i - a;
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
name: "Assigned",
|
|
7
|
+
value: r
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: "Active unassigned",
|
|
11
|
+
value: a
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "Other unassigned",
|
|
15
|
+
value: o
|
|
16
|
+
}
|
|
17
|
+
].filter((e) => e.value > 0);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { e as buildAssignmentCoverageSlices };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=utilization.js.map
|
|
@@ -0,0 +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"}
|