@fonderie/react-admin-screens 1.1.0 → 1.2.0

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/screens/AdminShell.tsx
2
2
  import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/react-config-admin-screens";
3
3
  import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/react-courier-admin-screens";
4
- import { useEffect, useState as useState6 } from "react";
4
+ import { useEffect, useState as useState7 } from "react";
5
5
 
6
6
  // src/styles.ts
7
7
  var t = {
@@ -1073,33 +1073,265 @@ function MigrationsScreen({ client }) {
1073
1073
  ] });
1074
1074
  }
1075
1075
 
1076
- // src/screens/TokensScreen.tsx
1077
- import { useAdminTokens } from "@fonderie/react-admin";
1076
+ // src/screens/OperatorsScreen.tsx
1077
+ import { useAdminOperators } from "@fonderie/react-admin";
1078
1078
  import { useState as useState2 } from "react";
1079
1079
  import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1080
+ var LEVELS = [
1081
+ { label: "Read only", scopes: ["read"], hint: "look around, change nothing" },
1082
+ { label: "Editor", scopes: ["read", "write"], hint: "change config, templates, users" },
1083
+ {
1084
+ label: "Owner",
1085
+ scopes: ["read", "write", "secrets"],
1086
+ hint: "secrets, tokens and operators too"
1087
+ }
1088
+ ];
1089
+ var levelOf = (scopes) => scopes.includes("secrets") ? "Owner" : scopes.includes("write") ? "Editor" : "Read only";
1090
+ function Minted({ link, onClose }) {
1091
+ const [copied, setCopied] = useState2(false);
1092
+ const url = `${window.location.origin}${link.url}`;
1093
+ return /* @__PURE__ */ jsxs9(
1094
+ "div",
1095
+ {
1096
+ style: {
1097
+ ...styles.notice,
1098
+ background: "color-mix(in srgb, var(--fonderie-accent,#00d294) 10%, transparent)",
1099
+ borderColor: "color-mix(in srgb, var(--fonderie-accent,#00d294) 35%, transparent)"
1100
+ },
1101
+ children: [
1102
+ /* @__PURE__ */ jsxs9("strong", { children: [
1103
+ "Send this link to ",
1104
+ link.email,
1105
+ ". It is shown once."
1106
+ ] }),
1107
+ " It works once and expires",
1108
+ " ",
1109
+ new Date(link.expiresAt).toLocaleString(),
1110
+ ".",
1111
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: 8, marginTop: 8, alignItems: "center" }, children: [
1112
+ /* @__PURE__ */ jsx9("code", { style: { ...styles.code, flex: 1, padding: "7px 10px", wordBreak: "break-all" }, children: url }),
1113
+ /* @__PURE__ */ jsx9(
1114
+ "button",
1115
+ {
1116
+ type: "button",
1117
+ style: styles.button,
1118
+ onClick: () => {
1119
+ void navigator.clipboard?.writeText(url);
1120
+ setCopied(true);
1121
+ },
1122
+ children: copied ? "Copied" : "Copy"
1123
+ }
1124
+ ),
1125
+ /* @__PURE__ */ jsx9("button", { type: "button", style: styles.buttonGhost, onClick: onClose, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx9(Icon, { name: "close", size: 14 }) })
1126
+ ] })
1127
+ ]
1128
+ }
1129
+ );
1130
+ }
1131
+ function OperatorsScreen({ client, me }) {
1132
+ const { report, isLoading, error, invite, recover, update, revokeLink } = useAdminOperators(client);
1133
+ const [email, setEmail] = useState2("");
1134
+ const [level, setLevel] = useState2("Editor");
1135
+ const [minted, setMinted] = useState2(null);
1136
+ const scopesFor = (label) => LEVELS.find((l) => l.label === label)?.scopes ?? ["read"];
1137
+ const status = (o) => o.disabledAt ? /* @__PURE__ */ jsx9(Pill, { tone: "neutral", children: "disabled" }) : o.locked ? /* @__PURE__ */ jsx9(Pill, { tone: "warn", children: "locked" }) : !o.enrolled ? /* @__PURE__ */ jsx9(Pill, { tone: "warn", children: "setting up" }) : /* @__PURE__ */ jsx9(Pill, { tone: "ok", children: "active" });
1138
+ return /* @__PURE__ */ jsxs9("div", { style: styles.container, children: [
1139
+ /* @__PURE__ */ jsx9(
1140
+ PageHeader,
1141
+ {
1142
+ title: "Operators",
1143
+ lead: "The people who can sign in to this console. Each one uses a password and an authenticator app; there is no sign-up."
1144
+ }
1145
+ ),
1146
+ error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: error.status === 403 && error.reason === "FORBIDDEN" ? "Managing operators needs the Owner level." : error.explanation }) : null,
1147
+ minted ? /* @__PURE__ */ jsx9(Minted, { link: minted, onClose: () => setMinted(null) }) : null,
1148
+ /* @__PURE__ */ jsxs9(
1149
+ "form",
1150
+ {
1151
+ style: {
1152
+ ...styles.card,
1153
+ display: "flex",
1154
+ gap: 8,
1155
+ alignItems: "center",
1156
+ flexWrap: "wrap",
1157
+ marginBottom: 20
1158
+ },
1159
+ onSubmit: (e) => {
1160
+ e.preventDefault();
1161
+ void invite({ email: email.trim(), scopes: scopesFor(level) }).then((l) => {
1162
+ setMinted(l);
1163
+ setEmail("");
1164
+ }).catch(() => {
1165
+ });
1166
+ },
1167
+ children: [
1168
+ /* @__PURE__ */ jsx9(Icon, { name: "users", size: 16 }),
1169
+ /* @__PURE__ */ jsx9("strong", { style: { fontSize: 13.5, marginRight: 4 }, children: "Invite" }),
1170
+ /* @__PURE__ */ jsx9(
1171
+ "input",
1172
+ {
1173
+ type: "email",
1174
+ required: true,
1175
+ value: email,
1176
+ onChange: (e) => setEmail(e.target.value),
1177
+ placeholder: "teammate@company.com",
1178
+ style: { ...styles.input, flex: 1, minWidth: 220 },
1179
+ "aria-label": "Email to invite"
1180
+ }
1181
+ ),
1182
+ /* @__PURE__ */ jsx9(
1183
+ "select",
1184
+ {
1185
+ value: level,
1186
+ onChange: (e) => setLevel(e.target.value),
1187
+ style: styles.input,
1188
+ "aria-label": "Access level",
1189
+ children: LEVELS.map((l) => /* @__PURE__ */ jsxs9("option", { value: l.label, children: [
1190
+ l.label,
1191
+ " \u2014 ",
1192
+ l.hint
1193
+ ] }, l.label))
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsxs9("button", { type: "submit", style: styles.buttonPrimary, disabled: !email.trim(), children: [
1197
+ /* @__PURE__ */ jsx9(Icon, { name: "plus", size: 14 }),
1198
+ "Create invite link"
1199
+ ] })
1200
+ ]
1201
+ }
1202
+ ),
1203
+ isLoading && !report ? /* @__PURE__ */ jsx9("p", { style: styles.status, children: "Loading\u2026" }) : report && report.operators.length === 0 ? /* @__PURE__ */ jsx9(Empty, { icon: "users", title: "No operators yet" }) : report ? /* @__PURE__ */ jsxs9("table", { style: styles.table, children: [
1204
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
1205
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Operator" }),
1206
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Access" }),
1207
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Status" }),
1208
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Last sign-in" }),
1209
+ /* @__PURE__ */ jsx9("th", { style: styles.th })
1210
+ ] }) }),
1211
+ /* @__PURE__ */ jsx9("tbody", { children: report.operators.map((o) => /* @__PURE__ */ jsxs9("tr", { children: [
1212
+ /* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
1213
+ /* @__PURE__ */ jsx9("div", { style: { fontWeight: 600 }, children: o.name || o.email }),
1214
+ o.name ? /* @__PURE__ */ jsx9("div", { style: styles.muted, children: o.email }) : null
1215
+ ] }),
1216
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: /* @__PURE__ */ jsx9(
1217
+ "select",
1218
+ {
1219
+ value: levelOf(o.scopes),
1220
+ onChange: (e) => void update(o.id, { scopes: scopesFor(e.target.value) }).catch(() => {
1221
+ }),
1222
+ style: { ...styles.input, height: 28, fontSize: 12.5 },
1223
+ disabled: o.email === me,
1224
+ "aria-label": `Access level for ${o.email}`,
1225
+ children: LEVELS.map((l) => /* @__PURE__ */ jsx9("option", { value: l.label, children: l.label }, l.label))
1226
+ }
1227
+ ) }),
1228
+ /* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
1229
+ status(o),
1230
+ o.enrolled && o.backupCodesLeft <= 2 && !o.disabledAt ? /* @__PURE__ */ jsxs9("div", { style: { ...styles.muted, marginTop: 4 }, children: [
1231
+ o.backupCodesLeft,
1232
+ " backup code(s) left"
1233
+ ] }) : null
1234
+ ] }),
1235
+ /* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.muted, whiteSpace: "nowrap" }, children: o.lastLoginAt ? new Date(o.lastLoginAt).toLocaleString() : "never" }),
1236
+ /* @__PURE__ */ jsx9("td", { style: { ...styles.td, textAlign: "right", whiteSpace: "nowrap" }, children: o.email === me ? /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "you" }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
1237
+ /* @__PURE__ */ jsx9(
1238
+ "button",
1239
+ {
1240
+ type: "button",
1241
+ style: { ...styles.button, height: 28, marginRight: 6 },
1242
+ onClick: () => {
1243
+ if (window.confirm(
1244
+ `Create a recovery link for ${o.email}? It signs them out everywhere; the link sets a new password and a new authenticator.`
1245
+ ))
1246
+ void recover(o.id).then(setMinted).catch(() => {
1247
+ });
1248
+ },
1249
+ children: "Recovery link"
1250
+ }
1251
+ ),
1252
+ /* @__PURE__ */ jsx9(
1253
+ "button",
1254
+ {
1255
+ type: "button",
1256
+ style: {
1257
+ ...o.disabledAt ? styles.button : styles.buttonDanger,
1258
+ height: 28
1259
+ },
1260
+ onClick: () => {
1261
+ if (o.disabledAt || window.confirm(`Disable ${o.email}? They are signed out immediately.`))
1262
+ void update(o.id, { disabled: !o.disabledAt }).catch(() => {
1263
+ });
1264
+ },
1265
+ children: o.disabledAt ? "Enable" : "Disable"
1266
+ }
1267
+ )
1268
+ ] }) })
1269
+ ] }, o.id)) })
1270
+ ] }) : null,
1271
+ report && report.links.length > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
1272
+ /* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Pending links" }),
1273
+ /* @__PURE__ */ jsx9("ul", { style: styles.list, children: report.links.map((l) => /* @__PURE__ */ jsxs9(
1274
+ "li",
1275
+ {
1276
+ style: { ...styles.row, display: "flex", gap: 12, alignItems: "center" },
1277
+ children: [
1278
+ /* @__PURE__ */ jsx9(Pill, { tone: l.kind === "invite" ? "info" : "warn", dot: false, children: l.kind }),
1279
+ /* @__PURE__ */ jsxs9("span", { style: { flex: 1 }, children: [
1280
+ l.email,
1281
+ l.kind === "invite" ? /* @__PURE__ */ jsxs9("span", { style: styles.muted, children: [
1282
+ " \xB7 ",
1283
+ levelOf(l.scopes)
1284
+ ] }) : null
1285
+ ] }),
1286
+ /* @__PURE__ */ jsxs9("span", { style: styles.muted, children: [
1287
+ "expires ",
1288
+ new Date(l.expiresAt).toLocaleString()
1289
+ ] }),
1290
+ /* @__PURE__ */ jsx9(
1291
+ "button",
1292
+ {
1293
+ type: "button",
1294
+ style: { ...styles.buttonDanger, height: 28 },
1295
+ onClick: () => void revokeLink(l.id).catch(() => {
1296
+ }),
1297
+ children: "Revoke"
1298
+ }
1299
+ )
1300
+ ]
1301
+ },
1302
+ l.id
1303
+ )) })
1304
+ ] }) : null
1305
+ ] });
1306
+ }
1307
+
1308
+ // src/screens/TokensScreen.tsx
1309
+ import { useAdminTokens } from "@fonderie/react-admin";
1310
+ import { useState as useState3 } from "react";
1311
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1080
1312
  var ALL = ["read", "write", "secrets"];
