@fonderie/react-admin-screens 0.2.0 → 0.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/README.md CHANGED
@@ -11,12 +11,13 @@ Status: **experimental** (0.x).
11
11
  | Today | Attention | what needs me today — empty is green |
12
12
  | System | Modules · Configuration · Doctor · Routes | what did I deploy · is it configured · is it working · what is exposed |
13
13
  | People | Users | why can't this person log in — lookup, sessions, sign-ins, suspend, sign out everywhere (`authClient`) |
14
+ | Money | Catalog · Subscriber | what am I selling (configured vs stored) · what is this subscriber on, their wallet, ledger, a manual grant (`billingClient`) |
14
15
  | Settings | Config & secrets | the `@fonderie/config` admin screens, as a sub-page |
15
16
  | Messaging | Templates | the `@fonderie/courier` admin screens, as a sub-page |
16
17
  | Activity | Admin log · Access | who did what · who can be here |
17
18
 
18
19
  ```ts
19
- import { AdminClient, AuthAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
20
+ import { AdminClient, AuthAdminClient, BillingAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
20
21
  import { AdminShell } from '@fonderie/react-admin-screens';
21
22
 
22
23
  const base = { baseUrl: 'https://api.example.com', adminToken, actor: 'louis' };
@@ -25,10 +26,11 @@ const client = new AdminClient(base);
25
26
  const configClient = new ConfigAdminClient({ ...base, prefix: '/_admin' });
26
27
  const courierClient = new CourierAdminClient({ ...base, prefix: '/_admin' });
27
28
  const authClient = new AuthAdminClient(base); // @fonderie/auth ≥ 7.8
29
+ const billingClient = new BillingAdminClient(base); // @fonderie/billing ≥ 9.10
28
30
  ```
29
31
 
30
32
  ```tsx
31
- <AdminShell client={client} configClient={configClient} courierClient={courierClient} authClient={authClient} />
33
+ <AdminShell client={client} configClient={configClient} courierClient={courierClient} authClient={authClient} billingClient={billingClient} />
32
34
  ```
33
35
 
34
36
  Pass `page` and `onNavigate` to own the URL (one route per page); omit both
package/dist/index.cjs CHANGED
@@ -23,10 +23,12 @@ __export(index_exports, {
23
23
  AdminLogScreen: () => AdminLogScreen,
24
24
  AdminShell: () => AdminShell,
25
25
  AttentionScreen: () => AttentionScreen,
26
+ CatalogScreen: () => CatalogScreen,
26
27
  ConfigScreen: () => ConfigScreen,
27
28
  DoctorScreen: () => DoctorScreen,
28
29
  ModulesScreen: () => ModulesScreen,
29
30
  RoutesScreen: () => RoutesScreen,
31
+ SubscriberScreen: () => SubscriberScreen,
30
32
  TokensScreen: () => TokensScreen,
31
33
  UsersScreen: () => UsersScreen
32
34
  });
@@ -35,7 +37,7 @@ module.exports = __toCommonJS(index_exports);
35
37
  // src/screens/AdminShell.tsx
36
38
  var import_react_config_admin_screens = require("@fonderie/react-config-admin-screens");
37
39
  var import_react_courier_admin_screens = require("@fonderie/react-courier-admin-screens");
38
- var import_react2 = require("react");
40
+ var import_react3 = require("react");
39
41
 
40
42
  // src/styles.ts
41
43
  var styles = {
@@ -509,8 +511,251 @@ function UsersScreen({ client }) {
509
511
  ] });
510
512
  }
511
513
 
512
- // src/screens/AdminShell.tsx
514
+ // src/screens/CatalogScreen.tsx
515
+ var import_react_admin9 = require("@fonderie/react-admin");
513
516
  var import_jsx_runtime9 = require("react/jsx-runtime");
