@fonderie/react-admin-screens 0.5.0 → 0.7.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 CHANGED
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  CatalogScreen: () => CatalogScreen,
28
28
  ConfigScreen: () => ConfigScreen,
29
29
  DoctorScreen: () => DoctorScreen,
30
+ MigrationsScreen: () => MigrationsScreen,
30
31
  ModulesScreen: () => ModulesScreen,
31
32
  RoutesScreen: () => RoutesScreen,
32
33
  SubscriberScreen: () => SubscriberScreen,
@@ -344,37 +345,101 @@ function RoutesScreen({ client }) {
344
345
  ] });
345
346
  }
346
347
 
347
- // src/screens/TokensScreen.tsx
348
+ // src/screens/MigrationsScreen.tsx
348
349
  var import_react_admin7 = require("@fonderie/react-admin");
349
- var import_react = require("react");
350
350
  var import_jsx_runtime7 = require("react/jsx-runtime");
351
+ function why(m, everApplied) {
352
+ if (m.pending.length === 0) return null;
353
+ if (m.blockedBy) return `Apply "${m.blockedBy}" first \u2014 it runs before this one and is behind.`;
354
+ if (everApplied && m.pending.some((p) => p.impact === "destructive"))
355
+ return "Contains a migration that deletes data. No down-migration brings it back \u2014 apply this one through CI or `npm run migrate`.";
356
+ return null;
357
+ }
358
+ function MigrationsScreen({ client }) {
359
+ const { report, isLoading, error, refresh, apply } = (0, import_react_admin7.useAdminMigrations)(client);
360
+ const behind = report?.modules.filter((m) => m.pending.length > 0) ?? [];
361
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.container, children: [
362
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { style: styles.title, children: "Migrations" }),
363
+ error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.error, role: "alert", children: error.status === 403 ? "Applying migrations needs a token with the write scope." : error.explanation }) : null,
364
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", style: styles.button, onClick: () => void refresh(), disabled: isLoading, children: isLoading ? "Checking\u2026" : "Refresh" }),
366
+ " ",
367
+ report && !report.everApplied ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.advice, children: "This database has never been migrated \u2014 treating it as a first install, so nothing is held back." }) : null
368
+ ] }),
369
+ report && behind.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.ok, children: "Every module is up to date." }) : null,
370
+ behind.map((m) => {
371
+ const blocked = why(m, report?.everApplied ?? false);
372
+ const files = m.pending.map((p) => p.file);
373
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { style: { marginBottom: 24 }, children: [
374
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h2", { style: styles.subtitle, children: [
375
+ m.name,
376
+ " \u2014 ",
377
+ m.pending.length,
378
+ " pending"
379
+ ] }),
380
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { children: m.pending.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: p.file }),
382
+ " ",
383
+ p.impact === "destructive" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.bad, children: "destructive" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.advice, children: "additive" }),
384
+ p.destructive.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { children: p.destructive.map((s) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: s.slice(0, 120) }) }, s)) }) : null
385
+ ] }, p.file)) }),
386
+ blocked ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.advice, children: blocked }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
387
+ "button",
388
+ {
389
+ type: "button",
390
+ style: styles.button,
391
+ disabled: isLoading || !m.appliable,
392
+ onClick: () => {
393
+ if (window.confirm(
394
+ `Apply ${m.pending.length} migration(s) to "${m.name}"? This changes the database schema.`
395
+ ))
396
+ void apply(m.name, files).catch(() => {
397
+ });
398
+ },
399
+ children: [
400
+ "Apply ",
401
+ m.pending.length,
402
+ " migration",
403
+ m.pending.length === 1 ? "" : "s"
404
+ ]
405
+ }
406
+ )
407
+ ] }, m.name);
408
+ })
409
+ ] });
410
+ }
411
+
412
+ // src/screens/TokensScreen.tsx
413
+ var import_react_admin8 = require("@fonderie/react-admin");
414
+ var import_react = require("react");
415
+ var import_jsx_runtime8 = require("react/jsx-runtime");
351
416
  var ALL = ["read", "write", "secrets"];
352
417
  function TokensScreen({ client }) {
353
- const { report, isLoading, error, issue, revoke } = (0, import_react_admin7.useAdminTokens)(client);
418
+ const { report, isLoading, error, issue, revoke } = (0, import_react_admin8.useAdminTokens)(client);
354
419
  const [name, setName] = (0, import_react.useState)("");
355
420
  const [scopes, setScopes] = (0, import_react.useState)(["read"]);
356
421
  const [days, setDays] = (0, import_react.useState)("");
357
422
  const [minted, setMinted] = (0, import_react.useState)(null);
358
423
  const toggle = (s) => setScopes((prev) => prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]);
