@burdenoff/microfe-assethandler 2026.826.6 → 2026.826.8

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.
@@ -0,0 +1,160 @@
1
+ import { useAssetHandlerNavigate as e } from "../hooks/useAssetHandlerNavigate.js";
2
+ import { SectionHeader as t } from "../components/SectionHeader.js";
3
+ import "../components/index.js";
4
+ import { useMemo as n, useState as r } from "react";
5
+ import { AlertTriangle as i, RefreshCw as a } from "lucide-react";
6
+ import { gql as o } from "@apollo/client";
7
+ import { useQuery as s } from "@apollo/client/react";
8
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
9
+ import { Badge as u, Button as d, GlassCard as f, IllustratedEmptyState as p, PagePurpose as m, Select as h, SelectContent as g, SelectItem as _, SelectTrigger as v, SelectValue as y } from "@burdenoff/fe-libs/ui";
10
+ //#region src/assethandler/pages/ForecastingAlertsPage.tsx
11
+ var b = o`
12
+ query AssetHandlerDeadlineAlerts($withinDays: Int!) {
13
+ assetDeadlineAlerts(withinDays: $withinDays) {
14
+ id
15
+ kind
16
+ subjectId
17
+ subjectName
18
+ assetId
19
+ dueAt
20
+ daysRemaining
21
+ status
22
+ headline
23
+ recommendedAction
24
+ }
25
+ }
26
+ `, x = {
27
+ WARRANTY_EXPIRY: "Warranty expiry",
28
+ LICENSE_RENEWAL: "Licence renewal",
29
+ END_OF_LIFE: "Replacement planning",
30
+ MAINTENANCE_DUE: "Maintenance due"
31
+ }, S = {
32
+ OVERDUE: "bg-status-error-bg text-status-error-text",
33
+ DUE_SOON: "bg-status-warning-bg text-status-warning-text",
34
+ UPCOMING: "bg-status-info-bg text-status-info-text"
35
+ };
36
+ function C(e) {
37
+ if (e.daysRemaining < 0) {
38
+ let t = Math.abs(e.daysRemaining);
39
+ return `${t} ${t === 1 ? "day" : "days"} overdue`;
40
+ }
41
+ return e.daysRemaining === 0 ? "Due today" : `Due in ${e.daysRemaining} days`;
42
+ }
43
+ function w() {
44
+ let o = e(), [w, T] = r(90), { data: E, loading: D, error: O, refetch: k } = s(b, {
45
+ variables: { withinDays: w },
46
+ fetchPolicy: "network-only"
47
+ }), A = n(() => E?.assetDeadlineAlerts ?? [], [E?.assetDeadlineAlerts]);
48
+ return /* @__PURE__ */ l("div", {
49
+ className: "space-y-5 p-4 sm:p-6",
50
+ children: [
51
+ /* @__PURE__ */ c(t, {
52
+ title: "Deadline Alerts",
53
+ description: "Warranty, licence, replacement, and maintenance deadlines derived from your saved registry facts.",
54
+ actions: /* @__PURE__ */ l("div", {
55
+ className: "flex items-center gap-2",
56
+ children: [/* @__PURE__ */ l(h, {
57
+ value: String(w),
58
+ onValueChange: (e) => T(Number(e)),
59
+ children: [/* @__PURE__ */ c(v, {
60
+ className: "w-36",
61
+ "aria-label": "Planning window",
62
+ children: /* @__PURE__ */ c(y, {})
63
+ }), /* @__PURE__ */ l(g, { children: [
64
+ /* @__PURE__ */ c(_, {
65
+ value: "30",
66
+ children: "Next 30 days"
67
+ }),
68
+ /* @__PURE__ */ c(_, {
69
+ value: "90",
70
+ children: "Next 90 days"
71
+ }),
72
+ /* @__PURE__ */ c(_, {
73
+ value: "180",
74
+ children: "Next 180 days"
75
+ }),
76
+ /* @__PURE__ */ c(_, {
77
+ value: "365",
78
+ children: "Next 365 days"
79
+ })
80
+ ] })]
81
+ }), /* @__PURE__ */ l(d, {
82
+ variant: "outline",
83
+ onClick: () => void k(),
84
+ disabled: D,
85
+ children: [/* @__PURE__ */ c(a, { className: "mr-2 h-4 w-4" }), "Refresh"]
86
+ })]
87
+ })
88
+ }),
89
+ /* @__PURE__ */ c(m, { children: "Alerts remain stable across reloads because each one is calculated from the persisted source record and carries a deterministic source-backed ID." }),
90
+ O ? /* @__PURE__ */ l(f, {
91
+ treatment: "glass",
92
+ className: "border-status-error-border p-4",
93
+ children: [/* @__PURE__ */ c("p", {
94
+ className: "font-medium text-status-error-text",
95
+ children: "Could not load deadline alerts"
96
+ }), /* @__PURE__ */ c("p", {
97
+ className: "mt-1 text-sm text-text-muted",
98
+ children: O.message
99
+ })]
100
+ }) : null,
101
+ !D && !O && A.length === 0 ? /* @__PURE__ */ c(p, {
102
+ illustration: "empty-notifications",
103
+ title: "No deadlines in this window",
104
+ description: "Add warranty, useful-life, licence-expiry, or scheduled-maintenance dates to raise planning alerts."
105
+ }) : null,
106
+ /* @__PURE__ */ c("div", {
107
+ className: "grid gap-3 lg:grid-cols-2",
108
+ "aria-busy": D,
109
+ children: A.map((e) => /* @__PURE__ */ l(f, {
110
+ treatment: "glass",
111
+ className: "border-border-seam p-4",
112
+ children: [/* @__PURE__ */ l("div", {
113
+ className: "flex items-start justify-between gap-3",
114
+ children: [/* @__PURE__ */ l("div", {
115
+ className: "min-w-0",
116
+ children: [
117
+ /* @__PURE__ */ l("div", {
118
+ className: "flex flex-wrap items-center gap-2",
119
+ children: [/* @__PURE__ */ c(u, {
120
+ variant: "outline",
121
+ children: x[e.kind]
122
+ }), /* @__PURE__ */ c("span", {
123
+ className: `rounded-full px-2 py-0.5 text-xs ${S[e.status]}`,
124
+ children: C(e)
125
+ })]
126
+ }),
127
+ /* @__PURE__ */ c("h2", {
128
+ className: "mt-3 font-semibold text-text-primary",
129
+ children: e.headline
130
+ }),
131
+ /* @__PURE__ */ c("p", {
132
+ className: "mt-1 break-words text-sm text-text-secondary",
133
+ children: e.subjectName
134
+ }),
135
+ /* @__PURE__ */ c("p", {
136
+ className: "mt-2 text-sm text-text-muted",
137
+ children: e.recommendedAction
138
+ }),
139
+ /* @__PURE__ */ l("p", {
140
+ className: "mt-3 text-xs text-text-muted",
141
+ children: ["Deadline ", new Date(e.dueAt).toLocaleDateString()]
142
+ })
143
+ ]
144
+ }), /* @__PURE__ */ c(i, { className: "h-5 w-5 shrink-0 text-status-warning-text" })]
145
+ }), e.assetId ? /* @__PURE__ */ c(d, {
146
+ className: "mt-4",
147
+ variant: "outline",
148
+ size: "sm",
149
+ onClick: () => o(`/assethandler/assets/${e.assetId}`),
150
+ children: "View asset"
151
+ }) : null]
152
+ }, e.id))
153
+ })
154
+ ]
155
+ });
156
+ }
157
+ //#endregion
158
+ export { w as ForecastingAlertsPage };
159
+
160
+ //# sourceMappingURL=ForecastingAlertsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ForecastingAlertsPage.js","names":[],"sources":["../../../src/assethandler/pages/ForecastingAlertsPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { useMemo, useState } from 'react';\n\nimport {\n Badge,\n Button,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { SectionHeader } from '../components';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\n\nconst DEADLINE_ALERTS_QUERY = gql`\n query AssetHandlerDeadlineAlerts($withinDays: Int!) {\n assetDeadlineAlerts(withinDays: $withinDays) {\n id\n kind\n subjectId\n subjectName\n assetId\n dueAt\n daysRemaining\n status\n headline\n recommendedAction\n }\n }\n`;\n\ntype AlertKind = 'WARRANTY_EXPIRY' | 'LICENSE_RENEWAL' | 'END_OF_LIFE' | 'MAINTENANCE_DUE';\n\ninterface DeadlineAlert {\n id: string;\n kind: AlertKind;\n subjectId: string;\n subjectName: string;\n assetId: string | null;\n dueAt: string;\n daysRemaining: number;\n status: 'OVERDUE' | 'DUE_SOON' | 'UPCOMING';\n headline: string;\n recommendedAction: string;\n}\n\ninterface DeadlineAlertsResult {\n assetDeadlineAlerts: DeadlineAlert[];\n}\n\nconst KIND_LABELS: Record<AlertKind, string> = {\n WARRANTY_EXPIRY: 'Warranty expiry',\n LICENSE_RENEWAL: 'Licence renewal',\n END_OF_LIFE: 'Replacement planning',\n MAINTENANCE_DUE: 'Maintenance due',\n};\n\nconst STATUS_CLASS: Record<DeadlineAlert['status'], string> = {\n OVERDUE: 'bg-status-error-bg text-status-error-text',\n DUE_SOON: 'bg-status-warning-bg text-status-warning-text',\n UPCOMING: 'bg-status-info-bg text-status-info-text',\n};\n\nfunction dueLabel(alert: DeadlineAlert): string {\n if (alert.daysRemaining < 0) {\n const overdueDays = Math.abs(alert.daysRemaining);\n return `${overdueDays} ${overdueDays === 1 ? 'day' : 'days'} overdue`;\n }\n if (alert.daysRemaining === 0) return 'Due today';\n return `Due in ${alert.daysRemaining} days`;\n}\n\nexport function ForecastingAlertsPage() {\n const navigate = useAssetHandlerNavigate();\n const [withinDays, setWithinDays] = useState(90);\n const { data, loading, error, refetch } = useQuery<DeadlineAlertsResult>(DEADLINE_ALERTS_QUERY, {\n variables: { withinDays },\n fetchPolicy: 'network-only',\n });\n const alerts = useMemo(() => data?.assetDeadlineAlerts ?? [], [data?.assetDeadlineAlerts]);\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <SectionHeader\n title=\"Deadline Alerts\"\n description=\"Warranty, licence, replacement, and maintenance deadlines derived from your saved registry facts.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <Select\n value={String(withinDays)}\n onValueChange={(value) => setWithinDays(Number(value))}\n >\n <SelectTrigger className=\"w-36\" aria-label=\"Planning window\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"30\">Next 30 days</SelectItem>\n <SelectItem value=\"90\">Next 90 days</SelectItem>\n <SelectItem value=\"180\">Next 180 days</SelectItem>\n <SelectItem value=\"365\">Next 365 days</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={() => void refetch()} disabled={loading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n Refresh\n </Button>\n </div>\n }\n />\n <PagePurpose>\n Alerts remain stable across reloads because each one is calculated from the persisted source\n record and carries a deterministic source-backed ID.\n </PagePurpose>\n\n {error ? (\n <GlassCard treatment=\"glass\" className=\"border-status-error-border p-4\">\n <p className=\"font-medium text-status-error-text\">Could not load deadline alerts</p>\n <p className=\"mt-1 text-sm text-text-muted\">{error.message}</p>\n </GlassCard>\n ) : null}\n\n {!loading && !error && alerts.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No deadlines in this window\"\n description=\"Add warranty, useful-life, licence-expiry, or scheduled-maintenance dates to raise planning alerts.\"\n />\n ) : null}\n\n <div className=\"grid gap-3 lg:grid-cols-2\" aria-busy={loading}>\n {alerts.map((alert) => (\n <GlassCard key={alert.id} treatment=\"glass\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\">{KIND_LABELS[alert.kind]}</Badge>\n <span\n className={`rounded-full px-2 py-0.5 text-xs ${STATUS_CLASS[alert.status]}`}\n >\n {dueLabel(alert)}\n </span>\n </div>\n <h2 className=\"mt-3 font-semibold text-text-primary\">{alert.headline}</h2>\n <p className=\"mt-1 break-words text-sm text-text-secondary\">{alert.subjectName}</p>\n <p className=\"mt-2 text-sm text-text-muted\">{alert.recommendedAction}</p>\n <p className=\"mt-3 text-xs text-text-muted\">\n Deadline {new Date(alert.dueAt).toLocaleDateString()}\n </p>\n </div>\n <AlertTriangle className=\"h-5 w-5 shrink-0 text-status-warning-text\" />\n </div>\n {alert.assetId ? (\n <Button\n className=\"mt-4\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(`/assethandler/assets/${alert.assetId}`)}\n >\n View asset\n </Button>\n ) : null}\n </GlassCard>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function PhysicalAuditsPage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=PhysicalAuditsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhysicalAuditsPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/PhysicalAuditsPage.tsx"],"names":[],"mappings":"AAoJA,wBAAgB,kBAAkB,gCA4XjC"}
@@ -0,0 +1,465 @@
1
+ import { notify as e } from "../utils/toast.js";
2
+ import { StatTile as t } from "../components/StatTile.js";
3
+ import { SectionHeader as n } from "../components/SectionHeader.js";
4
+ import "../components/index.js";
5
+ import { downloadCsv as r } from "../utils/csv.js";
6
+ import { useMemo as ee, useState as i } from "react";
7
+ import { CheckCircle2 as a, Download as o, MapPin as s, ScanLine as c, Trash2 as l } from "lucide-react";
8
+ import { gql as u } from "@apollo/client";
9
+ import { useMutation as d, useQuery as f } from "@apollo/client/react";
10
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
11
+ import { Button as g, GlassCard as _, Input as v, Label as y, PagePurpose as b, Textarea as x } from "@burdenoff/fe-libs/ui";
12
+ //#region src/assethandler/pages/PhysicalAuditsPage.tsx
13
+ var S = u`
14
+ fragment AssetHandlerPhysicalAuditFields on PhysicalAudit {
15
+ id
16
+ name
17
+ expectedLocation
18
+ status
19
+ notes
20
+ startedAt
21
+ completedAt
22
+ expectedCount
23
+ scannedCount
24
+ presentCount
25
+ missingCount
26
+ movedCount
27
+ unexpectedCount
28
+ unresolvedCount
29
+ items {
30
+ id
31
+ assetId
32
+ scannedTagId
33
+ assetNumber
34
+ assetName
35
+ expectedLocation
36
+ observedLocation
37
+ outcome
38
+ scannedAt
39
+ resolutionNotes
40
+ resolvedAt
41
+ }
42
+ }
43
+ `, C = u`
44
+ query AssetHandlerPhysicalAudits {
45
+ physicalAudits {
46
+ ...AssetHandlerPhysicalAuditFields
47
+ }
48
+ }
49
+ ${S}
50
+ `, w = u`
51
+ mutation AssetHandlerCreatePhysicalAudit($input: CreatePhysicalAuditInput!) {
52
+ createPhysicalAudit(input: $input) {
53
+ ...AssetHandlerPhysicalAuditFields
54
+ }
55
+ }
56
+ ${S}
57
+ `, T = u`
58
+ mutation AssetHandlerScanPhysicalAuditTag($input: ScanPhysicalAuditTagInput!) {
59
+ scanPhysicalAuditTag(input: $input) {
60
+ id
61
+ outcome
62
+ }
63
+ }
64
+ `, E = u`
65
+ mutation AssetHandlerCompletePhysicalAudit($id: ID!) {
66
+ completePhysicalAudit(id: $id) {
67
+ ...AssetHandlerPhysicalAuditFields
68
+ }
69
+ }
70
+ ${S}
71
+ `, D = u`
72
+ mutation AssetHandlerResolvePhysicalAuditItem($input: ResolvePhysicalAuditItemInput!) {
73
+ resolvePhysicalAuditItem(input: $input) {
74
+ id
75
+ resolutionNotes
76
+ resolvedAt
77
+ }
78
+ }
79
+ `, te = u`
80
+ mutation AssetHandlerDeletePhysicalAudit($id: ID!) {
81
+ deletePhysicalAudit(id: $id)
82
+ }
83
+ `, O = {
84
+ PENDING: "bg-bg-sunken text-text-secondary",
85
+ PRESENT: "bg-status-success-subtle text-status-success-text",
86
+ MISSING: "bg-status-error-subtle text-status-error-text",
87
+ MOVED: "bg-status-warning-subtle text-status-warning-text",
88
+ UNEXPECTED: "bg-status-info-bg-subtle text-status-info-text"
89
+ };
90
+ function ne(e) {
91
+ return e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
92
+ }
93
+ function k(e) {
94
+ return e ? new Date(e).toLocaleString() : "—";
95
+ }
96
+ function A() {
97
+ let { data: u, loading: S, error: A, refetch: j } = f(C), [M, N] = i(null), [P, F] = i(""), [I, L] = i(""), [R, z] = i(""), [B, V] = i(""), [H, U] = i(""), [W, G] = i(""), K = u?.physicalAudits ?? [], q = ee(() => K.find((e) => e.id === M) ?? K[0] ?? null, [K, M]), [J, Y] = d(w), [X, Z] = d(T), [re, ie] = d(E), [ae, oe] = d(D), [se, Q] = d(te), $ = async () => {
98
+ await j();
99
+ }, ce = async (t) => {
100
+ t.preventDefault();
101
+ let n = (await J({ variables: { input: {
102
+ name: P.trim(),
103
+ ...I.trim() ? { expectedLocation: I.trim() } : {},
104
+ ...R.trim() ? { notes: R.trim() } : {}
105
+ } } })).data?.createPhysicalAudit;
106
+ n && (N(n.id), F(""), z(""), U(n.expectedLocation ?? ""), await $(), e.success("Physical audit started", `${n.expectedCount} expected assets snapshotted.`));
107
+ }, le = async (t) => {
108
+ t.preventDefault(), q && (await X({ variables: { input: {
109
+ auditId: q.id,
110
+ tagId: B.trim(),
111
+ observedLocation: H.trim()
112
+ } } }), V(""), await $(), e.success("Tag reconciled", "The audit result was persisted against the expected inventory."));
113
+ }, ue = async () => {
114
+ q && (await re({ variables: { id: q.id } }), await $(), e.success("Audit completed", "Unscanned expected assets are now marked missing."));
115
+ }, de = async (t, n) => {
116
+ await ae({ variables: { input: {
117
+ itemId: t.id,
118
+ resolutionNotes: W.trim(),
119
+ updateRegistryLocation: n
120
+ } } }), await $(), e.success("Exception reconciled", n ? "The asset registry location was corrected." : "Review evidence saved.");
121
+ }, fe = async () => {
122
+ !q || !window.confirm(`Delete physical audit "${q.name}"?`) || (await se({ variables: { id: q.id } }), N(null), await $(), e.success("Audit deleted", "The audit and its scan evidence were removed."));
123
+ }, pe = () => {
124
+ q && r(`physical-audit-${ne(q.name) || q.id}.csv`, [
125
+ "Audit ID",
126
+ "Audit name",
127
+ "Status",
128
+ "Tag ID",
129
+ "Asset number",
130
+ "Asset name",
131
+ "Expected location",
132
+ "Observed location",
133
+ "Outcome",
134
+ "Scanned at",
135
+ "Resolved at",
136
+ "Resolution notes"
137
+ ], q.items.map((e) => [
138
+ q.id,
139
+ q.name,
140
+ q.status,
141
+ e.scannedTagId,
142
+ e.assetNumber,
143
+ e.assetName,
144
+ e.expectedLocation,
145
+ e.observedLocation,
146
+ e.outcome,
147
+ e.scannedAt,
148
+ e.resolvedAt,
149
+ e.resolutionNotes
150
+ ]));
151
+ };
152
+ if (A) throw A;
153
+ return /* @__PURE__ */ h("div", {
154
+ className: "space-y-6 p-6",
155
+ children: [
156
+ /* @__PURE__ */ m(n, {
157
+ hero: !0,
158
+ icon: c,
159
+ title: "Physical audits",
160
+ description: "Run a reproducible on-site stocktake."
161
+ }),
162
+ /* @__PURE__ */ m(b, { children: "Snapshot expected inventory, scan tags on site, reconcile present, missing, moved and unexpected items, then retain and export the evidence." }),
163
+ /* @__PURE__ */ h(_, {
164
+ className: "p-5",
165
+ children: [/* @__PURE__ */ m(n, {
166
+ title: "Start a stocktake",
167
+ description: "Expected inventory is frozen when the audit starts."
168
+ }), /* @__PURE__ */ h("form", {
169
+ className: "mt-4 grid gap-4 md:grid-cols-3",
170
+ onSubmit: (e) => void ce(e),
171
+ children: [
172
+ /* @__PURE__ */ h("div", {
173
+ className: "space-y-2",
174
+ children: [/* @__PURE__ */ m(y, {
175
+ htmlFor: "audit-name",
176
+ children: "Audit name"
177
+ }), /* @__PURE__ */ m(v, {
178
+ id: "audit-name",
179
+ value: P,
180
+ onChange: (e) => F(e.target.value),
181
+ required: !0
182
+ })]
183
+ }),
184
+ /* @__PURE__ */ h("div", {
185
+ className: "space-y-2",
186
+ children: [/* @__PURE__ */ m(y, {
187
+ htmlFor: "expected-location",
188
+ children: "Expected location"
189
+ }), /* @__PURE__ */ m(v, {
190
+ id: "expected-location",
191
+ value: I,
192
+ onChange: (e) => L(e.target.value),
193
+ placeholder: "Leave blank for all active assets"
194
+ })]
195
+ }),
196
+ /* @__PURE__ */ h("div", {
197
+ className: "space-y-2",
198
+ children: [/* @__PURE__ */ m(y, {
199
+ htmlFor: "audit-notes",
200
+ children: "Audit notes"
201
+ }), /* @__PURE__ */ m(v, {
202
+ id: "audit-notes",
203
+ value: R,
204
+ onChange: (e) => z(e.target.value)
205
+ })]
206
+ }),
207
+ /* @__PURE__ */ h(g, {
208
+ type: "submit",
209
+ disabled: !P.trim() || Y.loading,
210
+ children: [/* @__PURE__ */ m(c, { className: "mr-2 h-4 w-4" }), " Start physical audit"]
211
+ })
212
+ ]
213
+ })]
214
+ }),
215
+ /* @__PURE__ */ h(_, {
216
+ className: "p-5",
217
+ children: [/* @__PURE__ */ m(n, {
218
+ title: "Audit sessions",
219
+ description: S ? "Loading…" : `${K.length} retained`
220
+ }), /* @__PURE__ */ h("div", {
221
+ className: "mt-4 flex flex-wrap gap-2",
222
+ children: [K.map((e) => /* @__PURE__ */ h(g, {
223
+ variant: q?.id === e.id ? "default" : "outline",
224
+ onClick: () => {
225
+ N(e.id), U(e.expectedLocation ?? "");
226
+ },
227
+ children: [
228
+ e.name,
229
+ " · ",
230
+ e.status.replace("_", " ")
231
+ ]
232
+ }, e.id)), !S && K.length === 0 && /* @__PURE__ */ m("p", {
233
+ className: "text-sm text-text-secondary",
234
+ children: "No audits yet."
235
+ })]
236
+ })]
237
+ }),
238
+ q && /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ h("div", {
239
+ className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-7",
240
+ children: [
241
+ /* @__PURE__ */ m(t, {
242
+ label: "Expected",
243
+ value: q.expectedCount,
244
+ icon: s
245
+ }),
246
+ /* @__PURE__ */ m(t, {
247
+ label: "Scanned",
248
+ value: q.scannedCount,
249
+ icon: c
250
+ }),
251
+ /* @__PURE__ */ m(t, {
252
+ label: "Present",
253
+ value: q.presentCount,
254
+ icon: a
255
+ }),
256
+ /* @__PURE__ */ m(t, {
257
+ label: "Missing",
258
+ value: q.missingCount,
259
+ icon: c
260
+ }),
261
+ /* @__PURE__ */ m(t, {
262
+ label: "Moved",
263
+ value: q.movedCount,
264
+ icon: s
265
+ }),
266
+ /* @__PURE__ */ m(t, {
267
+ label: "Unexpected",
268
+ value: q.unexpectedCount,
269
+ icon: c
270
+ }),
271
+ /* @__PURE__ */ m(t, {
272
+ label: "Unresolved",
273
+ value: q.unresolvedCount,
274
+ icon: c
275
+ })
276
+ ]
277
+ }), /* @__PURE__ */ h(_, {
278
+ className: "p-5",
279
+ children: [
280
+ /* @__PURE__ */ h("div", {
281
+ className: "flex flex-wrap items-start justify-between gap-3",
282
+ children: [/* @__PURE__ */ h("div", { children: [
283
+ /* @__PURE__ */ m("h2", {
284
+ className: "text-lg font-semibold text-text-primary",
285
+ children: q.name
286
+ }),
287
+ /* @__PURE__ */ h("p", {
288
+ className: "text-sm text-text-secondary",
289
+ children: [
290
+ "ID ",
291
+ q.id,
292
+ " · expected ",
293
+ q.expectedLocation ?? "all active locations",
294
+ " ",
295
+ "· started ",
296
+ k(q.startedAt)
297
+ ]
298
+ }),
299
+ q.completedAt && /* @__PURE__ */ h("p", {
300
+ className: "text-sm text-text-secondary",
301
+ children: ["Completed ", k(q.completedAt)]
302
+ })
303
+ ] }), /* @__PURE__ */ h("div", {
304
+ className: "flex gap-2",
305
+ children: [/* @__PURE__ */ h(g, {
306
+ variant: "outline",
307
+ onClick: pe,
308
+ children: [/* @__PURE__ */ m(o, { className: "mr-2 h-4 w-4" }), " Export CSV"]
309
+ }), /* @__PURE__ */ h(g, {
310
+ variant: "outline",
311
+ onClick: () => void fe(),
312
+ disabled: Q.loading,
313
+ children: [/* @__PURE__ */ m(l, { className: "mr-2 h-4 w-4" }), " Delete audit"]
314
+ })]
315
+ })]
316
+ }),
317
+ q.status === "IN_PROGRESS" && /* @__PURE__ */ h("form", {
318
+ className: "mt-5 grid gap-4 md:grid-cols-[1fr_1fr_auto]",
319
+ onSubmit: (e) => void le(e),
320
+ children: [
321
+ /* @__PURE__ */ h("div", {
322
+ className: "space-y-2",
323
+ children: [/* @__PURE__ */ m(y, {
324
+ htmlFor: "audit-tag",
325
+ children: "Scan or enter tag ID"
326
+ }), /* @__PURE__ */ m(v, {
327
+ id: "audit-tag",
328
+ value: B,
329
+ onChange: (e) => V(e.target.value),
330
+ required: !0
331
+ })]
332
+ }),
333
+ /* @__PURE__ */ h("div", {
334
+ className: "space-y-2",
335
+ children: [/* @__PURE__ */ m(y, {
336
+ htmlFor: "observed-location",
337
+ children: "Observed location"
338
+ }), /* @__PURE__ */ m(v, {
339
+ id: "observed-location",
340
+ value: H,
341
+ onChange: (e) => U(e.target.value),
342
+ required: !0
343
+ })]
344
+ }),
345
+ /* @__PURE__ */ m(g, {
346
+ className: "self-end",
347
+ type: "submit",
348
+ disabled: Z.loading || !B.trim() || !H.trim(),
349
+ children: "Reconcile scan"
350
+ })
351
+ ]
352
+ }),
353
+ /* @__PURE__ */ m("div", {
354
+ className: "mt-5 overflow-x-auto",
355
+ children: /* @__PURE__ */ h("table", {
356
+ className: "w-full min-w-[980px] text-left text-sm",
357
+ children: [/* @__PURE__ */ m("thead", {
358
+ className: "border-b border-border text-text-secondary",
359
+ children: /* @__PURE__ */ h("tr", { children: [
360
+ /* @__PURE__ */ m("th", {
361
+ className: "px-3 py-2",
362
+ children: "Tag"
363
+ }),
364
+ /* @__PURE__ */ m("th", {
365
+ className: "px-3 py-2",
366
+ children: "Asset"
367
+ }),
368
+ /* @__PURE__ */ m("th", {
369
+ className: "px-3 py-2",
370
+ children: "Expected"
371
+ }),
372
+ /* @__PURE__ */ m("th", {
373
+ className: "px-3 py-2",
374
+ children: "Observed"
375
+ }),
376
+ /* @__PURE__ */ m("th", {
377
+ className: "px-3 py-2",
378
+ children: "Outcome"
379
+ }),
380
+ /* @__PURE__ */ m("th", {
381
+ className: "px-3 py-2",
382
+ children: "Resolution"
383
+ })
384
+ ] })
385
+ }), /* @__PURE__ */ m("tbody", { children: q.items.map((e) => /* @__PURE__ */ h("tr", {
386
+ className: "border-b border-border/60",
387
+ children: [
388
+ /* @__PURE__ */ m("td", {
389
+ className: "px-3 py-3 font-mono text-xs",
390
+ children: e.scannedTagId
391
+ }),
392
+ /* @__PURE__ */ m("td", {
393
+ className: "px-3 py-3",
394
+ children: e.assetName ?? e.assetNumber ?? "Unregistered tag"
395
+ }),
396
+ /* @__PURE__ */ m("td", {
397
+ className: "px-3 py-3",
398
+ children: e.expectedLocation ?? "—"
399
+ }),
400
+ /* @__PURE__ */ m("td", {
401
+ className: "px-3 py-3",
402
+ children: e.observedLocation ?? "—"
403
+ }),
404
+ /* @__PURE__ */ m("td", {
405
+ className: "px-3 py-3",
406
+ children: /* @__PURE__ */ m("span", {
407
+ className: `rounded-full px-2 py-1 text-xs font-medium ${O[e.outcome]}`,
408
+ children: e.outcome
409
+ })
410
+ }),
411
+ /* @__PURE__ */ m("td", {
412
+ className: "px-3 py-3",
413
+ children: e.resolutionNotes ?? "—"
414
+ })
415
+ ]
416
+ }, e.id)) })]
417
+ })
418
+ }),
419
+ q.status === "IN_PROGRESS" && /* @__PURE__ */ m(g, {
420
+ className: "mt-5",
421
+ onClick: () => void ue(),
422
+ disabled: ie.loading,
423
+ children: "Complete audit and mark unscanned items missing"
424
+ }),
425
+ q.status === "COMPLETED" && q.unresolvedCount > 0 && /* @__PURE__ */ h("div", {
426
+ className: "mt-5 space-y-3 rounded-lg border border-border p-4",
427
+ children: [
428
+ /* @__PURE__ */ m(y, {
429
+ htmlFor: "resolution-notes",
430
+ children: "Exception resolution evidence"
431
+ }),
432
+ /* @__PURE__ */ m(x, {
433
+ id: "resolution-notes",
434
+ value: W,
435
+ onChange: (e) => G(e.target.value),
436
+ placeholder: "Record who verified the discrepancy and what changed."
437
+ }),
438
+ /* @__PURE__ */ m("div", {
439
+ className: "flex flex-wrap gap-2",
440
+ children: q.items.filter((e) => [
441
+ "MISSING",
442
+ "MOVED",
443
+ "UNEXPECTED"
444
+ ].includes(e.outcome) && !e.resolvedAt).map((e) => /* @__PURE__ */ h(g, {
445
+ variant: "outline",
446
+ disabled: !W.trim() || oe.loading,
447
+ onClick: () => void de(e, e.outcome === "MOVED"),
448
+ children: [
449
+ "Resolve ",
450
+ e.scannedTagId,
451
+ e.outcome === "MOVED" ? " and update location" : ""
452
+ ]
453
+ }, e.id))
454
+ })
455
+ ]
456
+ })
457
+ ]
458
+ })] })
459
+ ]
460
+ });
461
+ }
462
+ //#endregion
463
+ export { A as PhysicalAuditsPage };
464
+
465
+ //# sourceMappingURL=PhysicalAuditsPage.js.map