@burdenoff/microfe-assethandler 2026.713.2 → 2026.714.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/assethandler/components/AttachmentsPanel.d.ts.map +1 -1
  2. package/dist/assethandler/components/AttachmentsPanel.js.map +1 -1
  3. package/dist/assethandler/hooks/useCopy.d.ts +28 -0
  4. package/dist/assethandler/hooks/useCopy.d.ts.map +1 -0
  5. package/dist/assethandler/hooks/useCopy.js +33 -0
  6. package/dist/assethandler/hooks/useCopy.js.map +1 -0
  7. package/dist/assethandler/index.d.ts +1 -1
  8. package/dist/assethandler/index.d.ts.map +1 -1
  9. package/dist/assethandler/pages/AnalyticsPage.d.ts.map +1 -1
  10. package/dist/assethandler/pages/AnalyticsPage.js +259 -258
  11. package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
  12. package/dist/assethandler/pages/AssetsAssetCategoriesPage.d.ts.map +1 -1
  13. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +1 -2
  14. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  15. package/dist/assethandler/pages/AssetsAssetListPage.d.ts.map +1 -1
  16. package/dist/assethandler/pages/AssetsAssetListPage.js +47 -48
  17. package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
  18. package/dist/assethandler/pages/DashboardPage.d.ts.map +1 -1
  19. package/dist/assethandler/pages/DashboardPage.js +236 -236
  20. package/dist/assethandler/pages/DashboardPage.js.map +1 -1
  21. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.d.ts.map +1 -1
  22. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js +126 -125
  23. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
  24. package/dist/assethandler/pages/TransactionsTransactionListPage.js +127 -127
  25. package/dist/assethandler/pages/TransactionsTransactionListPage.js.map +1 -1
  26. package/dist/assethandler/store/assethandlerStore.d.ts.map +1 -1
  27. package/dist/assethandler/store/assethandlerStore.js.map +1 -1
  28. package/dist/assethandler/types/index.d.ts +20 -0
  29. package/dist/assethandler/types/index.d.ts.map +1 -1
  30. package/dist/types/assistant.d.ts.map +1 -1
  31. package/package.json +1 -1
@@ -1,19 +1,20 @@
1
1
  import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
- import { cn as t } from "../utils/cn.js";
3
- import { formatCompact as n } from "../components/charts/chartTheme.js";
4
- import { BarsChart as r } from "../components/charts/BarsChart.js";
5
- import { DonutChart as i } from "../components/charts/DonutChart.js";
6
- import { StatTile as a } from "../components/StatTile.js";
7
- import { ChartCard as o } from "../components/ChartCard.js";
2
+ import { useCopy as t } from "../hooks/useCopy.js";
3
+ import { cn as n } from "../utils/cn.js";
4
+ import { formatCompact as r } from "../components/charts/chartTheme.js";
5
+ import { BarsChart as i } from "../components/charts/BarsChart.js";
6
+ import { DonutChart as a } from "../components/charts/DonutChart.js";
7
+ import { StatTile as o } from "../components/StatTile.js";
8
+ import { ChartCard as s } from "../components/ChartCard.js";
8
9
  import "../components/index.js";
9
- import { useMemo as s } from "react";
10
- import { Activity as c, Building2 as l, CheckCircle2 as u, DollarSign as d, Layers as f, MapPin as p, Package as m, PieChart as h, TrendingDown as g, Wallet as _, Wrench as v } from "lucide-react";
11
- import { gql as y } from "@apollo/client";
12
- import { useQuery as b } from "@apollo/client/react";
13
- import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
14
- import { GradientText as w, IllustratedEmptyState as T, PagePurpose as E, Table as D, TableBody as O, TableCell as k, TableHead as A, TableHeader as j, TableRow as M } from "@burdenoff/fe-libs/ui";
10
+ import { useMemo as c } from "react";
11
+ import { Activity as l, Building2 as u, CheckCircle2 as d, DollarSign as f, Layers as p, MapPin as m, Package as h, PieChart as g, TrendingDown as _, Wallet as v, Wrench as y } from "lucide-react";
12
+ import { gql as b } from "@apollo/client";
13
+ import { useQuery as x } from "@apollo/client/react";
14
+ import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
15
+ import { GradientText as T, IllustratedEmptyState as E, PagePurpose as D, Table as O, TableBody as k, TableCell as A, TableHead as j, TableHeader as M, TableRow as N } from "@burdenoff/fe-libs/ui";
15
16
  //#region src/assethandler/pages/PortfolioPortfolioOverviewPage.tsx