359
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.container, children: [
360
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { style: styles.title, children: "Access" }),
361
- error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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,
362
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Root token" }),
363
- report ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { children: [
364
- report.admin.ok ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.ok, children: "strong" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.bad, children: "weak" }),
365
- report.admin.problems.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.bad, children: p.message }, p.message))
424
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.container, children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: styles.title, children: "Access" }),
426
+ error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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,
427
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { style: styles.subtitle, children: "Root token" }),
428
+ report ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { children: [
429
+ report.admin.ok ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.ok, children: "strong" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.bad, children: "weak" }),
430
+ report.admin.problems.map((p) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.bad, children: p.message }, p.message))
366
431
  ] }) : null,
367
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Issued tokens" }),
368
- report?.issued === null ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
369
- minted ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { style: styles.ok, children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { style: styles.subtitle, children: "Issued tokens" }),
433
+ report?.issued === null ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
434
+ minted ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { style: styles.ok, children: [
370
435
  "Copy this now \u2014 it is never shown again.",
371
436
  " ",
372
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.mono, children: minted.token }),
437
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.mono, children: minted.token }),
373
438
  " (",
374
439
  minted.name,
375
440
  ")"
376
441
  ] }) : null,
377
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
442
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
378
443
  "form",
379
444
  {
380
445
  style: styles.toolbar,
@@ -393,7 +458,7 @@ function TokensScreen({ client }) {
393
458
  });
394
459
  },
395
460
  children: [
396
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
461
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
397
462
  "input",
398
463
  {
399
464
  value: name,
@@ -403,12 +468,12 @@ function TokensScreen({ client }) {
403
468
  "aria-label": "Name"
404
469
  }
405
470
  ),
406
- ALL.map((s) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { ...styles.badge, cursor: "pointer" }, children: [
407
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
471
+ ALL.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { ...styles.badge, cursor: "pointer" }, children: [
472
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
408
473
  " ",
409
474
  s
410
475
  ] }, s)),
411
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
476
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
412
477
  "input",
413
478
  {
414
479
  value: days,
@@ -418,7 +483,7 @@ function TokensScreen({ client }) {
418
483
  "aria-label": "Days"
419
484
  }
420
485
  ),
421
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
486
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
422
487
  "button",
423
488
  {
424
489
  type: "submit",
@@ -430,29 +495,29 @@ function TokensScreen({ client }) {
430
495
  ]
431
496
  }
432
497
  ),
433
- report?.issued && report.issued.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { style: styles.table, children: [
434
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
435
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Name" }),
436
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Scopes" }),
437
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Created" }),
438
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Expires" }),
439
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Last used" }),
440
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th })
498
+ report?.issued && report.issued.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { style: styles.table, children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th, children: "Name" }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th, children: "Scopes" }),
502
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th, children: "Created" }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th, children: "Expires" }),
504
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th, children: "Last used" }),
505
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: styles.th })
441
506
  ] }) }),
442
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: report.issued.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
443
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: styles.td, children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: report.issued.map((t) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: styles.td, children: [
444
509
  t.name,
445
- t.revokedAt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.badge, children: " revoked" }) : null
510
+ t.revokedAt ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.badge, children: " revoked" }) : null
446
511
  ] }),
447
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.scopes.join(", ") }),
448
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: { ...styles.td, ...styles.muted }, children: [
512
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.scopes.join(", ") }),
513
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: { ...styles.td, ...styles.muted }, children: [
449
514
  new Date(t.createdAt).toLocaleDateString(),
450
515
  " \xB7 ",
451
516
  t.createdBy
452
517
  ] }),
453
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never" }),
454
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never" }),
455
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: styles.td, children: t.revokedAt ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
518
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never" }),
519
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never" }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: t.revokedAt ? null : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
456
521
  "button",
457
522
  {
458
523
  type: "button",
@@ -468,44 +533,50 @@ function TokensScreen({ client }) {
468
533
  }
469
534
  ) })
470
535
  ] }, t.id)) })
471
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "None issued." })
536
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.muted, children: "None issued." })
472
537
  ] }),
473
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
474
- report && report.legacy.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { style: styles.row, children: [
475
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.mono, children: l.module }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
539
+ report && report.legacy.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { style: styles.row, children: [
540
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.mono, children: l.module }),
476
541
  " ",
477
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
542
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
478
543
  ] }, l.module)) })
479
544
  ] });
480
545
  }
481
546
 
482
547
  // src/screens/UsersScreen.tsx
483
- var import_react_admin8 = require("@fonderie/react-admin");
548
+ var import_react_admin9 = require("@fonderie/react-admin");
484
549
  var import_react2 = require("react");
