@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.
- package/dist/assethandler/AssetHandlerRoot.d.ts.map +1 -1
- package/dist/assethandler/AssetHandlerRoot.js +60 -46
- package/dist/assethandler/AssetHandlerRoot.js.map +1 -1
- package/dist/assethandler/AssetHandlerRoutes.d.ts.map +1 -1
- package/dist/assethandler/AssetHandlerRoutes.js +47 -39
- package/dist/assethandler/AssetHandlerRoutes.js.map +1 -1
- package/dist/assethandler/pages/ForecastingAlertsPage.d.ts +2 -0
- package/dist/assethandler/pages/ForecastingAlertsPage.d.ts.map +1 -0
- package/dist/assethandler/pages/ForecastingAlertsPage.js +160 -0
- package/dist/assethandler/pages/ForecastingAlertsPage.js.map +1 -0
- package/dist/assethandler/pages/PhysicalAuditsPage.d.ts +2 -0
- package/dist/assethandler/pages/PhysicalAuditsPage.d.ts.map +1 -0
- package/dist/assethandler/pages/PhysicalAuditsPage.js +465 -0
- package/dist/assethandler/pages/PhysicalAuditsPage.js.map +1 -0
- package/dist/generated/wspace-types.d.ts +101 -0
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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 @@
|
|
|
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
|