@burdenoff/microfe-assethandler 2026.710.1 → 2026.713.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/assethandler/AssetHandlerRoot.d.ts.map +1 -1
  2. package/dist/assethandler/AssetHandlerRoot.js +16 -8
  3. package/dist/assethandler/AssetHandlerRoot.js.map +1 -1
  4. package/dist/assethandler/components/AttachmentsPanel.d.ts.map +1 -1
  5. package/dist/assethandler/components/AttachmentsPanel.js.map +1 -1
  6. package/dist/assethandler/pages/AssetsAssetCategoriesPage.d.ts.map +1 -1
  7. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js +144 -137
  8. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  9. package/dist/assethandler/pages/AssetsAssetCreatePage.d.ts.map +1 -1
  10. package/dist/assethandler/pages/AssetsAssetCreatePage.js +202 -201
  11. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  12. package/dist/assethandler/pages/AssetsAssetListPage.d.ts.map +1 -1
  13. package/dist/assethandler/pages/AssetsAssetListPage.js +135 -132
  14. package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
  15. package/dist/assethandler/providers/AssetHandlerProvider.d.ts +14 -0
  16. package/dist/assethandler/providers/AssetHandlerProvider.d.ts.map +1 -1
  17. package/dist/assethandler/providers/AssetHandlerProvider.js +14 -2
  18. package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
  19. package/dist/assethandler/store/assethandlerStore.d.ts.map +1 -1
  20. package/dist/assethandler/store/assethandlerStore.js.map +1 -1
  21. package/dist/assethandler/types/index.d.ts +32 -0
  22. package/dist/assethandler/types/index.d.ts.map +1 -1
  23. package/dist/services/createSandboxAssistantTransport.d.ts +8 -26
  24. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -1
  25. package/dist/services/createSandboxAssistantTransport.js +8 -114
  26. package/dist/services/createSandboxAssistantTransport.js.map +1 -1
  27. package/dist/types/assistant.d.ts.map +1 -1
  28. package/dist/utils/pageContext.d.ts.map +1 -1
  29. package/dist/utils/pageContext.js.map +1 -1
  30. package/package.json +2 -2
  31. package/dist/generated/wspace-operations.js +0 -255
  32. package/dist/generated/wspace-operations.js.map +0 -1
  33. package/dist/services/assistantApi.d.ts +0 -71
  34. package/dist/services/assistantApi.d.ts.map +0 -1
  35. package/dist/services/assistantApi.js +0 -215
  36. package/dist/services/assistantApi.js.map +0 -1
@@ -1,26 +1,27 @@
1
1
  import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
- import { useAssetHandlerNavigate as t } from "../hooks/useAssetHandlerNavigate.js";
3
- import { notify as n } from "../utils/toast.js";
4
- import { formatCompact as r, formatCurrencyCompact as i } from "../components/charts/chartTheme.js";
5
- import { BarsChart as a } from "../components/charts/BarsChart.js";
6
- import { StatTile as o } from "../components/StatTile.js";
7
- import { ChartCard as s } from "../components/ChartCard.js";
2
+ import { useAssetTerminology as t, useHiddenCategoryIds as n } from "../providers/AssetHandlerProvider.js";
3
+ import { useAssetHandlerNavigate as r } from "../hooks/useAssetHandlerNavigate.js";
4
+ import { notify as i } from "../utils/toast.js";
5
+ import { formatCompact as a, formatCurrencyCompact as o } from "../components/charts/chartTheme.js";
6
+ import { BarsChart as s } from "../components/charts/BarsChart.js";
7
+ import { StatTile as c } from "../components/StatTile.js";
8
+ import { ChartCard as l } from "../components/ChartCard.js";
8
9
  import "../components/index.js";
9
- import { useMemo as c, useState as l } from "react";
10
- import { ArrowUpDown as u, Box as d, Code as f, Copy as p, ExternalLink as m, Layers as h, Monitor as g, MoreHorizontal as _, Server as v, Truck as y, Wifi as b, Wrench as x } from "lucide-react";
11
- import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
12
- import { Badge as T, Button as E, DropdownMenu as D, DropdownMenuContent as O, DropdownMenuItem as k, DropdownMenuTrigger as A, GlassCard as j, GradientText as M, IllustratedEmptyState as N, PagePurpose as P } from "@burdenoff/fe-libs/ui";
10
+ import { useMemo as u, useState as d } from "react";
11
+ import { ArrowUpDown as f, Box as p, Code as m, Copy as h, ExternalLink as g, Layers as _, Monitor as v, MoreHorizontal as y, Server as b, Truck as x, Wifi as S, Wrench as C } from "lucide-react";
12
+ import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
13
+ import { Badge as D, Button as O, DropdownMenu as k, DropdownMenuContent as A, DropdownMenuItem as j, DropdownMenuTrigger as M, GlassCard as N, GradientText as P, IllustratedEmptyState as F, PagePurpose as I } from "@burdenoff/fe-libs/ui";
13
14
  //#region src/assethandler/pages/AssetsAssetCategoriesPage.tsx
