@fonderie/react-admin-screens 0.1.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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/screens/AdminShell.tsx
2
2
  import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/react-config-admin-screens";
3
3
  import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/react-courier-admin-screens";
4
- import { useState } from "react";
4
+ import { useState as useState3 } from "react";
5
5
 
6
6
  // src/styles.ts
7
7
  var styles = {
@@ -330,8 +330,396 @@ function TokensScreen({ client }) {
330
330
  ] });
331
331
  }
332
332
 
333
- // src/screens/AdminShell.tsx
333
+ // src/screens/UsersScreen.tsx
334
+ import { useAdminLoginHistory, useAdminUser, useAdminUserSessions } from "@fonderie/react-admin";
335
+ import { useState } from "react";
334
336
  import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
337
+ function UsersScreen({ client }) {
338
+ const [input, setInput] = useState("");
339
+ const [email, setEmail] = useState(void 0);
340
+ const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
341
+ client,
342
+ email ? { email } : {}
343
+ );
344
+ const userId = user?.id ?? null;
345
+ const sessions = useAdminUserSessions(client, userId);
346
+ const history = useAdminLoginHistory(client, userId, { limit: 20 });
347
+ const yesNo = (v) => v ? /* @__PURE__ */ jsx8("span", { style: styles.ok, children: "yes" }) : /* @__PURE__ */ jsx8("span", { style: styles.muted, children: "no" });
348
+ return /* @__PURE__ */ jsxs8("div", { style: styles.container, children: [
349
+ /* @__PURE__ */ jsx8("h1", { style: styles.title, children: "Users" }),
350
+ /* @__PURE__ */ jsxs8(
351
+ "form",
352
+ {
353
+ style: styles.toolbar,
354
+ onSubmit: (e) => {
355
+ e.preventDefault();
356
+ setEmail(input.trim() || void 0);
357
+ },
358
+ children: [
359
+ /* @__PURE__ */ jsx8(
360
+ "input",
361
+ {
362
+ type: "email",
363
+ value: input,
364
+ onChange: (e) => setInput(e.target.value),
365
+ placeholder: "email address",
366
+ style: { ...styles.button, cursor: "text", minWidth: 280 },
367
+ "aria-label": "Email"
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsx8("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" })
371
+ ]
372
+ }
373
+ ),
374
+ error ? /* @__PURE__ */ jsx8("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
375
+ user ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
376
+ /* @__PURE__ */ jsxs8("h2", { style: styles.subtitle, children: [
377
+ user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
378
+ " ",
379
+ user.suspended ? /* @__PURE__ */ jsx8("span", { style: styles.badge, children: "suspended" }) : null,
380
+ user.deletedAt ? /* @__PURE__ */ jsx8("span", { style: styles.badge, children: "deleted" }) : null
381
+ ] }),
382
+ /* @__PURE__ */ jsx8("table", { style: styles.table, children: /* @__PURE__ */ jsxs8("tbody", { children: [
383
+ /* @__PURE__ */ jsxs8("tr", { children: [
384
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "id" }),
385
+ /* @__PURE__ */ jsx8("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
386
+ ] }),
387
+ /* @__PURE__ */ jsxs8("tr", { children: [
388
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "email" }),
389
+ /* @__PURE__ */ jsxs8("td", { style: styles.td, children: [
390
+ user.email,
391
+ " \xB7 verified ",
392
+ yesNo(user.isEmailVerified)
393
+ ] })
394
+ ] }),
395
+ /* @__PURE__ */ jsxs8("tr", { children: [
396
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "MFA" }),
397
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
398
+ ] }),
399
+ /* @__PURE__ */ jsxs8("tr", { children: [
400
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "provider" }),
401
+ /* @__PURE__ */ jsxs8("td", { style: styles.td, children: [
402
+ user.provider || "password",
403
+ user.hasPassword ? "" : " \xB7 no password set"
404
+ ] })
405
+ ] }),
406
+ /* @__PURE__ */ jsxs8("tr", { children: [
407
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "last login" }),
408
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx8("span", { style: styles.muted, children: "never" }) })
409
+ ] }),
410
+ /* @__PURE__ */ jsxs8("tr", { children: [
411
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: "created" }),
412
+ /* @__PURE__ */ jsx8("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
413
+ ] })
414
+ ] }) }),
415
+ /* @__PURE__ */ jsxs8("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
416
+ user.suspended ? /* @__PURE__ */ jsx8(
417
+ "button",
418
+ {
419
+ type: "button",
420
+ style: styles.button,
421
+ onClick: () => void unsuspend(),
422
+ disabled: isLoading,
423
+ children: "Unsuspend"
424
+ }
425
+ ) : /* @__PURE__ */ jsx8(
426
+ "button",
427
+ {
428
+ type: "button",
429
+ style: styles.button,
430
+ onClick: () => void suspend(),
431
+ disabled: isLoading,
432
+ children: "Suspend"
433
+ }
434
+ ),
435
+ /* @__PURE__ */ jsx8(
436
+ "button",
437
+ {
438
+ type: "button",
439
+ style: styles.button,
440
+ onClick: () => void revokeSessions().then(() => sessions.refresh()),
441
+ disabled: isLoading,
442
+ children: "Sign out everywhere"
443
+ }
444
+ )
445
+ ] }),
446
+ /* @__PURE__ */ jsx8("h2", { style: styles.subtitle, children: "Live sessions" }),
447
+ sessions.sessions.length === 0 ? /* @__PURE__ */ jsx8("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx8("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs8("li", { style: styles.row, children: [
448
+ /* @__PURE__ */ jsx8("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
449
+ " ",
450
+ /* @__PURE__ */ jsx8("span", { style: styles.muted, children: s.userAgent ?? "" }),
451
+ " ",
452
+ /* @__PURE__ */ jsxs8("span", { style: styles.muted, children: [
453
+ "since ",
454
+ new Date(s.createdAt).toLocaleString()
455
+ ] })
456
+ ] }, s.id)) }),
457
+ /* @__PURE__ */ jsx8("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
458
+ history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx8("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx8("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs8("li", { style: styles.row, children: [
459
+ /* @__PURE__ */ jsx8("span", { style: e.outcome === "success" ? styles.ok : styles.bad, children: e.outcome }),
460
+ " ",
461
+ /* @__PURE__ */ jsx8("span", { style: styles.muted, children: e.method }),
462
+ " ",
463
+ /* @__PURE__ */ jsx8("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
464
+ ] }, e.id)) }),
465
+ history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx8(
466
+ "button",
467
+ {
468
+ type: "button",
469
+ style: { ...styles.button, marginTop: 8 },
470
+ onClick: () => void history.loadMore(),
471
+ children: "Load more"
472
+ }
473
+ ) : null
474
+ ] }) : null
475
+ ] });
476
+ }
477
+
478
+ // src/screens/CatalogScreen.tsx
479
+ import { useAdminCatalog } from "@fonderie/react-admin";
480
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
481
+ function CatalogScreen({ client }) {
482
+ const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
483
+ const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
484
+ return /* @__PURE__ */ jsxs9("div", { style: styles.container, children: [
485
+ /* @__PURE__ */ jsxs9("div", { style: styles.toolbar, children: [
486
+ /* @__PURE__ */ jsx9("h1", { style: { ...styles.title, marginBottom: 0 }, children: "Catalog" }),
487
+ /* @__PURE__ */ jsx9(
488
+ "button",
489
+ {
490
+ type: "button",
491
+ style: styles.button,
492
+ onClick: () => void refresh(),
493
+ disabled: isLoading,
494
+ children: "Refresh"
495
+ }
496
+ )
497
+ ] }),
498
+ error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
499
+ isLoading && !catalog ? /* @__PURE__ */ jsx9("p", { style: styles.status, children: "Loading\u2026" }) : null,
500
+ catalog ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
501
+ /* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Configured (code)" }),
502
+ /* @__PURE__ */ jsx9("pre", { style: { ...styles.mono, background: "#f9fafb", padding: 12, overflowX: "auto" }, children: JSON.stringify(catalog.configured, null, 2) }),
503
+ /* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Stored (database)" }),
504
+ catalog.stored.length === 0 ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "No stored plans." }) : /* @__PURE__ */ jsxs9("table", { style: styles.table, children: [
505
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
506
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Plan" }),
507
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Tier" }),
508
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Seats" }),
509
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Monthly" }),
510
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Yearly" }),
511
+ /* @__PURE__ */ jsx9("th", { style: styles.th, children: "Trial" }),
512
+ /* @__PURE__ */ jsx9("th", { style: styles.th })
513
+ ] }) }),
514
+ /* @__PURE__ */ jsx9("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs9("tr", { children: [
515
+ /* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
516
+ /* @__PURE__ */ jsx9("strong", { children: p.name }),
517
+ p.description ? /* @__PURE__ */ jsx9("div", { style: styles.muted, children: p.description }) : null
518
+ ] }),
519
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: p.tier }),
520
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: p.seats ?? "\u221E" }),
521
+ /* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
522
+ money(p.pricing?.monthly),
523
+ " ",
524
+ p.pricing?.currency ?? ""
525
+ ] }),
526
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: money(p.pricing?.yearly) }),
527
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
528
+ /* @__PURE__ */ jsx9("td", { style: styles.td, children: /* @__PURE__ */ jsx9(
529
+ "button",
530
+ {
531
+ type: "button",
532
+ style: styles.button,
533
+ onClick: () => {
534
+ if (window.confirm(`Delete stored plan "${p.name}"?`))
535
+ void deletePlan(p.id).catch(() => {
536
+ });
537
+ },
538
+ children: "Delete"
539
+ }
540
+ ) })
541
+ ] }, p.id)) })
542
+ ] })
543
+ ] }) : null
544
+ ] });
545
+ }
546
+
547
+ // src/screens/SubscriberScreen.tsx
548
+ import { useAdminSubscriber } from "@fonderie/react-admin";
549
+ import { useState as useState2 } from "react";
550
+ import { Fragment as Fragment7, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
551
+ function SubscriberScreen({ client }) {
552
+ const [type, setType] = useState2("user");
553
+ const [idInput, setIdInput] = useState2("");
554
+ const [subscriber, setSubscriber] = useState2(null);
555
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
556
+ const [amount, setAmount] = useState2("");
557
+ const [note, setNote] = useState2("");
558
+ const [granted, setGranted] = useState2(null);
559
+ return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
560
+ /* @__PURE__ */ jsx10("h1", { style: styles.title, children: "Subscriber" }),
561
+ /* @__PURE__ */ jsxs10(
562
+ "form",
563
+ {
564
+ style: styles.toolbar,
565
+ onSubmit: (e) => {
566
+ e.preventDefault();
567
+ setGranted(null);
568
+ setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
569
+ },
570
+ children: [
571
+ /* @__PURE__ */ jsxs10(
572
+ "select",
573
+ {
574
+ value: type,
575
+ onChange: (e) => setType(e.target.value),
576
+ style: styles.button,
577
+ "aria-label": "Type",
578
+ children: [
579
+ /* @__PURE__ */ jsx10("option", { value: "user", children: "user" }),
580
+ /* @__PURE__ */ jsx10("option", { value: "workspace", children: "workspace" })
581
+ ]
582
+ }
583
+ ),
584
+ /* @__PURE__ */ jsx10(
585
+ "input",
586
+ {
587
+ value: idInput,
588
+ onChange: (e) => setIdInput(e.target.value),
589
+ placeholder: "subscriber id",
590
+ style: { ...styles.button, cursor: "text", minWidth: 280 },
591
+ "aria-label": "Subscriber id"
592
+ }
593
+ ),
594
+ /* @__PURE__ */ jsx10("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" })
595
+ ]
596
+ }
597
+ ),
598
+ error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
599
+ subscriber && !isLoading ? /* @__PURE__ */ jsxs10(Fragment7, { children: [
600
+ /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Subscription" }),
601
+ subscription ? /* @__PURE__ */ jsx10("table", { style: styles.table, children: /* @__PURE__ */ jsxs10("tbody", { children: [
602
+ /* @__PURE__ */ jsxs10("tr", { children: [
603
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: "plan" }),
604
+ /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
605
+ /* @__PURE__ */ jsx10("strong", { children: subscription.plan }),
606
+ " \xB7 ",
607
+ subscription.interval
608
+ ] })
609
+ ] }),
610
+ /* @__PURE__ */ jsxs10("tr", { children: [
611
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: "status" }),
612
+ /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
613
+ subscription.status,
614
+ subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
615
+ ] })
616
+ ] }),
617
+ /* @__PURE__ */ jsxs10("tr", { children: [
618
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: "period" }),
619
+ /* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
620
+ subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
621
+ " ",
622
+ "\u2192",
623
+ " ",
624
+ subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
625
+ ] })
626
+ ] }),
627
+ /* @__PURE__ */ jsxs10("tr", { children: [
628
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: "provider" }),
629
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
630
+ ] })
631
+ ] }) }) : /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "No subscription." }),
632
+ /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Wallet" }),
633
+ wallet ? /* @__PURE__ */ jsxs10(Fragment7, { children: [
634
+ /* @__PURE__ */ jsxs10("p", { children: [
635
+ /* @__PURE__ */ jsx10("strong", { children: wallet.balance }),
636
+ " ",
637
+ wallet.currency,
638
+ " ",
639
+ /* @__PURE__ */ jsxs10("span", { style: styles.muted, children: [
640
+ "(granted ",
641
+ wallet.granted ?? "0",
642
+ " \xB7 purchased ",
643
+ wallet.purchased ?? "0",
644
+ ") \xB7 minor units, precision ",
645
+ wallet.precision
646
+ ] })
647
+ ] }),
648
+ /* @__PURE__ */ jsxs10(
649
+ "form",
650
+ {
651
+ style: styles.toolbar,
652
+ onSubmit: (e) => {
653
+ e.preventDefault();
654
+ const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
655
+ void grant({
656
+ amount: amount.trim(),
657
+ ...note ? { description: note } : {},
658
+ idempotencyKey: key
659
+ }).then(() => {
660
+ setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
661
+ setAmount("");
662
+ setNote("");
663
+ }).catch(() => {
664
+ });
665
+ },
666
+ children: [
667
+ /* @__PURE__ */ jsx10(
668
+ "input",
669
+ {
670
+ value: amount,
671
+ onChange: (e) => setAmount(e.target.value),
672
+ placeholder: "amount (minor units)",
673
+ style: { ...styles.button, cursor: "text" },
674
+ "aria-label": "Amount"
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsx10(
678
+ "input",
679
+ {
680
+ value: note,
681
+ onChange: (e) => setNote(e.target.value),
682
+ placeholder: "reason (optional)",
683
+ style: { ...styles.button, cursor: "text", minWidth: 220 },
684
+ "aria-label": "Reason"
685
+ }
686
+ ),
687
+ /* @__PURE__ */ jsx10("button", { type: "submit", style: styles.button, disabled: !/^\d+$/.test(amount.trim()), children: "Grant" }),
688
+ granted ? /* @__PURE__ */ jsx10("span", { style: styles.ok, children: granted }) : null
689
+ ]
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Ledger" }),
693
+ ledger.length === 0 ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "Nothing moved yet." }) : /* @__PURE__ */ jsxs10("table", { style: styles.table, children: [
694
+ /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
695
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "When" }),
696
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Kind" }),
697
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Amount" }),
698
+ /* @__PURE__ */ jsx10("th", { style: styles.th, children: "Note" })
699
+ ] }) }),
700
+ /* @__PURE__ */ jsx10("tbody", { children: ledger.map((t) => /* @__PURE__ */ jsxs10("tr", { children: [
701
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t.createdAt).toLocaleString() }),
702
+ /* @__PURE__ */ jsx10("td", { style: styles.td, children: t.type }),
703
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.mono }, children: t.amount }),
704
+ /* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: t.description ?? "" })
705
+ ] }, t.id)) })
706
+ ] }),
707
+ hasMoreLedger ? /* @__PURE__ */ jsx10(
708
+ "button",
709
+ {
710
+ type: "button",
711
+ style: { ...styles.button, marginTop: 8 },
712
+ onClick: () => void loadMoreLedger(),
713
+ children: "Load more"
714
+ }
715
+ ) : null
716
+ ] }) : /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
717
+ ] }) : null
718
+ ] });
719
+ }
720
+
721
+ // src/screens/AdminShell.tsx
722
+ import { Fragment as Fragment8, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
335
723
  var NAV = [
336
724
  { group: "Today", items: [{ page: "attention", label: "Attention" }] },
337
725
  {
@@ -343,6 +731,14 @@ var NAV = [
343
731
  { page: "routes", label: "Routes" }
344
732
  ]
345
733
  },
734
+ { group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
735
+ {
736
+ group: "Money",
737
+ items: [
738
+ { page: "catalog", label: "Catalog", needs: "billing" },
739
+ { page: "subscriber", label: "Subscriber", needs: "billing" }
740
+ ]
741
+ },
346
742
  { group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
347
743
  { group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
348
744
  {
@@ -357,45 +753,61 @@ function AdminShell({
357
753
  client,
358
754
  configClient,
359
755
  courierClient,
756
+ authClient,
757
+ billingClient,
360
758
  environment,
361
759
  page,
362
760
  onNavigate
363
761
  }) {
364
- const [own, setOwn] = useState("attention");
762
+ const [own, setOwn] = useState3("attention");
365
763
  const current = page ?? own;
366
764
  const go = (p) => {
367
765
  if (onNavigate) onNavigate(p);
368
766
  if (page === void 0) setOwn(p);
369
767
  };
370
- const [editing, setEditing] = useState(null);
371
- const has = { config: Boolean(configClient), courier: Boolean(courierClient) };
768
+ const [editing, setEditing] = useState3(null);
769
+ const has = {
770
+ config: Boolean(configClient),
771
+ courier: Boolean(courierClient),
772
+ auth: Boolean(authClient),
773
+ billing: Boolean(billingClient)
774
+ };
372
775
  let body;
373
776
  switch (current) {
374
777
  case "attention":
375
- body = /* @__PURE__ */ jsx8(AttentionScreen, { client });
778
+ body = /* @__PURE__ */ jsx11(AttentionScreen, { client });
376
779
  break;
377
780
  case "modules":
378
- body = /* @__PURE__ */ jsx8(ModulesScreen, { client });
781
+ body = /* @__PURE__ */ jsx11(ModulesScreen, { client });
379
782
  break;
380
783
  case "doctor":
381
- body = /* @__PURE__ */ jsx8(DoctorScreen, { client });
784
+ body = /* @__PURE__ */ jsx11(DoctorScreen, { client });
382
785
  break;
383
786
  case "config":
384
- body = /* @__PURE__ */ jsx8(ConfigScreen, { client });
787
+ body = /* @__PURE__ */ jsx11(ConfigScreen, { client });
385
788
  break;
386
789
  case "routes":
387
- body = /* @__PURE__ */ jsx8(RoutesScreen, { client });
790
+ body = /* @__PURE__ */ jsx11(RoutesScreen, { client });
388
791
  break;
389
792
  case "tokens":
390
- body = /* @__PURE__ */ jsx8(TokensScreen, { client });
793
+ body = /* @__PURE__ */ jsx11(TokensScreen, { client });
391
794
  break;
392
795
  case "log":
393
- body = /* @__PURE__ */ jsx8(AdminLogScreen, { client });
796
+ body = /* @__PURE__ */ jsx11(AdminLogScreen, { client });
797
+ break;
798
+ case "catalog":
799
+ body = billingClient ? /* @__PURE__ */ jsx11(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
800
+ break;
801
+ case "subscriber":
802
+ body = billingClient ? /* @__PURE__ */ jsx11(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
803
+ break;
804
+ case "users":
805
+ body = authClient ? /* @__PURE__ */ jsx11(UsersScreen, { client: authClient }) : /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
394
806
  break;
395
807
  case "settings":
396
- body = !configClient ? /* @__PURE__ */ jsx8("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
397
- /* @__PURE__ */ jsx8("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
398
- /* @__PURE__ */ jsx8(
808
+ body = !configClient ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs11(Fragment8, { children: [
809
+ /* @__PURE__ */ jsx11("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
810
+ /* @__PURE__ */ jsx11(
399
811
  ConfigEditorScreen,
400
812
  {
401
813
  client: configClient,
@@ -405,7 +817,7 @@ function AdminShell({
405
817
  onSaved: () => setEditing(null)
406
818
  }
407
819
  )
408
- ] }) : /* @__PURE__ */ jsx8(
820
+ ] }) : /* @__PURE__ */ jsx11(
409
821
  ConfigListScreen,
410
822
  {
411
823
  client: configClient,
@@ -416,9 +828,9 @@ function AdminShell({
416
828
  );
417
829
  break;
418
830
  case "templates":
419
- body = !courierClient ? /* @__PURE__ */ jsx8("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
420
- /* @__PURE__ */ jsx8("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
421
- /* @__PURE__ */ jsx8(
831
+ body = !courierClient ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs11(Fragment8, { children: [
832
+ /* @__PURE__ */ jsx11("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
833
+ /* @__PURE__ */ jsx11(
422
834
  TemplateEditorScreen,
423
835
  {
424
836
  client: courierClient,
@@ -426,7 +838,7 @@ function AdminShell({
426
838
  onSaved: () => setEditing(null)
427
839
  }
428
840
  )
429
- ] }) : /* @__PURE__ */ jsx8(
841
+ ] }) : /* @__PURE__ */ jsx11(
430
842
  TemplateListScreen,
431
843
  {
432
844
  client: courierClient,
@@ -435,13 +847,13 @@ function AdminShell({
435
847
  );
436
848
  break;
437
849
  }
438
- return /* @__PURE__ */ jsxs8("div", { style: styles.shell, children: [
439
- /* @__PURE__ */ jsx8("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
850
+ return /* @__PURE__ */ jsxs11("div", { style: styles.shell, children: [
851
+ /* @__PURE__ */ jsx11("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
440
852
  const items = g.items.filter((i) => !i.needs || has[i.needs]);
441
853
  if (items.length === 0) return null;
442
- return /* @__PURE__ */ jsxs8("div", { children: [
443
- /* @__PURE__ */ jsx8("div", { style: styles.navGroup, children: g.group }),
444
- items.map((i) => /* @__PURE__ */ jsx8(
854
+ return /* @__PURE__ */ jsxs11("div", { children: [
855
+ /* @__PURE__ */ jsx11("div", { style: styles.navGroup, children: g.group }),
856
+ items.map((i) => /* @__PURE__ */ jsx11(
445
857
  "button",
446
858
  {
447
859
  type: "button",
@@ -457,17 +869,20 @@ function AdminShell({
457
869
  ))
458
870
  ] }, g.group);
459
871
  }) }),
460
- /* @__PURE__ */ jsx8("main", { style: styles.main, children: body })
872
+ /* @__PURE__ */ jsx11("main", { style: styles.main, children: body })
461
873
  ] });
462
874
  }
463
875
  export {
464
876
  AdminLogScreen,
465
877
  AdminShell,
466
878
  AttentionScreen,
879
+ CatalogScreen,
467
880
  ConfigScreen,
468
881
  DoctorScreen,
469
882
  ModulesScreen,
470
883
  RoutesScreen,
471
- TokensScreen
884
+ SubscriberScreen,
885
+ TokensScreen,
886
+ UsersScreen
472
887
  };
473
888
  //# sourceMappingURL=index.js.map