@burdenoff/microfe-assethandler 2026.1102.1 → 2026.1103.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import { AssetHandlerRootProps } from '../types';
2
+
3
+ /** Host props the admin surface needs from `admin-app`. */
4
+ export type AdminRootProps = Pick<AssetHandlerRootProps, 'basePath' | 'navigate' | 'currentUser' | 'workspaceId' | 'tenantId' | 'apiGatewayUrl' | 'authToken' | 'workspaceToken'>;
5
+ export declare const AdminContext: import('react').Context<AdminRootProps | null>;
6
+ export declare const useAssetHandlerAdmin: () => AdminRootProps;
7
+ //# sourceMappingURL=AdminContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminContext.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/AdminContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,qBAAqB,EACnB,UAAU,GACV,UAAU,GACV,aAAa,GACb,aAAa,GACb,UAAU,GACV,eAAe,GACf,WAAW,GACX,gBAAgB,CACnB,CAAC;AAEF,eAAO,MAAM,YAAY,gDAA6C,CAAC;AAEvE,eAAO,MAAM,oBAAoB,QAAO,cAMvC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { createContext as e, useContext as t } from "react";
2
+ //#region src/assethandler/admin/AdminContext.ts
3
+ var n = e(null), r = () => {
4
+ let e = t(n);
5
+ if (!e) throw Error("useAssetHandlerAdmin must be used within AdminRoot");
6
+ return e;
7
+ };
8
+ //#endregion
9
+ export { n as AdminContext, r as useAssetHandlerAdmin };
10
+
11
+ //# sourceMappingURL=AdminContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminContext.js","names":[],"sources":["../../../src/assethandler/admin/AdminContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nimport type { AssetHandlerRootProps } from '../types';\n\n/** Host props the admin surface needs from `admin-app`. */\nexport type AdminRootProps = Pick<\n AssetHandlerRootProps,\n | 'basePath'\n | 'navigate'\n | 'currentUser'\n | 'workspaceId'\n | 'tenantId'\n | 'apiGatewayUrl'\n | 'authToken'\n | 'workspaceToken'\n>;\n\nexport const AdminContext = createContext<AdminRootProps | null>(null);\n\nexport const useAssetHandlerAdmin = (): AdminRootProps => {\n const context = useContext(AdminContext);\n if (!context) {\n throw new Error('useAssetHandlerAdmin must be used within AdminRoot');\n }\n return context;\n};\n"],"mappings":";;AAiBA,IAAa,IAAe,EAAqC,IAAI,GAExD,UAA6C;CACxD,IAAM,IAAU,EAAW,CAAY;CACvC,IAAI,CAAC,GACH,MAAU,MAAM,oDAAoD;CAEtE,OAAO;AACT"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const AdminPage: FC;
4
+ //# sourceMappingURL=AdminPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/AdminPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAY,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAqD1C,eAAO,MAAM,SAAS,EAAE,EA+RvB,CAAC"}
@@ -0,0 +1,358 @@
1
+ import { StatTile as e } from "../components/StatTile.js";
2
+ import { SectionHeader as t } from "../components/SectionHeader.js";
3
+ import "../components/index.js";
4
+ import { useAssetHandlerAdmin as n } from "./AdminContext.js";
5
+ import { ADMIN_FEATURE_FLAGS as r, ADMIN_QUOTA_OVERVIEW as i, ADMIN_WORKSPACE_STATE as a, ASSETHANDLER_QUOTAS as o, OVERRIDE_QUOTA as s, REMOVE_FEATURE_FLAG_OVERRIDE as c, SET_FEATURE_FLAG_OVERRIDE as l, parseQuotaLimit as u, quotaLabel as d } from "./adminOperations.js";
6
+ import { useState as f } from "react";
7
+ import { Boxes as p, CreditCard as m, Flag as h, Gauge as g, LifeBuoy as _, ShieldAlert as v } from "lucide-react";
8
+ import { useMutation as y, useQuery as b } from "@apollo/client/react";
9
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
10
+ import { Button as C, GlassCard as w, Input as T, Label as E } from "@burdenoff/fe-libs/ui";
11
+ //#region src/assethandler/admin/AdminPage.tsx
12
+ var D = [
13
+ {
14
+ path: "/admin/billing",
15
+ label: "Billing & plan assignment",
16
+ description: "View the workspace billing account and change its plan.",
17
+ icon: m
18
+ },
19
+ {
20
+ path: "/support",
21
+ label: "User support",
22
+ description: "Support tickets and user assistance for this workspace.",
23
+ icon: _
24
+ },
25
+ {
26
+ path: "/products/feature-flags",
27
+ label: "All feature flags",
28
+ description: "Platform-wide feature-flag management.",
29
+ icon: h
30
+ }
31
+ ];
32
+ function O(e) {
33
+ return e instanceof Error ? e.message : "Request failed";
34
+ }
35
+ var k = () => {
36
+ let { workspaceId: m = "", navigate: _ } = n(), [k, A] = f(null), [j, M] = f(o[0]?.name ?? ""), [N, P] = f(""), [F, I] = f(!1), L = !m, R = { workspaceId: m }, z = b(a, {
37
+ variables: R,
38
+ skip: L
39
+ }), B = b(r, {
40
+ variables: R,
41
+ skip: L
42
+ }), V = b(i, {
43
+ variables: R,
44
+ skip: L
45
+ }), [H, { loading: U }] = y(l), [W, { loading: G }] = y(c), [K, { loading: q }] = y(s);
46
+ if (!m) return /* @__PURE__ */ S("div", {
47
+ className: "flex min-h-[40vh] items-center justify-center text-text-secondary",
48
+ children: [/* @__PURE__ */ x(v, { className: "mr-2 h-5 w-5" }), "Workspace context is required."]
49
+ });
50
+ let J = z.data?.assetHandlerAdminWorkspaceState, Y = B.data?.assetHandlerFeatureFlags.items ?? [], X = V.data?.assetHandlerWorkspaceQuotaOverview.assignments ?? [], Z = [
51
+ z.error,
52
+ B.error,
53
+ V.error
54
+ ].filter(Boolean), Q = u(N), $ = !q && !!j && (F || Q !== null), ee = async (e, t) => {
55
+ try {
56
+ await H({ variables: { input: {
57
+ workspaceId: m,
58
+ key: e,
59
+ enabled: t
60
+ } } }), await B.refetch(), A({
61
+ tone: "info",
62
+ text: `${e} ${t ? "enabled" : "disabled"} for this workspace.`
63
+ });
64
+ } catch (e) {
65
+ A({
66
+ tone: "error",
67
+ text: O(e)
68
+ });
69
+ }
70
+ }, te = async (e) => {
71
+ try {
72
+ await W({ variables: {
73
+ workspaceId: m,
74
+ key: e
75
+ } }), await B.refetch(), A({
76
+ tone: "info",
77
+ text: `Override for ${e} removed.`
78
+ });
79
+ } catch (e) {
80
+ A({
81
+ tone: "error",
82
+ text: O(e)
83
+ });
84
+ }
85
+ }, ne = async () => {
86
+ if ($) try {
87
+ await K({ variables: { input: {
88
+ workspaceId: m,
89
+ quotaName: j,
90
+ noLimit: F,
91
+ limit: F ? null : Q
92
+ } } }), await V.refetch(), A({
93
+ tone: "info",
94
+ text: `${d(j)} set to ${F ? "unlimited" : String(Q)}.`
95
+ });
96
+ } catch (e) {
97
+ A({
98
+ tone: "error",
99
+ text: O(e)
100
+ });
101
+ }
102
+ };
103
+ return /* @__PURE__ */ S("div", {
104
+ className: "space-y-8 p-6",
105
+ children: [
106
+ /* @__PURE__ */ x(t, {
107
+ hero: !0,
108
+ title: "AssetHandler Admin",
109
+ description: "Workspace state, feature flags, asset limits, and shortcuts to billing and support."
110
+ }),
111
+ k && /* @__PURE__ */ S("div", {
112
+ role: k.tone === "error" ? "alert" : "status",
113
+ className: k.tone === "error" ? "rounded-md bg-status-error-subtle px-4 py-3 text-sm text-status-error-text" : "rounded-md bg-status-info-subtle px-4 py-3 text-sm text-status-info-text",
114
+ children: [k.text, /* @__PURE__ */ x("button", {
115
+ type: "button",
116
+ className: "ml-4 underline",
117
+ onClick: () => A(null),
118
+ children: "Dismiss"
119
+ })]
120
+ }),
121
+ Z.length > 0 && /* @__PURE__ */ S("div", {
122
+ role: "alert",
123
+ className: "rounded-md bg-status-error-subtle px-4 py-3 text-sm text-status-error-text",
124
+ children: ["Failed to load admin data. ", Z.map((e) => e?.message).join("; ")]
125
+ }),
126
+ /* @__PURE__ */ S("section", {
127
+ "aria-labelledby": "ah-admin-state",
128
+ children: [/* @__PURE__ */ x(t, {
129
+ title: "Workspace state",
130
+ icon: p
131
+ }), /* @__PURE__ */ S("div", {
132
+ id: "ah-admin-state",
133
+ className: "grid grid-cols-2 gap-4 md:grid-cols-5",
134
+ children: [
135
+ /* @__PURE__ */ x(e, {
136
+ label: "Assets",
137
+ value: J?.assetCount ?? "—"
138
+ }),
139
+ /* @__PURE__ */ x(e, {
140
+ label: "Open incidents",
141
+ value: J?.openIncidentCount ?? "—"
142
+ }),
143
+ /* @__PURE__ */ x(e, {
144
+ label: "Open work orders",
145
+ value: J?.openWorkOrderCount ?? "—"
146
+ }),
147
+ /* @__PURE__ */ x(e, {
148
+ label: "Maintenance plans",
149
+ value: J?.maintenancePlanCount ?? "—"
150
+ }),
151
+ /* @__PURE__ */ x(e, {
152
+ label: "Discovery jobs",
153
+ value: J?.discoveryJobCount ?? "—"
154
+ })
155
+ ]
156
+ })]
157
+ }),
158
+ /* @__PURE__ */ S("section", {
159
+ "aria-labelledby": "ah-admin-flags",
160
+ children: [/* @__PURE__ */ x(t, {
161
+ title: "Feature flags",
162
+ description: "Override a flag for this workspace only. Removing the override restores the platform default.",
163
+ icon: h
164
+ }), /* @__PURE__ */ S(w, {
165
+ className: "divide-y divide-border",
166
+ children: [Y.length === 0 && !B.loading && /* @__PURE__ */ x("p", {
167
+ className: "p-4 text-sm text-text-secondary",
168
+ children: "No AssetHandler feature flags found."
169
+ }), /* @__PURE__ */ x("ul", {
170
+ id: "ah-admin-flags",
171
+ children: Y.map((e) => /* @__PURE__ */ S("li", {
172
+ className: "flex flex-wrap items-center justify-between gap-3 p-4",
173
+ children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("p", {
174
+ className: "font-mono text-sm text-text-primary",
175
+ children: e.key
176
+ }), /* @__PURE__ */ S("p", {
177
+ className: "text-xs text-text-secondary",
178
+ children: [
179
+ e.category,
180
+ " · default ",
181
+ e.defaultValue ? "on" : "off",
182
+ " · currently",
183
+ " ",
184
+ /* @__PURE__ */ x("strong", { children: e.value ? "on" : "off" })
185
+ ]
186
+ })] }), /* @__PURE__ */ S("div", {
187
+ className: "flex gap-2",
188
+ children: [/* @__PURE__ */ x(C, {
189
+ type: "button",
190
+ size: "sm",
191
+ variant: "outline",
192
+ disabled: U,
193
+ onClick: () => void ee(e.key, !e.value),
194
+ children: e.value ? "Disable" : "Enable"
195
+ }), /* @__PURE__ */ x(C, {
196
+ type: "button",
197
+ size: "sm",
198
+ variant: "ghost",
199
+ disabled: G,
200
+ onClick: () => void te(e.key),
201
+ children: "Reset to default"
202
+ })]
203
+ })]
204
+ }, e.key))
205
+ })]
206
+ })]
207
+ }),
208
+ /* @__PURE__ */ S("section", {
209
+ "aria-labelledby": "ah-admin-quotas",
210
+ children: [/* @__PURE__ */ x(t, {
211
+ title: "Workspace limits",
212
+ description: "Quota assignments for this workspace, including the active-asset limit.",
213
+ icon: g
214
+ }), /* @__PURE__ */ S(w, {
215
+ className: "space-y-4 p-4",
216
+ children: [/* @__PURE__ */ S("table", {
217
+ id: "ah-admin-quotas",
218
+ className: "w-full text-left text-sm",
219
+ children: [/* @__PURE__ */ x("thead", {
220
+ className: "text-text-secondary",
221
+ children: /* @__PURE__ */ S("tr", { children: [
222
+ /* @__PURE__ */ x("th", {
223
+ className: "py-2",
224
+ children: "Quota"
225
+ }),
226
+ /* @__PURE__ */ x("th", {
227
+ className: "py-2",
228
+ children: "Limit"
229
+ }),
230
+ /* @__PURE__ */ x("th", {
231
+ className: "py-2",
232
+ children: "Used"
233
+ }),
234
+ /* @__PURE__ */ x("th", {
235
+ className: "py-2",
236
+ children: "Resets"
237
+ })
238
+ ] })
239
+ }), /* @__PURE__ */ S("tbody", { children: [X.length === 0 && /* @__PURE__ */ x("tr", { children: /* @__PURE__ */ x("td", {
240
+ colSpan: 4,
241
+ className: "py-3 text-text-secondary",
242
+ children: "No quota assignments yet — limits come from the workspace plan."
243
+ }) }), X.map((e) => /* @__PURE__ */ S("tr", { children: [
244
+ /* @__PURE__ */ x("td", {
245
+ className: "py-2",
246
+ children: d(e.quotaName)
247
+ }),
248
+ /* @__PURE__ */ x("td", {
249
+ className: "py-2",
250
+ children: e.noLimit ? "Unlimited" : e.limit ?? "—"
251
+ }),
252
+ /* @__PURE__ */ x("td", {
253
+ className: "py-2",
254
+ children: e.used
255
+ }),
256
+ /* @__PURE__ */ x("td", {
257
+ className: "py-2",
258
+ children: e.resetPeriod
259
+ })
260
+ ] }, e.id))] })]
261
+ }), /* @__PURE__ */ S("form", {
262
+ className: "grid gap-4 border-t border-border pt-4 md:grid-cols-4",
263
+ onSubmit: (e) => {
264
+ e.preventDefault(), ne();
265
+ },
266
+ children: [
267
+ /* @__PURE__ */ S("div", {
268
+ className: "space-y-2",
269
+ children: [/* @__PURE__ */ x(E, {
270
+ htmlFor: "ah-quota-name",
271
+ children: "Quota"
272
+ }), /* @__PURE__ */ x("select", {
273
+ id: "ah-quota-name",
274
+ className: "w-full rounded-md border border-border bg-surface px-3 py-2 text-sm",
275
+ value: j,
276
+ onChange: (e) => M(e.target.value),
277
+ children: o.map((e) => /* @__PURE__ */ x("option", {
278
+ value: e.name,
279
+ children: e.label
280
+ }, e.name))
281
+ })]
282
+ }),
283
+ /* @__PURE__ */ S("div", {
284
+ className: "space-y-2",
285
+ children: [/* @__PURE__ */ x(E, {
286
+ htmlFor: "ah-quota-limit",
287
+ children: "Limit"
288
+ }), /* @__PURE__ */ x(T, {
289
+ id: "ah-quota-limit",
290
+ inputMode: "numeric",
291
+ placeholder: "e.g. 500",
292
+ value: N,
293
+ disabled: F,
294
+ onChange: (e) => P(e.target.value)
295
+ })]
296
+ }),
297
+ /* @__PURE__ */ S("div", {
298
+ className: "flex items-end gap-2 pb-2",
299
+ children: [/* @__PURE__ */ x("input", {
300
+ id: "ah-quota-unlimited",
301
+ type: "checkbox",
302
+ checked: F,
303
+ onChange: (e) => I(e.target.checked)
304
+ }), /* @__PURE__ */ x(E, {
305
+ htmlFor: "ah-quota-unlimited",
306
+ children: "Unlimited"
307
+ })]
308
+ }),
309
+ /* @__PURE__ */ x("div", {
310
+ className: "flex items-end",
311
+ children: /* @__PURE__ */ x(C, {
312
+ type: "submit",
313
+ disabled: !$,
314
+ children: q ? "Saving…" : "Save limit"
315
+ })
316
+ })
317
+ ]
318
+ })]
319
+ })]
320
+ }),
321
+ /* @__PURE__ */ S("section", {
322
+ "aria-labelledby": "ah-admin-links",
323
+ children: [/* @__PURE__ */ x(t, {
324
+ title: "Billing & support",
325
+ description: "Handled by the shared admin surfaces."
326
+ }), /* @__PURE__ */ x("div", {
327
+ id: "ah-admin-links",
328
+ className: "grid gap-4 md:grid-cols-3",
329
+ children: D.map(({ path: e, label: t, description: n, icon: r }) => /* @__PURE__ */ S(w, {
330
+ className: "space-y-2 p-4",
331
+ children: [
332
+ /* @__PURE__ */ S("p", {
333
+ className: "flex items-center gap-2 font-medium text-text-primary",
334
+ children: [/* @__PURE__ */ x(r, { className: "h-4 w-4" }), t]
335
+ }),
336
+ /* @__PURE__ */ x("p", {
337
+ className: "text-sm text-text-secondary",
338
+ children: n
339
+ }),
340
+ /* @__PURE__ */ x(C, {
341
+ type: "button",
342
+ size: "sm",
343
+ variant: "outline",
344
+ disabled: !_,
345
+ onClick: () => _?.(e),
346
+ children: "Open"
347
+ })
348
+ ]
349
+ }, e))
350
+ })]
351
+ })
352
+ ]
353
+ });
354
+ };
355
+ //#endregion
356
+ export { k as AdminPage };
357
+
358
+ //# sourceMappingURL=AdminPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPage.js","names":[],"sources":["../../../src/assethandler/admin/AdminPage.tsx"],"sourcesContent":["/**\n * AssetHandler Admin Page\n *\n * Product-specific governance surface mounted inside `admin-app` at\n * `/admin/assethandler`: workspace state, feature-flag overrides, workspace\n * quota (asset limit) overrides, and shortcuts to the shared admin surfaces for\n * billing plan assignment and user support. All writes are super-admin only\n * (enforced by `wspace-assethandler-svc`).\n */\nimport { useState, type FC } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { Boxes, CreditCard, Flag, Gauge, LifeBuoy, ShieldAlert } from 'lucide-react';\nimport { Button, GlassCard, Input, Label } from '@burdenoff/fe-libs/ui';\n\nimport { SectionHeader, StatTile } from '../components';\nimport {\n ADMIN_FEATURE_FLAGS,\n ADMIN_QUOTA_OVERVIEW,\n ADMIN_WORKSPACE_STATE,\n ASSETHANDLER_QUOTAS,\n OVERRIDE_QUOTA,\n REMOVE_FEATURE_FLAG_OVERRIDE,\n SET_FEATURE_FLAG_OVERRIDE,\n parseQuotaLimit,\n quotaLabel,\n type AdminFeatureFlagsData,\n type AdminQuotaOverviewData,\n type AdminWorkspaceStateData,\n} from './adminOperations';\nimport { useAssetHandlerAdmin } from './AdminContext';\n\n/** Shared admin surfaces that already cover billing and support actions. */\nconst SHARED_ADMIN_LINKS: ReadonlyArray<{\n path: string;\n label: string;\n description: string;\n icon: typeof Flag;\n}> = [\n {\n path: '/admin/billing',\n label: 'Billing & plan assignment',\n description: 'View the workspace billing account and change its plan.',\n icon: CreditCard,\n },\n {\n path: '/support',\n label: 'User support',\n description: 'Support tickets and user assistance for this workspace.',\n icon: LifeBuoy,\n },\n {\n path: '/products/feature-flags',\n label: 'All feature flags',\n description: 'Platform-wide feature-flag management.',\n icon: Flag,\n },\n];\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'Request failed';\n}\n\nexport const AdminPage: FC = () => {\n const { workspaceId = '', navigate } = useAssetHandlerAdmin();\n const [message, setMessage] = useState<{ tone: 'info' | 'error'; text: string } | null>(null);\n const [quotaName, setQuotaName] = useState(ASSETHANDLER_QUOTAS[0]?.name ?? '');\n const [quotaLimit, setQuotaLimit] = useState('');\n const [quotaUnlimited, setQuotaUnlimited] = useState(false);\n\n const skip = !workspaceId;\n const variables = { workspaceId };\n\n const state = useQuery<AdminWorkspaceStateData>(ADMIN_WORKSPACE_STATE, { variables, skip });\n const flags = useQuery<AdminFeatureFlagsData>(ADMIN_FEATURE_FLAGS, { variables, skip });\n const quotas = useQuery<AdminQuotaOverviewData>(ADMIN_QUOTA_OVERVIEW, { variables, skip });\n\n const [setFlag, { loading: settingFlag }] = useMutation(SET_FEATURE_FLAG_OVERRIDE);\n const [removeFlag, { loading: removingFlag }] = useMutation(REMOVE_FEATURE_FLAG_OVERRIDE);\n const [overrideQuota, { loading: savingQuota }] = useMutation(OVERRIDE_QUOTA);\n\n if (!workspaceId) {\n return (\n <div className=\"flex min-h-[40vh] items-center justify-center text-text-secondary\">\n <ShieldAlert className=\"mr-2 h-5 w-5\" />\n Workspace context is required.\n </div>\n );\n }\n\n const workspaceState = state.data?.assetHandlerAdminWorkspaceState;\n const flagItems = flags.data?.assetHandlerFeatureFlags.items ?? [];\n const assignments = quotas.data?.assetHandlerWorkspaceQuotaOverview.assignments ?? [];\n const loadErrors = [state.error, flags.error, quotas.error].filter(Boolean);\n const parsedLimit = parseQuotaLimit(quotaLimit);\n const canSaveQuota =\n !savingQuota && Boolean(quotaName) && (quotaUnlimited || parsedLimit !== null);\n\n const toggleFlag = async (key: string, enabled: boolean): Promise<void> => {\n try {\n await setFlag({ variables: { input: { workspaceId, key, enabled } } });\n await flags.refetch();\n setMessage({\n tone: 'info',\n text: `${key} ${enabled ? 'enabled' : 'disabled'} for this workspace.`,\n });\n } catch (error) {\n setMessage({ tone: 'error', text: errorMessage(error) });\n }\n };\n\n const clearOverride = async (key: string): Promise<void> => {\n try {\n await removeFlag({ variables: { workspaceId, key } });\n await flags.refetch();\n setMessage({ tone: 'info', text: `Override for ${key} removed.` });\n } catch (error) {\n setMessage({ tone: 'error', text: errorMessage(error) });\n }\n };\n\n const saveQuota = async (): Promise<void> => {\n if (!canSaveQuota) return;\n try {\n await overrideQuota({\n variables: {\n input: {\n workspaceId,\n quotaName,\n noLimit: quotaUnlimited,\n limit: quotaUnlimited ? null : parsedLimit,\n },\n },\n });\n await quotas.refetch();\n setMessage({\n tone: 'info',\n text: `${quotaLabel(quotaName)} set to ${quotaUnlimited ? 'unlimited' : String(parsedLimit)}.`,\n });\n } catch (error) {\n setMessage({ tone: 'error', text: errorMessage(error) });\n }\n };\n\n return (\n <div className=\"space-y-8 p-6\">\n <SectionHeader\n hero\n title=\"AssetHandler Admin\"\n description=\"Workspace state, feature flags, asset limits, and shortcuts to billing and support.\"\n />\n\n {message && (\n <div\n role={message.tone === 'error' ? 'alert' : 'status'}\n className={\n message.tone === 'error'\n ? 'rounded-md bg-status-error-subtle px-4 py-3 text-sm text-status-error-text'\n : 'rounded-md bg-status-info-subtle px-4 py-3 text-sm text-status-info-text'\n }\n >\n {message.text}\n <button type=\"button\" className=\"ml-4 underline\" onClick={() => setMessage(null)}>\n Dismiss\n </button>\n </div>\n )}\n\n {loadErrors.length > 0 && (\n <div\n role=\"alert\"\n className=\"rounded-md bg-status-error-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n Failed to load admin data. {loadErrors.map((error) => error?.message).join('; ')}\n </div>\n )}\n\n <section aria-labelledby=\"ah-admin-state\">\n <SectionHeader title=\"Workspace state\" icon={Boxes} />\n <div id=\"ah-admin-state\" className=\"grid grid-cols-2 gap-4 md:grid-cols-5\">\n <StatTile label=\"Assets\" value={workspaceState?.assetCount ?? '—'} />\n <StatTile label=\"Open incidents\" value={workspaceState?.openIncidentCount ?? '—'} />\n <StatTile label=\"Open work orders\" value={workspaceState?.openWorkOrderCount ?? '—'} />\n <StatTile label=\"Maintenance plans\" value={workspaceState?.maintenancePlanCount ?? '—'} />\n <StatTile label=\"Discovery jobs\" value={workspaceState?.discoveryJobCount ?? '—'} />\n </div>\n </section>\n\n <section aria-labelledby=\"ah-admin-flags\">\n <SectionHeader\n title=\"Feature flags\"\n description=\"Override a flag for this workspace only. Removing the override restores the platform default.\"\n icon={Flag}\n />\n <GlassCard className=\"divide-y divide-border\">\n {flagItems.length === 0 && !flags.loading && (\n <p className=\"p-4 text-sm text-text-secondary\">No AssetHandler feature flags found.</p>\n )}\n <ul id=\"ah-admin-flags\">\n {flagItems.map((flag) => (\n <li key={flag.key} className=\"flex flex-wrap items-center justify-between gap-3 p-4\">\n <div>\n <p className=\"font-mono text-sm text-text-primary\">{flag.key}</p>\n <p className=\"text-xs text-text-secondary\">\n {flag.category} · default {flag.defaultValue ? 'on' : 'off'} · currently{' '}\n <strong>{flag.value ? 'on' : 'off'}</strong>\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={settingFlag}\n onClick={() => void toggleFlag(flag.key, !flag.value)}\n >\n {flag.value ? 'Disable' : 'Enable'}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={removingFlag}\n onClick={() => void clearOverride(flag.key)}\n >\n Reset to default\n </Button>\n </div>\n </li>\n ))}\n </ul>\n </GlassCard>\n </section>\n\n <section aria-labelledby=\"ah-admin-quotas\">\n <SectionHeader\n title=\"Workspace limits\"\n description=\"Quota assignments for this workspace, including the active-asset limit.\"\n icon={Gauge}\n />\n <GlassCard className=\"space-y-4 p-4\">\n <table id=\"ah-admin-quotas\" className=\"w-full text-left text-sm\">\n <thead className=\"text-text-secondary\">\n <tr>\n <th className=\"py-2\">Quota</th>\n <th className=\"py-2\">Limit</th>\n <th className=\"py-2\">Used</th>\n <th className=\"py-2\">Resets</th>\n </tr>\n </thead>\n <tbody>\n {assignments.length === 0 && (\n <tr>\n <td colSpan={4} className=\"py-3 text-text-secondary\">\n No quota assignments yet — limits come from the workspace plan.\n </td>\n </tr>\n )}\n {assignments.map((assignment) => (\n <tr key={assignment.id}>\n <td className=\"py-2\">{quotaLabel(assignment.quotaName)}</td>\n <td className=\"py-2\">\n {assignment.noLimit ? 'Unlimited' : (assignment.limit ?? '—')}\n </td>\n <td className=\"py-2\">{assignment.used}</td>\n <td className=\"py-2\">{assignment.resetPeriod}</td>\n </tr>\n ))}\n </tbody>\n </table>\n\n <form\n className=\"grid gap-4 border-t border-border pt-4 md:grid-cols-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void saveQuota();\n }}\n >\n <div className=\"space-y-2\">\n <Label htmlFor=\"ah-quota-name\">Quota</Label>\n <select\n id=\"ah-quota-name\"\n className=\"w-full rounded-md border border-border bg-surface px-3 py-2 text-sm\"\n value={quotaName}\n onChange={(event) => setQuotaName(event.target.value)}\n >\n {ASSETHANDLER_QUOTAS.map((quota) => (\n <option key={quota.name} value={quota.name}>\n {quota.label}\n </option>\n ))}\n </select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"ah-quota-limit\">Limit</Label>\n <Input\n id=\"ah-quota-limit\"\n inputMode=\"numeric\"\n placeholder=\"e.g. 500\"\n value={quotaLimit}\n disabled={quotaUnlimited}\n onChange={(event) => setQuotaLimit(event.target.value)}\n />\n </div>\n <div className=\"flex items-end gap-2 pb-2\">\n <input\n id=\"ah-quota-unlimited\"\n type=\"checkbox\"\n checked={quotaUnlimited}\n onChange={(event) => setQuotaUnlimited(event.target.checked)}\n />\n <Label htmlFor=\"ah-quota-unlimited\">Unlimited</Label>\n </div>\n <div className=\"flex items-end\">\n <Button type=\"submit\" disabled={!canSaveQuota}>\n {savingQuota ? 'Saving…' : 'Save limit'}\n </Button>\n </div>\n </form>\n </GlassCard>\n </section>\n\n <section aria-labelledby=\"ah-admin-links\">\n <SectionHeader\n title=\"Billing & support\"\n description=\"Handled by the shared admin surfaces.\"\n />\n <div id=\"ah-admin-links\" className=\"grid gap-4 md:grid-cols-3\">\n {SHARED_ADMIN_LINKS.map(({ path, label, description, icon: Icon }) => (\n <GlassCard key={path} className=\"space-y-2 p-4\">\n <p className=\"flex items-center gap-2 font-medium text-text-primary\">\n <Icon className=\"h-4 w-4\" />\n {label}\n </p>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={!navigate}\n onClick={() => navigate?.(path)}\n >\n Open\n </Button>\n </GlassCard>\n ))}\n </div>\n </section>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { FC } from 'react';
2
+ import { AdminRootProps } from './AdminContext';
3
+
4
+ export type { AdminRootProps } from './AdminContext';
5
+ export declare const AdminRoot: FC<AdminRootProps>;
6
+ //# sourceMappingURL=AdminRoot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminRoot.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/AdminRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,EAAE,EAAqB,MAAM,OAAO,CAAC;AAMnD,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGnE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA+BrD,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAIxC,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { AdminContext as e } from "./AdminContext.js";
2
+ import { AdminPage as t } from "./AdminPage.js";
3
+ import { useMemo as n } from "react";
4
+ import { MemoryRouter as r } from "react-router";
5
+ import { ApolloClient as i, HttpLink as a, InMemoryCache as o } from "@apollo/client";
6
+ import { ApolloProvider as s } from "@apollo/client/react";
7
+ import { jsx as c } from "react/jsx-runtime";
8
+ //#region src/assethandler/admin/AdminRoot.tsx
9
+ var l = ({ children: t, ...l }) => {
10
+ let { apiGatewayUrl: u, authToken: d, workspaceToken: f, workspaceId: p, tenantId: m } = l;
11
+ return /* @__PURE__ */ c(s, {
12
+ client: n(() => new i({
13
+ link: new a({
14
+ uri: u ? `${u}/workspaces/graphql` : "/workspaces/graphql",
15
+ headers: {
16
+ ...d ? { Authorization: `Bearer ${d}` } : {},
17
+ ...f ? { "X-Workspace-Authorization": `Bearer ${f}` } : {},
18
+ ...p ? { "x-workspace-id": p } : {},
19
+ ...m ? { "x-tenant-id": m } : {}
20
+ }
21
+ }),
22
+ cache: new o()
23
+ }), [
24
+ u,
25
+ d,
26
+ f,
27
+ p,
28
+ m
29
+ ]),
30
+ children: /* @__PURE__ */ c(r, { children: /* @__PURE__ */ c(e.Provider, {
31
+ value: l,
32
+ children: t
33
+ }) })
34
+ });
35
+ }, u = (e) => /* @__PURE__ */ c(l, {
36
+ ...e,
37
+ children: /* @__PURE__ */ c(t, {})
38
+ });
39
+ //#endregion
40
+ export { u as AdminRoot };
41
+
42
+ //# sourceMappingURL=AdminRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminRoot.js","names":[],"sources":["../../../src/assethandler/admin/AdminRoot.tsx"],"sourcesContent":["/**\n * AssetHandler Admin Root\n *\n * Host-contract entry point for the AssetHandler admin surface inside\n * `admin-app` at `/admin/assethandler` (BOFF-3362).\n *\n * Deliberately does NOT reuse `AssetHandlerProvider`: that provider hydrates\n * the whole product store (thirteen bootstrap queries) which an operations\n * console never needs. This provider only builds the workspace-gateway Apollo\n * client and exposes the host context to the admin page.\n */\nimport type { FC, PropsWithChildren } from 'react';\nimport { useMemo } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';\nimport { ApolloProvider } from '@apollo/client/react';\n\nimport { AdminContext, type AdminRootProps } from './AdminContext';\nimport { AdminPage } from './AdminPage';\n\nexport type { AdminRootProps } from './AdminContext';\n\nconst AdminProvider: FC<PropsWithChildren<AdminRootProps>> = ({ children, ...props }) => {\n const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId } = props;\n\n const client = useMemo(\n () =>\n new ApolloClient({\n link: new HttpLink({\n uri: apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql',\n headers: {\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n ...(workspaceToken ? { 'X-Workspace-Authorization': `Bearer ${workspaceToken}` } : {}),\n ...(workspaceId ? { 'x-workspace-id': workspaceId } : {}),\n ...(tenantId ? { 'x-tenant-id': tenantId } : {}),\n },\n }),\n cache: new InMemoryCache(),\n }),\n [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId]\n );\n\n return (\n <ApolloProvider client={client}>\n <MemoryRouter>\n <AdminContext.Provider value={props}>{children}</AdminContext.Provider>\n </MemoryRouter>\n </ApolloProvider>\n );\n};\n\nexport const AdminRoot: FC<AdminRootProps> = (props) => (\n <AdminProvider {...props}>\n <AdminPage />\n </AdminProvider>\n);\n"],"mappings":";;;;;;;;AAsBA,IAAM,KAAwD,EAAE,aAAU,GAAG,QAAY;CACvF,IAAM,EAAE,kBAAe,cAAW,mBAAgB,gBAAa,gBAAa;CAmB5E,OACE,kBAAC,GAAD;EAAwB,QAlBX,QAEX,IAAI,EAAa;GACf,MAAM,IAAI,EAAS;IACjB,KAAK,IAAgB,GAAG,EAAc,uBAAuB;IAC7D,SAAS;KACP,GAAI,IAAY,EAAE,eAAe,UAAU,IAAY,IAAI,CAAC;KAC5D,GAAI,IAAiB,EAAE,6BAA6B,UAAU,IAAiB,IAAI,CAAC;KACpF,GAAI,IAAc,EAAE,kBAAkB,EAAY,IAAI,CAAC;KACvD,GAAI,IAAW,EAAE,eAAe,EAAS,IAAI,CAAC;IAChD;GACF,CAAC;GACD,OAAO,IAAI,EAAc;EAC3B,CAAC,GACH;GAAC;GAAe;GAAW;GAAgB;GAAa;EAAQ,CAIxC;YACtB,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAa,UAAd;GAAuB,OAAO;GAAQ;EAAgC,CAAA,EAC1D,CAAA;CACA,CAAA;AAEpB,GAEa,KAAiC,MAC5C,kBAAC,GAAD;CAAe,GAAI;WACjB,kBAAC,GAAD,CAAY,CAAA;AACC,CAAA"}
@@ -0,0 +1,61 @@
1
+ export declare const ADMIN_WORKSPACE_STATE: import('graphql').DocumentNode;
2
+ export declare const ADMIN_FEATURE_FLAGS: import('graphql').DocumentNode;
3
+ export declare const ADMIN_QUOTA_OVERVIEW: import('graphql').DocumentNode;
4
+ export declare const SET_FEATURE_FLAG_OVERRIDE: import('graphql').DocumentNode;
5
+ export declare const REMOVE_FEATURE_FLAG_OVERRIDE: import('graphql').DocumentNode;
6
+ export declare const OVERRIDE_QUOTA: import('graphql').DocumentNode;
7
+ export interface AdminWorkspaceState {
8
+ workspaceId: string;
9
+ assetCount: number;
10
+ openIncidentCount: number;
11
+ openWorkOrderCount: number;
12
+ maintenancePlanCount: number;
13
+ discoveryJobCount: number;
14
+ generatedAt: string;
15
+ }
16
+ export interface AdminFeatureFlag {
17
+ key: string;
18
+ defaultValue: boolean;
19
+ value: boolean;
20
+ category: string;
21
+ }
22
+ export interface AdminQuotaAssignment {
23
+ id: string;
24
+ quotaName: string;
25
+ productId: string;
26
+ limit: number | null;
27
+ used: number;
28
+ noLimit: boolean;
29
+ resetPeriod: string;
30
+ isActive: boolean;
31
+ updatedAt: string;
32
+ }
33
+ export interface AdminWorkspaceStateData {
34
+ assetHandlerAdminWorkspaceState: AdminWorkspaceState;
35
+ }
36
+ export interface AdminFeatureFlagsData {
37
+ assetHandlerFeatureFlags: {
38
+ workspaceId: string;
39
+ items: AdminFeatureFlag[];
40
+ };
41
+ }
42
+ export interface AdminQuotaOverviewData {
43
+ assetHandlerWorkspaceQuotaOverview: {
44
+ workspaceId: string;
45
+ assignments: AdminQuotaAssignment[];
46
+ };
47
+ }
48
+ /** Quotas AssetHandler enforces, with admin-facing labels. */
49
+ export declare const ASSETHANDLER_QUOTAS: ReadonlyArray<{
50
+ name: string;
51
+ label: string;
52
+ }>;
53
+ /** Human label for a quota name; falls back to the raw name for unknown ones. */
54
+ export declare function quotaLabel(name: string): string;
55
+ /**
56
+ * Parse the admin-entered quota limit. Returns `null` for anything that is not
57
+ * a whole number of zero or more, so the form can refuse it before it reaches
58
+ * the backend (which validates again).
59
+ */
60
+ export declare function parseQuotaLimit(raw: string): number | null;
61
+ //# sourceMappingURL=adminOperations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminOperations.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/adminOperations.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,qBAAqB,gCAYjC,CAAC;AAEF,eAAO,MAAM,mBAAmB,gCAY/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,gCAiBhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,gCAUrC,CAAC;AAEF,eAAO,MAAM,4BAA4B,gCAIxC,CAAC;AAEF,eAAO,MAAM,cAAc,gCAW1B,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,+BAA+B,EAAE,mBAAmB,CAAC;CACtD;AAED,MAAM,WAAW,qBAAqB;IACpC,wBAAwB,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,gBAAgB,EAAE,CAAA;KAAE,CAAC;CAC9E;AAED,MAAM,WAAW,sBAAsB;IACrC,kCAAkC,EAAE;QAClC,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,oBAAoB,EAAE,CAAC;KACrC,CAAC;CACH;AAED,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAS9E,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK1D"}
@@ -0,0 +1,115 @@
1
+ import { gql as e } from "@apollo/client";
2
+ //#region src/assethandler/admin/adminOperations.ts
3
+ var t = e`
4
+ query AssetHandlerAdminWorkspaceState($workspaceId: ID!) {
5
+ assetHandlerAdminWorkspaceState(workspaceId: $workspaceId) {
6
+ workspaceId
7
+ assetCount
8
+ openIncidentCount
9
+ openWorkOrderCount
10
+ maintenancePlanCount
11
+ discoveryJobCount
12
+ generatedAt
13
+ }
14
+ }
15
+ `, n = e`
16
+ query AssetHandlerFeatureFlags($workspaceId: ID!) {
17
+ assetHandlerFeatureFlags(workspaceId: $workspaceId) {
18
+ workspaceId
19
+ items {
20
+ key
21
+ defaultValue
22
+ value
23
+ category
24
+ }
25
+ }
26
+ }
27
+ `, r = e`
28
+ query AssetHandlerWorkspaceQuotaOverview($workspaceId: ID!) {
29
+ assetHandlerWorkspaceQuotaOverview(workspaceId: $workspaceId) {
30
+ workspaceId
31
+ assignments {
32
+ id
33
+ quotaName
34
+ productId
35
+ limit
36
+ used
37
+ noLimit
38
+ resetPeriod
39
+ isActive
40
+ updatedAt
41
+ }
42
+ }
43
+ }
44
+ `, i = e`
45
+ mutation SetAssetHandlerFeatureFlagOverride($input: SetAssetHandlerFeatureFlagOverrideInput!) {
46
+ setAssetHandlerFeatureFlagOverride(input: $input) {
47
+ id
48
+ featureFlagId
49
+ workspaceId
50
+ enabled
51
+ allowUserOptIn
52
+ }
53
+ }
54
+ `, a = e`
55
+ mutation RemoveAssetHandlerFeatureFlagOverride($workspaceId: ID!, $key: String!) {
56
+ removeAssetHandlerFeatureFlagOverride(workspaceId: $workspaceId, key: $key)
57
+ }
58
+ `, o = e`
59
+ mutation OverrideAssetHandlerWorkspaceQuota($input: OverrideAssetHandlerWorkspaceQuotaInput!) {
60
+ overrideAssetHandlerWorkspaceQuota(input: $input) {
61
+ id
62
+ workspaceId
63
+ quotaName
64
+ limit
65
+ used
66
+ noLimit
67
+ }
68
+ }
69
+ `, s = [
70
+ {
71
+ name: "assethandler.assets.count",
72
+ label: "Active assets (asset limit)"
73
+ },
74
+ {
75
+ name: "assethandler.work_orders.open",
76
+ label: "Open work orders"
77
+ },
78
+ {
79
+ name: "assethandler.cmdb.configuration_items",
80
+ label: "CMDB configuration items"
81
+ },
82
+ {
83
+ name: "assethandler.discovery.jobs.monthly",
84
+ label: "Discovery jobs / month"
85
+ },
86
+ {
87
+ name: "assethandler.incidents.monthly",
88
+ label: "Incidents / month"
89
+ },
90
+ {
91
+ name: "assethandler.maintenance_records.monthly",
92
+ label: "Maintenance records / month"
93
+ },
94
+ {
95
+ name: "assethandler.knowledge_articles.monthly",
96
+ label: "Knowledge articles / month"
97
+ },
98
+ {
99
+ name: "assethandler.esg_metrics.monthly",
100
+ label: "ESG metrics / month"
101
+ }
102
+ ];
103
+ function c(e) {
104
+ return s.find((t) => t.name === e)?.label ?? e;
105
+ }
106
+ function l(e) {
107
+ let t = e.trim();
108
+ if (!/^\d+$/.test(t)) return null;
109
+ let n = Number(t);
110
+ return Number.isSafeInteger(n) ? n : null;
111
+ }
112
+ //#endregion
113
+ export { n as ADMIN_FEATURE_FLAGS, r as ADMIN_QUOTA_OVERVIEW, t as ADMIN_WORKSPACE_STATE, s as ASSETHANDLER_QUOTAS, o as OVERRIDE_QUOTA, a as REMOVE_FEATURE_FLAG_OVERRIDE, i as SET_FEATURE_FLAG_OVERRIDE, l as parseQuotaLimit, c as quotaLabel };
114
+
115
+ //# sourceMappingURL=adminOperations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminOperations.js","names":[],"sources":["../../../src/assethandler/admin/adminOperations.ts"],"sourcesContent":["/**\n * GraphQL operations for the AssetHandler admin surface (BOFF-3362).\n *\n * Served by `wspace-assethandler-svc`'s admin module (super-admin only).\n * Typed locally rather than through codegen so this surface does not depend on\n * the composed supergraph snapshot being refreshed first.\n */\nimport { gql } from '@apollo/client';\n\nexport const ADMIN_WORKSPACE_STATE = gql`\n query AssetHandlerAdminWorkspaceState($workspaceId: ID!) {\n assetHandlerAdminWorkspaceState(workspaceId: $workspaceId) {\n workspaceId\n assetCount\n openIncidentCount\n openWorkOrderCount\n maintenancePlanCount\n discoveryJobCount\n generatedAt\n }\n }\n`;\n\nexport const ADMIN_FEATURE_FLAGS = gql`\n query AssetHandlerFeatureFlags($workspaceId: ID!) {\n assetHandlerFeatureFlags(workspaceId: $workspaceId) {\n workspaceId\n items {\n key\n defaultValue\n value\n category\n }\n }\n }\n`;\n\nexport const ADMIN_QUOTA_OVERVIEW = gql`\n query AssetHandlerWorkspaceQuotaOverview($workspaceId: ID!) {\n assetHandlerWorkspaceQuotaOverview(workspaceId: $workspaceId) {\n workspaceId\n assignments {\n id\n quotaName\n productId\n limit\n used\n noLimit\n resetPeriod\n isActive\n updatedAt\n }\n }\n }\n`;\n\nexport const SET_FEATURE_FLAG_OVERRIDE = gql`\n mutation SetAssetHandlerFeatureFlagOverride($input: SetAssetHandlerFeatureFlagOverrideInput!) {\n setAssetHandlerFeatureFlagOverride(input: $input) {\n id\n featureFlagId\n workspaceId\n enabled\n allowUserOptIn\n }\n }\n`;\n\nexport const REMOVE_FEATURE_FLAG_OVERRIDE = gql`\n mutation RemoveAssetHandlerFeatureFlagOverride($workspaceId: ID!, $key: String!) {\n removeAssetHandlerFeatureFlagOverride(workspaceId: $workspaceId, key: $key)\n }\n`;\n\nexport const OVERRIDE_QUOTA = gql`\n mutation OverrideAssetHandlerWorkspaceQuota($input: OverrideAssetHandlerWorkspaceQuotaInput!) {\n overrideAssetHandlerWorkspaceQuota(input: $input) {\n id\n workspaceId\n quotaName\n limit\n used\n noLimit\n }\n }\n`;\n\nexport interface AdminWorkspaceState {\n workspaceId: string;\n assetCount: number;\n openIncidentCount: number;\n openWorkOrderCount: number;\n maintenancePlanCount: number;\n discoveryJobCount: number;\n generatedAt: string;\n}\n\nexport interface AdminFeatureFlag {\n key: string;\n defaultValue: boolean;\n value: boolean;\n category: string;\n}\n\nexport interface AdminQuotaAssignment {\n id: string;\n quotaName: string;\n productId: string;\n limit: number | null;\n used: number;\n noLimit: boolean;\n resetPeriod: string;\n isActive: boolean;\n updatedAt: string;\n}\n\nexport interface AdminWorkspaceStateData {\n assetHandlerAdminWorkspaceState: AdminWorkspaceState;\n}\n\nexport interface AdminFeatureFlagsData {\n assetHandlerFeatureFlags: { workspaceId: string; items: AdminFeatureFlag[] };\n}\n\nexport interface AdminQuotaOverviewData {\n assetHandlerWorkspaceQuotaOverview: {\n workspaceId: string;\n assignments: AdminQuotaAssignment[];\n };\n}\n\n/** Quotas AssetHandler enforces, with admin-facing labels. */\nexport const ASSETHANDLER_QUOTAS: ReadonlyArray<{ name: string; label: string }> = [\n { name: 'assethandler.assets.count', label: 'Active assets (asset limit)' },\n { name: 'assethandler.work_orders.open', label: 'Open work orders' },\n { name: 'assethandler.cmdb.configuration_items', label: 'CMDB configuration items' },\n { name: 'assethandler.discovery.jobs.monthly', label: 'Discovery jobs / month' },\n { name: 'assethandler.incidents.monthly', label: 'Incidents / month' },\n { name: 'assethandler.maintenance_records.monthly', label: 'Maintenance records / month' },\n { name: 'assethandler.knowledge_articles.monthly', label: 'Knowledge articles / month' },\n { name: 'assethandler.esg_metrics.monthly', label: 'ESG metrics / month' },\n];\n\n/** Human label for a quota name; falls back to the raw name for unknown ones. */\nexport function quotaLabel(name: string): string {\n return ASSETHANDLER_QUOTAS.find((quota) => quota.name === name)?.label ?? name;\n}\n\n/**\n * Parse the admin-entered quota limit. Returns `null` for anything that is not\n * a whole number of zero or more, so the form can refuse it before it reaches\n * the backend (which validates again).\n */\nexport function parseQuotaLimit(raw: string): number | null {\n const trimmed = raw.trim();\n if (!/^\\d+$/.test(trimmed)) return null;\n const value = Number(trimmed);\n return Number.isSafeInteger(value) ? value : null;\n}\n"],"mappings":";;AASA,IAAa,IAAwB,CAAG;;;;;;;;;;;;GAc3B,IAAsB,CAAG;;;;;;;;;;;;GAczB,IAAuB,CAAG;;;;;;;;;;;;;;;;;GAmB1B,IAA4B,CAAG;;;;;;;;;;GAY/B,IAA+B,CAAG;;;;GAMlC,IAAiB,CAAG;;;;;;;;;;;GA0DpB,IAAsE;CACjF;EAAE,MAAM;EAA6B,OAAO;CAA8B;CAC1E;EAAE,MAAM;EAAiC,OAAO;CAAmB;CACnE;EAAE,MAAM;EAAyC,OAAO;CAA2B;CACnF;EAAE,MAAM;EAAuC,OAAO;CAAyB;CAC/E;EAAE,MAAM;EAAkC,OAAO;CAAoB;CACrE;EAAE,MAAM;EAA4C,OAAO;CAA8B;CACzF;EAAE,MAAM;EAA2C,OAAO;CAA6B;CACvF;EAAE,MAAM;EAAoC,OAAO;CAAsB;AAC3E;AAGA,SAAgB,EAAW,GAAsB;CAC/C,OAAO,EAAoB,MAAM,MAAU,EAAM,SAAS,CAAI,GAAG,SAAS;AAC5E;AAOA,SAAgB,EAAgB,GAA4B;CAC1D,IAAM,IAAU,EAAI,KAAK;CACzB,IAAI,CAAC,QAAQ,KAAK,CAAO,GAAG,OAAO;CACnC,IAAM,IAAQ,OAAO,CAAO;CAC5B,OAAO,OAAO,cAAc,CAAK,IAAI,IAAQ;AAC/C"}
@@ -2,4 +2,6 @@ export { AssetHandlerRoot } from './AssetHandlerRoot';
2
2
  export type { AssetHandlerRootProps, AssetHandlerCopy, AssetHandlerTerminology, NavItem, FooterItem, } from './types';