517
+ function CatalogScreen({ client }) {
518
+ const { catalog, isLoading, error, refresh, deletePlan } = (0, import_react_admin9.useAdminCatalog)(client);
519
+ const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
520
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.container, children: [
521
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.toolbar, children: [
522
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { style: { ...styles.title, marginBottom: 0 }, children: "Catalog" }),
523
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
524
+ "button",
525
+ {
526
+ type: "button",
527
+ style: styles.button,
528
+ onClick: () => void refresh(),
529
+ disabled: isLoading,
530
+ children: "Refresh"
531
+ }
532
+ )
533
+ ] }),
534
+ error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
535
+ isLoading && !catalog ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
536
+ catalog ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
537
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Configured (code)" }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { style: { ...styles.mono, background: "#f9fafb", padding: 12, overflowX: "auto" }, children: JSON.stringify(catalog.configured, null, 2) }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.subtitle, children: "Stored (database)" }),
540
+ 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: [
541
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
542
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Plan" }),
543
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Tier" }),
544
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Seats" }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Monthly" }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Yearly" }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th, children: "Trial" }),
548
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: styles.th })
549
+ ] }) }),
550
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { children: [
551
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
552
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: p.name }),
553
+ p.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.muted, children: p.description }) : null
554
+ ] }),
555
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.tier }),
556
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.seats ?? "\u221E" }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: styles.td, children: [
558
+ money(p.pricing?.monthly),
559
+ " ",
560
+ p.pricing?.currency ?? ""
561
+ ] }),
562
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: money(p.pricing?.yearly) }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
564
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: styles.td, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
565
+ "button",
566
+ {
567
+ type: "button",
568
+ style: styles.button,
569
+ onClick: () => {
570
+ if (window.confirm(`Delete stored plan "${p.name}"?`))
571
+ void deletePlan(p.id).catch(() => {
572
+ });
573
+ },
574
+ children: "Delete"
575
+ }
576
+ ) })
577
+ ] }, p.id)) })
578
+ ] })
579
+ ] }) : null
580
+ ] });
581
+ }
582
+
583
+ // src/screens/SubscriberScreen.tsx
584
+ var import_react_admin10 = require("@fonderie/react-admin");
585
+ var import_react2 = require("react");
586
+ var import_jsx_runtime10 = require("react/jsx-runtime");
587
+ function SubscriberScreen({ client }) {
588
+ const [type, setType] = (0, import_react2.useState)("user");
589
+ const [idInput, setIdInput] = (0, import_react2.useState)("");
590
+ const [subscriber, setSubscriber] = (0, import_react2.useState)(null);
591
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_react_admin10.useAdminSubscriber)(client, subscriber, { limit: 20 });
592
+ const [amount, setAmount] = (0, import_react2.useState)("");
593
+ const [note, setNote] = (0, import_react2.useState)("");
594
+ const [granted, setGranted] = (0, import_react2.useState)(null);
595
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.container, children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.title, children: "Subscriber" }),
597
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
598
+ "form",
599
+ {
600
+ style: styles.toolbar,
601
+ onSubmit: (e) => {
602
+ e.preventDefault();
603
+ setGranted(null);
604
+ setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
605
+ },
606
+ children: [
607
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
608
+ "select",
609
+ {
610
+ value: type,
611
+ onChange: (e) => setType(e.target.value),
612
+ style: styles.button,
613
+ "aria-label": "Type",
614
+ children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "user", children: "user" }),
616
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "workspace", children: "workspace" })
617
+ ]
618
+ }
619
+ ),
620
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
621
+ "input",
622
+ {
623
+ value: idInput,
624
+ onChange: (e) => setIdInput(e.target.value),
625
+ placeholder: "subscriber id",
626
+ style: { ...styles.button, cursor: "text", minWidth: 280 },
627
+ "aria-label": "Subscriber id"
628
+ }
629
+ ),
630
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" })
631
+ ]
632
+ }
633
+ ),
634
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
635
+ subscriber && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Subscription" }),
637
+ subscription ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("table", { style: styles.table, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tbody", { children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "plan" }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
641
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: subscription.plan }),
642
+ " \xB7 ",
643
+ subscription.interval
644
+ ] })
645
+ ] }),
646
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "status" }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
649
+ subscription.status,
650
+ subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
651
+ ] })
652
+ ] }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "period" }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("td", { style: styles.td, children: [
656
+ subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
657
+ " ",
658
+ "\u2192",
659
+ " ",
660
+ subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
661
+ ] })
662
+ ] }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: "provider" }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
666
+ ] })
667
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "No subscription." }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Wallet" }),
669
+ wallet ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
670
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: wallet.balance }),
672
+ " ",
673
+ wallet.currency,
674
+ " ",
675
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { style: styles.muted, children: [
676
+ "(granted ",
677
+ wallet.granted ?? "0",
678
+ " \xB7 purchased ",
679
+ wallet.purchased ?? "0",
680
+ ") \xB7 minor units, precision ",
681
+ wallet.precision
682
+ ] })
683
+ ] }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
685
+ "form",
686
+ {
687
+ style: styles.toolbar,
688
+ onSubmit: (e) => {
689
+ e.preventDefault();
690
+ const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
691
+ void grant({
692
+ amount: amount.trim(),
693
+ ...note ? { description: note } : {},
694
+ idempotencyKey: key
695
+ }).then(() => {
696
+ setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
697
+ setAmount("");
698
+ setNote("");
699
+ }).catch(() => {
700
+ });
701
+ },
702
+ children: [
703
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
704
+ "input",
705
+ {
706
+ value: amount,
707
+ onChange: (e) => setAmount(e.target.value),
708
+ placeholder: "amount (minor units)",
709
+ style: { ...styles.button, cursor: "text" },
710
+ "aria-label": "Amount"
711
+ }
712
+ ),
713
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
714
+ "input",
715
+ {
716
+ value: note,
717
+ onChange: (e) => setNote(e.target.value),
718
+ placeholder: "reason (optional)",
719
+ style: { ...styles.button, cursor: "text", minWidth: 220 },
720
+ "aria-label": "Reason"
721
+ }
722
+ ),
723
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", style: styles.button, disabled: !/^\d+$/.test(amount.trim()), children: "Grant" }),
724
+ granted ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.ok, children: granted }) : null
725
+ ]
726
+ }
727
+ ),
728
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: styles.subtitle, children: "Ledger" }),
729
+ 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: [
730
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "When" }),
732
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Kind" }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Amount" }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { style: styles.th, children: "Note" })
735
+ ] }) }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tbody", { children: ledger.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t.createdAt).toLocaleString() }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: styles.td, children: t.type }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.amount }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.description ?? "" })
741
+ ] }, t.id)) })
742
+ ] }),
743
+ hasMoreLedger ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
744
+ "button",
745
+ {
746
+ type: "button",
747
+ style: { ...styles.button, marginTop: 8 },
748
+ onClick: () => void loadMoreLedger(),
749
+ children: "Load more"
750
+ }
751
+ ) : null
752
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
753
+ ] }) : null
754
+ ] });
755
+ }
756
+
757
+ // src/screens/AdminShell.tsx
758
+ var import_jsx_runtime11 = require("react/jsx-runtime");
514
759
  var NAV = [
515
760
  { group: "Today", items: [{ page: "attention", label: "Attention" }] },
516
761
  {
@@ -523,6 +768,13 @@ var NAV = [
523
768
  ]
524
769
  },
525
770
  { group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
771
+ {
772
+ group: "Money",
773
+ items: [
774
+ { page: "catalog", label: "Catalog", needs: "billing" },
775
+ { page: "subscriber", label: "Subscriber", needs: "billing" }
776
+ ]
777
+ },
526
778
  { group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
527
779
  { group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
528
780
  {
@@ -538,52 +790,60 @@ function AdminShell({
538
790
  configClient,
539
791
  courierClient,
540
792
  authClient,
793
+ billingClient,
541
794
  environment,
542
795
  page,
543
796
  onNavigate
544
797
  }) {
545
- const [own, setOwn] = (0, import_react2.useState)("attention");
798
+ const [own, setOwn] = (0, import_react3.useState)("attention");
546
799
  const current = page ?? own;
547
800
  const go = (p) => {
548
801
  if (onNavigate) onNavigate(p);
549
802
  if (page === void 0) setOwn(p);
550
803
  };
551
- const [editing, setEditing] = (0, import_react2.useState)(null);
804
+ const [editing, setEditing] = (0, import_react3.useState)(null);
552
805
  const has = {
553
806
  config: Boolean(configClient),
554
807
  courier: Boolean(courierClient),
555
- auth: Boolean(authClient)
808
+ auth: Boolean(authClient),
809
+ billing: Boolean(billingClient)
556
810
  };
557
811
  let body;
558
812
  switch (current) {
559
813
  case "attention":
560
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AttentionScreen, { client });
814
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttentionScreen, { client });
561
815
  break;
562
816
  case "modules":
563
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ModulesScreen, { client });
817
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ModulesScreen, { client });
564
818
  break;
565
819
  case "doctor":
566
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DoctorScreen, { client });
820
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DoctorScreen, { client });
567
821
  break;
568
822
  case "config":
569
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfigScreen, { client });
823
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ConfigScreen, { client });
570
824
  break;