14
- var F = {
15
- electronics: /* @__PURE__ */ C(g, { className: "h-5 w-5" }),
16
- it_hardware: /* @__PURE__ */ C(g, { className: "h-5 w-5" }),
17
- furniture: /* @__PURE__ */ C(d, { className: "h-5 w-5" }),
18
- vehicles: /* @__PURE__ */ C(y, { className: "h-5 w-5" }),
19
- equipment: /* @__PURE__ */ C(x, { className: "h-5 w-5" }),
20
- software: /* @__PURE__ */ C(f, { className: "h-5 w-5" }),
21
- networking: /* @__PURE__ */ C(b, { className: "h-5 w-5" }),
22
- servers: /* @__PURE__ */ C(v, { className: "h-5 w-5" })
23
- }, I = {
15
+ var L = {
16
+ electronics: /* @__PURE__ */ T(v, { className: "h-5 w-5" }),
17
+ it_hardware: /* @__PURE__ */ T(v, { className: "h-5 w-5" }),
18
+ furniture: /* @__PURE__ */ T(p, { className: "h-5 w-5" }),
19
+ vehicles: /* @__PURE__ */ T(x, { className: "h-5 w-5" }),
20
+ equipment: /* @__PURE__ */ T(C, { className: "h-5 w-5" }),
21
+ software: /* @__PURE__ */ T(m, { className: "h-5 w-5" }),
22
+ networking: /* @__PURE__ */ T(S, { className: "h-5 w-5" }),
23
+ servers: /* @__PURE__ */ T(b, { className: "h-5 w-5" })
24
+ }, R = {
24
25
  electronics: "bg-status-info-bg-subtle text-status-info-text",
25
26
  it_hardware: "bg-status-info-bg-subtle text-status-info-text",
26
27
  furniture: "bg-status-warning-bg-subtle text-status-warning-text",
@@ -29,20 +30,20 @@ var F = {
29
30
  software: "bg-action-primary-bg/10 text-action-primary-bg",
30
31
  networking: "bg-status-info-bg-subtle text-status-info-text",
31
32
  servers: "bg-status-error-bg-subtle text-status-error-text"
32
- }, L = [
33
+ }, z = [
33
34
  "bg-status-info-bg-subtle text-status-info-text",
34
35
  "bg-status-success-bg-subtle text-status-success-text",
35
36
  "bg-status-warning-bg-subtle text-status-warning-text",
36
37
  "bg-status-error-bg-subtle text-status-error-text",
37
38
  "bg-action-primary-bg/10 text-action-primary-bg"
38
39
  ];
39
- function R(e) {
40
- if (I[e]) return I[e];
40
+ function B(e) {
41
+ if (R[e]) return R[e];
41
42
  let t = 0;
42
43
  for (let n = 0; n < e.length; n += 1) t = t * 31 + e.charCodeAt(n) >>> 0;
43
- return L[t % L.length];
44
+ return z[t % z.length];
44
45
  }
45
- var z = [
46
+ var V = [
46
47
  {
47
48
  key: "active",
48
49
  label: "Active",
@@ -68,17 +69,17 @@ var z = [
68
69
  label: "Disposed",
69
70
  varName: "var(--color-text-muted)"
70
71
  }
71
- ], B = {
72
+ ], H = {
72
73
  excellent: 5,
73
74
  good: 4,
74
75
  fair: 3,
75
76
  poor: 2,
76
77
  damaged: 1
77
- }, V = (e) => `$${e.toLocaleString()}`;
78
- function H() {
79
- let f = t(), { assets: g } = e(), [v, y] = l("count"), [b, x] = l("count"), I = c(() => {
78
+ }, U = (e) => `$${e.toLocaleString()}`;
79
+ function W() {
80
+ let m = r(), v = t(), b = n(), { assets: x } = e(), S = u(() => x.filter((e) => !b.has(e.category)), [x, b]), [C, R] = d("count"), [z, W] = d("count"), G = u(() => {
80
81
  let e = {};
81
- for (let t of g) {
82
+ for (let t of S) {
82
83
  e[t.category] || (e[t.category] = {
83
84
  key: t.category,
84
85
  count: 0,
@@ -93,165 +94,171 @@ function H() {
93
94
  for (let t of Object.values(e)) {
94
95
  let e = 0, n = 0;
95
96
  for (let [r, i] of Object.entries(t.conditions)) {
96
- let t = B[r];
97
+ let t = H[r];
97
98
  t && (e += t * i, n += i);
98
99
  }
99
100
  t.avgCondition = n > 0 ? e / n : 0;
100
101
  }
101
102
  return Object.values(e).sort((e, t) => t.count - e.count);
102
- }, [g]), L = g.length, H = c(() => g.reduce((e, t) => e + (t.currentValue ?? t.purchaseValue ?? 0), 0), [g]), U = c(() => I.map((e) => ({
103
+ }, [S]), K = S.length, q = u(() => S.reduce((e, t) => e + (t.currentValue ?? t.purchaseValue ?? 0), 0), [S]), J = u(() => G.map((e) => ({
103
104
  category: e.key.replace(/_/g, " "),
104
105
  count: e.count,
105
106
  value: e.totalValue
106
- })), [I]), W = c(() => [...I].sort((e, t) => b === "count" ? t.count - e.count : t.totalValue - e.totalValue), [I, b]), G = (e) => {
107
- navigator.clipboard?.writeText(e), n.success("Category copied", `"${e.replace(/_/g, " ")}" copied to clipboard.`);
107
+ })), [G]), Y = u(() => [...G].sort((e, t) => z === "count" ? t.count - e.count : t.totalValue - e.totalValue), [G, z]), X = (e) => {
108
+ navigator.clipboard?.writeText(e), i.success("Category copied", `"${e.replace(/_/g, " ")}" copied to clipboard.`);
108
109
  };
109
- return /* @__PURE__ */ w("div", {
110
+ return /* @__PURE__ */ E("div", {
110
111
  className: "space-y-6 p-4 sm:p-6",
111
112
  children: [
112
- /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("h1", {
113
+ /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
113
114
  className: "text-2xl font-bold",
114
- children: /* @__PURE__ */ C(M, { children: "Asset Categories" })
115
- }), /* @__PURE__ */ w("p", {
115
+ children: /* @__PURE__ */ E(P, { children: [v.asset, " Categories"] })
116
+ }), /* @__PURE__ */ E("p", {
116
117
  className: "text-sm text-text-secondary",
117
118
  children: [
118
- I.length,
119
+ G.length,
119
120
  " categories · ",
120
- L,
121
- " total assets"
121
+ K,
122
+ " total",
123
+ " ",
124
+ v.assets.toLowerCase()
122
125
  ]
123
126
  })] }),
124
- /* @__PURE__ */ C(P, { children: "Group and compare your assets by category to see where value and volume are concentrated. Each card rolls up how many assets you hold, their total book value and a proportional status mix — so you can plan procurement and replacements by category, not one asset at a time." }),
125
- I.length === 0 ? /* @__PURE__ */ C(N, {
127
+ /* @__PURE__ */ T(I, { children: "Group and compare your assets by category to see where value and volume are concentrated. Each card rolls up how many assets you hold, their total book value and a proportional status mix — so you can plan procurement and replacements by category, not one asset at a time." }),
128
+ G.length === 0 ? /* @__PURE__ */ T(F, {
126
129
  illustration: "empty-data",
127
130
  title: "No categories yet",
128
- description: "Add assets and they will be grouped into categories here automatically."
129
- }) : /* @__PURE__ */ w(S, { children: [
130
- /* @__PURE__ */ w("div", {
131
+ description: `Add ${v.assets.toLowerCase()} and they will be grouped into categories here automatically.`
132
+ }) : /* @__PURE__ */ E(w, { children: [
133
+ /* @__PURE__ */ E("div", {
131
134
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
132
135
  children: [
133
- /* @__PURE__ */ C(o, {
136
+ /* @__PURE__ */ T(c, {
134
137
  label: "Categories",
135
- value: I.length,
136
- format: r,
137
- icon: h,
138
+ value: G.length,
139
+ format: a,
140
+ icon: _,
138
141
  hero: !0
139
142
  }),
140
- /* @__PURE__ */ C(o, {
141
- label: "Assets tracked",
142
- value: L,
143
- format: r,
144
- icon: d
143
+ /* @__PURE__ */ T(c, {
144
+ label: `${v.assets} tracked`,
145
+ value: K,
146
+ format: a,
147
+ icon: p
145
148
  }),
146
- /* @__PURE__ */ C(o, {
149
+ /* @__PURE__ */ T(c, {
147
150
  label: "Total book value",
148
- value: H,
149
- format: i("USD"),
150
- icon: d
151
+ value: q,
152
+ format: o("USD"),
153
+ icon: p
151
154
  })
152
155
  ]
153
156
  }),
154
- /* @__PURE__ */ C("div", {
157
+ /* @__PURE__ */ T("div", {
155
158
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
156
- children: I.map((e, t) => {
157
- let n = L > 0 ? Math.round(e.count / L * 100) : 0, r = z.filter((t) => (e.statuses[t.key] || 0) > 0);
158
- return /* @__PURE__ */ C(j, {
159
+ children: G.map((e, t) => {
160
+ let n = K > 0 ? Math.round(e.count / K * 100) : 0, r = V.filter((t) => (e.statuses[t.key] || 0) > 0);
161
+ return /* @__PURE__ */ T(N, {
159
162
  treatment: t === 0 ? "glass" : "plain",
160
163
  glow: t === 0,
161
164
  className: "border-border-seam",
162
- children: /* @__PURE__ */ w("div", {
165
+ children: /* @__PURE__ */ E("div", {
163
166
  className: "space-y-4 p-5",
164
167
  children: [
165
- /* @__PURE__ */ w("div", {
168
+ /* @__PURE__ */ E("div", {
166
169
  className: "flex items-start justify-between gap-2",
167
- children: [/* @__PURE__ */ w("div", {
170
+ children: [/* @__PURE__ */ E("div", {
168
171
  className: "flex min-w-0 items-center gap-3",
169
- children: [/* @__PURE__ */ C("div", {
170
- className: `shrink-0 rounded-lg p-2 ${R(e.key)}`,
171
- children: F[e.key] || /* @__PURE__ */ C(d, { className: "h-5 w-5" })
172
- }), /* @__PURE__ */ w("div", {
172
+ children: [/* @__PURE__ */ T("div", {
173
+ className: `shrink-0 rounded-lg p-2 ${B(e.key)}`,
174
+ children: L[e.key] || /* @__PURE__ */ T(p, { className: "h-5 w-5" })
175
+ }), /* @__PURE__ */ E("div", {
173
176
  className: "min-w-0",
174
- children: [/* @__PURE__ */ C("p", {
177
+ children: [/* @__PURE__ */ T("p", {
175
178
  className: "truncate text-base font-semibold capitalize",
176
179
  children: e.key.replace(/_/g, " ")
177
- }), /* @__PURE__ */ w(T, {
180
+ }), /* @__PURE__ */ E(D, {
178
181
  variant: "outline",
179
182
  className: "mt-0.5 text-xs",
180
183
  children: [n, "% of estate"]
181
184
  })]
182
185
  })]
183
- }), /* @__PURE__ */ w(D, { children: [/* @__PURE__ */ C(A, {
186
+ }), /* @__PURE__ */ E(k, { children: [/* @__PURE__ */ T(M, {
184
187
  asChild: !0,
185
- children: /* @__PURE__ */ C(E, {
188
+ children: /* @__PURE__ */ T(O, {
186
189
  variant: "ghost",
187
190
  size: "icon",
188
191
  className: "h-8 w-8 shrink-0",
189
192
  "aria-label": `Actions for ${e.key.replace(/_/g, " ")}`,
190
- children: /* @__PURE__ */ C(_, { className: "h-4 w-4" })
193
+ children: /* @__PURE__ */ T(y, { className: "h-4 w-4" })
191
194
  })
192
- }), /* @__PURE__ */ w(O, {
195
+ }), /* @__PURE__ */ E(A, {
193
196
  align: "end",
194
- children: [/* @__PURE__ */ w(k, {
195
- onClick: () => f("/assets/asset-list"),
196
- children: [/* @__PURE__ */ C(m, { className: "mr-2 h-4 w-4" }), "View assets"]
197
- }), /* @__PURE__ */ w(k, {
198
- onClick: () => G(e.key),
199
- children: [/* @__PURE__ */ C(p, { className: "mr-2 h-4 w-4" }), "Copy category ID"]
197
+ children: [/* @__PURE__ */ E(j, {
198
+ onClick: () => m("/assets/asset-list"),
199
+ children: [
200
+ /* @__PURE__ */ T(g, { className: "mr-2 h-4 w-4" }),
201
+ "View ",
202
+ v.assets.toLowerCase()
203
+ ]
204
+ }), /* @__PURE__ */ E(j, {
205
+ onClick: () => X(e.key),
206
+ children: [/* @__PURE__ */ T(h, { className: "mr-2 h-4 w-4" }), "Copy category ID"]
200
207
  })]
201
208
  })] })]
202
209
  }),
203
- /* @__PURE__ */ w("div", {
210
+ /* @__PURE__ */ E("div", {
204
211
  className: "flex flex-wrap items-end justify-between gap-3",
205
- children: [/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
212
+ children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ E("p", {
206
213
  className: "text-xs text-text-secondary",
207
- children: "Asset count"
208
- }), /* @__PURE__ */ C("p", {
214
+ children: [v.asset, " count"]
215
+ }), /* @__PURE__ */ T("p", {
209
216
  className: "text-2xl font-bold tabular-nums",
210
217
  children: e.count
211
- })] }), /* @__PURE__ */ w("div", {
218
+ })] }), /* @__PURE__ */ E("div", {
212
219
  className: "text-right",
213
- children: [/* @__PURE__ */ C("p", {
220
+ children: [/* @__PURE__ */ T("p", {
214
221
  className: "text-xs text-text-secondary",
215
222
  children: "Total value"
216
- }), /* @__PURE__ */ C("p", {
223
+ }), /* @__PURE__ */ T("p", {
217
224
  className: "text-2xl font-bold tabular-nums",
218
- children: i("USD")(e.totalValue)
225
+ children: o("USD")(e.totalValue)
219
226
  })]
220
227
  })]
221
228
  }),
222
- /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("div", {
229
+ /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("div", {
223
230
  className: "flex h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken",
224
231
  role: "img",
225
232
  "aria-label": r.map((t) => `${t.label}: ${e.statuses[t.key]}`).join(", "),
226
- children: r.map((t) => /* @__PURE__ */ C("div", { style: {
233
+ children: r.map((t) => /* @__PURE__ */ T("div", { style: {
227
234
  width: `${(e.statuses[t.key] || 0) / e.count * 100}%`,
228
235
  backgroundColor: t.varName
229
236
  } }, t.key))
230
- }), /* @__PURE__ */ C("div", {
237
+ }), /* @__PURE__ */ T("div", {
231
238
  className: "mt-2 flex flex-wrap gap-x-3 gap-y-1",
232
- children: r.map((t) => /* @__PURE__ */ w("span", {
239
+ children: r.map((t) => /* @__PURE__ */ E("span", {
233
240
  className: "inline-flex items-center gap-1.5 text-xs text-text-secondary",
234
241
  children: [
235
- /* @__PURE__ */ C("span", {
242
+ /* @__PURE__ */ T("span", {
236
243
  className: "h-2.5 w-2.5 rounded-[3px]",
237
244
  style: { backgroundColor: t.varName }
238
245
  }),
239
246
  t.label,
240
- /* @__PURE__ */ C("span", {
247
+ /* @__PURE__ */ T("span", {
241
248
  className: "tabular-nums text-text-muted",
242
249
  children: e.statuses[t.key]
243
250
  })
244
251
  ]
245
252
  }, t.key))
246
253
  })] }),
247
- /* @__PURE__ */ w("div", {
254
+ /* @__PURE__ */ E("div", {
248
255
  className: "flex items-center justify-between border-t border-border-seam pt-3 text-sm",
249
- children: [/* @__PURE__ */ C("span", {
256
+ children: [/* @__PURE__ */ T("span", {
250
257
  className: "text-text-secondary",
251
258
  children: "Avg. condition"
252
- }), /* @__PURE__ */ w("span", {
259
+ }), /* @__PURE__ */ E("span", {
253
260
  className: "font-semibold tabular-nums",
254
- children: [e.avgCondition > 0 ? e.avgCondition.toFixed(1) : "—", /* @__PURE__ */ C("span", {
261
+ children: [e.avgCondition > 0 ? e.avgCondition.toFixed(1) : "—", /* @__PURE__ */ T("span", {
255
262
  className: "text-xs font-normal text-text-muted",
256
263
  children: " / 5"
257
264
  })]
@@ -262,92 +269,92 @@ function H() {
262
269
  }, e.key);
263
270
  })
264
271
  }),
265
- /* @__PURE__ */ C(s, {
272
+ /* @__PURE__ */ T(l, {
266
273
  title: "Category comparison",
267
- subtitle: v === "count" ? "Assets per category" : "Book value per category",
268
- actions: /* @__PURE__ */ w("div", {
274
+ subtitle: C === "count" ? `${v.assets} per category` : "Book value per category",
275
+ actions: /* @__PURE__ */ E("div", {
269
276
  className: "flex gap-1 rounded-md border border-border-seam p-0.5",
270
- children: [/* @__PURE__ */ C(E, {
271
- variant: v === "count" ? "default" : "ghost",
277
+ children: [/* @__PURE__ */ T(O, {
278
+ variant: C === "count" ? "default" : "ghost",
272
279
  size: "sm",
273
280
  className: "h-7 px-2 text-xs",
274
- onClick: () => y("count"),
281
+ onClick: () => R("count"),
275
282
  children: "Count"
276
- }), /* @__PURE__ */ C(E, {
277
- variant: v === "value" ? "default" : "ghost",
283
+ }), /* @__PURE__ */ T(O, {
284
+ variant: C === "value" ? "default" : "ghost",
278
285
  size: "sm",
279
286
  className: "h-7 px-2 text-xs",
280
- onClick: () => y("value"),
287
+ onClick: () => R("value"),
281
288
  children: "Value"
282
289
  })]
283
290
  }),
284
- children: /* @__PURE__ */ C(a, {
285
- data: U,
291
+ children: /* @__PURE__ */ T(s, {
292
+ data: J,
286
293
  xKey: "category",
287
294
  orientation: "horizontal",
288
- height: Math.max(200, U.length * 44),
295
+ height: Math.max(200, J.length * 44),
289
296
  categoryWidth: 120,
290
- valueFormat: v === "value" ? i("USD") : r,
297
+ valueFormat: C === "value" ? o("USD") : a,
291
298
  series: [{
292
- key: v,
293
- label: v === "count" ? "Assets" : "Book value"
299
+ key: C,
300
+ label: C === "count" ? v.assets : "Book value"
294
301
  }]
295
302
  })
296
303
  }),
297
- /* @__PURE__ */ C(s, {
304
+ /* @__PURE__ */ T(l, {
298
305
  title: "Category summary",
299
306
  subtitle: "Sortable roll-up across every category",
300
- icon: h,
301
- children: /* @__PURE__ */ C("div", {
307
+ icon: _,
308
+ children: /* @__PURE__ */ T("div", {
302
309
  className: "overflow-x-auto",
303
- children: /* @__PURE__ */ w("table", {
310
+ children: /* @__PURE__ */ E("table", {
304
311
  className: "w-full min-w-[520px] text-sm",
305
- children: [/* @__PURE__ */ C("thead", { children: /* @__PURE__ */ w("tr", {
312
+ children: [/* @__PURE__ */ T("thead", { children: /* @__PURE__ */ E("tr", {
306
313
  className: "border-b border-border-seam text-left text-text-secondary",
307
314
  children: [
308
- /* @__PURE__ */ C("th", {
315
+ /* @__PURE__ */ T("th", {
309
316
  className: "py-2 pr-4 font-medium",
310
317
  children: "Category"
311
318
  }),
312
- /* @__PURE__ */ C("th", {
319
+ /* @__PURE__ */ T("th", {
313
320
  className: "py-2 pr-4 font-medium",
314
- children: /* @__PURE__ */ w("button", {
321
+ children: /* @__PURE__ */ E("button", {
315
322
  type: "button",
316
323
  className: "inline-flex items-center gap-1 hover:text-text-primary",
317
- onClick: () => x("count"),
318
- children: ["Count", /* @__PURE__ */ C(u, { className: `h-3 w-3 ${b === "count" ? "text-action-primary-bg" : ""}` })]
324
+ onClick: () => W("count"),
325
+ children: ["Count", /* @__PURE__ */ T(f, { className: `h-3 w-3 ${z === "count" ? "text-action-primary-bg" : ""}` })]
319
326
  })
320
327
  }),
321
- /* @__PURE__ */ C("th", {
328
+ /* @__PURE__ */ T("th", {
322
329
  className: "py-2 pr-4 font-medium",
323
- children: /* @__PURE__ */ w("button", {
330
+ children: /* @__PURE__ */ E("button", {
324
331
  type: "button",
325
332
  className: "inline-flex items-center gap-1 hover:text-text-primary",
326
- onClick: () => x("value"),
327
- children: ["Value", /* @__PURE__ */ C(u, { className: `h-3 w-3 ${b === "value" ? "text-action-primary-bg" : ""}` })]
333
+ onClick: () => W("value"),
334
+ children: ["Value", /* @__PURE__ */ T(f, { className: `h-3 w-3 ${z === "value" ? "text-action-primary-bg" : ""}` })]
328
335
  })
329
336
  }),
330
- /* @__PURE__ */ C("th", {
337
+ /* @__PURE__ */ T("th", {
331
338
  className: "py-2 font-medium",
332
339
  children: "Avg. condition"
333
340
  })
334
341
  ]
335
- }) }), /* @__PURE__ */ C("tbody", { children: W.map((e) => /* @__PURE__ */ w("tr", {
342
+ }) }), /* @__PURE__ */ T("tbody", { children: Y.map((e) => /* @__PURE__ */ E("tr", {
336
343
  className: "border-b border-border-seam/60 last:border-0",
337
344
  children: [
338
- /* @__PURE__ */ C("td", {
345
+ /* @__PURE__ */ T("td", {
339
346
  className: "py-2.5 pr-4 font-medium capitalize",
340
347
  children: e.key.replace(/_/g, " ")
341
348
  }),
342
- /* @__PURE__ */ C("td", {
349
+ /* @__PURE__ */ T("td", {
343
350
  className: "py-2.5 pr-4 tabular-nums",
344
351
  children: e.count
345
352
  }),
346
- /* @__PURE__ */ C("td", {
353
+ /* @__PURE__ */ T("td", {
347
354
  className: "py-2.5 pr-4 tabular-nums",
348
- children: V(e.totalValue)
355
+ children: U(e.totalValue)
349
356
  }),
350
- /* @__PURE__ */ C("td", {
357
+ /* @__PURE__ */ T("td", {
351
358
  className: "py-2.5 tabular-nums",
352
359
  children: e.avgCondition > 0 ? e.avgCondition.toFixed(1) : "—"
353
360
  })
@@ -361,6 +368,6 @@ function H() {
361
368
  });
362
369
  }
363
370
  //#endregion
364
- export { H as AssetsAssetCategoriesPage };
371
+ export { W as AssetsAssetCategoriesPage };
365
372
 
366
373
  //# sourceMappingURL=AssetsAssetCategoriesPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n Monitor,\n Server,\n Wifi,\n Truck,\n Wrench,\n Code,\n MoreHorizontal,\n Copy,\n ExternalLink,\n ArrowUpDown,\n Layers,\n} from 'lucide-react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetHandlerStore, type AssetStatus } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n ChartCard,\n StatTile,\n BarsChart,\n formatCompact,\n formatCurrencyCompact,\n} from '../components';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\n// Stable fallback tint for any category outside the known slug map — hashes the\n// key into the semantic palette so every real category is visually differentiated.\nconst FALLBACK_TINTS = [\n 'bg-status-info-bg-subtle text-status-info-text',\n 'bg-status-success-bg-subtle text-status-success-text',\n 'bg-status-warning-bg-subtle text-status-warning-text',\n 'bg-status-error-bg-subtle text-status-error-text',\n 'bg-action-primary-bg/10 text-action-primary-bg',\n];\nfunction tintFor(key: string): string {\n if (categoryColors[key]) return categoryColors[key];\n let hash = 0;\n for (let i = 0; i < key.length; i += 1) hash = (hash * 31 + key.charCodeAt(i)) >>> 0;\n return FALLBACK_TINTS[hash % FALLBACK_TINTS.length];\n}\n\n// Proportional status-bar segments — semantic tokens (CSS vars), theme-aware.\nconst STATUS_SEGMENTS: { key: AssetStatus; label: string; varName: string }[] = [\n { key: 'active', label: 'Active', varName: 'var(--color-status-success-bg)' },\n { key: 'maintenance', label: 'Maintenance', varName: 'var(--color-status-warning-bg)' },\n { key: 'retired', label: 'Retired', varName: 'var(--color-status-error-bg)' },\n { key: 'inactive', label: 'Inactive', varName: 'var(--color-status-info-bg)' },\n { key: 'disposed', label: 'Disposed', varName: 'var(--color-text-muted)' },\n];\n\nconst CONDITION_SCORE: Record<string, number> = {\n excellent: 5,\n good: 4,\n fair: 3,\n poor: 2,\n damaged: 1,\n};\n\ninterface CategoryAggregate {\n key: string;\n count: number;\n totalValue: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n avgCondition: number;\n}\n\nconst money = (value: number): string => `$${value.toLocaleString()}`;\n\nexport function AssetsAssetCategoriesPage() {\n const navigate = useAssetHandlerNavigate();\n const { assets } = useAssetHandlerStore();\n const [chartMetric, setChartMetric] = useState<'count' | 'value'>('count');\n const [sortKey, setSortKey] = useState<'count' | 'value'>('count');\n\n const aggregates = useMemo<CategoryAggregate[]>(() => {\n const map: Record<string, CategoryAggregate> = {};\n for (const asset of assets) {\n if (!map[asset.category]) {\n map[asset.category] = {\n key: asset.category,\n count: 0,\n totalValue: 0,\n statuses: {},\n conditions: {},\n avgCondition: 0,\n };\n }\n const cat = map[asset.category];\n cat.count += 1;\n cat.totalValue += asset.currentValue ?? asset.purchaseValue ?? 0;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n }\n for (const cat of Object.values(map)) {\n let scoreSum = 0;\n let scoreN = 0;\n for (const [cond, n] of Object.entries(cat.conditions)) {\n const score = CONDITION_SCORE[cond];\n if (score) {\n scoreSum += score * n;\n scoreN += n;\n }\n }\n cat.avgCondition = scoreN > 0 ? scoreSum / scoreN : 0;\n }\n return Object.values(map).sort((a, b) => b.count - a.count);\n }, [assets]);\n\n const totalAssets = assets.length;\n const totalValue = useMemo(\n () => assets.reduce((sum, a) => sum + (a.currentValue ?? a.purchaseValue ?? 0), 0),\n [assets]\n );\n\n const chartData = useMemo(\n () =>\n aggregates.map((cat) => ({\n category: cat.key.replace(/_/g, ' '),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates]\n );\n\n const sortedRows = useMemo(\n () =>\n [...aggregates].sort((a, b) =>\n sortKey === 'count' ? b.count - a.count : b.totalValue - a.totalValue\n ),\n [aggregates, sortKey]\n );\n\n const copyId = (key: string) => {\n void navigator.clipboard?.writeText(key);\n notify.success('Category copied', `\"${key.replace(/_/g, ' ')}\" copied to clipboard.`);\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>Asset Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {aggregates.length} categories &middot; {totalAssets} total assets\n </p>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where value and volume are concentrated.\n Each card rolls up how many assets you hold, their total book value and a proportional\n status mix — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {aggregates.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description=\"Add assets and they will be grouped into categories here automatically.\"\n />\n ) : (\n <>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <StatTile\n label=\"Categories\"\n value={aggregates.length}\n format={formatCompact}\n icon={Layers}\n hero\n />\n <StatTile\n label=\"Assets tracked\"\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile\n label=\"Total book value\"\n value={totalValue}\n format={formatCurrencyCompact('USD')}\n icon={Box}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {aggregates.map((cat, idx) => {\n const pct = totalAssets > 0 ? Math.round((cat.count / totalAssets) * 100) : 0;\n const segments = STATUS_SEGMENTS.filter((s) => (cat.statuses[s.key] || 0) > 0);\n return (\n <GlassCard\n key={cat.key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <div className=\"space-y-4 p-5\">\n {/* Header: icon + title + overflow */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className={`shrink-0 rounded-lg p-2 ${tintFor(cat.key)}`}>\n {categoryIcons[cat.key] || <Box className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </p>\n <Badge variant=\"outline\" className=\"mt-0.5 text-xs\">\n {pct}% of estate\n </Badge>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0\"\n aria-label={`Actions for ${cat.key.replace(/_/g, ' ')}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => navigate('/assets/asset-list')}>\n <ExternalLink className=\"mr-2 h-4 w-4\" />\n View assets\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => copyId(cat.key)}>\n <Copy className=\"mr-2 h-4 w-4\" />\n Copy category ID\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Dual metric row */}\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <p className=\"text-xs text-text-secondary\">Asset count</p>\n <p className=\"text-2xl font-bold tabular-nums\">{cat.count}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary\">Total value</p>\n <p className=\"text-2xl font-bold tabular-nums\">\n {formatCurrencyCompact('USD')(cat.totalValue)}\n </p>\n </div>\n </div>\n\n {/* Proportional status bar */}\n <div>\n <div\n className=\"flex h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"img\"\n aria-label={segments\n .map((s) => `${s.label}: ${cat.statuses[s.key]}`)\n .join(', ')}\n >\n {segments.map((s) => (\n <div\n key={s.key}\n style={{\n width: `${((cat.statuses[s.key] || 0) / cat.count) * 100}%`,\n backgroundColor: s.varName,\n }}\n />\n ))}\n </div>\n <div className=\"mt-2 flex flex-wrap gap-x-3 gap-y-1\">\n {segments.map((s) => (\n <span\n key={s.key}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary\"\n >\n <span\n className=\"h-2.5 w-2.5 rounded-[3px]\"\n style={{ backgroundColor: s.varName }}\n />\n {s.label}\n <span className=\"tabular-nums text-text-muted\">\n {cat.statuses[s.key]}\n </span>\n </span>\n ))}\n </div>\n </div>\n\n {/* Avg condition */}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-sm\">\n <span className=\"text-text-secondary\">Avg. condition</span>\n <span className=\"font-semibold tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n <span className=\"text-xs font-normal text-text-muted\"> / 5</span>\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {/* Cross-category comparison chart */}\n <ChartCard\n title=\"Category comparison\"\n subtitle={chartMetric === 'count' ? 'Assets per category' : 'Book value per category'}\n actions={\n <div className=\"flex gap-1 rounded-md border border-border-seam p-0.5\">\n <Button\n variant={chartMetric === 'count' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('count')}\n >\n Count\n </Button>\n <Button\n variant={chartMetric === 'value' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('value')}\n >\n Value\n </Button>\n </div>\n }\n >\n <BarsChart\n data={chartData}\n xKey=\"category\"\n orientation=\"horizontal\"\n height={Math.max(200, chartData.length * 44)}\n categoryWidth={120}\n valueFormat={chartMetric === 'value' ? formatCurrencyCompact('USD') : formatCompact}\n series={[\n {\n key: chartMetric,\n label: chartMetric === 'count' ? 'Assets' : 'Book value',\n },\n ]}\n />\n </ChartCard>\n\n {/* Summary comparison table */}\n <ChartCard\n title=\"Category summary\"\n subtitle=\"Sortable roll-up across every category\"\n icon={Layers}\n >\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[520px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Category</th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('count')}\n >\n Count\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'count' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('value')}\n >\n Value\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'value' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 font-medium\">Avg. condition</th>\n </tr>\n </thead>\n <tbody>\n {sortedRows.map((cat) => (\n <tr key={cat.key} className=\"border-b border-border-seam/60 last:border-0\">\n <td className=\"py-2.5 pr-4 font-medium capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{cat.count}</td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{money(cat.totalValue)}</td>\n <td className=\"py-2.5 tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n Monitor,\n Server,\n Wifi,\n Truck,\n Wrench,\n Code,\n MoreHorizontal,\n Copy,\n ExternalLink,\n ArrowUpDown,\n Layers,\n} from 'lucide-react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetTerminology, useHiddenCategoryIds } from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerStore, type AssetStatus } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n ChartCard,\n StatTile,\n BarsChart,\n formatCompact,\n formatCurrencyCompact,\n} from '../components';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\n// Stable fallback tint for any category outside the known slug map — hashes the\n// key into the semantic palette so every real category is visually differentiated.\nconst FALLBACK_TINTS = [\n 'bg-status-info-bg-subtle text-status-info-text',\n 'bg-status-success-bg-subtle text-status-success-text',\n 'bg-status-warning-bg-subtle text-status-warning-text',\n 'bg-status-error-bg-subtle text-status-error-text',\n 'bg-action-primary-bg/10 text-action-primary-bg',\n];\nfunction tintFor(key: string): string {\n if (categoryColors[key]) return categoryColors[key];\n let hash = 0;\n for (let i = 0; i < key.length; i += 1) hash = (hash * 31 + key.charCodeAt(i)) >>> 0;\n return FALLBACK_TINTS[hash % FALLBACK_TINTS.length];\n}\n\n// Proportional status-bar segments — semantic tokens (CSS vars), theme-aware.\nconst STATUS_SEGMENTS: { key: AssetStatus; label: string; varName: string }[] = [\n { key: 'active', label: 'Active', varName: 'var(--color-status-success-bg)' },\n { key: 'maintenance', label: 'Maintenance', varName: 'var(--color-status-warning-bg)' },\n { key: 'retired', label: 'Retired', varName: 'var(--color-status-error-bg)' },\n { key: 'inactive', label: 'Inactive', varName: 'var(--color-status-info-bg)' },\n { key: 'disposed', label: 'Disposed', varName: 'var(--color-text-muted)' },\n];\n\nconst CONDITION_SCORE: Record<string, number> = {\n excellent: 5,\n good: 4,\n fair: 3,\n poor: 2,\n damaged: 1,\n};\n\ninterface CategoryAggregate {\n key: string;\n count: number;\n totalValue: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n avgCondition: number;\n}\n\nconst money = (value: number): string => `$${value.toLocaleString()}`;\n\nexport function AssetsAssetCategoriesPage() {\n const navigate = useAssetHandlerNavigate();\n const term = useAssetTerminology();\n const hiddenCategories = useHiddenCategoryIds();\n const { assets: allAssets } = useAssetHandlerStore();\n const assets = useMemo(\n () => allAssets.filter((a) => !hiddenCategories.has(a.category)),\n [allAssets, hiddenCategories]\n );\n const [chartMetric, setChartMetric] = useState<'count' | 'value'>('count');\n const [sortKey, setSortKey] = useState<'count' | 'value'>('count');\n\n const aggregates = useMemo<CategoryAggregate[]>(() => {\n const map: Record<string, CategoryAggregate> = {};\n for (const asset of assets) {\n if (!map[asset.category]) {\n map[asset.category] = {\n key: asset.category,\n count: 0,\n totalValue: 0,\n statuses: {},\n conditions: {},\n avgCondition: 0,\n };\n }\n const cat = map[asset.category];\n cat.count += 1;\n cat.totalValue += asset.currentValue ?? asset.purchaseValue ?? 0;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n }\n for (const cat of Object.values(map)) {\n let scoreSum = 0;\n let scoreN = 0;\n for (const [cond, n] of Object.entries(cat.conditions)) {\n const score = CONDITION_SCORE[cond];\n if (score) {\n scoreSum += score * n;\n scoreN += n;\n }\n }\n cat.avgCondition = scoreN > 0 ? scoreSum / scoreN : 0;\n }\n return Object.values(map).sort((a, b) => b.count - a.count);\n }, [assets]);\n\n const totalAssets = assets.length;\n const totalValue = useMemo(\n () => assets.reduce((sum, a) => sum + (a.currentValue ?? a.purchaseValue ?? 0), 0),\n [assets]\n );\n\n const chartData = useMemo(\n () =>\n aggregates.map((cat) => ({\n category: cat.key.replace(/_/g, ' '),\n count: cat.count,\n value: cat.totalValue,\n })),\n [aggregates]\n );\n\n const sortedRows = useMemo(\n () =>\n [...aggregates].sort((a, b) =>\n sortKey === 'count' ? b.count - a.count : b.totalValue - a.totalValue\n ),\n [aggregates, sortKey]\n );\n\n const copyId = (key: string) => {\n void navigator.clipboard?.writeText(key);\n notify.success('Category copied', `\"${key.replace(/_/g, ' ')}\" copied to clipboard.`);\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{term.asset} Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {aggregates.length} categories &middot; {totalAssets} total{' '}\n {term.assets.toLowerCase()}\n </p>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where value and volume are concentrated.\n Each card rolls up how many assets you hold, their total book value and a proportional\n status mix — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {aggregates.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description={`Add ${term.assets.toLowerCase()} and they will be grouped into categories here automatically.`}\n />\n ) : (\n <>\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <StatTile\n label=\"Categories\"\n value={aggregates.length}\n format={formatCompact}\n icon={Layers}\n hero\n />\n <StatTile\n label={`${term.assets} tracked`}\n value={totalAssets}\n format={formatCompact}\n icon={Box}\n />\n <StatTile\n label=\"Total book value\"\n value={totalValue}\n format={formatCurrencyCompact('USD')}\n icon={Box}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {aggregates.map((cat, idx) => {\n const pct = totalAssets > 0 ? Math.round((cat.count / totalAssets) * 100) : 0;\n const segments = STATUS_SEGMENTS.filter((s) => (cat.statuses[s.key] || 0) > 0);\n return (\n <GlassCard\n key={cat.key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <div className=\"space-y-4 p-5\">\n {/* Header: icon + title + overflow */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className={`shrink-0 rounded-lg p-2 ${tintFor(cat.key)}`}>\n {categoryIcons[cat.key] || <Box className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </p>\n <Badge variant=\"outline\" className=\"mt-0.5 text-xs\">\n {pct}% of estate\n </Badge>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0\"\n aria-label={`Actions for ${cat.key.replace(/_/g, ' ')}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => navigate('/assets/asset-list')}>\n <ExternalLink className=\"mr-2 h-4 w-4\" />\n View {term.assets.toLowerCase()}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => copyId(cat.key)}>\n <Copy className=\"mr-2 h-4 w-4\" />\n Copy category ID\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Dual metric row */}\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <p className=\"text-xs text-text-secondary\">{term.asset} count</p>\n <p className=\"text-2xl font-bold tabular-nums\">{cat.count}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary\">Total value</p>\n <p className=\"text-2xl font-bold tabular-nums\">\n {formatCurrencyCompact('USD')(cat.totalValue)}\n </p>\n </div>\n </div>\n\n {/* Proportional status bar */}\n <div>\n <div\n className=\"flex h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"img\"\n aria-label={segments\n .map((s) => `${s.label}: ${cat.statuses[s.key]}`)\n .join(', ')}\n >\n {segments.map((s) => (\n <div\n key={s.key}\n style={{\n width: `${((cat.statuses[s.key] || 0) / cat.count) * 100}%`,\n backgroundColor: s.varName,\n }}\n />\n ))}\n </div>\n <div className=\"mt-2 flex flex-wrap gap-x-3 gap-y-1\">\n {segments.map((s) => (\n <span\n key={s.key}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary\"\n >\n <span\n className=\"h-2.5 w-2.5 rounded-[3px]\"\n style={{ backgroundColor: s.varName }}\n />\n {s.label}\n <span className=\"tabular-nums text-text-muted\">\n {cat.statuses[s.key]}\n </span>\n </span>\n ))}\n </div>\n </div>\n\n {/* Avg condition */}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-sm\">\n <span className=\"text-text-secondary\">Avg. condition</span>\n <span className=\"font-semibold tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n <span className=\"text-xs font-normal text-text-muted\"> / 5</span>\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {/* Cross-category comparison chart */}\n <ChartCard\n title=\"Category comparison\"\n subtitle={\n chartMetric === 'count'\n ? `${term.assets} per category`\n : 'Book value per category'\n }\n actions={\n <div className=\"flex gap-1 rounded-md border border-border-seam p-0.5\">\n <Button\n variant={chartMetric === 'count' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('count')}\n >\n Count\n </Button>\n <Button\n variant={chartMetric === 'value' ? 'default' : 'ghost'}\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setChartMetric('value')}\n >\n Value\n </Button>\n </div>\n }\n >\n <BarsChart\n data={chartData}\n xKey=\"category\"\n orientation=\"horizontal\"\n height={Math.max(200, chartData.length * 44)}\n categoryWidth={120}\n valueFormat={chartMetric === 'value' ? formatCurrencyCompact('USD') : formatCompact}\n series={[\n {\n key: chartMetric,\n label: chartMetric === 'count' ? term.assets : 'Book value',\n },\n ]}\n />\n </ChartCard>\n\n {/* Summary comparison table */}\n <ChartCard\n title=\"Category summary\"\n subtitle=\"Sortable roll-up across every category\"\n icon={Layers}\n >\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[520px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Category</th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('count')}\n >\n Count\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'count' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 pr-4 font-medium\">\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1 hover:text-text-primary\"\n onClick={() => setSortKey('value')}\n >\n Value\n <ArrowUpDown\n className={`h-3 w-3 ${sortKey === 'value' ? 'text-action-primary-bg' : ''}`}\n />\n </button>\n </th>\n <th className=\"py-2 font-medium\">Avg. condition</th>\n </tr>\n </thead>\n <tbody>\n {sortedRows.map((cat) => (\n <tr key={cat.key} className=\"border-b border-border-seam/60 last:border-0\">\n <td className=\"py-2.5 pr-4 font-medium capitalize\">\n {cat.key.replace(/_/g, ' ')}\n </td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{cat.count}</td>\n <td className=\"py-2.5 pr-4 tabular-nums\">{money(cat.totalValue)}</td>\n <td className=\"py-2.5 tabular-nums\">\n {cat.avgCondition > 0 ? cat.avgCondition.toFixed(1) : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </ChartCard>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AAyIA,wBAAgB,qBAAqB,gCAsfpC"}
1
+ {"version":3,"file":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AA0IA,wBAAgB,qBAAqB,gCAufpC"}