485
- var import_jsx_runtime8 = require("react/jsx-runtime");
486
- function UsersScreen({ client }) {
550
+ var import_jsx_runtime9 = require("react/jsx-runtime");
551
+ function UsersScreen({ client, pageSize = 50 }) {
487
552
  const [input, setInput] = (0, import_react2.useState)("");
488
- const [email, setEmail] = (0, import_react2.useState)(void 0);
489
- const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_react_admin8.useAdminUser)(
553
+ const [selected, setSelected] = (0, import_react2.useState)(null);
554
+ const list = (0, import_react_admin9.useAdminUsers)(client, { limit: pageSize });
555
+ const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_react_admin9.useAdminUser)(
490
556
  client,
491
- email ? { email } : {}
557
+ selected ?? {}
492
558
  );
493
559
  const userId = user?.id ?? null;
494
- const sessions = (0, import_react_admin8.useAdminUserSessions)(client, userId);
495
- const history = (0, import_react_admin8.useAdminLoginHistory)(client, userId, { limit: 20 });
496
- const yesNo = (v) => v ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.ok, children: "yes" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: "no" });
497
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.container, children: [
498
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: styles.title, children: "Users" }),
499
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
560
+ const sessions = (0, import_react_admin9.useAdminUserSessions)(client, userId);
561
+ const history = (0, import_react_admin9.useAdminLoginHistory)(client, userId, { limit: 20 });
562
+ const yesNo = (v) => v ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.ok, children: "yes" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: "no" });
563
+ const clear = () => {
564
+ setSelected(null);
565
+ setInput("");
566
+ };
567
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.container, children: [
568
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { style: styles.title, children: "Users" }),
569
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
500
570
  "form",
501
571
  {
502
572
  style: styles.toolbar,
503
573
  onSubmit: (e) => {
504
574
  e.preventDefault();
505
- setEmail(input.trim() || void 0);
575
+ const email = input.trim();
576
+ setSelected(email ? { email } : null);
506
577
  },
507
578
  children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
579
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
509
580
  "input",
510
581
  {
511
582
  type: "email",
@@ -516,53 +587,102 @@ function UsersScreen({ client }) {
516
587
  "aria-label": "Email"
517
588
  }
518
589
  ),
519
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" })
590
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" }),
591
+ selected ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", style: styles.button, onClick: clear, children: "\u2190 All users" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ style: styles.button,
596
+ onClick: () => void list.refresh(),
597
+ disabled: list.isLoading,
598
+ children: "Refresh"
599
+ }
600
+ )
520
601
  ]
521
602
  }
522
603
  ),
523
- error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
524
- user ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
525
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("h2", { style: styles.subtitle, children: [
604
+ error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
605
+ !selected ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
606
+ list.error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
607
+ list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.muted, children: "No users yet." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { style: styles.table, children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
609
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Email" }),
610
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Name" }),
611
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Created" }),
612
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Status" })
613
+ ] }) }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: list.users.map((u) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
616
+ "button",
617
+ {
618
+ type: "button",
619
+ style: { ...styles.navItem, padding: 0 },
620
+ onClick: () => setSelected({ id: u.id }),
621
+ children: u.email
622
+ }
623
+ ) }),
624
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: "\u2014" }) }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
626
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
627
+ u.suspended ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.badge, children: "suspended" }) : null,
628
+ u.deletedAt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.badge, children: "deleted" }) : null,
629
+ !u.suspended && !u.deletedAt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: "active" }) : null
630
+ ] })
631
+ ] }, u.id)) })
632
+ ] }),
633
+ list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
634
+ list.hasMore && !list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
635
+ "button",
636
+ {
637
+ type: "button",
638
+ style: { ...styles.button, marginTop: 8 },
639
+ onClick: () => void list.loadMore(),
640
+ children: "Load more"
641
+ }
642
+ ) : null
643
+ ] }) : null,
644
+ user ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
645
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h2", { style: styles.subtitle, children: [
526
646
  user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
527
647
  " ",
528
- user.suspended ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.badge, children: "suspended" }) : null,
529
- user.deletedAt ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.badge, children: "deleted" }) : null
648
+ user.suspended ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.badge, children: "suspended" }) : null,
649
+ user.deletedAt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.badge, children: "deleted" }) : null
530
650
  ] }),
531
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("table", { style: styles.table, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tbody", { children: [
532
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
533
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "id" }),
534
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
651
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("table", { style: styles.table, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tbody", { children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "id" }),
654
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
535
655
  ] }),
536
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
537
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "email" }),
538
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: styles.td, children: [
656
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
657
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "email" }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
539
659
  user.email,
540
660
  " \xB7 verified ",