571
825
  case "routes":
572
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RoutesScreen, { client });
826
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RoutesScreen, { client });
573
827
  break;
574
828
  case "tokens":
575
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TokensScreen, { client });
829
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TokensScreen, { client });
576
830
  break;
577
831
  case "log":
578
- body = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AdminLogScreen, { client });
832
+ body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AdminLogScreen, { client });
833
+ break;
834
+ case "catalog":
835
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
836
+ break;
837
+ case "subscriber":
838
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
579
839
  break;
580
840
  case "users":
581
- body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
841
+ body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
582
842
  break;
583
843
  case "settings":
584
- body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
585
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
586
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
844
+ body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
587
847
  import_react_config_admin_screens.ConfigEditorScreen,
588
848
  {
589
849
  client: configClient,
@@ -593,7 +853,7 @@ function AdminShell({
593
853
  onSaved: () => setEditing(null)
594
854
  }
595
855
  )
596
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
856
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
597
857
  import_react_config_admin_screens.ConfigListScreen,
598
858
  {
599
859
  client: configClient,
@@ -604,9 +864,9 @@ function AdminShell({
604
864
  );
605
865
  break;
606
866
  case "templates":
607
- body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
608
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
609
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
867
+ body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
868
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
869
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
610
870
  import_react_courier_admin_screens.TemplateEditorScreen,
611
871
  {
612
872
  client: courierClient,
@@ -614,7 +874,7 @@ function AdminShell({
614
874
  onSaved: () => setEditing(null)
615
875
  }
616
876
  )
617
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
877
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
618
878
  import_react_courier_admin_screens.TemplateListScreen,
619
879
  {
620
880
  client: courierClient,
@@ -623,13 +883,13 @@ function AdminShell({
623
883
  );
624
884
  break;
625
885
  }
626
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.shell, children: [
627
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
886
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.shell, children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
628
888
  const items = g.items.filter((i) => !i.needs || has[i.needs]);
629
889
  if (items.length === 0) return null;
630
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
631
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.navGroup, children: g.group }),
632
- items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
890
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
891
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.navGroup, children: g.group }),
892
+ items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
633
893
  "button",
634
894
  {
635
895
  type: "button",
@@ -645,7 +905,7 @@ function AdminShell({
645
905
  ))
646
906
  ] }, g.group);
647
907
  }) }),
648
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("main", { style: styles.main, children: body })
908
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("main", { style: styles.main, children: body })
649
909
  ] });
650
910
  }
651
911
  // Annotate the CommonJS export names for ESM import in node:
@@ -653,10 +913,12 @@ function AdminShell({
653
913
  AdminLogScreen,
654
914
  AdminShell,
655
915
  AttentionScreen,
916
+ CatalogScreen,
656
917
  ConfigScreen,
657
918
  DoctorScreen,
658
919
  ModulesScreen,
659
920
  RoutesScreen,
921
+ SubscriberScreen,
660
922
  TokensScreen,
661
923
  UsersScreen
662
924
  });