@burdenoff/microfe-assethandler 2026.1103.1 → 2026.1103.3
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/admin/AdminPage.d.ts.map +1 -1
- package/dist/assethandler/admin/AdminPage.js +122 -186
- package/dist/assethandler/admin/AdminPage.js.map +1 -1
- package/dist/assethandler/admin/AdminRoot.d.ts.map +1 -1
- package/dist/assethandler/admin/AdminRoot.js +22 -23
- package/dist/assethandler/admin/AdminRoot.js.map +1 -1
- package/dist/assethandler/admin/adminOperations.d.ts +19 -5
- package/dist/assethandler/admin/adminOperations.d.ts.map +1 -1
- package/dist/assethandler/admin/adminOperations.js +23 -20
- package/dist/assethandler/admin/adminOperations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/AdminPage.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AdminPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/admin/AdminPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAY,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAkD1C,eAAO,MAAM,SAAS,EAAE,EA2NvB,CAAC"}
|
|
@@ -2,178 +2,161 @@ import { StatTile as e } from "../components/StatTile.js";
|
|
|
2
2
|
import { SectionHeader as t } from "../components/SectionHeader.js";
|
|
3
3
|
import "../components/index.js";
|
|
4
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,
|
|
6
|
-
import { useState as
|
|
7
|
-
import { Boxes as
|
|
8
|
-
import { useMutation as
|
|
9
|
-
import { jsx as
|
|
10
|
-
import { Button as
|
|
5
|
+
import { ADMIN_FEATURE_FLAGS as r, ADMIN_QUOTA_OVERVIEW as i, ADMIN_WORKSPACE_STATE as a, REMOVE_FEATURE_FLAG_OVERRIDE as o, SET_FEATURE_FLAG_OVERRIDE as s, summarizeQuotas as c } from "./adminOperations.js";
|
|
6
|
+
import { useState as l } from "react";
|
|
7
|
+
import { Boxes as u, CreditCard as d, Flag as f, Gauge as p, LifeBuoy as m, ShieldAlert as h } from "lucide-react";
|
|
8
|
+
import { useMutation as g, useQuery as _ } from "@apollo/client/react";
|
|
9
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
10
|
+
import { Button as b, GlassCard as x } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/assethandler/admin/AdminPage.tsx
|
|
12
|
-
var
|
|
12
|
+
var S = [
|
|
13
13
|
{
|
|
14
14
|
path: "/admin/billing",
|
|
15
15
|
label: "Billing & plan assignment",
|
|
16
16
|
description: "View the workspace billing account and change its plan.",
|
|
17
|
-
icon:
|
|
17
|
+
icon: d
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
path: "/support",
|
|
21
21
|
label: "User support",
|
|
22
22
|
description: "Support tickets and user assistance for this workspace.",
|
|
23
|
-
icon:
|
|
23
|
+
icon: m
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
path: "/products/feature-flags",
|
|
27
27
|
label: "All feature flags",
|
|
28
28
|
description: "Platform-wide feature-flag management.",
|
|
29
|
-
icon:
|
|
29
|
+
icon: f
|
|
30
30
|
}
|
|
31
31
|
];
|
|
32
|
-
function
|
|
32
|
+
function C(e) {
|
|
33
33
|
return e instanceof Error ? e.message : "Request failed";
|
|
34
34
|
}
|
|
35
|
-
var
|
|
36
|
-
let { workspaceId:
|
|
37
|
-
variables:
|
|
38
|
-
skip:
|
|
39
|
-
}),
|
|
40
|
-
variables:
|
|
41
|
-
skip:
|
|
42
|
-
}),
|
|
43
|
-
variables:
|
|
44
|
-
skip:
|
|
45
|
-
}), [
|
|
46
|
-
if (!
|
|
35
|
+
var w = () => {
|
|
36
|
+
let { workspaceId: d = "", navigate: m } = n(), [w, T] = l(null), E = !d, D = { workspaceId: d }, O = _(a, {
|
|
37
|
+
variables: D,
|
|
38
|
+
skip: E
|
|
39
|
+
}), k = _(r, {
|
|
40
|
+
variables: D,
|
|
41
|
+
skip: E
|
|
42
|
+
}), A = _(i, {
|
|
43
|
+
variables: D,
|
|
44
|
+
skip: E
|
|
45
|
+
}), [j, { loading: M }] = g(s), [N, { loading: P }] = g(o);
|
|
46
|
+
if (!d) return /* @__PURE__ */ y("div", {
|
|
47
47
|
className: "flex min-h-[40vh] items-center justify-center text-text-secondary",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
48
|
+
children: [/* @__PURE__ */ v(h, { className: "mr-2 h-5 w-5" }), "Workspace context is required."]
|
|
49
49
|
});
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
].filter(Boolean),
|
|
50
|
+
let F = O.data?.assetHandlerAdminWorkspaceState, I = k.data?.assetHandlerFeatureFlags.items ?? [], L = c(A.data?.assetHandlerWorkspaceQuotaOverview.assignments ?? []), R = [
|
|
51
|
+
O.error,
|
|
52
|
+
k.error,
|
|
53
|
+
A.error
|
|
54
|
+
].filter(Boolean), z = async (e, t) => {
|
|
55
55
|
try {
|
|
56
|
-
await
|
|
57
|
-
workspaceId:
|
|
56
|
+
await j({ variables: { input: {
|
|
57
|
+
workspaceId: d,
|
|
58
58
|
key: e,
|
|
59
59
|
enabled: t
|
|
60
|
-
} } }), await
|
|
60
|
+
} } }), await k.refetch(), T({
|
|
61
61
|
tone: "info",
|
|
62
62
|
text: `${e} ${t ? "enabled" : "disabled"} for this workspace.`
|
|
63
63
|
});
|
|
64
64
|
} catch (e) {
|
|
65
|
-
|
|
65
|
+
T({
|
|
66
66
|
tone: "error",
|
|
67
|
-
text:
|
|
67
|
+
text: C(e)
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
-
},
|
|
70
|
+
}, B = async (e) => {
|
|
71
71
|
try {
|
|
72
|
-
await
|
|
73
|
-
workspaceId:
|
|
72
|
+
await N({ variables: {
|
|
73
|
+
workspaceId: d,
|
|
74
74
|
key: e
|
|
75
|
-
} }), await
|
|
75
|
+
} }), await k.refetch(), T({
|
|
76
76
|
tone: "info",
|
|
77
77
|
text: `Override for ${e} removed.`
|
|
78
78
|
});
|
|
79
79
|
} catch (e) {
|
|
80
|
-
|
|
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({
|
|
80
|
+
T({
|
|
98
81
|
tone: "error",
|
|
99
|
-
text:
|
|
82
|
+
text: C(e)
|
|
100
83
|
});
|
|
101
84
|
}
|
|
102
85
|
};
|
|
103
|
-
return /* @__PURE__ */
|
|
86
|
+
return /* @__PURE__ */ y("div", {
|
|
104
87
|
className: "space-y-8 p-6",
|
|
105
88
|
children: [
|
|
106
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ v(t, {
|
|
107
90
|
hero: !0,
|
|
108
91
|
title: "AssetHandler Admin",
|
|
109
92
|
description: "Workspace state, feature flags, asset limits, and shortcuts to billing and support."
|
|
110
93
|
}),
|
|
111
|
-
|
|
112
|
-
role:
|
|
113
|
-
className:
|
|
114
|
-
children: [
|
|
94
|
+
w && /* @__PURE__ */ y("div", {
|
|
95
|
+
role: w.tone === "error" ? "alert" : "status",
|
|
96
|
+
className: w.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",
|
|
97
|
+
children: [w.text, /* @__PURE__ */ v("button", {
|
|
115
98
|
type: "button",
|
|
116
99
|
className: "ml-4 underline",
|
|
117
|
-
onClick: () =>
|
|
100
|
+
onClick: () => T(null),
|
|
118
101
|
children: "Dismiss"
|
|
119
102
|
})]
|
|
120
103
|
}),
|
|
121
|
-
|
|
104
|
+
R.length > 0 && /* @__PURE__ */ y("div", {
|
|
122
105
|
role: "alert",
|
|
123
106
|
className: "rounded-md bg-status-error-subtle px-4 py-3 text-sm text-status-error-text",
|
|
124
|
-
children: ["Failed to load admin data. ",
|
|
107
|
+
children: ["Failed to load admin data. ", R.map((e) => e?.message).join("; ")]
|
|
125
108
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ y("section", {
|
|
127
110
|
"aria-labelledby": "ah-admin-state",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ v(t, {
|
|
129
112
|
title: "Workspace state",
|
|
130
|
-
icon:
|
|
131
|
-
}), /* @__PURE__ */
|
|
113
|
+
icon: u
|
|
114
|
+
}), /* @__PURE__ */ y("div", {
|
|
132
115
|
id: "ah-admin-state",
|
|
133
116
|
className: "grid grid-cols-2 gap-4 md:grid-cols-5",
|
|
134
117
|
children: [
|
|
135
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ v(e, {
|
|
136
119
|
label: "Assets",
|
|
137
|
-
value:
|
|
120
|
+
value: F?.assetCount ?? "—"
|
|
138
121
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ v(e, {
|
|
140
123
|
label: "Open incidents",
|
|
141
|
-
value:
|
|
124
|
+
value: F?.openIncidentCount ?? "—"
|
|
142
125
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ v(e, {
|
|
144
127
|
label: "Open work orders",
|
|
145
|
-
value:
|
|
128
|
+
value: F?.openWorkOrderCount ?? "—"
|
|
146
129
|
}),
|
|
147
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ v(e, {
|
|
148
131
|
label: "Maintenance plans",
|
|
149
|
-
value:
|
|
132
|
+
value: F?.maintenancePlanCount ?? "—"
|
|
150
133
|
}),
|
|
151
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ v(e, {
|
|
152
135
|
label: "Discovery jobs",
|
|
153
|
-
value:
|
|
136
|
+
value: F?.discoveryJobCount ?? "—"
|
|
154
137
|
})
|
|
155
138
|
]
|
|
156
139
|
})]
|
|
157
140
|
}),
|
|
158
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ y("section", {
|
|
159
142
|
"aria-labelledby": "ah-admin-flags",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ v(t, {
|
|
161
144
|
title: "Feature flags",
|
|
162
145
|
description: "Override a flag for this workspace only. Removing the override restores the platform default.",
|
|
163
|
-
icon:
|
|
164
|
-
}), /* @__PURE__ */
|
|
146
|
+
icon: f
|
|
147
|
+
}), /* @__PURE__ */ y(x, {
|
|
165
148
|
className: "divide-y divide-border",
|
|
166
|
-
children: [
|
|
149
|
+
children: [I.length === 0 && !k.loading && /* @__PURE__ */ v("p", {
|
|
167
150
|
className: "p-4 text-sm text-text-secondary",
|
|
168
151
|
children: "No AssetHandler feature flags found."
|
|
169
|
-
}), /* @__PURE__ */
|
|
152
|
+
}), /* @__PURE__ */ v("ul", {
|
|
170
153
|
id: "ah-admin-flags",
|
|
171
|
-
children:
|
|
154
|
+
children: I.map((e) => /* @__PURE__ */ y("li", {
|
|
172
155
|
className: "flex flex-wrap items-center justify-between gap-3 p-4",
|
|
173
|
-
children: [/* @__PURE__ */
|
|
156
|
+
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
174
157
|
className: "font-mono text-sm text-text-primary",
|
|
175
158
|
children: e.key
|
|
176
|
-
}), /* @__PURE__ */
|
|
159
|
+
}), /* @__PURE__ */ y("p", {
|
|
177
160
|
className: "text-xs text-text-secondary",
|
|
178
161
|
children: [
|
|
179
162
|
e.category,
|
|
@@ -181,23 +164,23 @@ var k = () => {
|
|
|
181
164
|
e.defaultValue ? "on" : "off",
|
|
182
165
|
" · currently",
|
|
183
166
|
" ",
|
|
184
|
-
/* @__PURE__ */
|
|
167
|
+
/* @__PURE__ */ v("strong", { children: e.value ? "on" : "off" })
|
|
185
168
|
]
|
|
186
|
-
})] }), /* @__PURE__ */
|
|
169
|
+
})] }), /* @__PURE__ */ y("div", {
|
|
187
170
|
className: "flex gap-2",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ v(b, {
|
|
189
172
|
type: "button",
|
|
190
173
|
size: "sm",
|
|
191
174
|
variant: "outline",
|
|
192
|
-
disabled:
|
|
193
|
-
onClick: () => void
|
|
175
|
+
disabled: M,
|
|
176
|
+
onClick: () => void z(e.key, !e.value),
|
|
194
177
|
children: e.value ? "Disable" : "Enable"
|
|
195
|
-
}), /* @__PURE__ */
|
|
178
|
+
}), /* @__PURE__ */ v(b, {
|
|
196
179
|
type: "button",
|
|
197
180
|
size: "sm",
|
|
198
181
|
variant: "ghost",
|
|
199
|
-
disabled:
|
|
200
|
-
onClick: () => void
|
|
182
|
+
disabled: P,
|
|
183
|
+
onClick: () => void B(e.key),
|
|
201
184
|
children: "Reset to default"
|
|
202
185
|
})]
|
|
203
186
|
})]
|
|
@@ -205,144 +188,97 @@ var k = () => {
|
|
|
205
188
|
})]
|
|
206
189
|
})]
|
|
207
190
|
}),
|
|
208
|
-
/* @__PURE__ */
|
|
191
|
+
/* @__PURE__ */ y("section", {
|
|
209
192
|
"aria-labelledby": "ah-admin-quotas",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
193
|
+
children: [/* @__PURE__ */ v(t, {
|
|
211
194
|
title: "Workspace limits",
|
|
212
|
-
description: "
|
|
213
|
-
icon:
|
|
214
|
-
}), /* @__PURE__ */
|
|
195
|
+
description: "The limits this workspace is held to, including the active-asset limit. Read-only: limits are a billing entitlement and are changed through billing.",
|
|
196
|
+
icon: p
|
|
197
|
+
}), /* @__PURE__ */ y(x, {
|
|
215
198
|
className: "space-y-4 p-4",
|
|
216
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ y("table", {
|
|
217
200
|
id: "ah-admin-quotas",
|
|
218
201
|
className: "w-full text-left text-sm",
|
|
219
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ v("thead", {
|
|
220
203
|
className: "text-text-secondary",
|
|
221
|
-
children: /* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
204
|
+
children: /* @__PURE__ */ y("tr", { children: [
|
|
205
|
+
/* @__PURE__ */ v("th", {
|
|
223
206
|
className: "py-2",
|
|
224
207
|
children: "Quota"
|
|
225
208
|
}),
|
|
226
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ v("th", {
|
|
227
210
|
className: "py-2",
|
|
228
211
|
children: "Limit"
|
|
229
212
|
}),
|
|
230
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ v("th", {
|
|
231
214
|
className: "py-2",
|
|
232
215
|
children: "Used"
|
|
233
216
|
}),
|
|
234
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ v("th", {
|
|
235
218
|
className: "py-2",
|
|
236
219
|
children: "Resets"
|
|
237
220
|
})
|
|
238
221
|
] })
|
|
239
|
-
}), /* @__PURE__ */
|
|
222
|
+
}), /* @__PURE__ */ y("tbody", { children: [L.length === 0 && /* @__PURE__ */ v("tr", { children: /* @__PURE__ */ v("td", {
|
|
240
223
|
colSpan: 4,
|
|
241
224
|
className: "py-3 text-text-secondary",
|
|
242
225
|
children: "No quota assignments yet — limits come from the workspace plan."
|
|
243
|
-
}) }),
|
|
244
|
-
/* @__PURE__ */
|
|
226
|
+
}) }), L.map((e) => /* @__PURE__ */ y("tr", { children: [
|
|
227
|
+
/* @__PURE__ */ v("td", {
|
|
245
228
|
className: "py-2",
|
|
246
|
-
children:
|
|
229
|
+
children: e.label
|
|
247
230
|
}),
|
|
248
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ v("td", {
|
|
249
232
|
className: "py-2",
|
|
250
|
-
children: e.
|
|
233
|
+
children: e.unlimited ? "Unlimited" : e.limit
|
|
251
234
|
}),
|
|
252
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ v("td", {
|
|
253
236
|
className: "py-2",
|
|
254
237
|
children: e.used
|
|
255
238
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ v("td", {
|
|
257
240
|
className: "py-2",
|
|
258
241
|
children: e.resetPeriod
|
|
259
242
|
})
|
|
260
|
-
] }, e.
|
|
261
|
-
}), /* @__PURE__ */
|
|
262
|
-
className: "
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
]
|
|
243
|
+
] }, e.quotaName))] })]
|
|
244
|
+
}), /* @__PURE__ */ v("div", {
|
|
245
|
+
className: "border-t border-border pt-4",
|
|
246
|
+
children: /* @__PURE__ */ v(b, {
|
|
247
|
+
type: "button",
|
|
248
|
+
size: "sm",
|
|
249
|
+
variant: "outline",
|
|
250
|
+
disabled: !m,
|
|
251
|
+
onClick: () => m?.("/admin/billing"),
|
|
252
|
+
children: "Change limits in billing"
|
|
253
|
+
})
|
|
318
254
|
})]
|
|
319
255
|
})]
|
|
320
256
|
}),
|
|
321
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ y("section", {
|
|
322
258
|
"aria-labelledby": "ah-admin-links",
|
|
323
|
-
children: [/* @__PURE__ */
|
|
259
|
+
children: [/* @__PURE__ */ v(t, {
|
|
324
260
|
title: "Billing & support",
|
|
325
261
|
description: "Handled by the shared admin surfaces."
|
|
326
|
-
}), /* @__PURE__ */
|
|
262
|
+
}), /* @__PURE__ */ v("div", {
|
|
327
263
|
id: "ah-admin-links",
|
|
328
264
|
className: "grid gap-4 md:grid-cols-3",
|
|
329
|
-
children:
|
|
265
|
+
children: S.map(({ path: e, label: t, description: n, icon: r }) => /* @__PURE__ */ y(x, {
|
|
330
266
|
className: "space-y-2 p-4",
|
|
331
267
|
children: [
|
|
332
|
-
/* @__PURE__ */
|
|
268
|
+
/* @__PURE__ */ y("p", {
|
|
333
269
|
className: "flex items-center gap-2 font-medium text-text-primary",
|
|
334
|
-
children: [/* @__PURE__ */
|
|
270
|
+
children: [/* @__PURE__ */ v(r, { className: "h-4 w-4" }), t]
|
|
335
271
|
}),
|
|
336
|
-
/* @__PURE__ */
|
|
272
|
+
/* @__PURE__ */ v("p", {
|
|
337
273
|
className: "text-sm text-text-secondary",
|
|
338
274
|
children: n
|
|
339
275
|
}),
|
|
340
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ v(b, {
|
|
341
277
|
type: "button",
|
|
342
278
|
size: "sm",
|
|
343
279
|
variant: "outline",
|
|
344
|
-
disabled: !
|
|
345
|
-
onClick: () =>
|
|
280
|
+
disabled: !m,
|
|
281
|
+
onClick: () => m?.(e),
|
|
346
282
|
children: "Open"
|
|
347
283
|
})
|
|
348
284
|
]
|
|
@@ -353,6 +289,6 @@ var k = () => {
|
|
|
353
289
|
});
|
|
354
290
|
};
|
|
355
291
|
//#endregion
|
|
356
|
-
export {
|
|
292
|
+
export { w as AdminPage };
|
|
357
293
|
|
|
358
294
|
//# sourceMappingURL=AdminPage.js.map
|
|
@@ -1 +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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"}
|
|
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, the\n * workspace's effective quota limits (read-only), and shortcuts to the shared\n * admin surfaces for billing plan assignment and user support. Flag writes are\n * super-admin only (enforced by `wspace-assethandler-svc`). Limits are a billing\n * entitlement, so they are changed through billing, not from this page.\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 } 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 REMOVE_FEATURE_FLAG_OVERRIDE,\n SET_FEATURE_FLAG_OVERRIDE,\n summarizeQuotas,\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\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\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 quotaLines = summarizeQuotas(\n quotas.data?.assetHandlerWorkspaceQuotaOverview.assignments ?? []\n );\n const loadErrors = [state.error, flags.error, quotas.error].filter(Boolean);\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 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=\"The limits this workspace is held to, including the active-asset limit. Read-only: limits are a billing entitlement and are changed through billing.\"\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 {quotaLines.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 {quotaLines.map((line) => (\n <tr key={line.quotaName}>\n <td className=\"py-2\">{line.label}</td>\n <td className=\"py-2\">{line.unlimited ? 'Unlimited' : line.limit}</td>\n <td className=\"py-2\">{line.used}</td>\n <td className=\"py-2\">{line.resetPeriod}</td>\n </tr>\n ))}\n </tbody>\n </table>\n <div className=\"border-t border-border pt-4\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={!navigate}\n onClick={() => navigate?.('/admin/billing')}\n >\n Change limits in billing\n </Button>\n </div>\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":";;;;;;;;;;;AA8BA,IAAM,IAKD;CACH;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,MAAM;CACR;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,MAAM;CACR;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,MAAM;CACR;AACF;AAEA,SAAS,EAAa,GAAwB;CAC5C,OAAO,aAAiB,QAAQ,EAAM,UAAU;AAClD;AAEA,IAAa,UAAsB;CACjC,IAAM,EAAE,iBAAc,IAAI,gBAAa,EAAqB,GACtD,CAAC,GAAS,KAAc,EAA0D,IAAI,GAEtF,IAAO,CAAC,GACR,IAAY,EAAE,eAAY,GAE1B,IAAQ,EAAkC,GAAuB;EAAE;EAAW;CAAK,CAAC,GACpF,IAAQ,EAAgC,GAAqB;EAAE;EAAW;CAAK,CAAC,GAChF,IAAS,EAAiC,GAAsB;EAAE;EAAW;CAAK,CAAC,GAEnF,CAAC,GAAS,EAAE,SAAS,OAAiB,EAAY,CAAyB,GAC3E,CAAC,GAAY,EAAE,SAAS,OAAkB,EAAY,CAA4B;CAExF,IAAI,CAAC,GACH,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA,GAAC,gCAErC;;CAIT,IAAM,IAAiB,EAAM,MAAM,iCAC7B,IAAY,EAAM,MAAM,yBAAyB,SAAS,CAAC,GAC3D,IAAa,EACjB,EAAO,MAAM,mCAAmC,eAAe,CAAC,CAClE,GACM,IAAa;EAAC,EAAM;EAAO,EAAM;EAAO,EAAO;CAAK,EAAE,OAAO,OAAO,GAEpE,IAAa,OAAO,GAAa,MAAoC;EACzE,IAAI;GAGF,AAFA,MAAM,EAAQ,EAAE,WAAW,EAAE,OAAO;IAAE;IAAa;IAAK;GAAQ,EAAE,EAAE,CAAC,GACrE,MAAM,EAAM,QAAQ,GACpB,EAAW;IACT,MAAM;IACN,MAAM,GAAG,EAAI,GAAG,IAAU,YAAY,WAAW;GACnD,CAAC;EACH,SAAS,GAAO;GACd,EAAW;IAAE,MAAM;IAAS,MAAM,EAAa,CAAK;GAAE,CAAC;EACzD;CACF,GAEM,IAAgB,OAAO,MAA+B;EAC1D,IAAI;GAGF,AAFA,MAAM,EAAW,EAAE,WAAW;IAAE;IAAa;GAAI,EAAE,CAAC,GACpD,MAAM,EAAM,QAAQ,GACpB,EAAW;IAAE,MAAM;IAAQ,MAAM,gBAAgB,EAAI;GAAW,CAAC;EACnE,SAAS,GAAO;GACd,EAAW;IAAE,MAAM;IAAS,MAAM,EAAa,CAAK;GAAE,CAAC;EACzD;CACF;CAEA,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,MAAA;IACA,OAAM;IACN,aAAY;GACb,CAAA;GAEA,KACC,kBAAC,OAAD;IACE,MAAM,EAAQ,SAAS,UAAU,UAAU;IAC3C,WACE,EAAQ,SAAS,UACb,+EACA;cALR,CAQG,EAAQ,MACT,kBAAC,UAAD;KAAQ,MAAK;KAAS,WAAU;KAAiB,eAAe,EAAW,IAAI;eAAG;IAE1E,CAAA,CACL;;GAGN,EAAW,SAAS,KACnB,kBAAC,OAAD;IACE,MAAK;IACL,WAAU;cAFZ,CAGC,+BAC6B,EAAW,KAAK,MAAU,GAAO,OAAO,EAAE,KAAK,IAAI,CAC5E;;GAGP,kBAAC,WAAD;IAAS,mBAAgB;cAAzB,CACE,kBAAC,GAAD;KAAe,OAAM;KAAkB,MAAM;IAAQ,CAAA,GACrD,kBAAC,OAAD;KAAK,IAAG;KAAiB,WAAU;eAAnC;MACE,kBAAC,GAAD;OAAU,OAAM;OAAS,OAAO,GAAgB,cAAc;MAAM,CAAA;MACpE,kBAAC,GAAD;OAAU,OAAM;OAAiB,OAAO,GAAgB,qBAAqB;MAAM,CAAA;MACnF,kBAAC,GAAD;OAAU,OAAM;OAAmB,OAAO,GAAgB,sBAAsB;MAAM,CAAA;MACtF,kBAAC,GAAD;OAAU,OAAM;OAAoB,OAAO,GAAgB,wBAAwB;MAAM,CAAA;MACzF,kBAAC,GAAD;OAAU,OAAM;OAAiB,OAAO,GAAgB,qBAAqB;MAAM,CAAA;KAChF;MACE;;GAET,kBAAC,WAAD;IAAS,mBAAgB;cAAzB,CACE,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;KACZ,MAAM;IACP,CAAA,GACD,kBAAC,GAAD;KAAW,WAAU;eAArB,CACG,EAAU,WAAW,KAAK,CAAC,EAAM,WAChC,kBAAC,KAAD;MAAG,WAAU;gBAAkC;KAAuC,CAAA,GAExF,kBAAC,MAAD;MAAI,IAAG;gBACJ,EAAU,KAAK,MACd,kBAAC,MAAD;OAAmB,WAAU;iBAA7B,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuC,EAAK;OAAO,CAAA,GAChE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAK;SAAS;SAAY,EAAK,eAAe,OAAO;SAAM;SAAa;SACzE,kBAAC,UAAD,EAAA,UAAS,EAAK,QAAQ,OAAO,MAAc,CAAA;QAC1C;SACA,EAAA,CAAA,GACL,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SACE,MAAK;SACL,MAAK;SACL,SAAQ;SACR,UAAU;SACV,eAAe,KAAK,EAAW,EAAK,KAAK,CAAC,EAAK,KAAK;mBAEnD,EAAK,QAAQ,YAAY;QACpB,CAAA,GACR,kBAAC,GAAD;SACE,MAAK;SACL,MAAK;SACL,SAAQ;SACR,UAAU;SACV,eAAe,KAAK,EAAc,EAAK,GAAG;mBAC3C;QAEO,CAAA,CACL;SACH;SA5BK,EAAK,GA4BV,CACL;KACC,CAAA,CACK;MACJ;;GAET,kBAAC,WAAD;IAAS,mBAAgB;cAAzB,CACE,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;KACZ,MAAM;IACP,CAAA,GACD,kBAAC,GAAD;KAAW,WAAU;eAArB,CACE,kBAAC,SAAD;MAAO,IAAG;MAAkB,WAAU;gBAAtC,CACE,kBAAC,SAAD;OAAO,WAAU;iBACf,kBAAC,MAAD,EAAA,UAAA;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAO;QAAS,CAAA;QAC9B,kBAAC,MAAD;SAAI,WAAU;mBAAO;QAAS,CAAA;QAC9B,kBAAC,MAAD;SAAI,WAAU;mBAAO;QAAQ,CAAA;QAC7B,kBAAC,MAAD;SAAI,WAAU;mBAAO;QAAU,CAAA;OAC7B,EAAA,CAAA;MACC,CAAA,GACP,kBAAC,SAAD,EAAA,UAAA,CACG,EAAW,WAAW,KACrB,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;OAAI,SAAS;OAAG,WAAU;iBAA2B;MAEjD,CAAA,EACF,CAAA,GAEL,EAAW,KAAK,MACf,kBAAC,MAAD,EAAA,UAAA;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAQ,EAAK;OAAU,CAAA;OACrC,kBAAC,MAAD;QAAI,WAAU;kBAAQ,EAAK,YAAY,cAAc,EAAK;OAAU,CAAA;OACpE,kBAAC,MAAD;QAAI,WAAU;kBAAQ,EAAK;OAAS,CAAA;OACpC,kBAAC,MAAD;QAAI,WAAU;kBAAQ,EAAK;OAAgB,CAAA;MACzC,EAAA,GALK,EAAK,SAKV,CACL,CACI,EAAA,CAAA,CACF;SACP,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,SAAQ;OACR,UAAU,CAAC;OACX,eAAe,IAAW,gBAAgB;iBAC3C;MAEO,CAAA;KACL,CAAA,CACI;MACJ;;GAET,kBAAC,WAAD;IAAS,mBAAgB;cAAzB,CACE,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;IACb,CAAA,GACD,kBAAC,OAAD;KAAK,IAAG;KAAiB,WAAU;eAChC,EAAmB,KAAK,EAAE,SAAM,UAAO,gBAAa,MAAM,QACzD,kBAAC,GAAD;MAAsB,WAAU;gBAAhC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAb,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,GAC1B,CACA;;OACH,kBAAC,KAAD;QAAG,WAAU;kBAA+B;OAAe,CAAA;OAC3D,kBAAC,GAAD;QACE,MAAK;QACL,MAAK;QACL,SAAQ;QACR,UAAU,CAAC;QACX,eAAe,IAAW,CAAI;kBAC/B;OAEO,CAAA;MACC;QAfK,CAeL,CACZ;IACE,CAAA,CACE;;EACN;;AAET"}
|
|
@@ -1 +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;
|
|
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;AAKnD,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGnE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAgCrD,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAIxC,CAAC"}
|
|
@@ -1,42 +1,41 @@
|
|
|
1
1
|
import { AdminContext as e } from "./AdminContext.js";
|
|
2
2
|
import { AdminPage as t } from "./AdminPage.js";
|
|
3
3
|
import { useMemo as n } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { jsx as c } from "react/jsx-runtime";
|
|
4
|
+
import { ApolloClient as r, HttpLink as i, InMemoryCache as a } from "@apollo/client";
|
|
5
|
+
import { ApolloProvider as o } from "@apollo/client/react";
|
|
6
|
+
import { jsx as s } from "react/jsx-runtime";
|
|
8
7
|
//#region src/assethandler/admin/AdminRoot.tsx
|
|
9
|
-
var
|
|
10
|
-
let { apiGatewayUrl:
|
|
11
|
-
return /* @__PURE__ */
|
|
12
|
-
client: n(() => new
|
|
13
|
-
link: new
|
|
14
|
-
uri:
|
|
8
|
+
var c = ({ children: t, ...c }) => {
|
|
9
|
+
let { apiGatewayUrl: l, authToken: u, workspaceToken: d, workspaceId: f, tenantId: p } = c;
|
|
10
|
+
return /* @__PURE__ */ s(o, {
|
|
11
|
+
client: n(() => new r({
|
|
12
|
+
link: new i({
|
|
13
|
+
uri: l ? `${l}/workspaces/graphql` : "/workspaces/graphql",
|
|
15
14
|
headers: {
|
|
16
|
-
...
|
|
17
|
-
...
|
|
18
|
-
...
|
|
19
|
-
...
|
|
15
|
+
...u ? { Authorization: `Bearer ${u}` } : {},
|
|
16
|
+
...d ? { "X-Workspace-Authorization": `Bearer ${d}` } : {},
|
|
17
|
+
...f ? { "x-workspace-id": f } : {},
|
|
18
|
+
...p ? { "x-tenant-id": p } : {}
|
|
20
19
|
}
|
|
21
20
|
}),
|
|
22
|
-
cache: new
|
|
21
|
+
cache: new a()
|
|
23
22
|
}), [
|
|
23
|
+
l,
|
|
24
24
|
u,
|
|
25
25
|
d,
|
|
26
26
|
f,
|
|
27
|
-
p
|
|
28
|
-
m
|
|
27
|
+
p
|
|
29
28
|
]),
|
|
30
|
-
children: /* @__PURE__ */
|
|
31
|
-
value:
|
|
29
|
+
children: /* @__PURE__ */ s(e.Provider, {
|
|
30
|
+
value: c,
|
|
32
31
|
children: t
|
|
33
|
-
})
|
|
32
|
+
})
|
|
34
33
|
});
|
|
35
|
-
},
|
|
34
|
+
}, l = (e) => /* @__PURE__ */ s(c, {
|
|
36
35
|
...e,
|
|
37
|
-
children: /* @__PURE__ */
|
|
36
|
+
children: /* @__PURE__ */ s(t, {})
|
|
38
37
|
});
|
|
39
38
|
//#endregion
|
|
40
|
-
export {
|
|
39
|
+
export { l as AdminRoot };
|
|
41
40
|
|
|
42
41
|
//# sourceMappingURL=AdminRoot.js.map
|
|
@@ -1 +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 {
|
|
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 { 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 // No router here on purpose: admin-app already renders this inside its own\n // <Router>, and nesting another one throws. The page needs no routing —\n // navigation to other admin surfaces goes through the host's `navigate` prop.\n return (\n <ApolloProvider client={client}>\n <AdminContext.Provider value={props}>{children}</AdminContext.Provider>\n </ApolloProvider>\n );\n};\n\nexport const AdminRoot: FC<AdminRootProps> = (props) => (\n <AdminProvider {...props}>\n <AdminPage />\n </AdminProvider>\n);\n"],"mappings":";;;;;;;AAqBA,IAAM,KAAwD,EAAE,aAAU,GAAG,QAAY;CACvF,IAAM,EAAE,kBAAe,cAAW,mBAAgB,gBAAa,gBAAa;CAsB5E,OACE,kBAAC,GAAD;EAAwB,QArBX,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,CAOxC;YACtB,kBAAC,EAAa,UAAd;GAAuB,OAAO;GAAQ;EAAgC,CAAA;CACxD,CAAA;AAEpB,GAEa,KAAiC,MAC5C,kBAAC,GAAD;CAAe,GAAI;WACjB,kBAAC,GAAD,CAAY,CAAA;AACC,CAAA"}
|
|
@@ -3,7 +3,6 @@ export declare const ADMIN_FEATURE_FLAGS: import('graphql').DocumentNode;
|
|
|
3
3
|
export declare const ADMIN_QUOTA_OVERVIEW: import('graphql').DocumentNode;
|
|
4
4
|
export declare const SET_FEATURE_FLAG_OVERRIDE: import('graphql').DocumentNode;
|
|
5
5
|
export declare const REMOVE_FEATURE_FLAG_OVERRIDE: import('graphql').DocumentNode;
|
|
6
|
-
export declare const OVERRIDE_QUOTA: import('graphql').DocumentNode;
|
|
7
6
|
export interface AdminWorkspaceState {
|
|
8
7
|
workspaceId: string;
|
|
9
8
|
assetCount: number;
|
|
@@ -52,10 +51,25 @@ export declare const ASSETHANDLER_QUOTAS: ReadonlyArray<{
|
|
|
52
51
|
}>;
|
|
53
52
|
/** Human label for a quota name; falls back to the raw name for unknown ones. */
|
|
54
53
|
export declare function quotaLabel(name: string): string;
|
|
54
|
+
/** One line per quota: every active plan row for that quota folded together. */
|
|
55
|
+
export interface QuotaSummary {
|
|
56
|
+
quotaName: string;
|
|
57
|
+
label: string;
|
|
58
|
+
/** Effective limit; `null` when the quota is unlimited. */
|
|
59
|
+
limit: number | null;
|
|
60
|
+
unlimited: boolean;
|
|
61
|
+
used: number;
|
|
62
|
+
resetPeriod: string;
|
|
63
|
+
}
|
|
55
64
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* the
|
|
65
|
+
* Fold a workspace's active quota assignments into one line per quota.
|
|
66
|
+
*
|
|
67
|
+
* A workspace can hold several active assignments for the same quota (one per
|
|
68
|
+
* subscription). The quota service enforces their combined total: the limits
|
|
69
|
+
* add up, and any unlimited assignment makes the whole quota unlimited. This
|
|
70
|
+
* mirrors that rule so the admin sees the limit that is actually enforced, not
|
|
71
|
+
* a row per subscription. Quotas AssetHandler knows come first, in a stable
|
|
72
|
+
* order; anything else follows alphabetically.
|
|
59
73
|
*/
|
|
60
|
-
export declare function
|
|
74
|
+
export declare function summarizeQuotas(assignments: readonly AdminQuotaAssignment[]): QuotaSummary[];
|
|
61
75
|
//# sourceMappingURL=adminOperations.d.ts.map
|
|
@@ -1 +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,
|
|
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,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,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAAG,YAAY,EAAE,CA2B5F"}
|
|
@@ -55,18 +55,7 @@ var t = e`
|
|
|
55
55
|
mutation RemoveAssetHandlerFeatureFlagOverride($workspaceId: ID!, $key: String!) {
|
|
56
56
|
removeAssetHandlerFeatureFlagOverride(workspaceId: $workspaceId, key: $key)
|
|
57
57
|
}
|
|
58
|
-
`, o =
|
|
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 = [
|
|
58
|
+
`, o = [
|
|
70
59
|
{
|
|
71
60
|
name: "assethandler.assets.count",
|
|
72
61
|
label: "Active assets (asset limit)"
|
|
@@ -100,16 +89,30 @@ var t = e`
|
|
|
100
89
|
label: "ESG metrics / month"
|
|
101
90
|
}
|
|
102
91
|
];
|
|
103
|
-
function
|
|
104
|
-
return
|
|
92
|
+
function s(e) {
|
|
93
|
+
return o.find((t) => t.name === e)?.label ?? e;
|
|
105
94
|
}
|
|
106
|
-
function
|
|
107
|
-
let t =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
function c(e) {
|
|
96
|
+
let t = /* @__PURE__ */ new Map();
|
|
97
|
+
for (let n of e) {
|
|
98
|
+
if (!n.isActive) continue;
|
|
99
|
+
let e = t.get(n.quotaName);
|
|
100
|
+
e ? e.push(n) : t.set(n.quotaName, [n]);
|
|
101
|
+
}
|
|
102
|
+
let n = o.map((e) => e.name);
|
|
103
|
+
return [...n.filter((e) => t.has(e)), ...[...t.keys()].filter((e) => !n.includes(e)).sort()].map((e) => {
|
|
104
|
+
let n = t.get(e) ?? [], r = n.some((e) => e.noLimit || e.limit === null);
|
|
105
|
+
return {
|
|
106
|
+
quotaName: e,
|
|
107
|
+
label: s(e),
|
|
108
|
+
limit: r ? null : n.reduce((e, t) => e + (t.limit ?? 0), 0),
|
|
109
|
+
unlimited: r,
|
|
110
|
+
used: n.reduce((e, t) => e + t.used, 0),
|
|
111
|
+
resetPeriod: [...new Set(n.map((e) => e.resetPeriod))].join(" / ")
|
|
112
|
+
};
|
|
113
|
+
});
|
|
111
114
|
}
|
|
112
115
|
//#endregion
|
|
113
|
-
export { n as ADMIN_FEATURE_FLAGS, r as ADMIN_QUOTA_OVERVIEW, t as ADMIN_WORKSPACE_STATE,
|
|
116
|
+
export { n as ADMIN_FEATURE_FLAGS, r as ADMIN_QUOTA_OVERVIEW, t as ADMIN_WORKSPACE_STATE, a as REMOVE_FEATURE_FLAG_OVERRIDE, i as SET_FEATURE_FLAG_OVERRIDE, c as summarizeQuotas };
|
|
114
117
|
|
|
115
118
|
//# sourceMappingURL=adminOperations.js.map
|
|
@@ -1 +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
|
|
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 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/** One line per quota: every active plan row for that quota folded together. */\nexport interface QuotaSummary {\n quotaName: string;\n label: string;\n /** Effective limit; `null` when the quota is unlimited. */\n limit: number | null;\n unlimited: boolean;\n used: number;\n resetPeriod: string;\n}\n\n/**\n * Fold a workspace's active quota assignments into one line per quota.\n *\n * A workspace can hold several active assignments for the same quota (one per\n * subscription). The quota service enforces their combined total: the limits\n * add up, and any unlimited assignment makes the whole quota unlimited. This\n * mirrors that rule so the admin sees the limit that is actually enforced, not\n * a row per subscription. Quotas AssetHandler knows come first, in a stable\n * order; anything else follows alphabetically.\n */\nexport function summarizeQuotas(assignments: readonly AdminQuotaAssignment[]): QuotaSummary[] {\n const byQuota = new Map<string, AdminQuotaAssignment[]>();\n for (const assignment of assignments) {\n if (!assignment.isActive) continue;\n const rows = byQuota.get(assignment.quotaName);\n if (rows) rows.push(assignment);\n else byQuota.set(assignment.quotaName, [assignment]);\n }\n\n const known = ASSETHANDLER_QUOTAS.map((quota) => quota.name);\n const names = [\n ...known.filter((name) => byQuota.has(name)),\n ...[...byQuota.keys()].filter((name) => !known.includes(name)).sort(),\n ];\n\n return names.map((quotaName) => {\n const rows = byQuota.get(quotaName) ?? [];\n const unlimited = rows.some((row) => row.noLimit || row.limit === null);\n return {\n quotaName,\n label: quotaLabel(quotaName),\n limit: unlimited ? null : rows.reduce((sum, row) => sum + (row.limit ?? 0), 0),\n unlimited,\n used: rows.reduce((sum, row) => sum + row.used, 0),\n resetPeriod: [...new Set(rows.map((row) => row.resetPeriod))].join(' / '),\n };\n });\n}\n"],"mappings":";;AASA,IAAa,IAAwB,CAAG;;;;;;;;;;;;GAc3B,IAAsB,CAAG;;;;;;;;;;;;GAczB,IAAuB,CAAG;;;;;;;;;;;;;;;;;GAmB1B,IAA4B,CAAG;;;;;;;;;;GAY/B,IAA+B,CAAG;;;;GAmDlC,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;AAuBA,SAAgB,EAAgB,GAA8D;CAC5F,IAAM,oBAAU,IAAI,IAAoC;CACxD,KAAK,IAAM,KAAc,GAAa;EACpC,IAAI,CAAC,EAAW,UAAU;EAC1B,IAAM,IAAO,EAAQ,IAAI,EAAW,SAAS;EAC7C,AAAI,IAAM,EAAK,KAAK,CAAU,IACzB,EAAQ,IAAI,EAAW,WAAW,CAAC,CAAU,CAAC;CACrD;CAEA,IAAM,IAAQ,EAAoB,KAAK,MAAU,EAAM,IAAI;CAM3D,OAAO,CAJL,GAAG,EAAM,QAAQ,MAAS,EAAQ,IAAI,CAAI,CAAC,GAC3C,GAAG,CAAC,GAAG,EAAQ,KAAK,CAAC,EAAE,QAAQ,MAAS,CAAC,EAAM,SAAS,CAAI,CAAC,EAAE,KAAK,CAG/D,EAAM,KAAK,MAAc;EAC9B,IAAM,IAAO,EAAQ,IAAI,CAAS,KAAK,CAAC,GAClC,IAAY,EAAK,MAAM,MAAQ,EAAI,WAAW,EAAI,UAAU,IAAI;EACtE,OAAO;GACL;GACA,OAAO,EAAW,CAAS;GAC3B,OAAO,IAAY,OAAO,EAAK,QAAQ,GAAK,MAAQ,KAAO,EAAI,SAAS,IAAI,CAAC;GAC7E;GACA,MAAM,EAAK,QAAQ,GAAK,MAAQ,IAAM,EAAI,MAAM,CAAC;GACjD,aAAa,CAAC,GAAG,IAAI,IAAI,EAAK,KAAK,MAAQ,EAAI,WAAW,CAAC,CAAC,EAAE,KAAK,KAAK;EAC1E;CACF,CAAC;AACH"}
|