541
661
  yesNo(user.isEmailVerified)
542
662
  ] })
543
663
  ] }),
544
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
545
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "MFA" }),
546
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
664
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "MFA" }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
547
667
  ] }),
548
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
549
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "provider" }),
550
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: styles.td, children: [
668
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "provider" }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
551
671
  user.provider || "password",
552
672
  user.hasPassword ? "" : " \xB7 no password set"
553
673
  ] })
554
674
  ] }),
555
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
556
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "last login" }),
557
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: "never" }) })
675
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "last login" }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: "never" }) })
558
678
  ] }),
559
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
560
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: "created" }),
561
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
679
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: "created" }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
562
682
  ] })
563
683
  ] }) }),
564
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
565
- user.suspended ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
684
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
685
+ user.suspended ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
566
686
  "button",
567
687
  {
568
688
  type: "button",
@@ -571,7 +691,7 @@ function UsersScreen({ client }) {
571
691
  disabled: isLoading,
572
692
  children: "Unsuspend"
573
693
  }
574
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
694
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
575
695
  "button",
576
696
  {
577
697
  type: "button",
@@ -581,7 +701,7 @@ function UsersScreen({ client }) {
581
701
  children: "Suspend"
582
702
  }
583
703
  ),
584
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
704
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
585
705
  "button",
586
706
  {
587
707
  type: "button",
@@ -592,26 +712,26 @@ function UsersScreen({ client }) {
592
712
  }
593
713
  )
594
714
  ] }),
595
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { style: styles.subtitle, children: "Live sessions" }),
596
- sessions.sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { style: styles.row, children: [
597
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
715
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Live sessions" }),
716
+ sessions.sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { style: styles.row, children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
598
718
  " ",
599
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: s.userAgent ?? "" }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: s.userAgent ?? "" }),
600
720
  " ",
601
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: styles.muted, children: [
721
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: styles.muted, children: [
602
722
  "since ",
603
723
  new Date(s.createdAt).toLocaleString()
604
724
  ] })
605
725
  ] }, s.id)) }),
606
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
607
- history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { style: styles.row, children: [
608
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: e.outcome === "success" ? styles.ok : styles.bad, children: e.outcome }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
727
+ history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { style: styles.row, children: [
728
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: e.outcome === "success" ? styles.ok : styles.bad, children: e.outcome }),
609
729
  " ",
610
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: e.method }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: e.method }),
611
731
  " ",
612
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
732
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
613
733
  ] }, e.id)) }),
614
- history.hasMore && !history.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
734
+ history.hasMore && !history.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
615
735
  "button",
616
736
  {
617
737
  type: "button",
@@ -625,15 +745,15 @@ function UsersScreen({ client }) {
625
745
  }
626
746
 
627
747
  // src/screens/CatalogScreen.tsx
628
- var import_react_admin9 = require("@fonderie/react-admin");
629
- var import_jsx_runtime9 = require("react/jsx-runtime");
748
+ var import_react_admin10 = require("@fonderie/react-admin");
749
+ var import_jsx_runtime10 = require("react/jsx-runtime");
630
750
  function CatalogScreen({ client }) {
631
- const { catalog, isLoading, error, refresh, deletePlan } = (0, import_react_admin9.useAdminCatalog)(client);
751
+ const { catalog, isLoading, error, refresh, deletePlan } = (0, import_react_admin10.useAdminCatalog)(client);
632
752
  const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
633
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.container, children: [
634
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.toolbar, children: [
635
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { style: { ...styles.title, marginBottom: 0 }, children: "Catalog" }),
636
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
753
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.container, children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.toolbar, children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: { ...styles.title, marginBottom: 0 }, children: "Catalog" }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
637
757
  "button",
638
758
  {
639
759
  type: "button",
@@ -644,37 +764,37 @@ function CatalogScreen({ client }) {
644
764
  }
645
765
  )
646
766
  ] }),
647
- error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
648
- isLoading && !catalog ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
649
- catalog ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Configured (code)" }),
651
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { style: { ...styles.mono, background: "#f9fafb", padding: 12, overflowX: "auto" }, children: JSON.stringify(catalog.configured, null, 2) }),
652
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Stored (database)" }),
653
- catalog.stored.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.muted, children: "No stored plans." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { style: styles.table, children: [
654
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
655
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Plan" }),
656
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Tier" }),
657
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Seats" }),
658
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Monthly" }),
659
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Yearly" }),
660
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Trial" }),
661
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th })
767
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
768
+ isLoading && !catalog ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
769
+ catalog ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Configured (code)" }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pre", { style: { ...styles.mono, background: "#f9fafb", padding: 12, overflowX: "auto" }, children: JSON.stringify(catalog.configured, null, 2) }),
772
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Stored (database)" }),
773
+ catalog.stored.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "No stored plans." }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("table", { style: styles.table, children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
775
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Plan" }),
776
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Tier" }),
777
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Seats" }),
778
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Monthly" }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Yearly" }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Trial" }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th })
662
782
  ] }) }),