3
3
  export { useAssetHandler } from './providers/AssetHandlerProvider';
4
4
  export { useAssetHandlerStore } from './store/assethandlerStore';
5
+ export { AdminRoot as AssetHandlerAdminRoot } from './admin/AdminRoot';
6
+ export type { AdminRootProps as AssetHandlerAdminRootProps } from './admin/AdminContext';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assethandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,OAAO,EACP,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assethandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,OAAO,EACP,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,SAAS,IAAI,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,cAAc,IAAI,0BAA0B,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  import "./store/assethandlerStore.js";
2
2
  import "./providers/AssetHandlerProvider.js";
3
3
  import "./AssetHandlerRoot.js";
4
+ import "./admin/AdminRoot.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useAssetHandlerStore as e } from "./assethandler/store/assethandlerStore.js";
2
2
  import { useAssetHandler as t } from "./assethandler/providers/AssetHandlerProvider.js";
3
3
  import { AssetHandlerRoot as n } from "./assethandler/AssetHandlerRoot.js";
4
+ import { AdminRoot as r } from "./assethandler/admin/AdminRoot.js";
4
5
  import "./assethandler/index.js";
5
- import { useSandboxAssistantTransport as r } from "./services/createSandboxAssistantTransport.js";
6
- export { n as AssetHandlerRoot, t as useAssetHandler, e as useAssetHandlerStore, r as useSandboxAssistantTransport };
6
+ import { useSandboxAssistantTransport as i } from "./services/createSandboxAssistantTransport.js";
7
+ export { r as AssetHandlerAdminRoot, n as AssetHandlerRoot, t as useAssetHandler, e as useAssetHandlerStore, i as useSandboxAssistantTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-assethandler",
3
- "version": "2026.1102.1",
3
+ "version": "2026.1103.1",
4
4
  "description": "Burdenoff AssetHandler Microfrontend",
5
5
  "type": "module",
6
6
  "files": [