16
- var N = y`
17
+ var P = b`
17
18
  query AssetHandlerFinancialSummaryOverview {
18
19
  financialSummary {
19
20
  totalAssetValue
@@ -26,36 +27,36 @@ var N = y`
26
27
  transactionExpense
27
28
  }
28
29
  }
29
- `, P = (e) => `$${Math.round(e).toLocaleString()}`, F = (e) => e === void 0 ? "—" : e;
30
- function I(e) {
30
+ `, F = (e) => `$${Math.round(e).toLocaleString()}`, I = (e) => e === void 0 ? "—" : e;
31
+ function L(e) {
31
32
  return e.replace(/_/g, " ").replace(/\b\w/g, (e) => e.toUpperCase());
32
33
  }
33
- function L(e, t) {
34
+ function R(e, t) {
34
35
  let n = e.reduce((e, n) => {
35
36
  let r = t(n);
36
37
  return e[r] = (e[r] || 0) + 1, e;
37
38
  }, {});
38
39
  return Object.entries(n).sort((e, t) => t[1] - e[1]);
39
40
  }
40
- var R = {
41
+ var z = {
41
42
  success: "bg-status-success-bg-subtle text-status-success-text",
42
43
  warning: "bg-status-warning-bg-subtle text-status-warning-text",
43
44
  error: "bg-status-error-bg-subtle text-status-error-text",
44
45
  info: "bg-status-info-bg-subtle text-status-info-text",
45
46
  neutral: "bg-bg-sunken text-text-secondary"
46
- }, z = (e) => e === "active" ? "success" : e === "maintenance" ? "warning" : e === "disposed" ? "error" : "neutral";
47
- function B({ tone: e, children: n }) {
48
- return /* @__PURE__ */ S("span", {
49
- className: t("inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap", R[e]),
50
- children: n
47
+ }, B = (e) => e === "active" ? "success" : e === "maintenance" ? "warning" : e === "disposed" ? "error" : "neutral";
48
+ function V({ tone: e, children: t }) {
49
+ return /* @__PURE__ */ C("span", {
50
+ className: n("inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap", z[e]),
51
+ children: t
51
52
  });
52
53
  }
53
- function V() {
54
- let { assets: t } = e(), { data: y } = b(N, {
54
+ function H() {
55
+ let n = t(), { assets: b } = e(), { data: z } = x(P, {
55
56
  fetchPolicy: "cache-and-network",
56
57
  errorPolicy: "all"
57
- }), R = y?.financialSummary, V = s(() => t.reduce((e, t) => e + (t.totalCostOfOwnership ?? 0), 0), [t]), H = s(() => {
58
- let e = t.reduce((e, t) => {
58
+ }), H = z?.financialSummary, U = c(() => b.reduce((e, t) => e + (t.totalCostOfOwnership ?? 0), 0), [b]), W = c(() => {
59
+ let e = b.reduce((e, t) => {
59
60
  let n = t.department?.trim() || "Unassigned", r = e[n] ?? {
60
61
  count: 0,
61
62
  value: 0
@@ -63,16 +64,16 @@ function V() {
63
64
  return r.count += 1, r.value += t.currentValue ?? 0, e[n] = r, e;
64
65
  }, {});
65
66
  return Object.entries(e).sort((e, t) => t[1].value - e[1].value);
66
- }, [t]), U = s(() => H.filter(([, { value: e }]) => e > 0).slice(0, 8).map(([e, { value: t }]) => ({
67
+ }, [b]), G = c(() => W.filter(([, { value: e }]) => e > 0).slice(0, 8).map(([e, { value: t }]) => ({
67
68
  department: e,
68
69
  value: Math.round(t)
69
- })), [H]), W = s(() => L(t, (e) => I(e.category)), [t]), G = s(() => L(t, (e) => e.status), [t]), K = s(() => L(t, (e) => e.condition), [t]), q = s(() => L(t, (e) => e.location.trim() || "Unlocated"), [t]), J = s(() => W.map(([e, t]) => ({
70
+ })), [W]), K = c(() => R(b, (e) => L(e.category)), [b]), q = c(() => R(b, (e) => e.status), [b]), J = c(() => R(b, (e) => e.condition), [b]), Y = c(() => R(b, (e) => e.location.trim() || "Unlocated"), [b]), X = c(() => K.map(([e, t]) => ({
70
71
  name: e,
71
72
  value: t
72
- })), [W]), Y = s(() => G.map(([e, t]) => ({
73
- status: I(e),
73
+ })), [K]), Z = c(() => q.map(([e, t]) => ({
74
+ status: L(e),
74
75
  count: t
75
- })), [G]), X = s(() => {
76
+ })), [q]), Q = c(() => {
76
77
  let e = [
77
78
  "excellent",
78
79
  "good",
@@ -80,113 +81,113 @@ function V() {
80
81
  "poor",
81
82
  "damaged"
82
83
  ];
83
- return [...K].sort((t, n) => e.indexOf(t[0]) - e.indexOf(n[0])).map(([e, t]) => ({
84
- condition: I(e),
84
+ return [...J].sort((t, n) => e.indexOf(t[0]) - e.indexOf(n[0])).map(([e, t]) => ({
85
+ condition: L(e),
85
86
  count: t
86
87
  }));
87
- }, [K]), Z = G.find(([e]) => e === "active")?.[1] ?? 0, Q = G.find(([e]) => e === "maintenance")?.[1] ?? 0, $ = q.filter(([e]) => e !== "Unlocated").length;
88
- return /* @__PURE__ */ C("div", {
88
+ }, [J]), $ = q.find(([e]) => e === "active")?.[1] ?? 0, ee = q.find(([e]) => e === "maintenance")?.[1] ?? 0, te = Y.filter(([e]) => e !== "Unlocated").length;
89
+ return /* @__PURE__ */ w("div", {
89
90
  className: "space-y-6 p-4 sm:p-6",
90
91
  children: [
91
- /* @__PURE__ */ C("div", { children: [
92
- /* @__PURE__ */ S("p", {
92
+ /* @__PURE__ */ w("div", { children: [
93
+ /* @__PURE__ */ C("p", {
93
94
  className: "text-xs font-semibold uppercase tracking-wide text-text-muted",
94
95
  children: "Portfolio Overview"
95
96
  }),
96
- /* @__PURE__ */ S("h1", {
97
+ /* @__PURE__ */ C("h1", {
97
98
  className: "mt-1 text-2xl font-bold sm:text-3xl",
98
- children: /* @__PURE__ */ S(w, { children: "Portfolio Overview" })
99
+ children: /* @__PURE__ */ C(T, { children: "Portfolio Overview" })
99
100
  }),
100
- /* @__PURE__ */ S("p", {
101
+ /* @__PURE__ */ C("p", {
101
102
  className: "mt-1 text-sm text-text-secondary",
102
103
  children: "Management-level rollup of the entire estate — how it is distributed and what it is worth."
103
104
  })
104
105
  ] }),
105
- /* @__PURE__ */ S(E, { children: "The big-picture view of everything you own: how many assets you have and how they break down by category, status, condition and location. Use it to inform replacement planning and audit." }),
106
- t.length === 0 ? /* @__PURE__ */ S(T, {
106
+ /* @__PURE__ */ C(D, { children: "The big-picture view of everything you own: how many assets you have and how they break down by category, status, condition and location. Use it to inform replacement planning and audit." }),
107
+ b.length === 0 ? /* @__PURE__ */ C(E, {
107
108
  illustration: "empty-data",
108
109
  title: "No assets in your portfolio yet",
109
110
  description: "Once you add assets, this overview will summarise them by category, status, condition and location."
110
- }) : /* @__PURE__ */ C(x, { children: [
111
- /* @__PURE__ */ C("div", {
111
+ }) : /* @__PURE__ */ w(S, { children: [
112
+ /* @__PURE__ */ w("div", {
112
113
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
113
114
  children: [
114
- /* @__PURE__ */ S(a, {
115
+ /* @__PURE__ */ C(o, {
115
116
  hero: !0,
116
117
  label: "Total Asset Value",
117
- value: F(R?.totalAssetValue),
118
- format: P,
119
- icon: d
118
+ value: I(H?.totalAssetValue),
119
+ format: F,
120
+ icon: f
120
121
  }),
121
- /* @__PURE__ */ S(a, {
122
+ /* @__PURE__ */ C(o, {
122
123
  label: "Total Depreciation",
123
- value: F(R?.totalDepreciation),
124
- format: P,
125
- icon: g
124
+ value: I(H?.totalDepreciation),
125
+ format: F,
126
+ icon: _
126
127
  }),
127
- /* @__PURE__ */ S(a, {
128
+ /* @__PURE__ */ C(o, {
128
129
  label: "Total Cost of Ownership",
129
- value: V,
130
- format: P,
131
- icon: _
130
+ value: U,
131
+ format: F,
132
+ icon: v
132
133
  }),
133
- /* @__PURE__ */ S(a, {
134
- label: "Total Assets",
135
- value: t.length,
136
- format: n,
137
- icon: m
134
+ /* @__PURE__ */ C(o, {
135
+ label: n("assethandler.portfolio.metrics.totalAssets", "Total {{assets}}"),
136
+ value: b.length,
137
+ format: r,
138
+ icon: h
138
139
  })
139
140
  ]
140
141
  }),
141
- /* @__PURE__ */ C("div", {
142
+ /* @__PURE__ */ w("div", {
142
143
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
143
144
  children: [
144
- /* @__PURE__ */ S(a, {
145
+ /* @__PURE__ */ C(o, {
145
146
  label: "Active",
146
- value: Z,
147
- icon: u,
148
- delta: `${t.length > 0 ? Math.round(Z / t.length * 100) : 0}% of estate`,
147
+ value: $,
148
+ icon: d,
149
+ delta: `${b.length > 0 ? Math.round($ / b.length * 100) : 0}% of estate`,
149
150
  deltaTone: "neutral"
150
151
  }),
151
- /* @__PURE__ */ S(a, {
152
+ /* @__PURE__ */ C(o, {
152
153
  label: "In Maintenance",
153
- value: Q,
154
- icon: v
154
+ value: ee,
155
+ icon: y
155
156
  }),
156
- /* @__PURE__ */ S(a, {
157
+ /* @__PURE__ */ C(o, {
157
158
  label: "Locations",
158
- value: $,
159
- icon: p
159
+ value: te,
160
+ icon: m
160
161
  }),
161
- /* @__PURE__ */ S(a, {
162
+ /* @__PURE__ */ C(o, {
162
163
  label: "Categories",
163
- value: W.length,
164
- icon: f
164
+ value: K.length,
165
+ icon: p
165
166
  })
166
167
  ]
167
168
  }),
168
- /* @__PURE__ */ C("div", {
169
+ /* @__PURE__ */ w("div", {
169
170
  className: "grid gap-6 lg:grid-cols-2",
170
- children: [/* @__PURE__ */ S(o, {
171
+ children: [/* @__PURE__ */ C(s, {
171
172
  title: "Assets by Category",
172
- icon: h,
173
- children: /* @__PURE__ */ S(i, {
174
- data: J,
175
- centerLabel: "Assets",
176
- centerValue: t.length,
173
+ icon: g,
174
+ children: /* @__PURE__ */ C(a, {
175
+ data: X,
176
+ centerLabel: n("assethandler.portfolio.donut.centerLabel", "{{assets}}"),
177
+ centerValue: b.length,
177
178
  height: 280,
178
179
  ariaLabel: "Assets by category"
179
180
  })
180
- }), /* @__PURE__ */ S(o, {
181
+ }), /* @__PURE__ */ C(s, {
181
182
  title: "Value by Department",
182
183
  subtitle: "Summed current book value",
183
- icon: l,
184
- children: U.length === 0 ? /* @__PURE__ */ S(T, {
184
+ icon: u,
185
+ children: G.length === 0 ? /* @__PURE__ */ C(E, {
185
186
  illustration: "empty-data",
186
187
  title: "No department values yet",
187
188
  description: "Assign departments and record current values to see this breakdown."
188
- }) : /* @__PURE__ */ S(r, {
189
- data: U,
189
+ }) : /* @__PURE__ */ C(i, {
190
+ data: G,
190
191
  xKey: "department",
191
192
  series: [{
192
193
  key: "value",
@@ -194,19 +195,19 @@ function V() {
194
195
  }],
195
196
  orientation: "horizontal",
196
197
  categoryWidth: 120,
197
- height: Math.max(220, U.length * 44),
198
- valueFormat: P,
198
+ height: Math.max(220, G.length * 44),
199
+ valueFormat: F,
199
200
  ariaLabel: "Current value by department"
200
201
  })
201
202
  })]
202
203
  }),
203
- /* @__PURE__ */ C("div", {
204
+ /* @__PURE__ */ w("div", {
204
205
  className: "grid gap-6 lg:grid-cols-2",
205
- children: [/* @__PURE__ */ S(o, {
206
+ children: [/* @__PURE__ */ C(s, {
206
207
  title: "Assets by Status",
207
- icon: c,
208
- children: /* @__PURE__ */ S(r, {
209
- data: Y,
208
+ icon: l,
209
+ children: /* @__PURE__ */ C(i, {
210
+ data: Z,
210
211
  xKey: "status",
211
212
  series: [{
212
213
  key: "count",
@@ -215,31 +216,31 @@ function V() {
215
216
  height: 260,
216
217
  ariaLabel: "Assets by status"
217
218
  })
218
- }), /* @__PURE__ */ S(o, {
219
+ }), /* @__PURE__ */ C(s, {
219
220
  title: "Assets by Location",
220
- icon: p,
221
- children: /* @__PURE__ */ S("div", {
221
+ icon: m,
222
+ children: /* @__PURE__ */ C("div", {
222
223
  className: "overflow-x-auto",
223
- children: /* @__PURE__ */ C(D, { children: [/* @__PURE__ */ S(j, { children: /* @__PURE__ */ C(M, { children: [/* @__PURE__ */ S(A, { children: "Location" }), /* @__PURE__ */ S(A, {
224
+ children: /* @__PURE__ */ w(O, { children: [/* @__PURE__ */ C(M, { children: /* @__PURE__ */ w(N, { children: [/* @__PURE__ */ C(j, { children: "Location" }), /* @__PURE__ */ C(j, {
224
225
  className: "text-right",
225
226
  children: "Assets"
226
- })] }) }), /* @__PURE__ */ S(O, { children: q.map(([e, t]) => /* @__PURE__ */ C(M, { children: [/* @__PURE__ */ S(k, {
227
+ })] }) }), /* @__PURE__ */ C(k, { children: Y.map(([e, t]) => /* @__PURE__ */ w(N, { children: [/* @__PURE__ */ C(A, {
227
228
  className: "font-medium",
228
229
  children: e
229
- }), /* @__PURE__ */ S(k, {
230
+ }), /* @__PURE__ */ C(A, {
230
231
  className: "text-right tabular-nums",
231
232
  children: t
232
233
  })] }, e)) })] })
233
234
  })
234
235
  })]
235
236
  }),
236
- /* @__PURE__ */ C("div", {
237
+ /* @__PURE__ */ w("div", {
237
238
  className: "grid gap-6 lg:grid-cols-2",
238
- children: [/* @__PURE__ */ S(o, {
239
+ children: [/* @__PURE__ */ C(s, {
239
240
  title: "Asset Condition Mix",
240
- icon: v,
241
- children: /* @__PURE__ */ S(r, {
242
- data: X,
241
+ icon: y,
242
+ children: /* @__PURE__ */ C(i, {
243
+ data: Q,
243
244
  xKey: "condition",
244
245
  series: [{
245
246
  key: "count",
@@ -248,50 +249,50 @@ function V() {
248
249
  height: 260,
249
250
  ariaLabel: "Assets by condition"
250
251
  })
251
- }), /* @__PURE__ */ S(o, {
252
+ }), /* @__PURE__ */ C(s, {
252
253
  title: "Department Breakdown",
253
254
  subtitle: "Assets and current value",
254
- icon: _,
255
- children: /* @__PURE__ */ S("div", {
255
+ icon: v,
256
+ children: /* @__PURE__ */ C("div", {
256
257
  className: "overflow-x-auto",
257
- children: /* @__PURE__ */ C(D, { children: [/* @__PURE__ */ S(j, { children: /* @__PURE__ */ C(M, { children: [
258
- /* @__PURE__ */ S(A, { children: "Department" }),
259
- /* @__PURE__ */ S(A, {
258
+ children: /* @__PURE__ */ w(O, { children: [/* @__PURE__ */ C(M, { children: /* @__PURE__ */ w(N, { children: [
259
+ /* @__PURE__ */ C(j, { children: "Department" }),
260
+ /* @__PURE__ */ C(j, {
260
261
  className: "text-right",
261
262
  children: "Assets"
262
263
  }),
263
- /* @__PURE__ */ S(A, {
264
+ /* @__PURE__ */ C(j, {
264
265
  className: "text-right",
265
266
  children: "Current Value"
266
267
  })
267
- ] }) }), /* @__PURE__ */ S(O, { children: H.map(([e, { count: t, value: n }]) => /* @__PURE__ */ C(M, { children: [
268
- /* @__PURE__ */ S(k, {
268
+ ] }) }), /* @__PURE__ */ C(k, { children: W.map(([e, { count: t, value: n }]) => /* @__PURE__ */ w(N, { children: [
269
+ /* @__PURE__ */ C(A, {
269
270
  className: "font-medium",
270
271
  children: e
271
272
  }),
272
- /* @__PURE__ */ S(k, {
273
+ /* @__PURE__ */ C(A, {
273
274
  className: "text-right tabular-nums",
274
275
  children: t
275
276
  }),
276
- /* @__PURE__ */ S(k, {
277
+ /* @__PURE__ */ C(A, {
277
278
  className: "text-right tabular-nums",
278
- children: P(n)
279
+ children: F(n)
279
280
  })
280
281
  ] }, e)) })] })
281
282
  })
282
283
  })]
283
284
  }),
284
- /* @__PURE__ */ S(o, {
285
+ /* @__PURE__ */ C(s, {
285
286
  title: "Status Breakdown",
286
- icon: c,
287
- children: /* @__PURE__ */ S("div", {
287
+ icon: l,
288
+ children: /* @__PURE__ */ C("div", {
288
289
  className: "flex flex-wrap gap-3",
289
- children: G.map(([e, t]) => /* @__PURE__ */ C("div", {
290
+ children: q.map(([e, t]) => /* @__PURE__ */ w("div", {
290
291
  className: "flex items-center gap-2 rounded-lg border border-border-seam bg-gloss-card p-3",
291
- children: [/* @__PURE__ */ S(B, {
292
- tone: z(e),
292
+ children: [/* @__PURE__ */ C(V, {
293
+ tone: B(e),
293
294
  children: e.replace(/_/g, " ")
294
- }), /* @__PURE__ */ S("span", {
295
+ }), /* @__PURE__ */ C("span", {
295
296
  className: "text-lg font-bold tabular-nums",
296
297
  children: t
297
298
  })]
@@ -303,6 +304,6 @@ function V() {
303
304
  });
304
305
  }
305
306
  //#endregion
306
- export { V as PortfolioPortfolioOverviewPage };
307
+ export { H as PortfolioPortfolioOverviewPage };
307
308
 
308
309
  //# sourceMappingURL=PortfolioPortfolioOverviewPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PortfolioPortfolioOverviewPage.js","names":[],"sources":["../../../src/assethandler/pages/PortfolioPortfolioOverviewPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n GradientText,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Package,\n CheckCircle2,\n Wrench,\n MapPin,\n DollarSign,\n TrendingDown,\n Wallet,\n PieChart,\n Building2,\n Activity,\n Layers,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\nimport { cn } from '../utils/cn';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n formatCompact,\n type ChartDatum,\n type DonutSlice,\n} from '../components';\n\n// ─── Backend financial rollup (wspace-assethandler-svc) ──────────────────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\nconst FINANCIAL_SUMMARY_QUERY = gql`\n query AssetHandlerFinancialSummaryOverview {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n }\n`;\n\nconst moneyFull = (value: number): string => `$${Math.round(value).toLocaleString()}`;\nconst moneyOrDash = (value: number | undefined): number | string =>\n value === undefined ? '—' : value;\n\nfunction titleCase(value: string): string {\n return value.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction countBy(assets: Asset[], key: (a: Asset) => string): Array<[string, number]> {\n const map = assets.reduce<Record<string, number>>((acc, a) => {\n const k = key(a);\n acc[k] = (acc[k] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(map).sort((a, b) => b[1] - a[1]);\n}\n\ntype PillTone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\nconst PILL_CLASS: Record<PillTone, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\nconst statusTone = (status: string): PillTone =>\n status === 'active'\n ? 'success'\n : status === 'maintenance'\n ? 'warning'\n : status === 'disposed'\n ? 'error'\n : 'neutral';\nfunction StatusPill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap',\n PILL_CLASS[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nexport function PortfolioPortfolioOverviewPage() {\n const { assets } = useAssetHandlerStore();\n\n const { data: financialData } = useQuery<{ financialSummary: AssetHandlerFinancialSummary }>(\n FINANCIAL_SUMMARY_QUERY,\n { fetchPolicy: 'cache-and-network', errorPolicy: 'all' }\n );\n const financialSummary = financialData?.financialSummary;\n\n // TCO summed from store assets (undefined per-asset figures treated as 0).\n const totalCostOfOwnership = useMemo(\n () => assets.reduce((sum, a) => sum + (a.totalCostOfOwnership ?? 0), 0),\n [assets]\n );\n\n // Per-department count + summed current value, sorted by value desc.\n const byDepartment = useMemo(() => {\n const map = assets.reduce<Record<string, { count: number; value: number }>>((acc, a) => {\n const dept = a.department?.trim() || 'Unassigned';\n const entry = acc[dept] ?? { count: 0, value: 0 };\n entry.count += 1;\n entry.value += a.currentValue ?? 0;\n acc[dept] = entry;\n return acc;\n }, {});\n return Object.entries(map).sort((a, b) => b[1].value - a[1].value);\n }, [assets]);\n\n const departmentBars = useMemo<ChartDatum[]>(\n () =>\n byDepartment\n .filter(([, { value }]) => value > 0)\n .slice(0, 8)\n .map(([dept, { value }]) => ({ department: dept, value: Math.round(value) })),\n [byDepartment]\n );\n\n const byCategory = useMemo(() => countBy(assets, (a) => titleCase(a.category)), [assets]);\n const byStatus = useMemo(() => countBy(assets, (a) => a.status), [assets]);\n const byCondition = useMemo(() => countBy(assets, (a) => a.condition), [assets]);\n const byLocation = useMemo(\n () => countBy(assets, (a) => a.location.trim() || 'Unlocated'),\n [assets]\n );\n\n const categoryDonut = useMemo<DonutSlice[]>(\n () => byCategory.map(([name, value]) => ({ name, value })),\n [byCategory]\n );\n const statusBars = useMemo<ChartDatum[]>(\n () => byStatus.map(([status, count]) => ({ status: titleCase(status), count })),\n [byStatus]\n );\n const conditionBars = useMemo<ChartDatum[]>(() => {\n const order = ['excellent', 'good', 'fair', 'poor', 'damaged'];\n return [...byCondition]\n .sort((a, b) => order.indexOf(a[0]) - order.indexOf(b[0]))\n .map(([condition, count]) => ({ condition: titleCase(condition), count }));\n }, [byCondition]);\n\n const activeCount = byStatus.find(([s]) => s === 'active')?.[1] ?? 0;\n const maintenanceCount = byStatus.find(([s]) => s === 'maintenance')?.[1] ?? 0;\n const locationCount = byLocation.filter(([l]) => l !== 'Unlocated').length;\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">\n Portfolio Overview\n </p>\n <h1 className=\"mt-1 text-2xl font-bold sm:text-3xl\">\n <GradientText>Portfolio Overview</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Management-level rollup of the entire estate — how it is distributed and what it is worth.\n </p>\n </div>\n\n <PagePurpose>\n The big-picture view of everything you own: how many assets you have and how they break down\n by category, status, condition and location. Use it to inform replacement planning and\n audit.\n </PagePurpose>\n\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets in your portfolio yet\"\n description=\"Once you add assets, this overview will summarise them by category, status, condition and location.\"\n />\n ) : (\n <>\n {/* Primary value KPIs */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n hero\n label=\"Total Asset Value\"\n value={moneyOrDash(financialSummary?.totalAssetValue)}\n format={moneyFull}\n icon={DollarSign}\n />\n <StatTile\n label=\"Total Depreciation\"\n value={moneyOrDash(financialSummary?.totalDepreciation)}\n format={moneyFull}\n icon={TrendingDown}\n />\n <StatTile\n label=\"Total Cost of Ownership\"\n value={totalCostOfOwnership}\n format={moneyFull}\n icon={Wallet}\n />\n <StatTile\n label=\"Total Assets\"\n value={assets.length}\n format={formatCompact}\n icon={Package}\n />\n </div>\n\n {/* Secondary count KPIs */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Active\"\n value={activeCount}\n icon={CheckCircle2}\n delta={`${assets.length > 0 ? Math.round((activeCount / assets.length) * 100) : 0}% of estate`}\n deltaTone=\"neutral\"\n />\n <StatTile label=\"In Maintenance\" value={maintenanceCount} icon={Wrench} />\n <StatTile label=\"Locations\" value={locationCount} icon={MapPin} />\n <StatTile label=\"Categories\" value={byCategory.length} icon={Layers} />\n </div>\n\n {/* Category donut + Value by department */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Category\" icon={PieChart}>\n <DonutChart\n data={categoryDonut}\n centerLabel=\"Assets\"\n centerValue={assets.length}\n height={280}\n ariaLabel=\"Assets by category\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Value by Department\"\n subtitle=\"Summed current book value\"\n icon={Building2}\n >\n {departmentBars.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No department values yet\"\n description=\"Assign departments and record current values to see this breakdown.\"\n />\n ) : (\n <BarsChart\n data={departmentBars}\n xKey=\"department\"\n series={[{ key: 'value', label: 'Current value' }]}\n orientation=\"horizontal\"\n categoryWidth={120}\n height={Math.max(220, departmentBars.length * 44)}\n valueFormat={moneyFull}\n ariaLabel=\"Current value by department\"\n />\n )}\n </ChartCard>\n </div>\n\n {/* Status bars + Location table */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Status\" icon={Activity}>\n <BarsChart\n data={statusBars}\n xKey=\"status\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n\n <ChartCard title=\"Assets by Location\" icon={MapPin}>\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Location</TableHead>\n <TableHead className=\"text-right\">Assets</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {byLocation.map(([location, count]) => (\n <TableRow key={location}>\n <TableCell className=\"font-medium\">{location}</TableCell>\n <TableCell className=\"text-right tabular-nums\">{count}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </ChartCard>\n </div>\n\n {/* Condition mix + Department value table */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Asset Condition Mix\" icon={Wrench}>\n <BarsChart\n data={conditionBars}\n xKey=\"condition\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n ariaLabel=\"Assets by condition\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Department Breakdown\"\n subtitle=\"Assets and current value\"\n icon={Wallet}\n >\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Department</TableHead>\n <TableHead className=\"text-right\">Assets</TableHead>\n <TableHead className=\"text-right\">Current Value</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {byDepartment.map(([dept, { count, value }]) => (\n <TableRow key={dept}>\n <TableCell className=\"font-medium\">{dept}</TableCell>\n <TableCell className=\"text-right tabular-nums\">{count}</TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {moneyFull(value)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </ChartCard>\n </div>\n\n {/* Status legend chips */}\n <ChartCard title=\"Status Breakdown\" icon={Activity}>\n <div className=\"flex flex-wrap gap-3\">\n {byStatus.map(([status, count]) => (\n <div\n key={status}\n className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-gloss-card p-3\"\n >\n <StatusPill tone={statusTone(status)}>{status.replace(/_/g, ' ')}</StatusPill>\n <span className=\"text-lg font-bold tabular-nums\">{count}</span>\n </div>\n ))}\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PortfolioPortfolioOverviewPage.js","names":[],"sources":["../../../src/assethandler/pages/PortfolioPortfolioOverviewPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n GradientText,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Package,\n CheckCircle2,\n Wrench,\n MapPin,\n DollarSign,\n TrendingDown,\n Wallet,\n PieChart,\n Building2,\n Activity,\n Layers,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\nimport { useCopy } from '../hooks/useCopy';\nimport { cn } from '../utils/cn';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n formatCompact,\n type ChartDatum,\n type DonutSlice,\n} from '../components';\n\n// ─── Backend financial rollup (wspace-assethandler-svc) ──────────────────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\nconst FINANCIAL_SUMMARY_QUERY = gql`\n query AssetHandlerFinancialSummaryOverview {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n }\n`;\n\nconst moneyFull = (value: number): string => `$${Math.round(value).toLocaleString()}`;\nconst moneyOrDash = (value: number | undefined): number | string =>\n value === undefined ? '—' : value;\n\nfunction titleCase(value: string): string {\n return value.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction countBy(assets: Asset[], key: (a: Asset) => string): Array<[string, number]> {\n const map = assets.reduce<Record<string, number>>((acc, a) => {\n const k = key(a);\n acc[k] = (acc[k] || 0) + 1;\n return acc;\n }, {});\n return Object.entries(map).sort((a, b) => b[1] - a[1]);\n}\n\ntype PillTone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\nconst PILL_CLASS: Record<PillTone, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\nconst statusTone = (status: string): PillTone =>\n status === 'active'\n ? 'success'\n : status === 'maintenance'\n ? 'warning'\n : status === 'disposed'\n ? 'error'\n : 'neutral';\nfunction StatusPill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium capitalize whitespace-nowrap',\n PILL_CLASS[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nexport function PortfolioPortfolioOverviewPage() {\n const tc = useCopy();\n const { assets } = useAssetHandlerStore();\n\n const { data: financialData } = useQuery<{ financialSummary: AssetHandlerFinancialSummary }>(\n FINANCIAL_SUMMARY_QUERY,\n { fetchPolicy: 'cache-and-network', errorPolicy: 'all' }\n );\n const financialSummary = financialData?.financialSummary;\n\n // TCO summed from store assets (undefined per-asset figures treated as 0).\n const totalCostOfOwnership = useMemo(\n () => assets.reduce((sum, a) => sum + (a.totalCostOfOwnership ?? 0), 0),\n [assets]\n );\n\n // Per-department count + summed current value, sorted by value desc.\n const byDepartment = useMemo(() => {\n const map = assets.reduce<Record<string, { count: number; value: number }>>((acc, a) => {\n const dept = a.department?.trim() || 'Unassigned';\n const entry = acc[dept] ?? { count: 0, value: 0 };\n entry.count += 1;\n entry.value += a.currentValue ?? 0;\n acc[dept] = entry;\n return acc;\n }, {});\n return Object.entries(map).sort((a, b) => b[1].value - a[1].value);\n }, [assets]);\n\n const departmentBars = useMemo<ChartDatum[]>(\n () =>\n byDepartment\n .filter(([, { value }]) => value > 0)\n .slice(0, 8)\n .map(([dept, { value }]) => ({ department: dept, value: Math.round(value) })),\n [byDepartment]\n );\n\n const byCategory = useMemo(() => countBy(assets, (a) => titleCase(a.category)), [assets]);\n const byStatus = useMemo(() => countBy(assets, (a) => a.status), [assets]);\n const byCondition = useMemo(() => countBy(assets, (a) => a.condition), [assets]);\n const byLocation = useMemo(\n () => countBy(assets, (a) => a.location.trim() || 'Unlocated'),\n [assets]\n );\n\n const categoryDonut = useMemo<DonutSlice[]>(\n () => byCategory.map(([name, value]) => ({ name, value })),\n [byCategory]\n );\n const statusBars = useMemo<ChartDatum[]>(\n () => byStatus.map(([status, count]) => ({ status: titleCase(status), count })),\n [byStatus]\n );\n const conditionBars = useMemo<ChartDatum[]>(() => {\n const order = ['excellent', 'good', 'fair', 'poor', 'damaged'];\n return [...byCondition]\n .sort((a, b) => order.indexOf(a[0]) - order.indexOf(b[0]))\n .map(([condition, count]) => ({ condition: titleCase(condition), count }));\n }, [byCondition]);\n\n const activeCount = byStatus.find(([s]) => s === 'active')?.[1] ?? 0;\n const maintenanceCount = byStatus.find(([s]) => s === 'maintenance')?.[1] ?? 0;\n const locationCount = byLocation.filter(([l]) => l !== 'Unlocated').length;\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">\n Portfolio Overview\n </p>\n <h1 className=\"mt-1 text-2xl font-bold sm:text-3xl\">\n <GradientText>Portfolio Overview</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Management-level rollup of the entire estate — how it is distributed and what it is worth.\n </p>\n </div>\n\n <PagePurpose>\n The big-picture view of everything you own: how many assets you have and how they break down\n by category, status, condition and location. Use it to inform replacement planning and\n audit.\n </PagePurpose>\n\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets in your portfolio yet\"\n description=\"Once you add assets, this overview will summarise them by category, status, condition and location.\"\n />\n ) : (\n <>\n {/* Primary value KPIs */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n hero\n label=\"Total Asset Value\"\n value={moneyOrDash(financialSummary?.totalAssetValue)}\n format={moneyFull}\n icon={DollarSign}\n />\n <StatTile\n label=\"Total Depreciation\"\n value={moneyOrDash(financialSummary?.totalDepreciation)}\n format={moneyFull}\n icon={TrendingDown}\n />\n <StatTile\n label=\"Total Cost of Ownership\"\n value={totalCostOfOwnership}\n format={moneyFull}\n icon={Wallet}\n />\n <StatTile\n label={tc('assethandler.portfolio.metrics.totalAssets', 'Total {{assets}}')}\n value={assets.length}\n format={formatCompact}\n icon={Package}\n />\n </div>\n\n {/* Secondary count KPIs */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Active\"\n value={activeCount}\n icon={CheckCircle2}\n delta={`${assets.length > 0 ? Math.round((activeCount / assets.length) * 100) : 0}% of estate`}\n deltaTone=\"neutral\"\n />\n <StatTile label=\"In Maintenance\" value={maintenanceCount} icon={Wrench} />\n <StatTile label=\"Locations\" value={locationCount} icon={MapPin} />\n <StatTile label=\"Categories\" value={byCategory.length} icon={Layers} />\n </div>\n\n {/* Category donut + Value by department */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Category\" icon={PieChart}>\n <DonutChart\n data={categoryDonut}\n centerLabel={tc('assethandler.portfolio.donut.centerLabel', '{{assets}}')}\n centerValue={assets.length}\n height={280}\n ariaLabel=\"Assets by category\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Value by Department\"\n subtitle=\"Summed current book value\"\n icon={Building2}\n >\n {departmentBars.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No department values yet\"\n description=\"Assign departments and record current values to see this breakdown.\"\n />\n ) : (\n <BarsChart\n data={departmentBars}\n xKey=\"department\"\n series={[{ key: 'value', label: 'Current value' }]}\n orientation=\"horizontal\"\n categoryWidth={120}\n height={Math.max(220, departmentBars.length * 44)}\n valueFormat={moneyFull}\n ariaLabel=\"Current value by department\"\n />\n )}\n </ChartCard>\n </div>\n\n {/* Status bars + Location table */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Assets by Status\" icon={Activity}>\n <BarsChart\n data={statusBars}\n xKey=\"status\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n ariaLabel=\"Assets by status\"\n />\n </ChartCard>\n\n <ChartCard title=\"Assets by Location\" icon={MapPin}>\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Location</TableHead>\n <TableHead className=\"text-right\">Assets</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {byLocation.map(([location, count]) => (\n <TableRow key={location}>\n <TableCell className=\"font-medium\">{location}</TableCell>\n <TableCell className=\"text-right tabular-nums\">{count}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </ChartCard>\n </div>\n\n {/* Condition mix + Department value table */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <ChartCard title=\"Asset Condition Mix\" icon={Wrench}>\n <BarsChart\n data={conditionBars}\n xKey=\"condition\"\n series={[{ key: 'count', label: 'Assets' }]}\n height={260}\n ariaLabel=\"Assets by condition\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Department Breakdown\"\n subtitle=\"Assets and current value\"\n icon={Wallet}\n >\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Department</TableHead>\n <TableHead className=\"text-right\">Assets</TableHead>\n <TableHead className=\"text-right\">Current Value</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {byDepartment.map(([dept, { count, value }]) => (\n <TableRow key={dept}>\n <TableCell className=\"font-medium\">{dept}</TableCell>\n <TableCell className=\"text-right tabular-nums\">{count}</TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {moneyFull(value)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </ChartCard>\n </div>\n\n {/* Status legend chips */}\n <ChartCard title=\"Status Breakdown\" icon={Activity}>\n <div className=\"flex flex-wrap gap-3\">\n {byStatus.map(([status, count]) => (\n <div\n key={status}\n className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-gloss-card p-3\"\n >\n <StatusPill tone={statusTone(status)}>{status.replace(/_/g, ' ')}</StatusPill>\n <span className=\"text-lg font-bold tabular-nums\">{count}</span>\n </div>\n ))}\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}