@fonderie/react-admin-screens 1.1.0 → 1.3.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.cjs +904 -526
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -6
- package/dist/index.d.ts +30 -6
- package/dist/index.js +892 -516
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
4
|
+
import { useEffect as useEffect2, useState as useState9 } 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/
|
|
1077
|
-
import {
|
|
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] =
|
|
1084
|
-
const [scopes, setScopes] =
|
|
1085
|
-
const [days, setDays] =
|
|
1086
|
-
const [minted, setMinted] =
|
|
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__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1320
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
|
|
1321
|
+
/* @__PURE__ */ jsx10(
|
|
1090
1322
|
PageHeader,
|
|
1091
1323
|
{
|
|
1092
|
-
title: "
|
|
1093
|
-
lead: "
|
|
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__ */
|
|
1097
|
-
/* @__PURE__ */
|
|
1098
|
-
report ? /* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1102
|
-
report.admin.problems.map((p) => /* @__PURE__ */
|
|
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__ */
|
|
1113
|
-
report?.issued === null ? /* @__PURE__ */
|
|
1114
|
-
minted ? /* @__PURE__ */
|
|
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__ */
|
|
1355
|
+
/* @__PURE__ */ jsx10("strong", { children: "Copy this now \u2014 it is never shown again." }),
|
|
1124
1356
|
" (",
|
|
1125
1357
|
minted.name,
|
|
1126
1358
|
")",
|
|
1127
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1217
|
-
/* @__PURE__ */
|
|
1218
|
-
/* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1222
|
-
/* @__PURE__ */
|
|
1223
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1226
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1460
|
+
t2.revokedAt ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
1229
1461
|
" ",
|
|
1230
|
-
/* @__PURE__ */
|
|
1462
|
+
/* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "revoked" })
|
|
1231
1463
|
] }) : null
|
|
1232
1464
|
] }),
|
|
1233
|
-
/* @__PURE__ */
|
|
1234
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1240
|
-
/* @__PURE__ */
|
|
1241
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1260
|
-
report && report.legacy.length === 0 ? /* @__PURE__ */
|
|
1261
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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,218 @@ import {
|
|
|
1273
1505
|
useAdminUserSessions,
|
|
1274
1506
|
useAdminUsers
|
|
1275
1507
|
} from "@fonderie/react-admin";
|
|
1276
|
-
import { useState as
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1508
|
+
import { useState as useState6 } from "react";
|
|
1509
|
+
|
|
1510
|
+
// src/billing.ts
|
|
1511
|
+
import { useEffect, useState as useState4 } from "react";
|
|
1512
|
+
function periodEnd(s) {
|
|
1513
|
+
if (!s.currentPeriodEnd) return null;
|
|
1514
|
+
const date = new Date(s.currentPeriodEnd).toLocaleDateString();
|
|
1515
|
+
if (s.status === "canceled")
|
|
1516
|
+
return { label: new Date(s.currentPeriodEnd) < /* @__PURE__ */ new Date() ? "Ended" : "Ends", date };
|
|
1517
|
+
if (s.cancelAtPeriodEnd) return { label: "Ends", date };
|
|
1518
|
+
return { label: "Renews", date };
|
|
1519
|
+
}
|
|
1520
|
+
var statusTone = (status) => status === "active" ? "ok" : status === "trialing" ? "info" : status === "past_due" || status === "unpaid" ? "warn" : "neutral";
|
|
1521
|
+
function useSubscriptionIndex(client) {
|
|
1522
|
+
const [index, setIndex] = useState4(null);
|
|
1523
|
+
useEffect(() => {
|
|
1524
|
+
if (!client) return;
|
|
1525
|
+
let live = true;
|
|
1526
|
+
(async () => {
|
|
1527
|
+
const map = /* @__PURE__ */ new Map();
|
|
1528
|
+
let cursor;
|
|
1529
|
+
for (let page = 0; page < 20; page++) {
|
|
1530
|
+
const { result } = await client.listSubscriptions({
|
|
1531
|
+
limit: 100,
|
|
1532
|
+
...cursor ? { cursor } : {}
|
|
1533
|
+
});
|
|
1534
|
+
for (const s of result.subscriptions) {
|
|
1535
|
+
const key = `${s.subscriberType}/${s.subscriberId}`;
|
|
1536
|
+
if (!map.has(key)) map.set(key, s);
|
|
1537
|
+
}
|
|
1538
|
+
if (!result.nextCursor) break;
|
|
1539
|
+
cursor = result.nextCursor;
|
|
1540
|
+
}
|
|
1541
|
+
if (live) setIndex(map);
|
|
1542
|
+
})().catch(() => {
|
|
1543
|
+
if (live) setIndex(/* @__PURE__ */ new Map());
|
|
1544
|
+
});
|
|
1545
|
+
return () => {
|
|
1546
|
+
live = false;
|
|
1547
|
+
};
|
|
1548
|
+
}, [client]);
|
|
1549
|
+
return index;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// src/screens/SubscriberBilling.tsx
|
|
1553
|
+
import { useAdminSubscriber } from "@fonderie/react-admin";
|
|
1554
|
+
import { useState as useState5 } from "react";
|
|
1555
|
+
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1556
|
+
function SubscriberBilling({ client, subscriber }) {
|
|
1557
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
|
|
1558
|
+
const [amount, setAmount] = useState5("");
|
|
1559
|
+
const [note, setNote] = useState5("");
|
|
1560
|
+
const [granted, setGranted] = useState5(null);
|
|
1561
|
+
if (isLoading && !wallet && !subscription) return /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading billing\u2026" });
|
|
1562
|
+
const end = subscription ? periodEnd(subscription) : null;
|
|
1563
|
+
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1564
|
+
error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1565
|
+
/* @__PURE__ */ jsxs11("div", { style: { ...styles.grid, marginBottom: 16 }, children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.card, children: [
|
|
1567
|
+
/* @__PURE__ */ jsx11("div", { style: styles.statLabel, children: "Plan" }),
|
|
1568
|
+
/* @__PURE__ */ jsxs11(
|
|
1569
|
+
"div",
|
|
1570
|
+
{
|
|
1571
|
+
style: {
|
|
1572
|
+
display: "flex",
|
|
1573
|
+
alignItems: "center",
|
|
1574
|
+
gap: 8,
|
|
1575
|
+
marginTop: 6,
|
|
1576
|
+
flexWrap: "wrap"
|
|
1577
|
+
},
|
|
1578
|
+
children: [
|
|
1579
|
+
/* @__PURE__ */ jsx11("strong", { style: { fontSize: 18 }, children: subscription ? subscription.plan : "Free" }),
|
|
1580
|
+
subscription ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1581
|
+
/* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
|
|
1582
|
+
"\xB7 ",
|
|
1583
|
+
subscription.interval
|
|
1584
|
+
] }),
|
|
1585
|
+
/* @__PURE__ */ jsx11(Pill, { tone: statusTone(subscription.status), children: subscription.status })
|
|
1586
|
+
] }) : /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "no subscription" })
|
|
1587
|
+
]
|
|
1588
|
+
}
|
|
1589
|
+
),
|
|
1590
|
+
end ? /* @__PURE__ */ jsxs11("div", { style: styles.statHint, children: [
|
|
1591
|
+
end.label,
|
|
1592
|
+
" ",
|
|
1593
|
+
end.date
|
|
1594
|
+
] }) : null,
|
|
1595
|
+
subscription?.providerSubscriptionId ? /* @__PURE__ */ jsx11("div", { style: { ...styles.statHint, fontFamily: "var(--fonderie-mono,monospace)" }, children: subscription.providerSubscriptionId }) : null
|
|
1596
|
+
] }),
|
|
1597
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.card, children: [
|
|
1598
|
+
/* @__PURE__ */ jsx11("div", { style: styles.statLabel, children: "Credits" }),
|
|
1599
|
+
wallet ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1600
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.statValue, children: [
|
|
1601
|
+
wallet.balance,
|
|
1602
|
+
" ",
|
|
1603
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 14, fontWeight: 500 }, children: wallet.currency })
|
|
1604
|
+
] }),
|
|
1605
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.statHint, children: [
|
|
1606
|
+
wallet.purchased ?? "0",
|
|
1607
|
+
" permanent \xB7 ",
|
|
1608
|
+
wallet.granted ?? "0",
|
|
1609
|
+
" plan allowance",
|
|
1610
|
+
wallet.grantedExpiresAt ? ` (expires ${new Date(wallet.grantedExpiresAt).toLocaleDateString()})` : ""
|
|
1611
|
+
] })
|
|
1612
|
+
] }) : /* @__PURE__ */ jsx11("div", { style: styles.statHint, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
1613
|
+
] })
|
|
1614
|
+
] }),
|
|
1615
|
+
wallet ? /* @__PURE__ */ jsxs11(
|
|
1616
|
+
"form",
|
|
1617
|
+
{
|
|
1618
|
+
style: {
|
|
1619
|
+
...styles.card,
|
|
1620
|
+
display: "flex",
|
|
1621
|
+
gap: 8,
|
|
1622
|
+
alignItems: "center",
|
|
1623
|
+
flexWrap: "wrap",
|
|
1624
|
+
marginBottom: 16
|
|
1625
|
+
},
|
|
1626
|
+
onSubmit: (e) => {
|
|
1627
|
+
e.preventDefault();
|
|
1628
|
+
const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
|
|
1629
|
+
void grant({
|
|
1630
|
+
amount: amount.trim(),
|
|
1631
|
+
...note ? { description: note } : {},
|
|
1632
|
+
idempotencyKey: key
|
|
1633
|
+
}).then(() => {
|
|
1634
|
+
setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
|
|
1635
|
+
setAmount("");
|
|
1636
|
+
setNote("");
|
|
1637
|
+
}).catch(() => {
|
|
1638
|
+
});
|
|
1639
|
+
},
|
|
1640
|
+
children: [
|
|
1641
|
+
/* @__PURE__ */ jsx11("strong", { style: { fontSize: 13.5, marginRight: 4 }, children: "Grant credits" }),
|
|
1642
|
+
/* @__PURE__ */ jsx11(
|
|
1643
|
+
"input",
|
|
1644
|
+
{
|
|
1645
|
+
value: amount,
|
|
1646
|
+
onChange: (e) => setAmount(e.target.value),
|
|
1647
|
+
placeholder: `amount (${wallet.currency})`,
|
|
1648
|
+
inputMode: "numeric",
|
|
1649
|
+
style: { ...styles.input, width: 150 },
|
|
1650
|
+
"aria-label": "Amount"
|
|
1651
|
+
}
|
|
1652
|
+
),
|
|
1653
|
+
/* @__PURE__ */ jsx11(
|
|
1654
|
+
"input",
|
|
1655
|
+
{
|
|
1656
|
+
value: note,
|
|
1657
|
+
onChange: (e) => setNote(e.target.value),
|
|
1658
|
+
placeholder: "reason (optional)",
|
|
1659
|
+
style: { ...styles.input, flex: 1, minWidth: 180 },
|
|
1660
|
+
"aria-label": "Reason"
|
|
1661
|
+
}
|
|
1662
|
+
),
|
|
1663
|
+
/* @__PURE__ */ jsxs11(
|
|
1664
|
+
"button",
|
|
1665
|
+
{
|
|
1666
|
+
type: "submit",
|
|
1667
|
+
style: styles.buttonPrimary,
|
|
1668
|
+
disabled: !/^\d+$/.test(amount.trim()),
|
|
1669
|
+
children: [
|
|
1670
|
+
/* @__PURE__ */ jsx11(Icon, { name: "plus", size: 14 }),
|
|
1671
|
+
"Grant"
|
|
1672
|
+
]
|
|
1673
|
+
}
|
|
1674
|
+
),
|
|
1675
|
+
granted ? /* @__PURE__ */ jsx11("span", { style: styles.ok, children: granted }) : null
|
|
1676
|
+
]
|
|
1677
|
+
}
|
|
1678
|
+
) : null,
|
|
1679
|
+
wallet ? ledger.length === 0 ? /* @__PURE__ */ jsx11(Empty, { icon: "subscriber", title: "No credit movements yet" }) : /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1680
|
+
/* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
|
|
1681
|
+
/* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1682
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "When" }),
|
|
1683
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Kind" }),
|
|
1684
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Amount" }),
|
|
1685
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Note" })
|
|
1686
|
+
] }) }),
|
|
1687
|
+
/* @__PURE__ */ jsx11("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1688
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted, whiteSpace: "nowrap" }, children: new Date(t2.createdAt).toLocaleString() }),
|
|
1689
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: t2.type }),
|
|
1690
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
|
|
1691
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
|
|
1692
|
+
] }, t2.id)) })
|
|
1693
|
+
] }),
|
|
1694
|
+
hasMoreLedger ? /* @__PURE__ */ jsx11(
|
|
1695
|
+
"button",
|
|
1696
|
+
{
|
|
1697
|
+
type: "button",
|
|
1698
|
+
style: { ...styles.button, marginTop: 8 },
|
|
1699
|
+
onClick: () => void loadMoreLedger(),
|
|
1700
|
+
children: "Load more"
|
|
1701
|
+
}
|
|
1702
|
+
) : null
|
|
1703
|
+
] }) : null
|
|
1704
|
+
] });
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// src/screens/UsersScreen.tsx
|
|
1708
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1709
|
+
function UsersScreen({
|
|
1710
|
+
client,
|
|
1711
|
+
pageSize = 50,
|
|
1712
|
+
billingClient,
|
|
1713
|
+
openUserId
|
|
1714
|
+
}) {
|
|
1715
|
+
const [input, setInput] = useState6("");
|
|
1716
|
+
const [selected, setSelected] = useState6(
|
|
1717
|
+
openUserId ? { id: openUserId } : null
|
|
1718
|
+
);
|
|
1719
|
+
const plans = useSubscriptionIndex(billingClient);
|
|
1281
1720
|
const list = useAdminUsers(client, { limit: pageSize });
|
|
1282
1721
|
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
|
|
1283
1722
|
client,
|
|
@@ -1286,20 +1725,20 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1286
1725
|
const userId = user?.id ?? null;
|
|
1287
1726
|
const sessions = useAdminUserSessions(client, userId);
|
|
1288
1727
|
const history = useAdminLoginHistory(client, userId, { limit: 20 });
|
|
1289
|
-
const yesNo = (v) => v ? /* @__PURE__ */
|
|
1728
|
+
const yesNo = (v) => v ? /* @__PURE__ */ jsx12(Pill, { tone: "ok", children: "yes" }) : /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "no" });
|
|
1290
1729
|
const clear = () => {
|
|
1291
1730
|
setSelected(null);
|
|
1292
1731
|
setInput("");
|
|
1293
1732
|
};
|
|
1294
|
-
return /* @__PURE__ */
|
|
1295
|
-
/* @__PURE__ */
|
|
1733
|
+
return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
|
|
1734
|
+
/* @__PURE__ */ jsx12(
|
|
1296
1735
|
PageHeader,
|
|
1297
1736
|
{
|
|
1298
1737
|
title: "Users",
|
|
1299
1738
|
lead: "Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
1300
1739
|
}
|
|
1301
1740
|
),
|
|
1302
|
-
/* @__PURE__ */
|
|
1741
|
+
/* @__PURE__ */ jsxs12(
|
|
1303
1742
|
"form",
|
|
1304
1743
|
{
|
|
1305
1744
|
style: styles.toolbar,
|
|
@@ -1309,7 +1748,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1309
1748
|
setSelected(email ? { email } : null);
|
|
1310
1749
|
},
|
|
1311
1750
|
children: [
|
|
1312
|
-
/* @__PURE__ */
|
|
1751
|
+
/* @__PURE__ */ jsx12(
|
|
1313
1752
|
"input",
|
|
1314
1753
|
{
|
|
1315
1754
|
type: "email",
|
|
@@ -1320,14 +1759,14 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1320
1759
|
"aria-label": "Email"
|
|
1321
1760
|
}
|
|
1322
1761
|
),
|
|
1323
|
-
/* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1762
|
+
/* @__PURE__ */ jsxs12("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1763
|
+
/* @__PURE__ */ jsx12(Icon, { name: "search", size: 14 }),
|
|
1325
1764
|
"Look up"
|
|
1326
1765
|
] }),
|
|
1327
|
-
selected ? /* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1766
|
+
selected ? /* @__PURE__ */ jsxs12("button", { type: "button", style: styles.buttonGhost, onClick: clear, children: [
|
|
1767
|
+
/* @__PURE__ */ jsx12(Icon, { name: "back", size: 14 }),
|
|
1329
1768
|
"All users"
|
|
1330
|
-
] }) : /* @__PURE__ */
|
|
1769
|
+
] }) : /* @__PURE__ */ jsx12(
|
|
1331
1770
|
"button",
|
|
1332
1771
|
{
|
|
1333
1772
|
type: "button",
|
|
@@ -1340,18 +1779,19 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1340
1779
|
]
|
|
1341
1780
|
}
|
|
1342
1781
|
),
|
|
1343
|
-
error ? /* @__PURE__ */
|
|
1344
|
-
!selected ? /* @__PURE__ */
|
|
1345
|
-
list.error ? /* @__PURE__ */
|
|
1346
|
-
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */
|
|
1347
|
-
/* @__PURE__ */
|
|
1348
|
-
/* @__PURE__ */
|
|
1349
|
-
/* @__PURE__ */
|
|
1350
|
-
/* @__PURE__ */
|
|
1351
|
-
/* @__PURE__ */
|
|
1782
|
+
error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
|
|
1783
|
+
!selected ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1784
|
+
list.error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
1785
|
+
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx12(Empty, { icon: "users", title: "No users yet", children: "Sign-ups appear here as they happen." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
|
|
1786
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1787
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Email" }),
|
|
1788
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Name" }),
|
|
1789
|
+
billingClient ? /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Plan" }) : null,
|
|
1790
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Created" }),
|
|
1791
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Status" })
|
|
1352
1792
|
] }) }),
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
/* @__PURE__ */
|
|
1793
|
+
/* @__PURE__ */ jsx12("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1794
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsx12(
|
|
1355
1795
|
"button",
|
|
1356
1796
|
{
|
|
1357
1797
|
type: "button",
|
|
@@ -1360,17 +1800,28 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1360
1800
|
children: u.email
|
|
1361
1801
|
}
|
|
1362
1802
|
) }),
|
|
1363
|
-
/* @__PURE__ */
|
|
1364
|
-
/* @__PURE__ */
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1803
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "\u2014" }) }),
|
|
1804
|
+
billingClient ? /* @__PURE__ */ jsx12("td", { style: styles.td, children: (() => {
|
|
1805
|
+
const sub = plans?.get(`user/${u.id}`);
|
|
1806
|
+
if (!plans) return /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "\u2026" });
|
|
1807
|
+
if (!sub || sub.status === "canceled")
|
|
1808
|
+
return /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "free" });
|
|
1809
|
+
return /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1810
|
+
/* @__PURE__ */ jsx12("strong", { children: sub.plan }),
|
|
1811
|
+
" ",
|
|
1812
|
+
sub.status !== "active" ? /* @__PURE__ */ jsx12(Pill, { tone: statusTone(sub.status), children: sub.status }) : null
|
|
1813
|
+
] });
|
|
1814
|
+
})() }) : null,
|
|
1815
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
|
|
1816
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1817
|
+
u.suspended ? /* @__PURE__ */ jsx12(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1818
|
+
u.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "deleted" }) : null,
|
|
1819
|
+
!u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "ok", children: "active" }) : null
|
|
1369
1820
|
] })
|
|
1370
1821
|
] }, u.id)) })
|
|
1371
1822
|
] }),
|
|
1372
|
-
list.isLoading ? /* @__PURE__ */
|
|
1373
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */
|
|
1823
|
+
list.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1824
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx12(
|
|
1374
1825
|
"button",
|
|
1375
1826
|
{
|
|
1376
1827
|
type: "button",
|
|
@@ -1380,48 +1831,48 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1380
1831
|
}
|
|
1381
1832
|
) : null
|
|
1382
1833
|
] }) : null,
|
|
1383
|
-
user ? /* @__PURE__ */
|
|
1384
|
-
/* @__PURE__ */
|
|
1834
|
+
user ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1835
|
+
/* @__PURE__ */ jsxs12("h2", { style: styles.subtitle, children: [
|
|
1385
1836
|
user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
|
|
1386
1837
|
" ",
|
|
1387
|
-
user.suspended ? /* @__PURE__ */
|
|
1388
|
-
user.deletedAt ? /* @__PURE__ */
|
|
1838
|
+
user.suspended ? /* @__PURE__ */ jsx12(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1839
|
+
user.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "deleted" }) : null
|
|
1389
1840
|
] }),
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1841
|
+
/* @__PURE__ */ jsx12("table", { style: styles.table, children: /* @__PURE__ */ jsxs12("tbody", { children: [
|
|
1842
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1843
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "id" }),
|
|
1844
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
|
|
1394
1845
|
] }),
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
/* @__PURE__ */
|
|
1397
|
-
/* @__PURE__ */
|
|
1846
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1847
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "email" }),
|
|
1848
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1398
1849
|
user.email,
|
|
1399
1850
|
" \xB7 verified ",
|
|
1400
1851
|
yesNo(user.isEmailVerified)
|
|
1401
1852
|
] })
|
|
1402
1853
|
] }),
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1854
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1855
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "MFA" }),
|
|
1856
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
|
|
1406
1857
|
] }),
|
|
1407
|
-
/* @__PURE__ */
|
|
1408
|
-
/* @__PURE__ */
|
|
1409
|
-
/* @__PURE__ */
|
|
1858
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1859
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "provider" }),
|
|
1860
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1410
1861
|
user.provider || "password",
|
|
1411
1862
|
user.hasPassword ? "" : " \xB7 no password set"
|
|
1412
1863
|
] })
|
|
1413
1864
|
] }),
|
|
1414
|
-
/* @__PURE__ */
|
|
1415
|
-
/* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1865
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1866
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "last login" }),
|
|
1867
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "never" }) })
|
|
1417
1868
|
] }),
|
|
1418
|
-
/* @__PURE__ */
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1869
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1870
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "created" }),
|
|
1871
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
|
|
1421
1872
|
] })
|
|
1422
1873
|
] }) }),
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
user.suspended ? /* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsxs12("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
|
|
1875
|
+
user.suspended ? /* @__PURE__ */ jsx12(
|
|
1425
1876
|
"button",
|
|
1426
1877
|
{
|
|
1427
1878
|
type: "button",
|
|
@@ -1430,7 +1881,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1430
1881
|
disabled: isLoading,
|
|
1431
1882
|
children: "Unsuspend"
|
|
1432
1883
|
}
|
|
1433
|
-
) : /* @__PURE__ */
|
|
1884
|
+
) : /* @__PURE__ */ jsx12(
|
|
1434
1885
|
"button",
|
|
1435
1886
|
{
|
|
1436
1887
|
type: "button",
|
|
@@ -1440,7 +1891,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1440
1891
|
children: "Suspend"
|
|
1441
1892
|
}
|
|
1442
1893
|
),
|
|
1443
|
-
/* @__PURE__ */
|
|
1894
|
+
/* @__PURE__ */ jsx12(
|
|
1444
1895
|
"button",
|
|
1445
1896
|
{
|
|
1446
1897
|
type: "button",
|
|
@@ -1451,38 +1902,48 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1451
1902
|
}
|
|
1452
1903
|
)
|
|
1453
1904
|
] }),
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
|
|
1456
|
-
/* @__PURE__ */
|
|
1905
|
+
billingClient ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1906
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Plan & credits" }),
|
|
1907
|
+
/* @__PURE__ */ jsx12(
|
|
1908
|
+
SubscriberBilling,
|
|
1909
|
+
{
|
|
1910
|
+
client: billingClient,
|
|
1911
|
+
subscriber: { type: "user", id: user.id }
|
|
1912
|
+
}
|
|
1913
|
+
)
|
|
1914
|
+
] }) : null,
|
|
1915
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Live sessions" }),
|
|
1916
|
+
sessions.sessions.length === 0 ? /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx12("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs12("li", { style: styles.row, children: [
|
|
1917
|
+
/* @__PURE__ */ jsx12("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
|
|
1457
1918
|
" ",
|
|
1458
|
-
describeLocation(s.location) ? /* @__PURE__ */
|
|
1919
|
+
describeLocation(s.location) ? /* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
|
|
1459
1920
|
describeLocation(s.location),
|
|
1460
1921
|
" "
|
|
1461
1922
|
] }) : null,
|
|
1462
|
-
/* @__PURE__ */
|
|
1923
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: s.userAgent ?? "" }),
|
|
1463
1924
|
" ",
|
|
1464
|
-
/* @__PURE__ */
|
|
1925
|
+
/* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
|
|
1465
1926
|
"since ",
|
|
1466
1927
|
new Date(s.createdAt).toLocaleString()
|
|
1467
1928
|
] })
|
|
1468
1929
|
] }, s.id)) }),
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */
|
|
1471
|
-
/* @__PURE__ */
|
|
1930
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
|
|
1931
|
+
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx12("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs12("li", { style: styles.row, children: [
|
|
1932
|
+
/* @__PURE__ */ jsx12(Pill, { tone: e.outcome === "success" ? "ok" : "bad", children: e.outcome }),
|
|
1472
1933
|
" ",
|
|
1473
|
-
/* @__PURE__ */
|
|
1934
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: e.method }),
|
|
1474
1935
|
" ",
|
|
1475
|
-
/* @__PURE__ */
|
|
1936
|
+
/* @__PURE__ */ jsx12("span", { style: styles.mono, children: e.ipAddress ?? "\u2014" }),
|
|
1476
1937
|
" ",
|
|
1477
|
-
describeLocation(e.location) ? /* @__PURE__ */
|
|
1478
|
-
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */
|
|
1938
|
+
describeLocation(e.location) ? /* @__PURE__ */ jsx12("span", { style: styles.muted, children: describeLocation(e.location) }) : null,
|
|
1939
|
+
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1479
1940
|
" ",
|
|
1480
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsx12(Pill, { tone: "warn", children: e.location.proxy ? "proxy/VPN" : "hosting" })
|
|
1481
1942
|
] }) : null,
|
|
1482
1943
|
" ",
|
|
1483
|
-
/* @__PURE__ */
|
|
1944
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
|
|
1484
1945
|
] }, e.id)) }),
|
|
1485
|
-
history.hasMore && !history.isLoading ? /* @__PURE__ */
|
|
1946
|
+
history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx12(
|
|
1486
1947
|
"button",
|
|
1487
1948
|
{
|
|
1488
1949
|
type: "button",
|
|
@@ -1497,24 +1958,24 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1497
1958
|
|
|
1498
1959
|
// src/screens/CatalogScreen.tsx
|
|
1499
1960
|
import { useAdminCatalog } from "@fonderie/react-admin";
|
|
1500
|
-
import { Fragment as
|
|
1961
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1501
1962
|
function CatalogScreen({ client }) {
|
|
1502
1963
|
const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
|
|
1503
1964
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
1504
|
-
return /* @__PURE__ */
|
|
1505
|
-
/* @__PURE__ */
|
|
1965
|
+
return /* @__PURE__ */ jsxs13("div", { style: styles.container, children: [
|
|
1966
|
+
/* @__PURE__ */ jsx13(
|
|
1506
1967
|
PageHeader,
|
|
1507
1968
|
{
|
|
1508
1969
|
title: "Catalog",
|
|
1509
1970
|
lead: "What you sell: plans as configured in code, and as stored in the database, side by side.",
|
|
1510
|
-
actions: /* @__PURE__ */
|
|
1971
|
+
actions: /* @__PURE__ */ jsx13(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
1511
1972
|
}
|
|
1512
1973
|
),
|
|
1513
|
-
error ? /* @__PURE__ */
|
|
1514
|
-
isLoading && !catalog ? /* @__PURE__ */
|
|
1515
|
-
catalog ? /* @__PURE__ */
|
|
1516
|
-
/* @__PURE__ */
|
|
1517
|
-
/* @__PURE__ */
|
|
1974
|
+
error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1975
|
+
isLoading && !catalog ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1976
|
+
catalog ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1977
|
+
/* @__PURE__ */ jsx13("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Configured (code)" }),
|
|
1978
|
+
/* @__PURE__ */ jsx13(
|
|
1518
1979
|
"pre",
|
|
1519
1980
|
{
|
|
1520
1981
|
style: {
|
|
@@ -1528,32 +1989,32 @@ function CatalogScreen({ client }) {
|
|
|
1528
1989
|
children: JSON.stringify(catalog.configured, null, 2)
|
|
1529
1990
|
}
|
|
1530
1991
|
),
|
|
1531
|
-
/* @__PURE__ */
|
|
1532
|
-
catalog.stored.length === 0 ? /* @__PURE__ */
|
|
1533
|
-
/* @__PURE__ */
|
|
1534
|
-
/* @__PURE__ */
|
|
1535
|
-
/* @__PURE__ */
|
|
1536
|
-
/* @__PURE__ */
|
|
1537
|
-
/* @__PURE__ */
|
|
1538
|
-
/* @__PURE__ */
|
|
1539
|
-
/* @__PURE__ */
|
|
1540
|
-
/* @__PURE__ */
|
|
1992
|
+
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Stored (database)" }),
|
|
1993
|
+
catalog.stored.length === 0 ? /* @__PURE__ */ jsx13(Empty, { icon: "catalog", title: "No stored plans", children: "Plans are configured in code; nothing has been written to the database." }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
|
|
1994
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
1995
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Plan" }),
|
|
1996
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Tier" }),
|
|
1997
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Seats" }),
|
|
1998
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Monthly" }),
|
|
1999
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Yearly" }),
|
|
2000
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Trial" }),
|
|
2001
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th })
|
|
1541
2002
|
] }) }),
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
p.description ? /* @__PURE__ */
|
|
2003
|
+
/* @__PURE__ */ jsx13("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2004
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
2005
|
+
/* @__PURE__ */ jsx13("strong", { children: p.name }),
|
|
2006
|
+
p.description ? /* @__PURE__ */ jsx13("div", { style: styles.muted, children: p.description }) : null
|
|
1546
2007
|
] }),
|
|
1547
|
-
/* @__PURE__ */
|
|
1548
|
-
/* @__PURE__ */
|
|
1549
|
-
/* @__PURE__ */
|
|
2008
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.tier }),
|
|
2009
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.seats ?? "\u221E" }),
|
|
2010
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1550
2011
|
money(p.pricing?.monthly),
|
|
1551
2012
|
" ",
|
|
1552
2013
|
p.pricing?.currency ?? ""
|
|
1553
2014
|
] }),
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
/* @__PURE__ */
|
|
2015
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: money(p.pricing?.yearly) }),
|
|
2016
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
|
|
2017
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: /* @__PURE__ */ jsx13(
|
|
1557
2018
|
"button",
|
|
1558
2019
|
{
|
|
1559
2020
|
type: "button",
|
|
@@ -1573,114 +2034,148 @@ function CatalogScreen({ client }) {
|
|
|
1573
2034
|
}
|
|
1574
2035
|
|
|
1575
2036
|
// src/screens/SubscriberScreen.tsx
|
|
1576
|
-
import {
|
|
1577
|
-
import { useState as
|
|
1578
|
-
import { Fragment as
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
2037
|
+
import { useAdminSubscribers } from "@fonderie/react-admin";
|
|
2038
|
+
import { useState as useState7 } from "react";
|
|
2039
|
+
import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2040
|
+
var FILTERS = [
|
|
2041
|
+
{ label: "All", match: () => true },
|
|
2042
|
+
{ label: "Active", match: (s) => s === "active" },
|
|
2043
|
+
{ label: "Trialing", match: (s) => s === "trialing" },
|
|
2044
|
+
{ label: "Past due", match: (s) => s === "past_due" || s === "unpaid" },
|
|
2045
|
+
{ label: "Canceled", match: (s) => s === "canceled" }
|
|
2046
|
+
];
|
|
2047
|
+
function SubscriberScreen({ client, pageSize = 50, onOpenUser }) {
|
|
1583
2048
|
const list = useAdminSubscribers(client, { limit: pageSize });
|
|
1584
|
-
const
|
|
1585
|
-
const [
|
|
1586
|
-
const [
|
|
1587
|
-
const [
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
2049
|
+
const [filter, setFilter] = useState7("All");
|
|
2050
|
+
const [open, setOpen] = useState7(null);
|
|
2051
|
+
const [type, setType] = useState7("workspace");
|
|
2052
|
+
const [idInput, setIdInput] = useState7("");
|
|
2053
|
+
if (open) {
|
|
2054
|
+
return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
|
|
2055
|
+
/* @__PURE__ */ jsx14(
|
|
2056
|
+
PageHeader,
|
|
2057
|
+
{
|
|
2058
|
+
title: `${open.type}/${open.id}`,
|
|
2059
|
+
lead: "Plan, credits and what moved.",
|
|
2060
|
+
actions: /* @__PURE__ */ jsxs14("button", { type: "button", style: styles.buttonGhost, onClick: () => setOpen(null), children: [
|
|
2061
|
+
/* @__PURE__ */ jsx14(Icon, { name: "back", size: 14 }),
|
|
2062
|
+
"All subscriptions"
|
|
2063
|
+
] })
|
|
2064
|
+
}
|
|
2065
|
+
),
|
|
2066
|
+
/* @__PURE__ */ jsx14(SubscriberBilling, { client, subscriber: open })
|
|
2067
|
+
] });
|
|
2068
|
+
}
|
|
2069
|
+
const match = FILTERS.find((f) => f.label === filter)?.match ?? (() => true);
|
|
2070
|
+
const rows = list.subscriptions.filter((s) => match(s.status));
|
|
2071
|
+
const openRow = (t2, id) => t2 === "user" && onOpenUser ? onOpenUser(id) : setOpen({ type: t2, id });
|
|
2072
|
+
return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
|
|
2073
|
+
/* @__PURE__ */ jsx14(
|
|
1596
2074
|
PageHeader,
|
|
1597
2075
|
{
|
|
1598
|
-
title: "
|
|
1599
|
-
lead: "Who is
|
|
2076
|
+
title: "Subscriptions",
|
|
2077
|
+
lead: "Who pays, and who is behind. A user's plan and credits are also on their page under Users \u2014 free users appear only there.",
|
|
2078
|
+
actions: /* @__PURE__ */ jsx14(RefreshButton, { onClick: () => void list.refresh(), busy: list.isLoading })
|
|
1600
2079
|
}
|
|
1601
2080
|
),
|
|
1602
|
-
/* @__PURE__ */
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
placeholder: "subscriber id",
|
|
1631
|
-
style: { ...styles.input, minWidth: 280 },
|
|
1632
|
-
"aria-label": "Subscriber id"
|
|
1633
|
-
}
|
|
1634
|
-
),
|
|
1635
|
-
/* @__PURE__ */ jsxs12("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1636
|
-
/* @__PURE__ */ jsx12(Icon, { name: "search", size: 14 }),
|
|
1637
|
-
"Look up"
|
|
1638
|
-
] }),
|
|
1639
|
-
subscriber ? /* @__PURE__ */ jsxs12(
|
|
1640
|
-
"button",
|
|
1641
|
-
{
|
|
1642
|
-
type: "button",
|
|
1643
|
-
style: styles.button,
|
|
1644
|
-
onClick: () => {
|
|
1645
|
-
setSubscriber(null);
|
|
1646
|
-
setIdInput("");
|
|
1647
|
-
setGranted(null);
|
|
2081
|
+
/* @__PURE__ */ jsxs14("div", { style: { ...styles.toolbar, justifyContent: "space-between" }, children: [
|
|
2082
|
+
/* @__PURE__ */ jsx14(
|
|
2083
|
+
"fieldset",
|
|
2084
|
+
{
|
|
2085
|
+
style: {
|
|
2086
|
+
display: "flex",
|
|
2087
|
+
gap: 6,
|
|
2088
|
+
flexWrap: "wrap",
|
|
2089
|
+
border: "none",
|
|
2090
|
+
margin: 0,
|
|
2091
|
+
padding: 0,
|
|
2092
|
+
minWidth: 0
|
|
2093
|
+
},
|
|
2094
|
+
"aria-label": "Filter by status",
|
|
2095
|
+
children: FILTERS.map((f) => {
|
|
2096
|
+
const n = list.subscriptions.filter((s) => f.match(s.status)).length;
|
|
2097
|
+
const on = filter === f.label;
|
|
2098
|
+
return /* @__PURE__ */ jsxs14(
|
|
2099
|
+
"button",
|
|
2100
|
+
{
|
|
2101
|
+
type: "button",
|
|
2102
|
+
"aria-pressed": on,
|
|
2103
|
+
onClick: () => setFilter(f.label),
|
|
2104
|
+
style: { ...on ? styles.buttonPrimary : styles.button, height: 28 },
|
|
2105
|
+
children: [
|
|
2106
|
+
f.label,
|
|
2107
|
+
/* @__PURE__ */ jsx14("span", { style: { opacity: 0.7 }, children: n })
|
|
2108
|
+
]
|
|
1648
2109
|
},
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2110
|
+
f.label
|
|
2111
|
+
);
|
|
2112
|
+
})
|
|
2113
|
+
}
|
|
2114
|
+
),
|
|
2115
|
+
/* @__PURE__ */ jsxs14(
|
|
2116
|
+
"form",
|
|
2117
|
+
{
|
|
2118
|
+
style: { display: "flex", gap: 6 },
|
|
2119
|
+
onSubmit: (e) => {
|
|
2120
|
+
e.preventDefault();
|
|
2121
|
+
if (idInput.trim()) openRow(type, idInput.trim());
|
|
2122
|
+
},
|
|
2123
|
+
children: [
|
|
2124
|
+
/* @__PURE__ */ jsxs14(
|
|
2125
|
+
"select",
|
|
2126
|
+
{
|
|
2127
|
+
value: type,
|
|
2128
|
+
onChange: (e) => setType(e.target.value),
|
|
2129
|
+
style: styles.input,
|
|
2130
|
+
"aria-label": "Type",
|
|
2131
|
+
children: [
|
|
2132
|
+
/* @__PURE__ */ jsx14("option", { value: "workspace", children: "workspace" }),
|
|
2133
|
+
/* @__PURE__ */ jsx14("option", { value: "user", children: "user" })
|
|
2134
|
+
]
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
/* @__PURE__ */ jsx14(
|
|
2138
|
+
"input",
|
|
2139
|
+
{
|
|
2140
|
+
value: idInput,
|
|
2141
|
+
onChange: (e) => setIdInput(e.target.value),
|
|
2142
|
+
placeholder: "subscriber id",
|
|
2143
|
+
style: { ...styles.input, width: 220 },
|
|
2144
|
+
"aria-label": "Subscriber id"
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
/* @__PURE__ */ jsxs14("button", { type: "submit", style: styles.button, disabled: !idInput.trim(), children: [
|
|
2148
|
+
/* @__PURE__ */ jsx14(Icon, { name: "search", size: 14 }),
|
|
2149
|
+
"Open"
|
|
2150
|
+
] })
|
|
2151
|
+
]
|
|
2152
|
+
}
|
|
2153
|
+
)
|
|
2154
|
+
] }),
|
|
2155
|
+
list.error ? /* @__PURE__ */ jsx14("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
2156
|
+
rows.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx14(
|
|
2157
|
+
Empty,
|
|
2158
|
+
{
|
|
2159
|
+
icon: "subscriber",
|
|
2160
|
+
title: filter === "All" ? "No subscriptions yet" : `No ${filter.toLowerCase()} subscriptions`,
|
|
2161
|
+
children: "Subscriptions appear here once someone checks out."
|
|
1665
2162
|
}
|
|
1666
|
-
),
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
/* @__PURE__ */
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
/* @__PURE__ */ jsx12("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1678
|
-
/* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsxs12(
|
|
2163
|
+
) : /* @__PURE__ */ jsxs14("table", { style: styles.table, children: [
|
|
2164
|
+
/* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2165
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Subscriber" }),
|
|
2166
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Plan" }),
|
|
2167
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Status" }),
|
|
2168
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Renews / ends" })
|
|
2169
|
+
] }) }),
|
|
2170
|
+
/* @__PURE__ */ jsx14("tbody", { children: rows.map((s) => {
|
|
2171
|
+
const end = periodEnd(s);
|
|
2172
|
+
return /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2173
|
+
/* @__PURE__ */ jsx14("td", { style: styles.td, children: /* @__PURE__ */ jsxs14(
|
|
1679
2174
|
"button",
|
|
1680
2175
|
{
|
|
1681
2176
|
type: "button",
|
|
1682
2177
|
style: { ...styles.link, ...styles.mono },
|
|
1683
|
-
onClick: () =>
|
|
2178
|
+
onClick: () => openRow(s.subscriberType, s.subscriberId),
|
|
1684
2179
|
children: [
|
|
1685
2180
|
s.subscriberType,
|
|
1686
2181
|
"/",
|
|
@@ -1688,206 +2183,56 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
1688
2183
|
]
|
|
1689
2184
|
}
|
|
1690
2185
|
) }),
|
|
1691
|
-
/* @__PURE__ */
|
|
1692
|
-
/* @__PURE__ */
|
|
1693
|
-
"
|
|
1694
|
-
|
|
2186
|
+
/* @__PURE__ */ jsxs14("td", { style: styles.td, children: [
|
|
2187
|
+
/* @__PURE__ */ jsx14("strong", { children: s.plan }),
|
|
2188
|
+
" ",
|
|
2189
|
+
/* @__PURE__ */ jsxs14("span", { style: styles.muted, children: [
|
|
2190
|
+
"\xB7 ",
|
|
2191
|
+
s.interval
|
|
2192
|
+
] })
|
|
1695
2193
|
] }),
|
|
1696
|
-
/* @__PURE__ */
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
|
|
1699
|
-
{
|
|
1700
|
-
tone: s.status === "active" ? "ok" : s.status === "trialing" ? "info" : s.status === "past_due" ? "warn" : "neutral",
|
|
1701
|
-
children: s.status
|
|
1702
|
-
}
|
|
1703
|
-
),
|
|
1704
|
-
s.cancelAtPeriodEnd ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
2194
|
+
/* @__PURE__ */ jsxs14("td", { style: styles.td, children: [
|
|
2195
|
+
/* @__PURE__ */ jsx14(Pill, { tone: statusTone(s.status), children: s.status }),
|
|
2196
|
+
s.cancelAtPeriodEnd && s.status !== "canceled" ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1705
2197
|
" ",
|
|
1706
|
-
/* @__PURE__ */
|
|
2198
|
+
/* @__PURE__ */ jsx14(Pill, { tone: "warn", dot: false, children: "cancels" })
|
|
1707
2199
|
] }) : null
|
|
1708
2200
|
] }),
|
|
1709
|
-
/* @__PURE__ */
|
|
1710
|
-
|
|
1711
|
-
] }),
|
|
1712
|
-
list.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1713
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx12(
|
|
1714
|
-
"button",
|
|
1715
|
-
{
|
|
1716
|
-
type: "button",
|
|
1717
|
-
style: { ...styles.button, marginTop: 8 },
|
|
1718
|
-
onClick: () => void list.loadMore(),
|
|
1719
|
-
children: "Load more"
|
|
1720
|
-
}
|
|
1721
|
-
) : null
|
|
1722
|
-
] }) : 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 }),
|
|
1730
|
-
" \xB7 ",
|
|
1731
|
-
subscription.interval
|
|
1732
|
-
] })
|
|
1733
|
-
] }),
|
|
1734
|
-
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1735
|
-
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "status" }),
|
|
1736
|
-
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1737
|
-
subscription.status,
|
|
1738
|
-
subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
|
|
1739
|
-
] })
|
|
1740
|
-
] }),
|
|
1741
|
-
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1742
|
-
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "period" }),
|
|
1743
|
-
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1744
|
-
subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
|
|
1745
|
-
" ",
|
|
1746
|
-
"\u2192",
|
|
2201
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, whiteSpace: "nowrap" }, children: end ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
2202
|
+
/* @__PURE__ */ jsx14("span", { style: styles.muted, children: end.label }),
|
|
1747
2203
|
" ",
|
|
1748
|
-
|
|
1749
|
-
] })
|
|
1750
|
-
] })
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
display: "flex",
|
|
1764
|
-
alignItems: "baseline",
|
|
1765
|
-
gap: 8,
|
|
1766
|
-
flexWrap: "wrap"
|
|
1767
|
-
},
|
|
1768
|
-
children: [
|
|
1769
|
-
/* @__PURE__ */ jsx12(
|
|
1770
|
-
"strong",
|
|
1771
|
-
{
|
|
1772
|
-
style: {
|
|
1773
|
-
fontSize: 22,
|
|
1774
|
-
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
1775
|
-
},
|
|
1776
|
-
children: wallet.balance
|
|
1777
|
-
}
|
|
1778
|
-
),
|
|
1779
|
-
" ",
|
|
1780
|
-
wallet.currency,
|
|
1781
|
-
" ",
|
|
1782
|
-
/* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
|
|
1783
|
-
"(granted ",
|
|
1784
|
-
wallet.granted ?? "0",
|
|
1785
|
-
" \xB7 purchased ",
|
|
1786
|
-
wallet.purchased ?? "0",
|
|
1787
|
-
") \xB7 minor units, precision ",
|
|
1788
|
-
wallet.precision
|
|
1789
|
-
] })
|
|
1790
|
-
]
|
|
1791
|
-
}
|
|
1792
|
-
),
|
|
1793
|
-
/* @__PURE__ */ jsxs12(
|
|
1794
|
-
"form",
|
|
1795
|
-
{
|
|
1796
|
-
style: styles.toolbar,
|
|
1797
|
-
onSubmit: (e) => {
|
|
1798
|
-
e.preventDefault();
|
|
1799
|
-
const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
|
|
1800
|
-
void grant({
|
|
1801
|
-
amount: amount.trim(),
|
|
1802
|
-
...note ? { description: note } : {},
|
|
1803
|
-
idempotencyKey: key
|
|
1804
|
-
}).then(() => {
|
|
1805
|
-
setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
|
|
1806
|
-
setAmount("");
|
|
1807
|
-
setNote("");
|
|
1808
|
-
}).catch(() => {
|
|
1809
|
-
});
|
|
1810
|
-
},
|
|
1811
|
-
children: [
|
|
1812
|
-
/* @__PURE__ */ jsx12(
|
|
1813
|
-
"input",
|
|
1814
|
-
{
|
|
1815
|
-
value: amount,
|
|
1816
|
-
onChange: (e) => setAmount(e.target.value),
|
|
1817
|
-
placeholder: "amount (minor units)",
|
|
1818
|
-
style: styles.input,
|
|
1819
|
-
"aria-label": "Amount"
|
|
1820
|
-
}
|
|
1821
|
-
),
|
|
1822
|
-
/* @__PURE__ */ jsx12(
|
|
1823
|
-
"input",
|
|
1824
|
-
{
|
|
1825
|
-
value: note,
|
|
1826
|
-
onChange: (e) => setNote(e.target.value),
|
|
1827
|
-
placeholder: "reason (optional)",
|
|
1828
|
-
style: { ...styles.input, minWidth: 220 },
|
|
1829
|
-
"aria-label": "Reason"
|
|
1830
|
-
}
|
|
1831
|
-
),
|
|
1832
|
-
/* @__PURE__ */ jsxs12(
|
|
1833
|
-
"button",
|
|
1834
|
-
{
|
|
1835
|
-
type: "submit",
|
|
1836
|
-
style: styles.buttonPrimary,
|
|
1837
|
-
disabled: !/^\d+$/.test(amount.trim()),
|
|
1838
|
-
children: [
|
|
1839
|
-
/* @__PURE__ */ jsx12(Icon, { name: "plus", size: 14 }),
|
|
1840
|
-
"Grant"
|
|
1841
|
-
]
|
|
1842
|
-
}
|
|
1843
|
-
),
|
|
1844
|
-
granted ? /* @__PURE__ */ jsx12("span", { style: styles.ok, children: granted }) : null
|
|
1845
|
-
]
|
|
1846
|
-
}
|
|
1847
|
-
),
|
|
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" })
|
|
1855
|
-
] }) }),
|
|
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 ?? "" })
|
|
1861
|
-
] }, t2.id)) })
|
|
1862
|
-
] }),
|
|
1863
|
-
hasMoreLedger ? /* @__PURE__ */ jsx12(
|
|
1864
|
-
"button",
|
|
1865
|
-
{
|
|
1866
|
-
type: "button",
|
|
1867
|
-
style: { ...styles.button, marginTop: 8 },
|
|
1868
|
-
onClick: () => void loadMoreLedger(),
|
|
1869
|
-
children: "Load more"
|
|
1870
|
-
}
|
|
1871
|
-
) : null
|
|
1872
|
-
] }) : /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
1873
|
-
] }) : null
|
|
2204
|
+
end.date
|
|
2205
|
+
] }) : /* @__PURE__ */ jsx14("span", { style: styles.muted, children: "\u2014" }) })
|
|
2206
|
+
] }, s.id);
|
|
2207
|
+
}) })
|
|
2208
|
+
] }),
|
|
2209
|
+
list.isLoading ? /* @__PURE__ */ jsx14("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
2210
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx14(
|
|
2211
|
+
"button",
|
|
2212
|
+
{
|
|
2213
|
+
type: "button",
|
|
2214
|
+
style: { ...styles.button, marginTop: 8 },
|
|
2215
|
+
onClick: () => void list.loadMore(),
|
|
2216
|
+
children: "Load more"
|
|
2217
|
+
}
|
|
2218
|
+
) : null
|
|
1874
2219
|
] });
|
|
1875
2220
|
}
|
|
1876
2221
|
|
|
1877
2222
|
// src/screens/AuditScreen.tsx
|
|
1878
2223
|
import { useAdminAudit } from "@fonderie/react-admin";
|
|
1879
|
-
import { useState as
|
|
1880
|
-
import { jsx as
|
|
2224
|
+
import { useState as useState8 } from "react";
|
|
2225
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1881
2226
|
function AuditScreen({ client, pageSize = 50 }) {
|
|
1882
|
-
const [draft, setDraft] =
|
|
1883
|
-
const [filter, setFilter] =
|
|
2227
|
+
const [draft, setDraft] = useState8({ workspaceId: "", type: "", actorId: "" });
|
|
2228
|
+
const [filter, setFilter] = useState8(
|
|
1884
2229
|
{}
|
|
1885
2230
|
);
|
|
1886
2231
|
const { events, hasMore, isLoading, error, refresh, loadMore } = useAdminAudit(client, {
|
|
1887
2232
|
...filter,
|
|
1888
2233
|
limit: pageSize
|
|
1889
2234
|
});
|
|
1890
|
-
const field = (key, placeholder) => /* @__PURE__ */
|
|
2235
|
+
const field = (key, placeholder) => /* @__PURE__ */ jsx15(
|
|
1891
2236
|
"input",
|
|
1892
2237
|
{
|
|
1893
2238
|
value: draft[key],
|
|
@@ -1897,15 +2242,15 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1897
2242
|
"aria-label": placeholder
|
|
1898
2243
|
}
|
|
1899
2244
|
);
|
|
1900
|
-
return /* @__PURE__ */
|
|
1901
|
-
/* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ jsxs15("div", { style: styles.container, children: [
|
|
2246
|
+
/* @__PURE__ */ jsx15(
|
|
1902
2247
|
PageHeader,
|
|
1903
2248
|
{
|
|
1904
2249
|
title: "Audit",
|
|
1905
2250
|
lead: "What happened, across every workspace unless you name one. The chain's integrity verdict is on the Doctor page."
|
|
1906
2251
|
}
|
|
1907
2252
|
),
|
|
1908
|
-
/* @__PURE__ */
|
|
2253
|
+
/* @__PURE__ */ jsxs15(
|
|
1909
2254
|
"form",
|
|
1910
2255
|
{
|
|
1911
2256
|
style: styles.toolbar,
|
|
@@ -1921,11 +2266,11 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1921
2266
|
field("workspaceId", "workspace id (all if empty)"),
|
|
1922
2267
|
field("type", "event type"),
|
|
1923
2268
|
field("actorId", "actor id"),
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
2269
|
+
/* @__PURE__ */ jsxs15("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
2270
|
+
/* @__PURE__ */ jsx15(Icon, { name: "search", size: 14 }),
|
|
1926
2271
|
"Filter"
|
|
1927
2272
|
] }),
|
|
1928
|
-
/* @__PURE__ */
|
|
2273
|
+
/* @__PURE__ */ jsx15(
|
|
1929
2274
|
"button",
|
|
1930
2275
|
{
|
|
1931
2276
|
type: "button",
|
|
@@ -1938,25 +2283,25 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1938
2283
|
]
|
|
1939
2284
|
}
|
|
1940
2285
|
),
|
|
1941
|
-
error ? /* @__PURE__ */
|
|
1942
|
-
events.length === 0 && !isLoading ? /* @__PURE__ */
|
|
1943
|
-
/* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
1946
|
-
/* @__PURE__ */
|
|
1947
|
-
/* @__PURE__ */
|
|
1948
|
-
/* @__PURE__ */
|
|
2286
|
+
error ? /* @__PURE__ */ jsx15("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
2287
|
+
events.length === 0 && !isLoading ? /* @__PURE__ */ jsx15(Empty, { icon: "audit", title: "Nothing recorded for this filter" }) : /* @__PURE__ */ jsxs15("table", { style: styles.table, children: [
|
|
2288
|
+
/* @__PURE__ */ jsx15("thead", { children: /* @__PURE__ */ jsxs15("tr", { children: [
|
|
2289
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "When" }),
|
|
2290
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Type" }),
|
|
2291
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Workspace" }),
|
|
2292
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Actor" }),
|
|
2293
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Request" })
|
|
1949
2294
|
] }) }),
|
|
1950
|
-
/* @__PURE__ */
|
|
1951
|
-
/* @__PURE__ */
|
|
1952
|
-
/* @__PURE__ */
|
|
1953
|
-
/* @__PURE__ */
|
|
1954
|
-
/* @__PURE__ */
|
|
1955
|
-
/* @__PURE__ */
|
|
2295
|
+
/* @__PURE__ */ jsx15("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs15("tr", { children: [
|
|
2296
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
|
|
2297
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
|
|
2298
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
|
|
2299
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
|
|
2300
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
|
|
1956
2301
|
] }, e.id)) })
|
|
1957
2302
|
] }),
|
|
1958
|
-
isLoading ? /* @__PURE__ */
|
|
1959
|
-
hasMore && !isLoading ? /* @__PURE__ */
|
|
2303
|
+
isLoading ? /* @__PURE__ */ jsx15("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
2304
|
+
hasMore && !isLoading ? /* @__PURE__ */ jsx15(
|
|
1960
2305
|
"button",
|
|
1961
2306
|
{
|
|
1962
2307
|
type: "button",
|
|
@@ -1969,7 +2314,7 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1969
2314
|
}
|
|
1970
2315
|
|
|
1971
2316
|
// src/screens/AdminShell.tsx
|
|
1972
|
-
import { Fragment as
|
|
2317
|
+
import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1973
2318
|
var ICON = {
|
|
1974
2319
|
attention: "attention",
|
|
1975
2320
|
modules: "modules",
|
|
@@ -1984,13 +2329,14 @@ var ICON = {
|
|
|
1984
2329
|
audit: "audit",
|
|
1985
2330
|
log: "log",
|
|
1986
2331
|
tokens: "tokens",
|
|
2332
|
+
operators: "users",
|
|
1987
2333
|
migrations: "migrations"
|
|
1988
2334
|
};
|
|
1989
2335
|
var NARROW = "(max-width: 820px)";
|
|
1990
2336
|
function useNarrow() {
|
|
1991
2337
|
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
1992
|
-
const [narrow, setNarrow] =
|
|
1993
|
-
|
|
2338
|
+
const [narrow, setNarrow] = useState9(() => query?.matches ?? false);
|
|
2339
|
+
useEffect2(() => {
|
|
1994
2340
|
if (!query) return;
|
|
1995
2341
|
const on = (e) => setNarrow(e.matches);
|
|
1996
2342
|
query.addEventListener("change", on);
|
|
@@ -2014,7 +2360,7 @@ var NAV = [
|
|
|
2014
2360
|
group: "Money",
|
|
2015
2361
|
items: [
|
|
2016
2362
|
{ page: "catalog", label: "Catalog", needs: "billing" },
|
|
2017
|
-
{ page: "subscriber", label: "
|
|
2363
|
+
{ page: "subscriber", label: "Subscriptions", needs: "billing" }
|
|
2018
2364
|
]
|
|
2019
2365
|
},
|
|
2020
2366
|
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
@@ -2024,7 +2370,8 @@ var NAV = [
|
|
|
2024
2370
|
items: [
|
|
2025
2371
|
{ page: "audit", label: "Audit", needs: "audit" },
|
|
2026
2372
|
{ page: "log", label: "Admin log" },
|
|
2027
|
-
{ page: "
|
|
2373
|
+
{ page: "operators", label: "Operators", needs: "operators" },
|
|
2374
|
+
{ page: "tokens", label: "Tokens" },
|
|
2028
2375
|
{ page: "migrations", label: "Migrations" }
|
|
2029
2376
|
]
|
|
2030
2377
|
}
|
|
@@ -2041,70 +2388,96 @@ function AdminShell({
|
|
|
2041
2388
|
onNavigate,
|
|
2042
2389
|
appName = "Admin",
|
|
2043
2390
|
envLabel,
|
|
2044
|
-
footer
|
|
2391
|
+
footer,
|
|
2392
|
+
operators = false,
|
|
2393
|
+
currentOperator
|
|
2045
2394
|
}) {
|
|
2046
2395
|
const narrow = useNarrow();
|
|
2047
|
-
const [drawer, setDrawer] =
|
|
2048
|
-
const [own, setOwn] =
|
|
2396
|
+
const [drawer, setDrawer] = useState9(false);
|
|
2397
|
+
const [own, setOwn] = useState9("attention");
|
|
2398
|
+
const [openUser, setOpenUser] = useState9(void 0);
|
|
2049
2399
|
const current = page ?? own;
|
|
2050
2400
|
const go = (p) => {
|
|
2051
2401
|
if (onNavigate) onNavigate(p);
|
|
2052
2402
|
if (page === void 0) setOwn(p);
|
|
2053
2403
|
setDrawer(false);
|
|
2054
2404
|
};
|
|
2055
|
-
const [editing, setEditing] =
|
|
2405
|
+
const [editing, setEditing] = useState9(null);
|
|
2056
2406
|
const has = {
|
|
2057
2407
|
config: Boolean(configClient),
|
|
2058
2408
|
courier: Boolean(courierClient),
|
|
2059
2409
|
auth: Boolean(authClient),
|
|
2060
2410
|
billing: Boolean(billingClient),
|
|
2061
|
-
audit: Boolean(auditClient)
|
|
2411
|
+
audit: Boolean(auditClient),
|
|
2412
|
+
operators
|
|
2062
2413
|
};
|
|
2063
2414
|
let body;
|
|
2064
2415
|
switch (current) {
|
|
2065
2416
|
case "attention":
|
|
2066
|
-
body = /* @__PURE__ */
|
|
2417
|
+
body = /* @__PURE__ */ jsx16(AttentionScreen, { client });
|
|
2067
2418
|
break;
|
|
2068
2419
|
case "modules":
|
|
2069
|
-
body = /* @__PURE__ */
|
|
2420
|
+
body = /* @__PURE__ */ jsx16(ModulesScreen, { client });
|
|
2070
2421
|
break;
|
|
2071
2422
|
case "doctor":
|
|
2072
|
-
body = /* @__PURE__ */
|
|
2423
|
+
body = /* @__PURE__ */ jsx16(DoctorScreen, { client });
|
|
2073
2424
|
break;
|
|
2074
2425
|
case "environment":
|
|
2075
|
-
body = /* @__PURE__ */
|
|
2426
|
+
body = /* @__PURE__ */ jsx16(EnvironmentScreen, { client });
|
|
2076
2427
|
break;
|
|
2077
2428
|
case "routes":
|
|
2078
|
-
body = /* @__PURE__ */
|
|
2429
|
+
body = /* @__PURE__ */ jsx16(RoutesScreen, { client });
|
|
2079
2430
|
break;
|
|
2080
2431
|
case "tokens":
|
|
2081
|
-
body = /* @__PURE__ */
|
|
2432
|
+
body = /* @__PURE__ */ jsx16(TokensScreen, { client });
|
|
2433
|
+
break;
|
|
2434
|
+
case "operators":
|
|
2435
|
+
body = /* @__PURE__ */ jsx16(OperatorsScreen, { client, me: currentOperator });
|
|
2082
2436
|
break;
|
|
2083
2437
|
case "migrations":
|
|
2084
|
-
body = /* @__PURE__ */
|
|
2438
|
+
body = /* @__PURE__ */ jsx16(MigrationsScreen, { client });
|
|
2085
2439
|
break;
|
|
2086
2440
|
case "log":
|
|
2087
|
-
body = /* @__PURE__ */
|
|
2441
|
+
body = /* @__PURE__ */ jsx16(AdminLogScreen, { client });
|
|
2088
2442
|
break;
|
|
2089
2443
|
case "catalog":
|
|
2090
|
-
body = billingClient ? /* @__PURE__ */
|
|
2444
|
+
body = billingClient ? /* @__PURE__ */ jsx16(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to see the catalog here." });
|
|
2091
2445
|
break;
|
|
2092
2446
|
case "subscriber":
|
|
2093
|
-
body = billingClient ? /* @__PURE__ */
|
|
2447
|
+
body = billingClient ? /* @__PURE__ */ jsx16(
|
|
2448
|
+
SubscriberScreen,
|
|
2449
|
+
{
|
|
2450
|
+
client: billingClient,
|
|
2451
|
+
...authClient ? {
|
|
2452
|
+
onOpenUser: (id) => {
|
|
2453
|
+
setOpenUser(id);
|
|
2454
|
+
go("users");
|
|
2455
|
+
}
|
|
2456
|
+
} : {}
|
|
2457
|
+
}
|
|
2458
|
+
) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to look up subscribers here." });
|
|
2094
2459
|
break;
|
|
2095
2460
|
case "audit":
|
|
2096
|
-
body = auditClient ? /* @__PURE__ */
|
|
2461
|
+
body = auditClient ? /* @__PURE__ */ jsx16(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuditAdminClient to see the audit trail here." });
|
|
2097
2462
|
break;
|
|
2098
2463
|
case "users":
|
|
2099
|
-
body = authClient ? /* @__PURE__ */
|
|
2464
|
+
body = authClient ? /* @__PURE__ */ jsx16(
|
|
2465
|
+
UsersScreen,
|
|
2466
|
+
{
|
|
2467
|
+
client: authClient,
|
|
2468
|
+
billingClient,
|
|
2469
|
+
openUserId: openUser
|
|
2470
|
+
},
|
|
2471
|
+
openUser ?? "list"
|
|
2472
|
+
) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuthAdminClient to look up users here." });
|
|
2100
2473
|
break;
|
|
2101
2474
|
case "settings":
|
|
2102
|
-
body = !configClient ? /* @__PURE__ */
|
|
2103
|
-
/* @__PURE__ */
|
|
2104
|
-
/* @__PURE__ */
|
|
2475
|
+
body = !configClient ? /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2476
|
+
/* @__PURE__ */ jsx16("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs16("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2477
|
+
/* @__PURE__ */ jsx16(Icon, { name: "back", size: 14 }),
|
|
2105
2478
|
"Config & secrets"
|
|
2106
2479
|
] }) }),
|
|
2107
|
-
/* @__PURE__ */
|
|
2480
|
+
/* @__PURE__ */ jsx16(
|
|
2108
2481
|
ConfigEditorScreen,
|
|
2109
2482
|
{
|
|
2110
2483
|
client: configClient,
|
|
@@ -2114,7 +2487,7 @@ function AdminShell({
|
|
|
2114
2487
|
onSaved: () => setEditing(null)
|
|
2115
2488
|
}
|
|
2116
2489
|
)
|
|
2117
|
-
] }) : /* @__PURE__ */
|
|
2490
|
+
] }) : /* @__PURE__ */ jsx16(
|
|
2118
2491
|
ConfigListScreen,
|
|
2119
2492
|
{
|
|
2120
2493
|
client: configClient,
|
|
@@ -2127,12 +2500,12 @@ function AdminShell({
|
|
|
2127
2500
|
);
|
|
2128
2501
|
break;
|
|
2129
2502
|
case "templates":
|
|
2130
|
-
body = !courierClient ? /* @__PURE__ */
|
|
2131
|
-
/* @__PURE__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2503
|
+
body = !courierClient ? /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2504
|
+
/* @__PURE__ */ jsx16("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs16("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2505
|
+
/* @__PURE__ */ jsx16(Icon, { name: "back", size: 14 }),
|
|
2133
2506
|
"Templates"
|
|
2134
2507
|
] }) }),
|
|
2135
|
-
/* @__PURE__ */
|
|
2508
|
+
/* @__PURE__ */ jsx16(
|
|
2136
2509
|
TemplateEditorScreen,
|
|
2137
2510
|
{
|
|
2138
2511
|
client: courierClient,
|
|
@@ -2140,7 +2513,7 @@ function AdminShell({
|
|
|
2140
2513
|
onSaved: () => setEditing(null)
|
|
2141
2514
|
}
|
|
2142
2515
|
)
|
|
2143
|
-
] }) : /* @__PURE__ */
|
|
2516
|
+
] }) : /* @__PURE__ */ jsx16(
|
|
2144
2517
|
TemplateListScreen,
|
|
2145
2518
|
{
|
|
2146
2519
|
client: courierClient,
|
|
@@ -2150,11 +2523,11 @@ function AdminShell({
|
|
|
2150
2523
|
break;
|
|
2151
2524
|
}
|
|
2152
2525
|
const production = envLabel ? /^prod/i.test(envLabel) : false;
|
|
2153
|
-
const brand = /* @__PURE__ */
|
|
2154
|
-
/* @__PURE__ */
|
|
2155
|
-
/* @__PURE__ */
|
|
2156
|
-
/* @__PURE__ */
|
|
2157
|
-
envLabel ? /* @__PURE__ */
|
|
2526
|
+
const brand = /* @__PURE__ */ jsxs16("div", { style: styles.navBrand, children: [
|
|
2527
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navMark, children: /* @__PURE__ */ jsx16(Icon, { name: "mark", size: 15 }) }),
|
|
2528
|
+
/* @__PURE__ */ jsxs16("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
2529
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navBrandTitle, children: appName }),
|
|
2530
|
+
envLabel ? /* @__PURE__ */ jsx16(
|
|
2158
2531
|
"span",
|
|
2159
2532
|
{
|
|
2160
2533
|
style: {
|
|
@@ -2171,18 +2544,18 @@ function AdminShell({
|
|
|
2171
2544
|
}
|
|
2172
2545
|
) : null
|
|
2173
2546
|
] }),
|
|
2174
|
-
narrow ? /* @__PURE__ */
|
|
2547
|
+
narrow ? /* @__PURE__ */ jsx16(
|
|
2175
2548
|
"button",
|
|
2176
2549
|
{
|
|
2177
2550
|
type: "button",
|
|
2178
2551
|
style: styles.buttonGhost,
|
|
2179
2552
|
"aria-label": "Close menu",
|
|
2180
2553
|
onClick: () => setDrawer(false),
|
|
2181
|
-
children: /* @__PURE__ */
|
|
2554
|
+
children: /* @__PURE__ */ jsx16(Icon, { name: "close" })
|
|
2182
2555
|
}
|
|
2183
2556
|
) : null
|
|
2184
2557
|
] });
|
|
2185
|
-
const nav = /* @__PURE__ */
|
|
2558
|
+
const nav = /* @__PURE__ */ jsxs16(
|
|
2186
2559
|
"nav",
|
|
2187
2560
|
{
|
|
2188
2561
|
style: {
|
|
@@ -2192,14 +2565,14 @@ function AdminShell({
|
|
|
2192
2565
|
"aria-label": "Admin",
|
|
2193
2566
|
children: [
|
|
2194
2567
|
brand,
|
|
2195
|
-
/* @__PURE__ */
|
|
2568
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navScroll, children: NAV.map((g) => {
|
|
2196
2569
|
const items = g.items.filter((i) => !i.needs || has[i.needs]);
|
|
2197
2570
|
if (items.length === 0) return null;
|
|
2198
|
-
return /* @__PURE__ */
|
|
2199
|
-
/* @__PURE__ */
|
|
2571
|
+
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
2572
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navGroup, children: g.group }),
|
|
2200
2573
|
items.map((i) => {
|
|
2201
2574
|
const active = current === i.page;
|
|
2202
|
-
return /* @__PURE__ */
|
|
2575
|
+
return /* @__PURE__ */ jsxs16(
|
|
2203
2576
|
"button",
|
|
2204
2577
|
{
|
|
2205
2578
|
type: "button",
|
|
@@ -2208,10 +2581,11 @@ function AdminShell({
|
|
|
2208
2581
|
"aria-current": active ? "page" : void 0,
|
|
2209
2582
|
onClick: () => {
|
|
2210
2583
|
setEditing(null);
|
|
2584
|
+
setOpenUser(void 0);
|
|
2211
2585
|
go(i.page);
|
|
2212
2586
|
},
|
|
2213
2587
|
children: [
|
|
2214
|
-
/* @__PURE__ */
|
|
2588
|
+
/* @__PURE__ */ jsx16(Icon, { name: ICON[i.page], style: active ? styles.navIconActive : void 0 }),
|
|
2215
2589
|
i.label
|
|
2216
2590
|
]
|
|
2217
2591
|
},
|
|
@@ -2220,15 +2594,15 @@ function AdminShell({
|
|
|
2220
2594
|
})
|
|
2221
2595
|
] }, g.group);
|
|
2222
2596
|
}) }),
|
|
2223
|
-
footer ? /* @__PURE__ */
|
|
2597
|
+
footer ? /* @__PURE__ */ jsx16("div", { style: styles.navFooter, children: footer }) : null
|
|
2224
2598
|
]
|
|
2225
2599
|
}
|
|
2226
2600
|
);
|
|
2227
2601
|
const title = NAV.flatMap((g) => g.items).find((i) => i.page === current)?.label ?? "";
|
|
2228
|
-
return /* @__PURE__ */
|
|
2229
|
-
narrow ? /* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
2231
|
-
/* @__PURE__ */
|
|
2602
|
+
return /* @__PURE__ */ jsxs16("div", { style: { ...styles.shell, ...narrow ? { flexDirection: "column" } : {} }, children: [
|
|
2603
|
+
narrow ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsxs16("div", { style: styles.topbar, children: [
|
|
2605
|
+
/* @__PURE__ */ jsx16(
|
|
2232
2606
|
"button",
|
|
2233
2607
|
{
|
|
2234
2608
|
type: "button",
|
|
@@ -2236,18 +2610,18 @@ function AdminShell({
|
|
|
2236
2610
|
"aria-label": "Open menu",
|
|
2237
2611
|
"aria-expanded": drawer,
|
|
2238
2612
|
onClick: () => setDrawer(true),
|
|
2239
|
-
children: /* @__PURE__ */
|
|
2613
|
+
children: /* @__PURE__ */ jsx16(Icon, { name: "menu" })
|
|
2240
2614
|
}
|
|
2241
2615
|
),
|
|
2242
|
-
/* @__PURE__ */
|
|
2243
|
-
envLabel ? /* @__PURE__ */
|
|
2616
|
+
/* @__PURE__ */ jsx16("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
|
|
2617
|
+
envLabel ? /* @__PURE__ */ jsx16("span", { style: { ...styles.badge, marginLeft: "auto" }, children: envLabel }) : null
|
|
2244
2618
|
] }),
|
|
2245
|
-
drawer ? /* @__PURE__ */
|
|
2619
|
+
drawer ? /* @__PURE__ */ jsxs16("div", { style: styles.drawer, children: [
|
|
2246
2620
|
nav,
|
|
2247
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsx16("div", { style: styles.scrim, onClick: () => setDrawer(false), "aria-hidden": "true" })
|
|
2248
2622
|
] }) : null
|
|
2249
2623
|
] }) : nav,
|
|
2250
|
-
/* @__PURE__ */
|
|
2624
|
+
/* @__PURE__ */ jsx16("main", { className: "fonderie-admin-main", style: styles.main, children: body })
|
|
2251
2625
|
] });
|
|
2252
2626
|
}
|
|
2253
2627
|
export {
|
|
@@ -2260,7 +2634,9 @@ export {
|
|
|
2260
2634
|
EnvironmentScreen,
|
|
2261
2635
|
MigrationsScreen,
|
|
2262
2636
|
ModulesScreen,
|
|
2637
|
+
OperatorsScreen,
|
|
2263
2638
|
RoutesScreen,
|
|
2639
|
+
SubscriberBilling,
|
|
2264
2640
|
SubscriberScreen,
|
|
2265
2641
|
TokensScreen,
|
|
2266
2642
|
UsersScreen
|