663
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
664
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
665
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: p.name }),
666
- p.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.muted, children: p.description }) : null
783
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
785
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: p.name }),
786
+ p.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.muted, children: p.description }) : null
667
787
  ] }),
668
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.tier }),
669
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.seats ?? "\u221E" }),
670
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
788
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: p.tier }),
789
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: p.seats ?? "\u221E" }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
671
791
  money(p.pricing?.monthly),
672
792
  " ",
673
793
  p.pricing?.currency ?? ""
674
794
  ] }),
675
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: money(p.pricing?.yearly) }),
676
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
677
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
795
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: money(p.pricing?.yearly) }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
678
798
  "button",
679
799
  {
680
800
  type: "button",
@@ -694,20 +814,27 @@ function CatalogScreen({ client }) {
694
814
  }
695
815
 
696
816
  // src/screens/SubscriberScreen.tsx
697
- var import_react_admin10 = require("@fonderie/react-admin");
817
+ var import_react_admin11 = require("@fonderie/react-admin");
698
818
  var import_react3 = require("react");
699
- var import_jsx_runtime10 = require("react/jsx-runtime");
700
- function SubscriberScreen({ client }) {
819
+ var import_jsx_runtime11 = require("react/jsx-runtime");
820
+ function SubscriberScreen({ client, pageSize = 50 }) {
701
821
  const [type, setType] = (0, import_react3.useState)("user");
702
822
  const [idInput, setIdInput] = (0, import_react3.useState)("");
703
823
  const [subscriber, setSubscriber] = (0, import_react3.useState)(null);
704
- const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_react_admin10.useAdminSubscriber)(client, subscriber, { limit: 20 });
824
+ const list = (0, import_react_admin11.useAdminSubscribers)(client, { limit: pageSize });
825
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_react_admin11.useAdminSubscriber)(client, subscriber, { limit: 20 });
705
826
  const [amount, setAmount] = (0, import_react3.useState)("");
706
827
  const [note, setNote] = (0, import_react3.useState)("");
707
828
  const [granted, setGranted] = (0, import_react3.useState)(null);
708
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.container, children: [
709
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.title, children: "Subscriber" }),
710
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
829
+ const open = (t, id) => {
830
+ setGranted(null);
831
+ setType(t);
832
+ setIdInput(id);
833
+ setSubscriber({ type: t, id });
834
+ };
835
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.container, children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { style: styles.title, children: "Subscriber" }),
837
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
711
838
  "form",
712
839
  {
713
840
  style: styles.toolbar,
@@ -717,7 +844,7 @@ function SubscriberScreen({ client }) {
717
844
  setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
718
845
  },
719
846
  children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
847
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
721
848
  "select",
722
849
  {
723
850
  value: type,
@@ -725,12 +852,12 @@ function SubscriberScreen({ client }) {
725
852
  style: styles.button,
726
853
  "aria-label": "Type",
727
854
  children: [
728
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "user", children: "user" }),
729
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "workspace", children: "workspace" })
855
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "user", children: "user" }),
856
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "workspace", children: "workspace" })
730
857
  ]
731
858
  }
732
859
  ),
733
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
860
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
734
861
  "input",
735
862
  {
736
863
  value: idInput,
@@ -740,32 +867,100 @@ function SubscriberScreen({ client }) {
740
867
  "aria-label": "Subscriber id"
741
868
  }
742
869
  ),
743
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" })
870
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" }),
871
+ subscriber ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
872
+ "button",
873
+ {
874
+ type: "button",
875
+ style: styles.button,
876
+ onClick: () => {
877
+ setSubscriber(null);
878
+ setIdInput("");
879
+ setGranted(null);
880
+ },
881
+ children: "\u2190 All subscribers"
882
+ }
883
+ ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
884
+ "button",
885
+ {
886
+ type: "button",
887
+ style: styles.button,
888
+ onClick: () => void list.refresh(),
889
+ disabled: list.isLoading,
890
+ children: "Refresh"
891
+ }
892
+ )
744
893
  ]
745
894
  }
746
895
  ),