1081
1313
  function TokensScreen({ client }) {
1082
1314
  const { report, isLoading, error, issue, revoke } = useAdminTokens(client);
1083
- const [name, setName] = useState2("");
1084
- const [scopes, setScopes] = useState2(["read"]);
1085
- const [days, setDays] = useState2("");
1086
- const [minted, setMinted] = useState2(null);
1315
+ const [name, setName] = useState3("");
1316
+ const [scopes, setScopes] = useState3(["read"]);
1317
+ const [days, setDays] = useState3("");
1318
+ const [minted, setMinted] = useState3(null);
1087
1319
  const toggle = (s) => setScopes((prev) => prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]);
1088
- return /* @__PURE__ */ jsxs9("div", { style: styles.container, children: [
1089
- /* @__PURE__ */ jsx9(
1320
+ return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
1321
+ /* @__PURE__ */ jsx10(
1090
1322
  PageHeader,
1091
1323
  {
1092
- title: "Access",
1093
- lead: "Who can open this surface: the root token's strength, scoped tokens you have issued, and any legacy per-module tokens."
1324
+ title: "Tokens",
1325
+ lead: "Credentials for machines: the root token's strength, scoped tokens for the CLI and CI, and any legacy per-module tokens. People sign in as operators."
1094
1326
  }
1095
1327
  ),
1096
- error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: error.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.explanation }) : null,
1097
- /* @__PURE__ */ jsx9("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Root token" }),
1098
- report ? /* @__PURE__ */ jsxs9("div", { style: { ...styles.card, display: "flex", gap: 12, alignItems: "flex-start" }, children: [
1099
- /* @__PURE__ */ jsx9(Icon, { name: "lock", size: 18, style: { marginTop: 2 } }),
1100
- /* @__PURE__ */ jsxs9("div", { children: [
1101
- /* @__PURE__ */ jsx9(Pill, { tone: report.admin.ok ? "ok" : "bad", children: report.admin.ok ? "strong" : "weak" }),
1102
- report.admin.problems.map((p) => /* @__PURE__ */ jsx9(
1328
+ error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: error.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.explanation }) : null,
1329
+ /* @__PURE__ */ jsx10("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Root token" }),
1330
+ report ? /* @__PURE__ */ jsxs10("div", { style: { ...styles.card, display: "flex", gap: 12, alignItems: "flex-start" }, children: [
1331
+ /* @__PURE__ */ jsx10(Icon, { name: "lock", size: 18, style: { marginTop: 2 } }),
1332
+ /* @__PURE__ */ jsxs10("div", { children: [
1333
+ /* @__PURE__ */ jsx10(Pill, { tone: report.admin.ok ? "ok" : "bad", children: report.admin.ok ? "strong" : "weak" }),
1334
+ report.admin.problems.map((p) => /* @__PURE__ */ jsx10(
1103
1335
  "div",
1104
1336
  {
1105
1337
  style: { marginTop: 6, color: "var(--fonderie-danger,#e00)", fontSize: 13 },
@@ -1109,9 +1341,9 @@ function TokensScreen({ client }) {
1109
1341
  ))
1110
1342
  ] })
1111
1343
  ] }) : null,
1112
- /* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Issued tokens" }),
1113
- report?.issued === null ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
1114
- minted ? /* @__PURE__ */ jsxs9(
1344
+ /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Issued tokens" }),
1345
+ report?.issued === null ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
1346
+ minted ? /* @__PURE__ */ jsxs10(
1115
1347
  "div",
1116
1348
  {
1117
1349
  style: {
@@ -1120,11 +1352,11 @@ function TokensScreen({ client }) {
1120
1352
  borderColor: "color-mix(in srgb, var(--fonderie-accent,#00d294) 35%, transparent)"
1121
1353
  },
1122
1354
  children: [
1123
- /* @__PURE__ */ jsx9("strong", { children: "Copy this now \u2014 it is never shown again." }),
1355
+ /* @__PURE__ */ jsx10("strong", { children: "Copy this now \u2014 it is never shown again." }),
1124
1356
  " (",
1125
1357
  minted.name,
1126
1358
  ")",
1127
- /* @__PURE__ */ jsx9(
1359
+ /* @__PURE__ */ jsx10(
1128
1360
  "div",
1129
1361
  {
1130
1362
  style: {
@@ -1140,7 +1372,7 @@ function TokensScreen({ client }) {
1140
1372
  ]
1141
1373
  }
1142
1374
  ) : null,
1143
- /* @__PURE__ */ jsxs9(
1375
+ /* @__PURE__ */ jsxs10(
1144
1376
  "form",
1145
1377
  {
1146
1378
  style: styles.toolbar,
@@ -1159,7 +1391,7 @@ function TokensScreen({ client }) {
1159
1391
  });
1160
1392
  },
1161
1393
  children: [
1162
- /* @__PURE__ */ jsx9(
1394
+ /* @__PURE__ */ jsx10(
1163
1395
  "input",
1164
1396
  {
1165
1397
  value: name,
@@ -1169,7 +1401,7 @@ function TokensScreen({ client }) {
1169
1401
  "aria-label": "Name"
1170
1402
  }
1171
1403
  ),
1172
- ALL.map((s) => /* @__PURE__ */ jsxs9(
1404
+ ALL.map((s) => /* @__PURE__ */ jsxs10(
1173
1405
  "label",
1174
1406
  {
1175
1407
  style: {
@@ -1181,14 +1413,14 @@ function TokensScreen({ client }) {
1181
1413
  fontSize: 13
1182
1414
  },
1183
1415
  children: [
1184
- /* @__PURE__ */ jsx9("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
1416
+ /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
1185
1417
  " ",
1186
1418
  s
1187
1419
  ]
1188
1420
  },
1189
1421
  s
1190
1422
  )),
1191
- /* @__PURE__ */ jsx9(
1423
+ /* @__PURE__ */ jsx10(
1192
1424
  "input",
1193
1425
  {
1194
1426
  value: days,
@@ -1198,14 +1430,14 @@ function TokensScreen({ client }) {
1198
1430
  "aria-label": "Days"
1199
1431
  }
1200
1432
  ),
1201
- /* @__PURE__ */ jsxs9(
1433
+ /* @__PURE__ */ jsxs10(
1202
1434
  "button",
1203
1435
  {
1204
1436
  type: "submit",
1205
1437
  style: styles.buttonPrimary,
1206
1438
  disabled: !name.trim() || scopes.length === 0 || isLoading,
1207
1439
  children: [
1208
- /* @__PURE__ */ jsx9(Icon, { name: "plus", size: 14 }),
1440
+ /* @__PURE__ */ jsx10(Icon, { name: "plus", size: 14 }),
1209
1441
  "Issue token"
1210
1442
  ]
1211
1443
  }
@@ -1213,32 +1445,32 @@ function TokensScreen({ client }) {
1213
1445
  ]
1214
1446
  }
1215
1447
  ),
1216
- report?.issued && report.issued.length > 0 ? /* @__PURE__ */ jsxs9("table", { style: styles.table, children: [
1217
- /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
1218
- /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Name" }),
1219
- /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Scopes" }),
1220
- /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Created" }),
1221
- /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Expires" }),
1222
- /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Last used" }),
1223
- /* @__PURE__ */ jsx9("th", { style: styles.th })
1448
+ report?.issued && report.issued.length > 0 ? /* @__PURE__ */ jsxs10("table", { style: styles.table, children: [
1449
+ /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
1450
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Name" }),
1451
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Scopes" }),
1452
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Created" }),
1453
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Expires" }),
1454
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Last used" }),
1455
+ /* @__PURE__ */ jsx10("th", { style: styles.th })
1224
1456
  ] }) }),
1225
- /* @__PURE__ */ jsx9("tbody", { children: report.issued.map((t2) => /* @__PURE__ */ jsxs9("tr", { children: [
1226
- /* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
1457
+ /* @__PURE__ */ jsx10("tbody", { children: report.issued.map((t2) => /* @__PURE__ */ jsxs10("tr", { children: [
1458
+ /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
1227
1459
  t2.name,
1228
- t2.revokedAt ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
1460
+ t2.revokedAt ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1229
1461
  " ",
1230
- /* @__PURE__ */ jsx9(Pill, { tone: "neutral", children: "revoked" })
1462
+ /* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "revoked" })
1231
1463
  ] }) : null
1232
1464
  ] }),
1233
- /* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.mono }, children: t2.scopes.join(", ") }),
1234
- /* @__PURE__ */ jsxs9("td", { style: { ...styles.td, ...styles.muted }, children: [
1465
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.mono }, children: t2.scopes.join(", ") }),
1466
+ /* @__PURE__ */ jsxs10("td", { style: { ...styles.td, ...styles.muted }, children: [
1235
1467
  new Date(t2.createdAt).toLocaleDateString(),
1236
1468
  " \xB7 ",
1237
1469
  t2.createdBy
1238
1470
  ] }),
1239
- /* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.muted }, children: t2.expiresAt ? new Date(t2.expiresAt).toLocaleDateString() : "never" }),
1240
- /* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.muted }, children: t2.lastUsedAt ? new Date(t2.lastUsedAt).toLocaleString() : "never" }),
1241
- /* @__PURE__ */ jsx9("td", { style: styles.td, children: t2.revokedAt ? null : /* @__PURE__ */ jsx9(
1471
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: t2.expiresAt ? new Date(t2.expiresAt).toLocaleDateString() : "never" }),
1472
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: t2.lastUsedAt ? new Date(t2.lastUsedAt).toLocaleString() : "never" }),
1473
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: t2.revokedAt ? null : /* @__PURE__ */ jsx10(
1242
1474
  "button",
1243
1475
  {
1244
1476
  type: "button",
@@ -1254,13 +1486,13 @@ function TokensScreen({ client }) {
1254
1486
  }
1255
1487
  ) })
1256
1488
  ] }, t2.id)) })
1257
- ] }) : /* @__PURE__ */ jsx9(Empty, { icon: "tokens", title: "No scoped tokens yet", children: "Issue a read-only token for a dashboard or a teammate instead of sharing the root." })
1489
+ ] }) : /* @__PURE__ */ jsx10(Empty, { icon: "tokens", title: "No scoped tokens yet", children: "Issue a read-only token for a dashboard or a teammate instead of sharing the root." })
1258
1490
  ] }),
1259
- /* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
1260
- report && report.legacy.length === 0 ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ jsx9("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ jsxs9("li", { style: styles.row, children: [
1261
- /* @__PURE__ */ jsx9("span", { style: styles.mono, children: l.module }),
1491
+ /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
1492
+ report && report.legacy.length === 0 ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ jsx10("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ jsxs10("li", { style: styles.row, children: [
1493
+ /* @__PURE__ */ jsx10("span", { style: styles.mono, children: l.module }),
1262
1494
  " ",
1263
- /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
1495
+ /* @__PURE__ */ jsx10("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
1264
1496
  ] }, l.module)) })
1265
1497
  ] });
1266
1498
  }
@@ -1273,11 +1505,11 @@ import {
1273
1505
  useAdminUserSessions,
1274
1506
  useAdminUsers
1275
1507
  } from "@fonderie/react-admin";
1276
- import { useState as useState3 } from "react";
1277
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1508
+ import { useState as useState4 } from "react";
1509
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1278
1510
  function UsersScreen({ client, pageSize = 50 }) {
1279
- const [input, setInput] = useState3("");
1280
- const [selected, setSelected] = useState3(null);
1511
+ const [input, setInput] = useState4("");
1512
+ const [selected, setSelected] = useState4(null);
1281
1513
  const list = useAdminUsers(client, { limit: pageSize });
1282
1514
  const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
1283
1515
  client,
@@ -1286,20 +1518,20 @@ function UsersScreen({ client, pageSize = 50 }) {
1286
1518
  const userId = user?.id ?? null;
1287
1519
  const sessions = useAdminUserSessions(client, userId);
1288
1520
  const history = useAdminLoginHistory(client, userId, { limit: 20 });
1289
- const yesNo = (v) => v ? /* @__PURE__ */ jsx10(Pill, { tone: "ok", children: "yes" }) : /* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "no" });
1521
+ const yesNo = (v) => v ? /* @__PURE__ */ jsx11(Pill, { tone: "ok", children: "yes" }) : /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "no" });
1290
1522
  const clear = () => {
1291
1523
  setSelected(null);
1292
1524
  setInput("");
1293
1525
  };
1294
- return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
1295
- /* @__PURE__ */ jsx10(
1526
+ return /* @__PURE__ */ jsxs11("div", { style: styles.container, children: [
1527
+ /* @__PURE__ */ jsx11(
1296
1528
  PageHeader,
1297
1529
  {
1298
1530
  title: "Users",
1299
1531
  lead: "Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
1300
1532
  }
1301
1533
  ),
1302
- /* @__PURE__ */ jsxs10(
1534
+ /* @__PURE__ */ jsxs11(
1303
1535
  "form",
1304
1536
  {
1305
1537
  style: styles.toolbar,
@@ -1309,7 +1541,7 @@ function UsersScreen({ client, pageSize = 50 }) {
1309
1541
  setSelected(email ? { email } : null);
1310
1542
  },
1311
1543
  children: [
1312
- /* @__PURE__ */ jsx10(
1544
+ /* @__PURE__ */ jsx11(
1313
1545
  "input",
1314
1546
  {
1315
1547
  type: "email",
@@ -1320,14 +1552,14 @@ function UsersScreen({ client, pageSize = 50 }) {
1320
1552
  "aria-label": "Email"
1321
1553
  }
1322
1554
  ),
1323
- /* @__PURE__ */ jsxs10("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
1324
- /* @__PURE__ */ jsx10(Icon, { name: "search", size: 14 }),
1555
+ /* @__PURE__ */ jsxs11("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
1556
+ /* @__PURE__ */ jsx11(Icon, { name: "search", size: 14 }),
1325
1557
  "Look up"
1326
1558
  ] }),
1327
- selected ? /* @__PURE__ */ jsxs10("button", { type: "button", style: styles.buttonGhost, onClick: clear, children: [
1328
- /* @__PURE__ */ jsx10(Icon, { name: "back", size: 14 }),
1559
+ selected ? /* @__PURE__ */ jsxs11("button", { type: "button", style: styles.buttonGhost, onClick: clear, children: [
1560
+ /* @__PURE__ */ jsx11(Icon, { name: "back", size: 14 }),
1329
1561
  "All users"
1330
- ] }) : /* @__PURE__ */ jsx10(
1562
+ ] }) : /* @__PURE__ */ jsx11(
1331
1563
  "button",
1332
1564
  {
1333
1565
  type: "button",
@@ -1340,18 +1572,18 @@ function UsersScreen({ client, pageSize = 50 }) {
1340
1572
  ]
1341
1573
  }
1342
1574
  ),
1343
- error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
1344
- !selected ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1345
- list.error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
1346
- list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx10(Empty, { icon: "users", title: "No users yet", children: "Sign-ups appear here as they happen." }) : /* @__PURE__ */ jsxs10("table", { style: styles.table, children: [
1347
- /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
1348
- /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Email" }),
1349
- /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Name" }),
1350
- /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Created" }),
1351
- /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Status" })
1575
+ error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
1576
+ !selected ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
1577
+ list.error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
1578
+ list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx11(Empty, { icon: "users", title: "No users yet", children: "Sign-ups appear here as they happen." }) : /* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
1579
+ /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
1580
+ /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Email" }),
1581
+ /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Name" }),
1582
+ /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Created" }),
1583
+ /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Status" })
1352
1584
  ] }) }),
1353
- /* @__PURE__ */ jsx10("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs10("tr", { children: [
1354
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: /* @__PURE__ */ jsx10(
1585
+ /* @__PURE__ */ jsx11("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs11("tr", { children: [
1586
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: /* @__PURE__ */ jsx11(
1355
1587
  "button",
1356
1588
  {
1357
1589
  type: "button",
@@ -1360,17 +1592,17 @@ function UsersScreen({ client, pageSize = 50 }) {
1360
1592
  children: u.email
1361
1593
  }
1362
1594
  ) }),
1363
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx10("span", { style: styles.muted, children: "\u2014" }) }),
1364
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
1365
- /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
1366
- u.suspended ? /* @__PURE__ */ jsx10(Pill, { tone: "warn", children: "suspended" }) : null,
1367
- u.deletedAt ? /* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "deleted" }) : null,
1368
- !u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx10(Pill, { tone: "ok", children: "active" }) : null
1595
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "\u2014" }) }),
1596
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
1597
+ /* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
1598
+ u.suspended ? /* @__PURE__ */ jsx11(Pill, { tone: "warn", children: "suspended" }) : null,
1599
+ u.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "deleted" }) : null,
1600
+ !u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "ok", children: "active" }) : null
1369
1601
  ] })
1370
1602
  ] }, u.id)) })
1371
1603
  ] }),
1372
- list.isLoading ? /* @__PURE__ */ jsx10("p", { style: styles.status, children: "Loading\u2026" }) : null,
1373
- list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx10(
1604
+ list.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading\u2026" }) : null,
1605
+ list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx11(
1374
1606
  "button",
1375
1607
  {
1376
1608
  type: "button",
@@ -1380,48 +1612,48 @@ function UsersScreen({ client, pageSize = 50 }) {
1380
1612
  }
1381
1613
  ) : null
1382
1614
  ] }) : null,
1383
- user ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1384
- /* @__PURE__ */ jsxs10("h2", { style: styles.subtitle, children: [
1615
+ user ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
1616
+ /* @__PURE__ */ jsxs11("h2", { style: styles.subtitle, children: [
1385
1617
  user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
1386
1618
  " ",
1387
- user.suspended ? /* @__PURE__ */ jsx10(Pill, { tone: "warn", children: "suspended" }) : null,
1388
- user.deletedAt ? /* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "deleted" }) : null
1619
+ user.suspended ? /* @__PURE__ */ jsx11(Pill, { tone: "warn", children: "suspended" }) : null,
1620
+ user.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "deleted" }) : null
1389
1621
  ] }),
1390
- /* @__PURE__ */ jsx10("table", { style: styles.table, children: /* @__PURE__ */ jsxs10("tbody", { children: [
1391
- /* @__PURE__ */ jsxs10("tr", { children: [
1392
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "id" }),
1393
- /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
1622
+ /* @__PURE__ */ jsx11("table", { style: styles.table, children: /* @__PURE__ */ jsxs11("tbody", { children: [
1623
+ /* @__PURE__ */ jsxs11("tr", { children: [
1624
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "id" }),
1625
+ /* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
1394
1626
  ] }),
1395
- /* @__PURE__ */ jsxs10("tr", { children: [
1396
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "email" }),
1397
- /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
1627
+ /* @__PURE__ */ jsxs11("tr", { children: [
1628
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "email" }),
1629
+ /* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
1398
1630
  user.email,
1399
1631
  " \xB7 verified ",
1400
1632
  yesNo(user.isEmailVerified)
1401
1633
  ] })
1402
1634
  ] }),
1403
- /* @__PURE__ */ jsxs10("tr", { children: [
1404
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "MFA" }),
1405
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
1635
+ /* @__PURE__ */ jsxs11("tr", { children: [
1636
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "MFA" }),
1637
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
1406
1638
  ] }),
1407
- /* @__PURE__ */ jsxs10("tr", { children: [
1408
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "provider" }),
1409
- /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
1639
+ /* @__PURE__ */ jsxs11("tr", { children: [
1640
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "provider" }),
1641
+ /* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
1410
1642
  user.provider || "password",
1411
1643
  user.hasPassword ? "" : " \xB7 no password set"
1412
1644
  ] })
1413
1645
  ] }),
1414
- /* @__PURE__ */ jsxs10("tr", { children: [
1415
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "last login" }),
1416
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx10("span", { style: styles.muted, children: "never" }) })
1646
+ /* @__PURE__ */ jsxs11("tr", { children: [
1647
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "last login" }),
1648
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "never" }) })
1417
1649
  ] }),
1418
- /* @__PURE__ */ jsxs10("tr", { children: [
1419
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: "created" }),
1420
- /* @__PURE__ */ jsx10("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
1650
+ /* @__PURE__ */ jsxs11("tr", { children: [
1651
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: "created" }),
1652
+ /* @__PURE__ */ jsx11("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
1421
1653
  ] })
1422
1654
  ] }) }),
1423
- /* @__PURE__ */ jsxs10("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
1424
- user.suspended ? /* @__PURE__ */ jsx10(
1655
+ /* @__PURE__ */ jsxs11("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
1656
+ user.suspended ? /* @__PURE__ */ jsx11(
1425
1657
  "button",
1426
1658
  {
1427
1659
  type: "button",
@@ -1430,7 +1662,7 @@ function UsersScreen({ client, pageSize = 50 }) {
1430
1662
  disabled: isLoading,
1431
1663
  children: "Unsuspend"
1432
1664
  }
1433
- ) : /* @__PURE__ */ jsx10(
1665
+ ) : /* @__PURE__ */ jsx11(
1434
1666
  "button",
1435
1667
  {
1436
1668
  type: "button",
@@ -1440,7 +1672,7 @@ function UsersScreen({ client, pageSize = 50 }) {
1440
1672
  children: "Suspend"
1441
1673
  }
1442
1674
  ),
1443
- /* @__PURE__ */ jsx10(
1675
+ /* @__PURE__ */ jsx11(
1444
1676
  "button",
1445
1677
  {
1446
1678
  type: "button",
@@ -1451,38 +1683,38 @@ function UsersScreen({ client, pageSize = 50 }) {
1451
1683
  }
1452
1684
  )
1453
1685
  ] }),
1454
- /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Live sessions" }),
1455
- sessions.sessions.length === 0 ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx10("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs10("li", { style: styles.row, children: [
1456
- /* @__PURE__ */ jsx10("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
1686
+ /* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Live sessions" }),
1687
+ sessions.sessions.length === 0 ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx11("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs11("li", { style: styles.row, children: [
1688
+ /* @__PURE__ */ jsx11("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
1457
1689
  " ",
1458
- describeLocation(s.location) ? /* @__PURE__ */ jsxs10("span", { style: styles.muted, children: [
1690
+ describeLocation(s.location) ? /* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
1459
1691
  describeLocation(s.location),
1460
1692
  " "
1461
1693
  ] }) : null,
1462
- /* @__PURE__ */ jsx10("span", { style: styles.muted, children: s.userAgent ?? "" }),
1694
+ /* @__PURE__ */ jsx11("span", { style: styles.muted, children: s.userAgent ?? "" }),
1463
1695
  " ",
1464
- /* @__PURE__ */ jsxs10("span", { style: styles.muted, children: [
1696
+ /* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
1465
1697
  "since ",
1466
1698
  new Date(s.createdAt).toLocaleString()
1467
1699
  ] })
1468
1700
  ] }, s.id)) }),
1469
- /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
1470
- history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx10("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs10("li", { style: styles.row, children: [
1471
- /* @__PURE__ */ jsx10(Pill, { tone: e.outcome === "success" ? "ok" : "bad", children: e.outcome }),
1701
+ /* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
1702
+ history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx11("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs11("li", { style: styles.row, children: [
1703
+ /* @__PURE__ */ jsx11(Pill, { tone: e.outcome === "success" ? "ok" : "bad", children: e.outcome }),
1472
1704
  " ",
1473
- /* @__PURE__ */ jsx10("span", { style: styles.muted, children: e.method }),
1705
+ /* @__PURE__ */ jsx11("span", { style: styles.muted, children: e.method }),
1474
1706
  " ",
1475
- /* @__PURE__ */ jsx10("span", { style: styles.mono, children: e.ipAddress ?? "\u2014" }),
1707
+ /* @__PURE__ */ jsx11("span", { style: styles.mono, children: e.ipAddress ?? "\u2014" }),
1476
1708
  " ",
1477
- describeLocation(e.location) ? /* @__PURE__ */ jsx10("span", { style: styles.muted, children: describeLocation(e.location) }) : null,
1478
- e.location?.proxy || e.location?.hosting ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1709
+ describeLocation(e.location) ? /* @__PURE__ */ jsx11("span", { style: styles.muted, children: describeLocation(e.location) }) : null,
1710
+ e.location?.proxy || e.location?.hosting ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
1479
1711
  " ",
1480
- /* @__PURE__ */ jsx10(Pill, { tone: "warn", children: e.location.proxy ? "proxy/VPN" : "hosting" })
1712
+ /* @__PURE__ */ jsx11(Pill, { tone: "warn", children: e.location.proxy ? "proxy/VPN" : "hosting" })
1481
1713
  ] }) : null,
1482
1714
  " ",
1483
- /* @__PURE__ */ jsx10("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
1715
+ /* @__PURE__ */ jsx11("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
1484
1716
  ] }, e.id)) }),
1485
- history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx10(
1717
+ history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx11(
1486
1718
  "button",
1487
1719
  {
1488
1720
  type: "button",
@@ -1497,24 +1729,24 @@ function UsersScreen({ client, pageSize = 50 }) {
1497
1729
 
1498
1730
  // src/screens/CatalogScreen.tsx
1499
1731
  import { useAdminCatalog } from "@fonderie/react-admin";
1500
- import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1732
+ import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1501
1733
  function CatalogScreen({ client }) {
1502
1734
  const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
1503
1735
  const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
1504
- return /* @__PURE__ */ jsxs11("div", { style: styles.container, children: [
1505
- /* @__PURE__ */ jsx11(
1736
+ return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
1737
+ /* @__PURE__ */ jsx12(
1506
1738
  PageHeader,
1507
1739
  {
1508
1740
  title: "Catalog",
1509
1741
  lead: "What you sell: plans as configured in code, and as stored in the database, side by side.",
1510
- actions: /* @__PURE__ */ jsx11(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
1742
+ actions: /* @__PURE__ */ jsx12(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
1511
1743
  }
1512
1744
  ),
1513
- error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1514
- isLoading && !catalog ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading\u2026" }) : null,
1515
- catalog ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
1516
- /* @__PURE__ */ jsx11("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Configured (code)" }),
1517
- /* @__PURE__ */ jsx11(
1745
+ error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1746
+ isLoading && !catalog ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
1747
+ catalog ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
1748
+ /* @__PURE__ */ jsx12("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Configured (code)" }),
1749
+ /* @__PURE__ */ jsx12(
1518
1750
  "pre",
1519
1751
  {
1520
1752
  style: {
@@ -1528,32 +1760,32 @@ function CatalogScreen({ client }) {
1528
1760
  children: JSON.stringify(catalog.configured, null, 2)
1529
1761
  }
1530
1762
  ),
1531
- /* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Stored (database)" }),
1532
- catalog.stored.length === 0 ? /* @__PURE__ */ jsx11(Empty, { icon: "catalog", title: "No stored plans", children: "Plans are configured in code; nothing has been written to the database." }) : /* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
1533
- /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
1534
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Plan" }),
1535
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Tier" }),
1536
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Seats" }),
1537
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Monthly" }),
1538
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Yearly" }),
1539
- /* @__PURE__ */ jsx11("th", { style: styles.th, children: "Trial" }),
1540
- /* @__PURE__ */ jsx11("th", { style: styles.th })
1763
+ /* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Stored (database)" }),
1764
+ catalog.stored.length === 0 ? /* @__PURE__ */ jsx12(Empty, { icon: "catalog", title: "No stored plans", children: "Plans are configured in code; nothing has been written to the database." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
1765
+ /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
1766
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Plan" }),
1767
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Tier" }),
1768
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Seats" }),
1769
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Monthly" }),
1770
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Yearly" }),
1771
+ /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Trial" }),
1772
+ /* @__PURE__ */ jsx12("th", { style: styles.th })
1541
1773
  ] }) }),
1542
- /* @__PURE__ */ jsx11("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs11("tr", { children: [
1543
- /* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
1544
- /* @__PURE__ */ jsx11("strong", { children: p.name }),
1545
- p.description ? /* @__PURE__ */ jsx11("div", { style: styles.muted, children: p.description }) : null
1774
+ /* @__PURE__ */ jsx12("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs12("tr", { children: [
1775
+ /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1776
+ /* @__PURE__ */ jsx12("strong", { children: p.name }),
1777
+ p.description ? /* @__PURE__ */ jsx12("div", { style: styles.muted, children: p.description }) : null
1546
1778
  ] }),
1547
- /* @__PURE__ */ jsx11("td", { style: styles.td, children: p.tier }),
1548
- /* @__PURE__ */ jsx11("td", { style: styles.td, children: p.seats ?? "\u221E" }),
1549
- /* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
1779
+ /* @__PURE__ */ jsx12("td", { style: styles.td, children: p.tier }),
1780
+ /* @__PURE__ */ jsx12("td", { style: styles.td, children: p.seats ?? "\u221E" }),
1781
+ /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1550
1782
  money(p.pricing?.monthly),
1551
1783
  " ",
1552
1784
  p.pricing?.currency ?? ""
1553
1785
  ] }),
1554
- /* @__PURE__ */ jsx11("td", { style: styles.td, children: money(p.pricing?.yearly) }),
1555
- /* @__PURE__ */ jsx11("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
1556
- /* @__PURE__ */ jsx11("td", { style: styles.td, children: /* @__PURE__ */ jsx11(
1786
+ /* @__PURE__ */ jsx12("td", { style: styles.td, children: money(p.pricing?.yearly) }),
1787
+ /* @__PURE__ */ jsx12("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
1788
+ /* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsx12(
1557
1789
  "button",
1558
1790
  {
1559
1791
  type: "button",
@@ -1574,32 +1806,32 @@ function CatalogScreen({ client }) {
1574
1806
 
1575
1807
  // src/screens/SubscriberScreen.tsx
1576
1808
  import { useAdminSubscriber, useAdminSubscribers } from "@fonderie/react-admin";
1577
- import { useState as useState4 } from "react";
1578
- import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1809
+ import { useState as useState5 } from "react";
1810
+ import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1579
1811
  function SubscriberScreen({ client, pageSize = 50 }) {
1580
- const [type, setType] = useState4("user");
1581
- const [idInput, setIdInput] = useState4("");
1582
- const [subscriber, setSubscriber] = useState4(null);
1812
+ const [type, setType] = useState5("user");
1813
+ const [idInput, setIdInput] = useState5("");
1814
+ const [subscriber, setSubscriber] = useState5(null);
1583
1815
  const list = useAdminSubscribers(client, { limit: pageSize });
1584
1816
  const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
1585
- const [amount, setAmount] = useState4("");
1586
- const [note, setNote] = useState4("");
1587
- const [granted, setGranted] = useState4(null);
1817
+ const [amount, setAmount] = useState5("");
1818
+ const [note, setNote] = useState5("");
1819
+ const [granted, setGranted] = useState5(null);
1588
1820
  const open = (t2, id) => {
1589
1821
  setGranted(null);
1590
1822
  setType(t2);
1591
1823
  setIdInput(id);
1592
1824
  setSubscriber({ type: t2, id });
1593
1825
  };
1594
- return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
1595
- /* @__PURE__ */ jsx12(
1826
+ return /* @__PURE__ */ jsxs13("div", { style: styles.container, children: [
1827
+ /* @__PURE__ */ jsx13(
1596
1828
  PageHeader,
1597
1829
  {
1598
1830
  title: "Subscribers",
1599
1831
  lead: "Who is subscribed. Open one for their plan, wallet and ledger, and to grant credit."
1600
1832
  }
1601
1833
  ),
1602
- /* @__PURE__ */ jsxs12(
1834
+ /* @__PURE__ */ jsxs13(
1603
1835
  "form",
1604
1836
  {
1605
1837
  style: styles.toolbar,
@@ -1609,7 +1841,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1609
1841
  setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
1610
1842
  },
1611
1843
  children: [
1612
- /* @__PURE__ */ jsxs12(
1844
+ /* @__PURE__ */ jsxs13(
1613
1845
  "select",
1614
1846
  {
1615
1847
  value: type,
@@ -1617,12 +1849,12 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1617
1849
  style: styles.input,
1618
1850
  "aria-label": "Type",
1619
1851
  children: [
1620
- /* @__PURE__ */ jsx12("option", { value: "user", children: "user" }),
1621
- /* @__PURE__ */ jsx12("option", { value: "workspace", children: "workspace" })
1852
+ /* @__PURE__ */ jsx13("option", { value: "user", children: "user" }),
1853
+ /* @__PURE__ */ jsx13("option", { value: "workspace", children: "workspace" })
1622
1854
  ]
1623
1855
  }
1624
1856
  ),
1625
- /* @__PURE__ */ jsx12(
1857
+ /* @__PURE__ */ jsx13(
1626
1858
  "input",
1627
1859
  {
1628
1860
  value: idInput,
@@ -1632,11 +1864,11 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1632
1864
  "aria-label": "Subscriber id"
1633
1865
  }
1634
1866
  ),
1635
- /* @__PURE__ */ jsxs12("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
1636
- /* @__PURE__ */ jsx12(Icon, { name: "search", size: 14 }),
1867
+ /* @__PURE__ */ jsxs13("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
1868
+ /* @__PURE__ */ jsx13(Icon, { name: "search", size: 14 }),
1637
1869
  "Look up"
1638
1870
  ] }),
1639
- subscriber ? /* @__PURE__ */ jsxs12(
1871
+ subscriber ? /* @__PURE__ */ jsxs13(
1640
1872
  "button",
1641
1873
  {
1642
1874
  type: "button",
@@ -1647,11 +1879,11 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1647
1879
  setGranted(null);
1648
1880
  },
1649
1881
  children: [
1650
- /* @__PURE__ */ jsx12(Icon, { name: "back", size: 14 }),
1882
+ /* @__PURE__ */ jsx13(Icon, { name: "back", size: 14 }),
1651
1883
  "All subscribers"
1652
1884
  ]
1653
1885
  }
1654
- ) : /* @__PURE__ */ jsx12(
1886
+ ) : /* @__PURE__ */ jsx13(
1655
1887
  "button",
1656
1888
  {
1657
1889
  type: "button",
@@ -1664,18 +1896,18 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1664
1896
  ]
1665
1897
  }
1666
1898
  ),
1667
- error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1668
- !subscriber ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
1669
- list.error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
1670
- list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx12(Empty, { icon: "subscriber", title: "No subscribers yet", children: "Subscriptions appear here once someone checks out." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
1671
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
1672
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Subscriber" }),
1673
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Plan" }),
1674
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Status" }),
1675
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Renews" })
1899
+ error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1900
+ !subscriber ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
1901
+ list.error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
1902
+ list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx13(Empty, { icon: "subscriber", title: "No subscribers yet", children: "Subscriptions appear here once someone checks out." }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
1903
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
1904
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Subscriber" }),
1905
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Plan" }),
1906
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Status" }),
1907
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Renews" })
1676
1908
  ] }) }),
1677
- /* @__PURE__ */ jsx12("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs12("tr", { children: [
1678
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsxs12(
1909
+ /* @__PURE__ */ jsx13("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs13("tr", { children: [
1910
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: /* @__PURE__ */ jsxs13(
1679
1911
  "button",
1680
1912
  {
1681
1913
  type: "button",
@@ -1688,29 +1920,29 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1688
1920
  ]
1689
1921
  }
1690
1922
  ) }),
1691
- /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1692
- /* @__PURE__ */ jsx12("strong", { children: s.plan }),
1923
+ /* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
1924
+ /* @__PURE__ */ jsx13("strong", { children: s.plan }),
1693
1925
  " \xB7 ",
1694
1926
  s.interval
1695
1927
  ] }),
1696
- /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1697
- /* @__PURE__ */ jsx12(
1928
+ /* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
1929
+ /* @__PURE__ */ jsx13(
1698
1930
  Pill,
1699
1931
  {
1700
1932
  tone: s.status === "active" ? "ok" : s.status === "trialing" ? "info" : s.status === "past_due" ? "warn" : "neutral",
1701
1933
  children: s.status
1702
1934
  }
1703
1935
  ),
1704
- s.cancelAtPeriodEnd ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
1936
+ s.cancelAtPeriodEnd ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
1705
1937
  " ",
1706
- /* @__PURE__ */ jsx12(Pill, { tone: "warn", dot: false, children: "cancels" })
1938
+ /* @__PURE__ */ jsx13(Pill, { tone: "warn", dot: false, children: "cancels" })
1707
1939
  ] }) : null
1708
1940
  ] }),
1709
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "\u2014" }) })
1941
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : /* @__PURE__ */ jsx13("span", { style: styles.muted, children: "\u2014" }) })
1710
1942
  ] }, s.id)) })
1711
1943
  ] }),
1712
- list.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
1713
- list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx12(
1944
+ list.isLoading ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
1945
+ list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx13(
1714
1946
  "button",
1715
1947
  {
1716
1948
  type: "button",
@@ -1720,27 +1952,27 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1720
1952
  }
1721
1953
  ) : null
1722
1954
  ] }) : null,
1723
- subscriber && !isLoading ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
1724
- /* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Subscription" }),
1725
- subscription ? /* @__PURE__ */ jsx12("table", { style: styles.table, children: /* @__PURE__ */ jsxs12("tbody", { children: [
1726
- /* @__PURE__ */ jsxs12("tr", { children: [
1727
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: "plan" }),
1728
- /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1729
- /* @__PURE__ */ jsx12("strong", { children: subscription.plan }),
1955
+ subscriber && !isLoading ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
1956
+ /* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Subscription" }),
1957
+ subscription ? /* @__PURE__ */ jsx13("table", { style: styles.table, children: /* @__PURE__ */ jsxs13("tbody", { children: [
1958
+ /* @__PURE__ */ jsxs13("tr", { children: [
1959
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: "plan" }),
1960
+ /* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
1961
+ /* @__PURE__ */ jsx13("strong", { children: subscription.plan }),
1730
1962
  " \xB7 ",
1731
1963
  subscription.interval
1732
1964
  ] })
1733
1965
  ] }),
1734
- /* @__PURE__ */ jsxs12("tr", { children: [
1735
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: "status" }),
1736
- /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1966
+ /* @__PURE__ */ jsxs13("tr", { children: [
1967
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: "status" }),
1968
+ /* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
1737
1969
  subscription.status,
1738
1970
  subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
1739
1971
  ] })
1740
1972
  ] }),
1741
- /* @__PURE__ */ jsxs12("tr", { children: [
1742
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: "period" }),
1743
- /* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
1973
+ /* @__PURE__ */ jsxs13("tr", { children: [
1974
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: "period" }),
1975
+ /* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
1744
1976
  subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
1745
1977
  " ",
1746
1978
  "\u2192",
@@ -1748,14 +1980,14 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1748
1980
  subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
1749
1981
  ] })
1750
1982
  ] }),
1751
- /* @__PURE__ */ jsxs12("tr", { children: [
1752
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: "provider" }),
1753
- /* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
1983
+ /* @__PURE__ */ jsxs13("tr", { children: [
1984
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: "provider" }),
1985
+ /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
1754
1986
  ] })
1755
- ] }) }) : /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "No subscription." }),
1756
- /* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Wallet" }),
1757
- wallet ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
1758
- /* @__PURE__ */ jsxs12(
1987
+ ] }) }) : /* @__PURE__ */ jsx13("p", { style: styles.muted, children: "No subscription." }),
1988
+ /* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Wallet" }),
1989
+ wallet ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
1990
+ /* @__PURE__ */ jsxs13(
1759
1991
  "p",
1760
1992
  {
1761
1993
  style: {
@@ -1766,7 +1998,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1766
1998
  flexWrap: "wrap"
1767
1999
  },
1768
2000
  children: [
1769
- /* @__PURE__ */ jsx12(
2001
+ /* @__PURE__ */ jsx13(
1770
2002
  "strong",
1771
2003
  {
1772
2004
  style: {
@@ -1779,7 +2011,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1779
2011
  " ",
1780
2012
  wallet.currency,
1781
2013
  " ",
1782
- /* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
2014
+ /* @__PURE__ */ jsxs13("span", { style: styles.muted, children: [
1783
2015
  "(granted ",
1784
2016
  wallet.granted ?? "0",
1785
2017
  " \xB7 purchased ",
@@ -1790,7 +2022,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1790
2022
  ]
1791
2023
  }
1792
2024
  ),
1793
- /* @__PURE__ */ jsxs12(
2025
+ /* @__PURE__ */ jsxs13(
1794
2026
  "form",
1795
2027
  {
1796
2028
  style: styles.toolbar,
@@ -1809,7 +2041,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1809
2041
  });
1810
2042
  },
1811
2043
  children: [
1812
- /* @__PURE__ */ jsx12(
2044
+ /* @__PURE__ */ jsx13(
1813
2045
  "input",
1814
2046
  {
1815
2047
  value: amount,
@@ -1819,7 +2051,7 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1819
2051
  "aria-label": "Amount"
1820
2052
  }
1821
2053
  ),
1822
- /* @__PURE__ */ jsx12(
2054
+ /* @__PURE__ */ jsx13(
1823
2055
  "input",
1824
2056
  {
1825
2057
  value: note,
@@ -1829,38 +2061,38 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1829
2061
  "aria-label": "Reason"
1830
2062
  }
1831
2063
  ),
1832
- /* @__PURE__ */ jsxs12(
2064
+ /* @__PURE__ */ jsxs13(
1833
2065
  "button",
1834
2066
  {
1835
2067
  type: "submit",
1836
2068
  style: styles.buttonPrimary,
1837
2069
  disabled: !/^\d+$/.test(amount.trim()),
1838
2070
  children: [
1839
- /* @__PURE__ */ jsx12(Icon, { name: "plus", size: 14 }),
2071
+ /* @__PURE__ */ jsx13(Icon, { name: "plus", size: 14 }),
1840
2072
  "Grant"
1841
2073
  ]
1842
2074
  }
1843
2075
  ),
1844
- granted ? /* @__PURE__ */ jsx12("span", { style: styles.ok, children: granted }) : null
2076
+ granted ? /* @__PURE__ */ jsx13("span", { style: styles.ok, children: granted }) : null
1845
2077
  ]
1846
2078
  }
1847
2079
  ),
1848
- /* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Ledger" }),
1849
- ledger.length === 0 ? /* @__PURE__ */ jsx12(Empty, { icon: "subscriber", title: "Nothing moved yet" }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
1850
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
1851
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "When" }),
1852
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Kind" }),
1853
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Amount" }),
1854
- /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Note" })
2080
+ /* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Ledger" }),
2081
+ ledger.length === 0 ? /* @__PURE__ */ jsx13(Empty, { icon: "subscriber", title: "Nothing moved yet" }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
2082
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
2083
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "When" }),
2084
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Kind" }),
2085
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Amount" }),
2086
+ /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Note" })
1855
2087
  ] }) }),
1856
- /* @__PURE__ */ jsx12("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs12("tr", { children: [
1857
- /* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t2.createdAt).toLocaleString() }),
1858
- /* @__PURE__ */ jsx12("td", { style: styles.td, children: t2.type }),
1859
- /* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
1860
- /* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
2088
+ /* @__PURE__ */ jsx13("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs13("tr", { children: [
2089
+ /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t2.createdAt).toLocaleString() }),
2090
+ /* @__PURE__ */ jsx13("td", { style: styles.td, children: t2.type }),
2091
+ /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
2092
+ /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
1861
2093
  ] }, t2.id)) })
1862
2094
  ] }),