747
- error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
748
- subscriber && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
749
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Subscription" }),
750
- subscription ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("table", { style: styles.table, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tbody", { children: [
751
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
752
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "plan" }),
753
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
754
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: subscription.plan }),
896
+ error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
897
+ !subscriber ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
898
+ list.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
899
+ list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "No subscribers yet." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("table", { style: styles.table, children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
901
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Subscriber" }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Plan" }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Status" }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Renews" })
905
+ ] }) }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
907
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
908
+ "button",
909
+ {
910
+ type: "button",
911
+ style: { ...styles.navItem, padding: 0, ...styles.mono },
912
+ onClick: () => open(s.subscriberType, s.subscriberId),
913
+ children: [
914
+ s.subscriberType,
915
+ "/",
916
+ s.subscriberId
917
+ ]
918
+ }
919
+ ) }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("td", { style: styles.td, children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: s.plan }),
922
+ " \xB7 ",
923
+ s.interval
924
+ ] }),
925
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("td", { style: styles.td, children: [
926
+ s.status,
927
+ s.cancelAtPeriodEnd ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: styles.badge, children: "cancels" }) : null
928
+ ] }),
929
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: styles.muted, children: "\u2014" }) })
930
+ ] }, s.id)) })
931
+ ] }),
932
+ list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
933
+ list.hasMore && !list.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
934
+ "button",
935
+ {
936
+ type: "button",
937
+ style: { ...styles.button, marginTop: 8 },
938
+ onClick: () => void list.loadMore(),
939
+ children: "Load more"
940
+ }
941
+ ) : null
942
+ ] }) : null,
943
+ subscriber && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
944
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { style: styles.subtitle, children: "Subscription" }),
945
+ subscription ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("table", { style: styles.table, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tbody", { children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: "plan" }),
948
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("td", { style: styles.td, children: [
949
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: subscription.plan }),
755
950
  " \xB7 ",
756
951
  subscription.interval
757
952
  ] })
758
953
  ] }),
759
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
760
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "status" }),
761
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
954
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
955
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: "status" }),
956
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("td", { style: styles.td, children: [
762
957
  subscription.status,
763
958
  subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
764
959
  ] })
765
960
  ] }),
766
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
767
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "period" }),
768
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
961
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
962
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: "period" }),
963
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("td", { style: styles.td, children: [
769
964
  subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
770
965
  " ",
771
966
  "\u2192",
@@ -773,19 +968,19 @@ function SubscriberScreen({ client }) {
773
968
  subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
774
969
  ] })
775
970
  ] }),
776
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
777
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "provider" }),
778
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
971
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
972
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: "provider" }),
973
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
779
974
  ] })
780
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "No subscription." }),
781
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Wallet" }),
782
- wallet ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
783
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { children: [
784
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: wallet.balance }),
975
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "No subscription." }),
976
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { style: styles.subtitle, children: "Wallet" }),
977
+ wallet ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
978
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: wallet.balance }),
785
980
  " ",
786
981
  wallet.currency,
787
982
  " ",
788
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { style: styles.muted, children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { style: styles.muted, children: [
789
984
  "(granted ",
790
985
  wallet.granted ?? "0",
791
986
  " \xB7 purchased ",
@@ -794,7 +989,7 @@ function SubscriberScreen({ client }) {
794
989
  wallet.precision
795
990
  ] })
796
991
  ] }),
797
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
992
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
798
993
  "form",
799
994
  {
800
995
  style: styles.toolbar,
@@ -813,7 +1008,7 @@ function SubscriberScreen({ client }) {
813
1008
  });
814
1009
  },
815
1010
  children: [
816
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1011
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
817
1012
  "input",
818
1013
  {
819
1014
  value: amount,
@@ -823,7 +1018,7 @@ function SubscriberScreen({ client }) {
823
1018
  "aria-label": "Amount"
824
1019
  }
825
1020
  ),
826
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1021
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
827
1022
  "input",
828
1023
  {
829
1024
  value: note,
@@ -833,27 +1028,27 @@ function SubscriberScreen({ client }) {
833
1028
  "aria-label": "Reason"
834
1029
  }
835
1030
  ),
836
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", style: styles.button, disabled: !/^\d+$/.test(amount.trim()), children: "Grant" }),
837
- granted ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.ok, children: granted }) : null
1031
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", style: styles.button, disabled: !/^\d+$/.test(amount.trim()), children: "Grant" }),
1032
+ granted ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: styles.ok, children: granted }) : null
838
1033
  ]
839
1034
  }
840
1035
  ),
841
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Ledger" }),
842
- ledger.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "Nothing moved yet." }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("table", { style: styles.table, children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
844
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "When" }),
845
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Kind" }),
846
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Amount" }),
847
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Note" })
1036
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { style: styles.subtitle, children: "Ledger" }),
1037
+ ledger.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "Nothing moved yet." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("table", { style: styles.table, children: [
1038
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
1039
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "When" }),
1040
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Kind" }),
1041
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Amount" }),
1042
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Note" })
848
1043
  ] }) }),
849
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tbody", { children: ledger.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
850
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t.createdAt).toLocaleString() }),
851
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: t.type }),
852
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.amount }),
853
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.description ?? "" })
1044
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", { children: ledger.map((t) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t.createdAt).toLocaleString() }),
1046
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: styles.td, children: t.type }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.amount }),
1048
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.description ?? "" })
854
1049
  ] }, t.id)) })
855
1050
  ] }),
856
- hasMoreLedger ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1051
+ hasMoreLedger ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
857
1052
  "button",
858
1053
  {
859
1054
  type: "button",
@@ -862,25 +1057,25 @@ function SubscriberScreen({ client }) {
862
1057
  children: "Load more"
863
1058
  }
864
1059
  ) : null
865
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
1060
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
866
1061
  ] }) : null
867
1062
  ] });
868
1063
  }
869
1064
 
870
1065
  // src/screens/AuditScreen.tsx
871
- var import_react_admin11 = require("@fonderie/react-admin");
1066
+ var import_react_admin12 = require("@fonderie/react-admin");
872
1067
  var import_react4 = require("react");
873
- var import_jsx_runtime11 = require("react/jsx-runtime");
1068
+ var import_jsx_runtime12 = require("react/jsx-runtime");
874
1069
  function AuditScreen({ client, pageSize = 50 }) {
875
1070
  const [draft, setDraft] = (0, import_react4.useState)({ workspaceId: "", type: "", actorId: "" });
876
1071
  const [filter, setFilter] = (0, import_react4.useState)(
877
1072
  {}
878
1073
  );
879
- const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_react_admin11.useAdminAudit)(client, {
1074
+ const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_react_admin12.useAdminAudit)(client, {
880
1075
  ...filter,
881
1076
  limit: pageSize
882
1077
  });
883
- const field = (key, placeholder) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1078
+ const field = (key, placeholder) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
884
1079
  "input",
885
1080
  {
886
1081
  value: draft[key],
@@ -890,9 +1085,9 @@ function AuditScreen({ client, pageSize = 50 }) {
890
1085
  "aria-label": placeholder
891
1086
  }
892
1087
  );
893
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.container, children: [
894
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { style: styles.title, children: "Audit" }),
895
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1088
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.container, children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { style: styles.title, children: "Audit" }),
1090
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
896
1091
  "form",
897
1092
  {
898
1093
  style: styles.toolbar,
@@ -908,8 +1103,8 @@ function AuditScreen({ client, pageSize = 50 }) {
908
1103
  field("workspaceId", "workspace id (all if empty)"),
909
1104
  field("type", "event type"),
910
1105
  field("actorId", "actor id"),
911
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Filter" }),
912
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1106
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Filter" }),
1107
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
913
1108
  "button",
914
1109
  {
915
1110
  type: "button",
@@ -922,25 +1117,25 @@ function AuditScreen({ client, pageSize = 50 }) {
922
1117
  ]
923
1118
  }
924
1119
  ),
925
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
926
- events.length === 0 && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "Nothing recorded for this filter." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("table", { style: styles.table, children: [
927
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
928
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "When" }),
929
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Type" }),
930
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Workspace" }),
931
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Actor" }),
932
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Request" })
1120
+ error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
1121
+ events.length === 0 && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.muted, children: "Nothing recorded for this filter." }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { style: styles.table, children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { children: [
1123
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { style: styles.th, children: "When" }),
1124
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { style: styles.th, children: "Type" }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { style: styles.th, children: "Workspace" }),
1126
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { style: styles.th, children: "Actor" }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { style: styles.th, children: "Request" })
933
1128
  ] }) }),
934
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", { children: events.map((e) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
935
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
936
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
937
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
938
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
939
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
1129
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: events.map((e) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { children: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
1131
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
1132
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
1134
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
940
1135
  ] }, e.id)) })
941
1136
  ] }),
942
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
943
- hasMore && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1137
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
1138
+ hasMore && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
944
1139
  "button",