1863
- hasMoreLedger ? /* @__PURE__ */ jsx12(
2095
+ hasMoreLedger ? /* @__PURE__ */ jsx13(
1864
2096
  "button",
1865
2097
  {
1866
2098
  type: "button",
@@ -1869,25 +2101,25 @@ function SubscriberScreen({ client, pageSize = 50 }) {
1869
2101
  children: "Load more"
1870
2102
  }
1871
2103
  ) : null
1872
- ] }) : /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
2104
+ ] }) : /* @__PURE__ */ jsx13("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
1873
2105
  ] }) : null
1874
2106
  ] });
1875
2107
  }
1876
2108
 
1877
2109
  // src/screens/AuditScreen.tsx
1878
2110
  import { useAdminAudit } from "@fonderie/react-admin";
1879
- import { useState as useState5 } from "react";
1880
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2111
+ import { useState as useState6 } from "react";
2112
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1881
2113
  function AuditScreen({ client, pageSize = 50 }) {
1882
- const [draft, setDraft] = useState5({ workspaceId: "", type: "", actorId: "" });
1883
- const [filter, setFilter] = useState5(
2114
+ const [draft, setDraft] = useState6({ workspaceId: "", type: "", actorId: "" });
2115
+ const [filter, setFilter] = useState6(
1884
2116
  {}
1885
2117
  );
1886
2118
  const { events, hasMore, isLoading, error, refresh, loadMore } = useAdminAudit(client, {
1887
2119
  ...filter,
1888
2120
  limit: pageSize
1889
2121
  });
1890
- const field = (key, placeholder) => /* @__PURE__ */ jsx13(
2122
+ const field = (key, placeholder) => /* @__PURE__ */ jsx14(
1891
2123
  "input",
1892
2124
  {
1893
2125
  value: draft[key],
@@ -1897,15 +2129,15 @@ function AuditScreen({ client, pageSize = 50 }) {
1897
2129
  "aria-label": placeholder
1898
2130
  }
1899
2131
  );
1900
- return /* @__PURE__ */ jsxs13("div", { style: styles.container, children: [
1901
- /* @__PURE__ */ jsx13(
2132
+ return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
2133
+ /* @__PURE__ */ jsx14(
1902
2134
  PageHeader,
1903
2135
  {
1904
2136
  title: "Audit",
1905
2137
  lead: "What happened, across every workspace unless you name one. The chain's integrity verdict is on the Doctor page."
1906
2138
  }
1907
2139
  ),
1908
- /* @__PURE__ */ jsxs13(
2140
+ /* @__PURE__ */ jsxs14(
1909
2141
  "form",
1910
2142
  {
1911
2143
  style: styles.toolbar,
@@ -1921,11 +2153,11 @@ function AuditScreen({ client, pageSize = 50 }) {
1921
2153
  field("workspaceId", "workspace id (all if empty)"),
1922
2154
  field("type", "event type"),
1923
2155
  field("actorId", "actor id"),
1924
- /* @__PURE__ */ jsxs13("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
1925
- /* @__PURE__ */ jsx13(Icon, { name: "search", size: 14 }),
2156
+ /* @__PURE__ */ jsxs14("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
2157
+ /* @__PURE__ */ jsx14(Icon, { name: "search", size: 14 }),
1926
2158
  "Filter"
1927
2159
  ] }),
1928
- /* @__PURE__ */ jsx13(
2160
+ /* @__PURE__ */ jsx14(
1929
2161
  "button",
1930
2162
  {
1931
2163
  type: "button",
@@ -1938,25 +2170,25 @@ function AuditScreen({ client, pageSize = 50 }) {
1938
2170
  ]
1939
2171
  }
1940
2172
  ),
1941
- error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1942
- events.length === 0 && !isLoading ? /* @__PURE__ */ jsx13(Empty, { icon: "audit", title: "Nothing recorded for this filter" }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
1943
- /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
1944
- /* @__PURE__ */ jsx13("th", { style: styles.th, children: "When" }),
1945
- /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Type" }),
1946
- /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Workspace" }),
1947
- /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Actor" }),
1948
- /* @__PURE__ */ jsx13("th", { style: styles.th, children: "Request" })
2173
+ error ? /* @__PURE__ */ jsx14("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
2174
+ events.length === 0 && !isLoading ? /* @__PURE__ */ jsx14(Empty, { icon: "audit", title: "Nothing recorded for this filter" }) : /* @__PURE__ */ jsxs14("table", { style: styles.table, children: [
2175
+ /* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs14("tr", { children: [
2176
+ /* @__PURE__ */ jsx14("th", { style: styles.th, children: "When" }),
2177
+ /* @__PURE__ */ jsx14("th", { style: styles.th, children: "Type" }),
2178
+ /* @__PURE__ */ jsx14("th", { style: styles.th, children: "Workspace" }),
2179
+ /* @__PURE__ */ jsx14("th", { style: styles.th, children: "Actor" }),
2180
+ /* @__PURE__ */ jsx14("th", { style: styles.th, children: "Request" })
1949
2181
  ] }) }),
1950
- /* @__PURE__ */ jsx13("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs13("tr", { children: [
1951
- /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
1952
- /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
1953
- /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
1954
- /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
1955
- /* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
2182
+ /* @__PURE__ */ jsx14("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs14("tr", { children: [
2183
+ /* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
2184
+ /* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
2185
+ /* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
2186
+ /* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
2187
+ /* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
1956
2188
  ] }, e.id)) })
1957
2189
  ] }),
1958
- isLoading ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
1959
- hasMore && !isLoading ? /* @__PURE__ */ jsx13(
2190
+ isLoading ? /* @__PURE__ */ jsx14("p", { style: styles.status, children: "Loading\u2026" }) : null,
2191
+ hasMore && !isLoading ? /* @__PURE__ */ jsx14(
1960
2192
  "button",
1961
2193
  {
1962
2194
  type: "button",
@@ -1969,7 +2201,7 @@ function AuditScreen({ client, pageSize = 50 }) {
1969
2201
  }
1970
2202
 
1971
2203
  // src/screens/AdminShell.tsx
1972
- import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2204
+ import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1973
2205
  var ICON = {
1974
2206
  attention: "attention",
1975
2207
  modules: "modules",
@@ -1984,12 +2216,13 @@ var ICON = {
1984
2216
  audit: "audit",
1985
2217
  log: "log",
1986
2218
  tokens: "tokens",
2219
+ operators: "users",
1987
2220
  migrations: "migrations"
1988
2221
  };
1989
2222
  var NARROW = "(max-width: 820px)";
1990
2223
  function useNarrow() {
1991
2224
  const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
1992
- const [narrow, setNarrow] = useState6(() => query?.matches ?? false);
2225
+ const [narrow, setNarrow] = useState7(() => query?.matches ?? false);
1993
2226
  useEffect(() => {
1994
2227
  if (!query) return;
1995
2228
  const on = (e) => setNarrow(e.matches);
@@ -2024,7 +2257,8 @@ var NAV = [
2024
2257
  items: [
2025
2258
  { page: "audit", label: "Audit", needs: "audit" },
2026
2259
  { page: "log", label: "Admin log" },
2027
- { page: "tokens", label: "Access" },
2260
+ { page: "operators", label: "Operators", needs: "operators" },
2261
+ { page: "tokens", label: "Tokens" },
2028
2262
  { page: "migrations", label: "Migrations" }
2029
2263
  ]
2030
2264
  }
@@ -2041,70 +2275,76 @@ function AdminShell({
2041
2275
  onNavigate,
2042
2276
  appName = "Admin",
2043
2277
  envLabel,
2044
- footer
2278
+ footer,
2279
+ operators = false,
2280
+ currentOperator
2045
2281
  }) {
2046
2282
  const narrow = useNarrow();
2047
- const [drawer, setDrawer] = useState6(false);
2048
- const [own, setOwn] = useState6("attention");
2283
+ const [drawer, setDrawer] = useState7(false);
2284
+ const [own, setOwn] = useState7("attention");
2049
2285
  const current = page ?? own;
2050
2286
  const go = (p) => {
2051
2287
  if (onNavigate) onNavigate(p);
2052
2288
  if (page === void 0) setOwn(p);
2053
2289
  setDrawer(false);
2054
2290
  };
2055
- const [editing, setEditing] = useState6(null);
2291
+ const [editing, setEditing] = useState7(null);
2056
2292
  const has = {
2057
2293
  config: Boolean(configClient),
2058
2294
  courier: Boolean(courierClient),
2059
2295
  auth: Boolean(authClient),
2060
2296
  billing: Boolean(billingClient),
2061
- audit: Boolean(auditClient)
2297
+ audit: Boolean(auditClient),
2298
+ operators
2062
2299
  };
2063
2300
  let body;
2064
2301
  switch (current) {
2065
2302
  case "attention":
2066
- body = /* @__PURE__ */ jsx14(AttentionScreen, { client });
2303
+ body = /* @__PURE__ */ jsx15(AttentionScreen, { client });
2067
2304
  break;
2068
2305
  case "modules":
2069
- body = /* @__PURE__ */ jsx14(ModulesScreen, { client });
2306
+ body = /* @__PURE__ */ jsx15(ModulesScreen, { client });
2070
2307
  break;
2071
2308
  case "doctor":
2072
- body = /* @__PURE__ */ jsx14(DoctorScreen, { client });
2309
+ body = /* @__PURE__ */ jsx15(DoctorScreen, { client });
2073
2310
  break;
2074
2311
  case "environment":
2075
- body = /* @__PURE__ */ jsx14(EnvironmentScreen, { client });
2312
+ body = /* @__PURE__ */ jsx15(EnvironmentScreen, { client });
2076
2313
  break;
2077
2314
  case "routes":
2078
- body = /* @__PURE__ */ jsx14(RoutesScreen, { client });
2315
+ body = /* @__PURE__ */ jsx15(RoutesScreen, { client });
2079
2316
  break;
2080
2317
  case "tokens":
2081
- body = /* @__PURE__ */ jsx14(TokensScreen, { client });
2318
+ body = /* @__PURE__ */ jsx15(TokensScreen, { client });
2319
+ break;
2320
+ case "operators":
2321
+ body = /* @__PURE__ */ jsx15(OperatorsScreen, { client, me: currentOperator });
2082
2322
  break;
2083
2323
  case "migrations":
2084
- body = /* @__PURE__ */ jsx14(MigrationsScreen, { client });
2324
+ body = /* @__PURE__ */ jsx15(MigrationsScreen, { client });
2085
2325
  break;
2086
2326
  case "log":
2087
- body = /* @__PURE__ */ jsx14(AdminLogScreen, { client });
2327
+ body = /* @__PURE__ */ jsx15(AdminLogScreen, { client });
2088
2328
  break;
2089
2329
  case "catalog":
2090
- body = billingClient ? /* @__PURE__ */ jsx14(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to see the catalog here." });
2330
+ body = billingClient ? /* @__PURE__ */ jsx15(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to see the catalog here." });
2091
2331
  break;
2092
2332
  case "subscriber":
2093
- body = billingClient ? /* @__PURE__ */ jsx14(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to look up subscribers here." });
2333
+ body = billingClient ? /* @__PURE__ */ jsx15(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to look up subscribers here." });
2094
2334
  break;
2095
2335
  case "audit":
2096
- body = auditClient ? /* @__PURE__ */ jsx14(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuditAdminClient to see the audit trail here." });
2336
+ body = auditClient ? /* @__PURE__ */ jsx15(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuditAdminClient to see the audit trail here." });
2097
2337
  break;
2098
2338
  case "users":
2099
- body = authClient ? /* @__PURE__ */ jsx14(UsersScreen, { client: authClient }) : /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuthAdminClient to look up users here." });
2339
+ body = authClient ? /* @__PURE__ */ jsx15(UsersScreen, { client: authClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuthAdminClient to look up users here." });
2100
2340
  break;
2101
2341
  case "settings":
2102
- body = !configClient ? /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs14(Fragment8, { children: [
2103
- /* @__PURE__ */ jsx14("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs14("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
2104
- /* @__PURE__ */ jsx14(Icon, { name: "back", size: 14 }),
2342
+ body = !configClient ? /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
2343
+ /* @__PURE__ */ jsx15("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs15("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
2344
+ /* @__PURE__ */ jsx15(Icon, { name: "back", size: 14 }),
2105
2345
  "Config & secrets"
2106
2346
  ] }) }),
2107
- /* @__PURE__ */ jsx14(
2347
+ /* @__PURE__ */ jsx15(
2108
2348
  ConfigEditorScreen,
2109
2349
  {
2110
2350
  client: configClient,
@@ -2114,7 +2354,7 @@ function AdminShell({
2114
2354
  onSaved: () => setEditing(null)
2115
2355
  }
2116
2356
  )
2117
- ] }) : /* @__PURE__ */ jsx14(
2357
+ ] }) : /* @__PURE__ */ jsx15(
2118
2358
  ConfigListScreen,
2119
2359
  {
2120
2360
  client: configClient,
@@ -2127,12 +2367,12 @@ function AdminShell({
2127
2367
  );
2128
2368
  break;
2129
2369
  case "templates":
2130
- body = !courierClient ? /* @__PURE__ */ jsx14("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs14(Fragment8, { children: [
2131
- /* @__PURE__ */ jsx14("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs14("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
2132
- /* @__PURE__ */ jsx14(Icon, { name: "back", size: 14 }),
2370
+ body = !courierClient ? /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
2371
+ /* @__PURE__ */ jsx15("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs15("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
2372
+ /* @__PURE__ */ jsx15(Icon, { name: "back", size: 14 }),
2133
2373
  "Templates"
2134
2374
  ] }) }),
2135
- /* @__PURE__ */ jsx14(
2375
+ /* @__PURE__ */ jsx15(
2136
2376
  TemplateEditorScreen,
2137
2377
  {
2138
2378
  client: courierClient,
@@ -2140,7 +2380,7 @@ function AdminShell({
2140
2380
  onSaved: () => setEditing(null)
2141
2381
  }
2142
2382
  )
2143
- ] }) : /* @__PURE__ */ jsx14(
2383
+ ] }) : /* @__PURE__ */ jsx15(
2144
2384
  TemplateListScreen,
2145
2385
  {
2146
2386
  client: courierClient,
@@ -2150,11 +2390,11 @@ function AdminShell({
2150
2390
  break;
2151
2391
  }
2152
2392
  const production = envLabel ? /^prod/i.test(envLabel) : false;
2153
- const brand = /* @__PURE__ */ jsxs14("div", { style: styles.navBrand, children: [
2154
- /* @__PURE__ */ jsx14("div", { style: styles.navMark, children: /* @__PURE__ */ jsx14(Icon, { name: "mark", size: 15 }) }),
2155
- /* @__PURE__ */ jsxs14("div", { style: { minWidth: 0, flex: 1 }, children: [
2156
- /* @__PURE__ */ jsx14("div", { style: styles.navBrandTitle, children: appName }),
2157
- envLabel ? /* @__PURE__ */ jsx14(
2393
+ const brand = /* @__PURE__ */ jsxs15("div", { style: styles.navBrand, children: [
2394
+ /* @__PURE__ */ jsx15("div", { style: styles.navMark, children: /* @__PURE__ */ jsx15(Icon, { name: "mark", size: 15 }) }),
2395
+ /* @__PURE__ */ jsxs15("div", { style: { minWidth: 0, flex: 1 }, children: [
2396
+ /* @__PURE__ */ jsx15("div", { style: styles.navBrandTitle, children: appName }),
2397
+ envLabel ? /* @__PURE__ */ jsx15(
2158
2398
  "span",
2159
2399
  {
2160
2400
  style: {
@@ -2171,18 +2411,18 @@ function AdminShell({
2171
2411
  }
2172
2412
  ) : null
2173
2413
  ] }),
2174
- narrow ? /* @__PURE__ */ jsx14(
2414
+ narrow ? /* @__PURE__ */ jsx15(
2175
2415
  "button",
2176
2416
  {
2177
2417
  type: "button",
2178
2418
  style: styles.buttonGhost,
2179
2419
  "aria-label": "Close menu",
2180
2420
  onClick: () => setDrawer(false),
2181
- children: /* @__PURE__ */ jsx14(Icon, { name: "close" })
2421
+ children: /* @__PURE__ */ jsx15(Icon, { name: "close" })
2182
2422
  }
2183
2423
  ) : null
2184
2424
  ] });
2185
- const nav = /* @__PURE__ */ jsxs14(
2425
+ const nav = /* @__PURE__ */ jsxs15(
2186
2426
  "nav",
2187
2427
  {
2188
2428
  style: {
@@ -2192,14 +2432,14 @@ function AdminShell({
2192
2432
  "aria-label": "Admin",
2193
2433
  children: [
2194
2434
  brand,
2195
- /* @__PURE__ */ jsx14("div", { style: styles.navScroll, children: NAV.map((g) => {
2435
+ /* @__PURE__ */ jsx15("div", { style: styles.navScroll, children: NAV.map((g) => {
2196
2436
  const items = g.items.filter((i) => !i.needs || has[i.needs]);
2197
2437
  if (items.length === 0) return null;
2198
- return /* @__PURE__ */ jsxs14("div", { children: [
2199
- /* @__PURE__ */ jsx14("div", { style: styles.navGroup, children: g.group }),
2438
+ return /* @__PURE__ */ jsxs15("div", { children: [
2439
+ /* @__PURE__ */ jsx15("div", { style: styles.navGroup, children: g.group }),
2200
2440
  items.map((i) => {
2201
2441
  const active = current === i.page;
2202
- return /* @__PURE__ */ jsxs14(
2442
+ return /* @__PURE__ */ jsxs15(
2203
2443
  "button",
2204
2444
  {
2205
2445
  type: "button",
@@ -2211,7 +2451,7 @@ function AdminShell({
2211
2451
  go(i.page);
2212
2452
  },
2213
2453
  children: [
2214
- /* @__PURE__ */ jsx14(Icon, { name: ICON[i.page], style: active ? styles.navIconActive : void 0 }),
2454
+ /* @__PURE__ */ jsx15(Icon, { name: ICON[i.page], style: active ? styles.navIconActive : void 0 }),
2215
2455
  i.label
2216
2456
  ]
2217
2457
  },
@@ -2220,15 +2460,15 @@ function AdminShell({
2220
2460
  })
2221
2461
  ] }, g.group);
2222
2462
  }) }),
2223
- footer ? /* @__PURE__ */ jsx14("div", { style: styles.navFooter, children: footer }) : null
2463
+ footer ? /* @__PURE__ */ jsx15("div", { style: styles.navFooter, children: footer }) : null
2224
2464
  ]
2225
2465
  }
2226
2466
  );
2227
2467
  const title = NAV.flatMap((g) => g.items).find((i) => i.page === current)?.label ?? "";
2228
- return /* @__PURE__ */ jsxs14("div", { style: { ...styles.shell, ...narrow ? { flexDirection: "column" } : {} }, children: [
2229
- narrow ? /* @__PURE__ */ jsxs14(Fragment8, { children: [
2230
- /* @__PURE__ */ jsxs14("div", { style: styles.topbar, children: [
2231
- /* @__PURE__ */ jsx14(
2468
+ return /* @__PURE__ */ jsxs15("div", { style: { ...styles.shell, ...narrow ? { flexDirection: "column" } : {} }, children: [
2469
+ narrow ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
2470
+ /* @__PURE__ */ jsxs15("div", { style: styles.topbar, children: [
2471
+ /* @__PURE__ */ jsx15(
2232
2472
  "button",
2233
2473
  {
2234
2474
  type: "button",
@@ -2236,18 +2476,18 @@ function AdminShell({
2236
2476
  "aria-label": "Open menu",
2237
2477
  "aria-expanded": drawer,
2238
2478
  onClick: () => setDrawer(true),
2239
- children: /* @__PURE__ */ jsx14(Icon, { name: "menu" })
2479
+ children: /* @__PURE__ */ jsx15(Icon, { name: "menu" })
2240
2480
  }
2241
2481
  ),
2242
- /* @__PURE__ */ jsx14("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
2243
- envLabel ? /* @__PURE__ */ jsx14("span", { style: { ...styles.badge, marginLeft: "auto" }, children: envLabel }) : null
2482
+ /* @__PURE__ */ jsx15("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
2483
+ envLabel ? /* @__PURE__ */ jsx15("span", { style: { ...styles.badge, marginLeft: "auto" }, children: envLabel }) : null
2244
2484
  ] }),
2245
- drawer ? /* @__PURE__ */ jsxs14("div", { style: styles.drawer, children: [
2485
+ drawer ? /* @__PURE__ */ jsxs15("div", { style: styles.drawer, children: [
2246
2486
  nav,
2247
- /* @__PURE__ */ jsx14("div", { style: styles.scrim, onClick: () => setDrawer(false), "aria-hidden": "true" })
2487
+ /* @__PURE__ */ jsx15("div", { style: styles.scrim, onClick: () => setDrawer(false), "aria-hidden": "true" })
2248
2488
  ] }) : null
2249
2489
  ] }) : nav,
2250
- /* @__PURE__ */ jsx14("main", { className: "fonderie-admin-main", style: styles.main, children: body })
2490
+ /* @__PURE__ */ jsx15("main", { className: "fonderie-admin-main", style: styles.main, children: body })
2251
2491
  ] });
2252
2492
  }
2253
2493
  export {
@@ -2260,6 +2500,7 @@ export {
2260
2500
  EnvironmentScreen,
2261
2501
  MigrationsScreen,
2262
2502
  ModulesScreen,
2503
+ OperatorsScreen,
2263
2504
  RoutesScreen,
2264
2505
  SubscriberScreen,
2265
2506
  TokensScreen,