945
1140
  {
946
1141
  type: "button",
@@ -953,7 +1148,7 @@ function AuditScreen({ client, pageSize = 50 }) {
953
1148
  }
954
1149
 
955
1150
  // src/screens/AdminShell.tsx
956
- var import_jsx_runtime12 = require("react/jsx-runtime");
1151
+ var import_jsx_runtime13 = require("react/jsx-runtime");
957
1152
  var NAV = [
958
1153
  { group: "Today", items: [{ page: "attention", label: "Attention" }] },
959
1154
  {
@@ -980,7 +1175,8 @@ var NAV = [
980
1175
  items: [
981
1176
  { page: "audit", label: "Audit", needs: "audit" },
982
1177
  { page: "log", label: "Admin log" },
983
- { page: "tokens", label: "Access" }
1178
+ { page: "tokens", label: "Access" },
1179
+ { page: "migrations", label: "Migrations" }
984
1180
  ]
985
1181
  }
986
1182
  ];
@@ -1012,42 +1208,45 @@ function AdminShell({
1012
1208
  let body;
1013
1209
  switch (current) {
1014
1210
  case "attention":
1015
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttentionScreen, { client });
1211
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AttentionScreen, { client });
1016
1212
  break;
1017
1213
  case "modules":
1018
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModulesScreen, { client });
1214
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModulesScreen, { client });
1019
1215
  break;
1020
1216
  case "doctor":
1021
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DoctorScreen, { client });
1217
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DoctorScreen, { client });
1022
1218
  break;
1023
1219
  case "config":
1024
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ConfigScreen, { client });
1220
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ConfigScreen, { client });
1025
1221
  break;
1026
1222
  case "routes":
1027
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RoutesScreen, { client });
1223
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RoutesScreen, { client });
1028
1224
  break;
1029
1225
  case "tokens":
1030
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TokensScreen, { client });
1226
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TokensScreen, { client });
1227
+ break;
1228
+ case "migrations":
1229
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MigrationsScreen, { client });
1031
1230
  break;
1032
1231
  case "log":
1033
- body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AdminLogScreen, { client });
1232
+ body = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AdminLogScreen, { client });
1034
1233
  break;
1035
1234
  case "catalog":
1036
- body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
1235
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
1037
1236
  break;
1038
1237
  case "subscriber":
1039
- body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
1238
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
1040
1239
  break;
1041
1240
  case "audit":
1042
- body = auditClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AuditScreen, { client: auditClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass an AuditAdminClient to see the audit trail here." });
1241
+ body = auditClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AuditScreen, { client: auditClient }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass an AuditAdminClient to see the audit trail here." });
1043
1242
  break;
1044
1243
  case "users":
1045
- body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
1244
+ body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
1046
1245
  break;
1047
1246
  case "settings":
1048
- body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1049
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
1050
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1247
+ body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1248
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
1249
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1051
1250
  import_react_config_admin_screens.ConfigEditorScreen,
1052
1251
  {
1053
1252
  client: configClient,
@@ -1057,7 +1256,7 @@ function AdminShell({
1057
1256
  onSaved: () => setEditing(null)
1058
1257
  }
1059
1258
  )
1060
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1259
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1061
1260
  import_react_config_admin_screens.ConfigListScreen,
1062
1261
  {
1063
1262
  client: configClient,
@@ -1068,9 +1267,9 @@ function AdminShell({
1068
1267
  );
1069
1268
  break;
1070
1269
  case "templates":
1071
- body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1072
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
1073
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1270
+ body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1271
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1074
1273
  import_react_courier_admin_screens.TemplateEditorScreen,
1075
1274
  {
1076
1275
  client: courierClient,
@@ -1078,7 +1277,7 @@ function AdminShell({
1078
1277
  onSaved: () => setEditing(null)
1079
1278
  }
1080
1279
  )
1081
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1280
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1082
1281
  import_react_courier_admin_screens.TemplateListScreen,
1083
1282
  {
1084
1283
  client: courierClient,
@@ -1087,13 +1286,13 @@ function AdminShell({
1087
1286
  );
1088
1287
  break;
1089
1288
  }
1090
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.shell, children: [
1091
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
1289
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: styles.shell, children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
1092
1291
  const items = g.items.filter((i) => !i.needs || has[i.needs]);
1093
1292
  if (items.length === 0) return null;
1094
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1095
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.navGroup, children: g.group }),
1096
- items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1293
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1294
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: styles.navGroup, children: g.group }),
1295
+ items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1097
1296
  "button",
1098
1297
  {
1099
1298
  type: "button",
@@ -1109,7 +1308,7 @@ function AdminShell({
1109
1308
  ))
1110
1309
  ] }, g.group);
1111
1310
  }) }),
1112
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("main", { style: styles.main, children: body })
1311
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("main", { style: styles.main, children: body })
1113
1312
  ] });
1114
1313
  }
1115
1314
  // Annotate the CommonJS export names for ESM import in node:
@@ -1121,6 +1320,7 @@ function AdminShell({
1121
1320
  CatalogScreen,
1122
1321
  ConfigScreen,
1123
1322
  DoctorScreen,
1323
+ MigrationsScreen,
1124
1324
  ModulesScreen,
1125
1325
  RoutesScreen,
1126
1326
  SubscriberScreen,