@fonderie/vue-admin-screens 0.6.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,
@@ -38,7 +39,7 @@ module.exports = __toCommonJS(index_exports);
38
39
  // src/screens/AdminShell.ts
39
40
  var import_vue_config_admin_screens = require("@fonderie/vue-config-admin-screens");
40
41
  var import_vue_courier_admin_screens = require("@fonderie/vue-courier-admin-screens");
41
- var import_vue13 = require("vue");
42
+ var import_vue14 = require("vue");
42
43
 
43
44
  // src/styles.ts
44
45
  var styles = {
@@ -416,52 +417,133 @@ var RoutesScreen = (0, import_vue7.defineComponent)({
416
417
  }
417
418
  });
418
419
 
419
- // src/screens/TokensScreen.ts
420
+ // src/screens/MigrationsScreen.ts
420
421
  var import_vue_admin7 = require("@fonderie/vue-admin");
421
422
  var import_vue8 = require("vue");
423
+ function why(m, everApplied) {
424
+ if (m.pending.length === 0) return null;
425
+ if (m.blockedBy) return `Apply "${m.blockedBy}" first \u2014 it runs before this one and is behind.`;
426
+ if (everApplied && m.pending.some((p) => p.impact === "destructive"))
427
+ return "Contains a migration that deletes data. No down-migration brings it back \u2014 apply this one through CI or `npm run migrate`.";
428
+ return null;
429
+ }
430
+ var MigrationsScreen = (0, import_vue8.defineComponent)({
431
+ name: "FonderieMigrationsScreen",
432
+ props: { client: { type: Object, required: true } },
433
+ setup(props) {
434
+ const { report, isLoading, error, refresh, apply } = (0, import_vue_admin7.useAdminMigrations)(props.client);
435
+ const section = (m, everApplied) => {
436
+ const blocked = why(m, everApplied);
437
+ const files = m.pending.map((p) => p.file);
438
+ return (0, import_vue8.h)("section", { style: { marginBottom: "24px" } }, [
439
+ (0, import_vue8.h)("h2", { style: styles.subtitle }, `${m.name} \u2014 ${m.pending.length} pending`),
440
+ (0, import_vue8.h)(
441
+ "ul",
442
+ m.pending.map(
443
+ (p) => (0, import_vue8.h)("li", [
444
+ (0, import_vue8.h)("code", p.file),
445
+ " ",
446
+ (0, import_vue8.h)(
447
+ "span",
448
+ { style: p.impact === "destructive" ? styles.bad : styles.advice },
449
+ p.impact
450
+ ),
451
+ p.destructive.length > 0 ? (0, import_vue8.h)(
452
+ "ul",
453
+ p.destructive.map((s) => (0, import_vue8.h)("li", [(0, import_vue8.h)("code", s.slice(0, 120))]))
454
+ ) : null
455
+ ])
456
+ )
457
+ ),
458
+ blocked ? (0, import_vue8.h)("p", { style: styles.advice }, blocked) : (0, import_vue8.h)(
459
+ "button",
460
+ {
461
+ type: "button",
462
+ style: styles.button,
463
+ disabled: isLoading.value || !m.appliable,
464
+ onClick: () => {
465
+ if (window.confirm(
466
+ `Apply ${m.pending.length} migration(s) to "${m.name}"? This changes the database schema.`
467
+ ))
468
+ void apply(m.name, files).catch(() => {
469
+ });
470
+ }
471
+ },
472
+ `Apply ${m.pending.length} migration${m.pending.length === 1 ? "" : "s"}`
473
+ )
474
+ ]);
475
+ };
476
+ return () => page(
477
+ "Migrations",
478
+ { isLoading, error },
479
+ () => {
480
+ const r = report.value;
481
+ if (!r) return null;
482
+ const behind = r.modules.filter((m) => m.pending.length > 0);
483
+ if (behind.length === 0) return (0, import_vue8.h)("p", { style: styles.ok }, "Every module is up to date.");
484
+ return [
485
+ r.everApplied ? null : (0, import_vue8.h)(
486
+ "p",
487
+ { style: styles.advice },
488
+ "This database has never been migrated \u2014 treating it as a first install, so nothing is held back."
489
+ ),
490
+ ...behind.map((m) => section(m, r.everApplied))
491
+ ].filter(Boolean);
492
+ },
493
+ [refreshButton("Refresh", isLoading.value, () => void refresh())],
494
+ {
495
+ errorText: (e) => e.status === 403 ? "Applying migrations needs a token with the write scope." : e.explanation
496
+ }
497
+ );
498
+ }
499
+ });
500
+
501
+ // src/screens/TokensScreen.ts
502
+ var import_vue_admin8 = require("@fonderie/vue-admin");
503
+ var import_vue9 = require("vue");
422
504
  var ALL = ["read", "write", "secrets"];
423
- var TokensScreen = (0, import_vue8.defineComponent)({
505
+ var TokensScreen = (0, import_vue9.defineComponent)({
424
506
  name: "FonderieTokensScreen",
425
507
  props: { client: { type: Object, required: true } },
426
508
  setup(props) {
427
- const { report, isLoading, error, issue, revoke } = (0, import_vue_admin7.useAdminTokens)(props.client);
428
- const name = (0, import_vue8.ref)("");
429
- const scopes = (0, import_vue8.ref)(["read"]);
430
- const days = (0, import_vue8.ref)("");
431
- const minted = (0, import_vue8.ref)(null);
509
+ const { report, isLoading, error, issue, revoke } = (0, import_vue_admin8.useAdminTokens)(props.client);
510
+ const name = (0, import_vue9.ref)("");
511
+ const scopes = (0, import_vue9.ref)(["read"]);
512
+ const days = (0, import_vue9.ref)("");
513
+ const minted = (0, import_vue9.ref)(null);
432
514
  const toggle = (s) => scopes.value = scopes.value.includes(s) ? scopes.value.filter((x) => x !== s) : [...scopes.value, s];
433
515
  return () => {
434
516
  const r = report.value;
435
- return (0, import_vue8.h)("div", { style: styles.container }, [
436
- (0, import_vue8.h)("h1", { style: styles.title }, "Access"),
437
- error.value ? (0, import_vue8.h)(
517
+ return (0, import_vue9.h)("div", { style: styles.container }, [
518
+ (0, import_vue9.h)("h1", { style: styles.title }, "Access"),
519
+ error.value ? (0, import_vue9.h)(
438
520
  "p",
439
521
  { style: styles.error, role: "alert" },
440
522
  error.value.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.value.explanation
441
523
  ) : null,
442
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Root token"),
443
- r ? (0, import_vue8.h)("p", [
444
- (0, import_vue8.h)(
524
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Root token"),
525
+ r ? (0, import_vue9.h)("p", [
526
+ (0, import_vue9.h)(
445
527
  "span",
446
528
  { style: r.admin.ok ? styles.ok : styles.bad },
447
529
  r.admin.ok ? "strong" : "weak"
448
530
  ),
449
531
  ...r.admin.problems.map(
450
- (p) => (0, import_vue8.h)("div", { key: p.message, style: styles.bad }, p.message)
532
+ (p) => (0, import_vue9.h)("div", { key: p.message, style: styles.bad }, p.message)
451
533
  )
452
534
  ]) : null,
453
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Issued tokens"),
454
- r?.issued === null ? (0, import_vue8.h)(
535
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Issued tokens"),
536
+ r?.issued === null ? (0, import_vue9.h)(
455
537
  "p",
456
538
  { style: styles.muted },
457
539
  "Issuing is off \u2014 give AdminModule a store and run its migrations."
458
540
  ) : [
459
- minted.value ? (0, import_vue8.h)("p", { style: styles.ok }, [
541
+ minted.value ? (0, import_vue9.h)("p", { style: styles.ok }, [
460
542
  "Copy this now \u2014 it is never shown again. ",
461
- (0, import_vue8.h)("span", { style: styles.mono }, minted.value.token),
543
+ (0, import_vue9.h)("span", { style: styles.mono }, minted.value.token),
462
544
  ` (${minted.value.name})`
463
545
  ]) : null,
464
- (0, import_vue8.h)(
546
+ (0, import_vue9.h)(
465
547
  "form",
466
548
  {
467
549
  style: styles.toolbar,
@@ -481,7 +563,7 @@ var TokensScreen = (0, import_vue8.defineComponent)({
481
563
  }
482
564
  },
483
565
  [
484
- (0, import_vue8.h)("input", {
566
+ (0, import_vue9.h)("input", {
485
567
  value: name.value,
486
568
  onInput: (e) => name.value = e.target.value,
487
569
  placeholder: "name",
@@ -489,8 +571,8 @@ var TokensScreen = (0, import_vue8.defineComponent)({
489
571
  "aria-label": "Name"
490
572
  }),
491
573
  ...ALL.map(
492
- (s) => (0, import_vue8.h)("label", { key: s, style: { ...styles.badge, cursor: "pointer" } }, [
493
- (0, import_vue8.h)("input", {
574
+ (s) => (0, import_vue9.h)("label", { key: s, style: { ...styles.badge, cursor: "pointer" } }, [
575
+ (0, import_vue9.h)("input", {
494
576
  type: "checkbox",
495
577
  checked: scopes.value.includes(s),
496
578
  onChange: () => toggle(s)
@@ -498,14 +580,14 @@ var TokensScreen = (0, import_vue8.defineComponent)({
498
580
  ` ${s}`
499
581
  ])
500
582
  ),
501
- (0, import_vue8.h)("input", {
583
+ (0, import_vue9.h)("input", {
502
584
  value: days.value,
503
585
  onInput: (e) => days.value = e.target.value,
504
586
  placeholder: "days (optional)",
505
587
  style: { ...styles.button, cursor: "text", width: "140px" },
506
588
  "aria-label": "Days"
507
589
  }),
508
- (0, import_vue8.h)(
590
+ (0, import_vue9.h)(
509
591
  "button",
510
592
  {
511
593
  type: "submit",
@@ -519,10 +601,10 @@ var TokensScreen = (0, import_vue8.defineComponent)({
519
601
  r?.issued && r.issued.length > 0 ? table(
520
602
  ["Name", "Scopes", "Created", "Expires", "Last used", ""],
521
603
  r.issued.map(
522
- (t) => (0, import_vue8.h)("tr", { key: t.id }, [
604
+ (t) => (0, import_vue9.h)("tr", { key: t.id }, [
523
605
  td([
524
606
  t.name,
525
- t.revokedAt ? (0, import_vue8.h)("span", { style: styles.badge }, " revoked") : ""
607
+ t.revokedAt ? (0, import_vue9.h)("span", { style: styles.badge }, " revoked") : ""
526
608
  ]),
527
609
  td(t.scopes.join(", "), styles.mono),
528
610
  td(
@@ -538,7 +620,7 @@ var TokensScreen = (0, import_vue8.defineComponent)({
538
620
  styles.muted
539
621
  ),
540
622
  td(
541
- t.revokedAt ? "" : (0, import_vue8.h)(
623
+ t.revokedAt ? "" : (0, import_vue9.h)(
542
624
  "button",
543
625
  {
544
626
  type: "button",
@@ -556,21 +638,21 @@ var TokensScreen = (0, import_vue8.defineComponent)({
556
638
  )
557
639
  ])
558
640
  )
559
- ) : (0, import_vue8.h)("p", { style: styles.muted }, "None issued.")
641
+ ) : (0, import_vue9.h)("p", { style: styles.muted }, "None issued.")
560
642
  ],
561
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Legacy per-brick tokens"),
562
- r && r.legacy.length === 0 ? (0, import_vue8.h)(
643
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Legacy per-brick tokens"),
644
+ r && r.legacy.length === 0 ? (0, import_vue9.h)(
563
645
  "p",
564
646
  { style: styles.muted },
565
647
  "None \u2014 every brick's admin surface goes through this token."
566
- ) : (0, import_vue8.h)(
648
+ ) : (0, import_vue9.h)(
567
649
  "ul",
568
650
  { style: styles.list },
569
651
  (r?.legacy ?? []).map(
570
- (l) => (0, import_vue8.h)("li", { key: l.module, style: styles.row }, [
571
- (0, import_vue8.h)("span", { style: styles.mono }, l.module),
652
+ (l) => (0, import_vue9.h)("li", { key: l.module, style: styles.row }, [
653
+ (0, import_vue9.h)("span", { style: styles.mono }, l.module),
572
654
  " ",
573
- (0, import_vue8.h)(
655
+ (0, import_vue9.h)(
574
656
  "span",
575
657
  { style: styles.muted },
576
658
  "still registers its standalone routes with its own token (deprecated)"
@@ -584,29 +666,29 @@ var TokensScreen = (0, import_vue8.defineComponent)({
584
666
  });
585
667
 
586
668
  // src/screens/UsersScreen.ts
587
- var import_vue_admin8 = require("@fonderie/vue-admin");
588
- var import_vue9 = require("vue");
589
- var UsersScreen = (0, import_vue9.defineComponent)({
669
+ var import_vue_admin9 = require("@fonderie/vue-admin");
670
+ var import_vue10 = require("vue");
671
+ var UsersScreen = (0, import_vue10.defineComponent)({
590
672
  name: "FonderieUsersScreen",
591
673
  props: {
592
674
  client: { type: Object, required: true },
593
675
  pageSize: { type: Number, default: 50 }
594
676
  },
595
677
  setup(props) {
596
- const input = (0, import_vue9.ref)("");
597
- const email = (0, import_vue9.ref)("");
598
- const selectedId = (0, import_vue9.ref)("");
599
- const list = (0, import_vue_admin8.useAdminUsers)(props.client, { limit: props.pageSize });
600
- const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin8.useAdminUser)(
678
+ const input = (0, import_vue10.ref)("");
679
+ const email = (0, import_vue10.ref)("");
680
+ const selectedId = (0, import_vue10.ref)("");
681
+ const list = (0, import_vue_admin9.useAdminUsers)(props.client, { limit: props.pageSize });
682
+ const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin9.useAdminUser)(
601
683
  props.client,
602
684
  { email, id: selectedId }
603
685
  );
604
- const userId = (0, import_vue9.computed)(() => user.value?.id ?? null);
605
- const sessions = (0, import_vue_admin8.useAdminUserSessions)(props.client, userId);
606
- const history = (0, import_vue_admin8.useAdminLoginHistory)(props.client, userId, { limit: 20 });
607
- const showList = (0, import_vue9.computed)(() => !email.value && !selectedId.value);
608
- const yesNo = (v) => (0, import_vue9.h)("span", { style: v ? styles.ok : styles.muted }, v ? "yes" : "no");
609
- const row = (k, v) => (0, import_vue9.h)("tr", [td(k), td(v)]);
686
+ const userId = (0, import_vue10.computed)(() => user.value?.id ?? null);
687
+ const sessions = (0, import_vue_admin9.useAdminUserSessions)(props.client, userId);
688
+ const history = (0, import_vue_admin9.useAdminLoginHistory)(props.client, userId, { limit: 20 });
689
+ const showList = (0, import_vue10.computed)(() => !email.value && !selectedId.value);
690
+ const yesNo = (v) => (0, import_vue10.h)("span", { style: v ? styles.ok : styles.muted }, v ? "yes" : "no");
691
+ const row = (k, v) => (0, import_vue10.h)("tr", [td(k), td(v)]);
610
692
  const clear = () => {
611
693
  email.value = "";
612
694
  selectedId.value = "";
@@ -614,9 +696,9 @@ var UsersScreen = (0, import_vue9.defineComponent)({
614
696
  };
615
697
  return () => {
616
698
  const u = user.value;
617
- return (0, import_vue9.h)("div", { style: styles.container }, [
618
- (0, import_vue9.h)("h1", { style: styles.title }, "Users"),
619
- (0, import_vue9.h)(
699
+ return (0, import_vue10.h)("div", { style: styles.container }, [
700
+ (0, import_vue10.h)("h1", { style: styles.title }, "Users"),
701
+ (0, import_vue10.h)(
620
702
  "form",
621
703
  {
622
704
  style: styles.toolbar,
@@ -627,7 +709,7 @@ var UsersScreen = (0, import_vue9.defineComponent)({
627
709
  }
628
710
  },
629
711
  [
630
- (0, import_vue9.h)("input", {
712
+ (0, import_vue10.h)("input", {
631
713
  type: "email",
632
714
  value: input.value,
633
715
  onInput: (e) => input.value = e.target.value,
@@ -635,7 +717,7 @@ var UsersScreen = (0, import_vue9.defineComponent)({
635
717
  style: { ...styles.button, cursor: "text", minWidth: "280px" },
636
718
  "aria-label": "Email"
637
719
  }),
638
- (0, import_vue9.h)(
720
+ (0, import_vue10.h)(
639
721
  "button",
640
722
  { type: "submit", style: styles.button, disabled: isLoading.value },
641
723
  "Look up"
@@ -643,14 +725,14 @@ var UsersScreen = (0, import_vue9.defineComponent)({
643
725
  showList.value ? refreshButton("Refresh", list.isLoading.value, () => void list.refresh()) : refreshButton("\u2190 All users", false, clear)
644
726
  ]
645
727
  ),
646
- showList.value ? (0, import_vue9.h)("div", [
647
- list.error.value ? (0, import_vue9.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
648
- list.users.value.length === 0 && !list.isLoading.value ? (0, import_vue9.h)("p", { style: styles.muted }, "No users yet.") : table(
728
+ showList.value ? (0, import_vue10.h)("div", [
729
+ list.error.value ? (0, import_vue10.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
730
+ list.users.value.length === 0 && !list.isLoading.value ? (0, import_vue10.h)("p", { style: styles.muted }, "No users yet.") : table(
649
731
  ["Email", "Name", "Created", "Status"],
650
732
  list.users.value.map(
651
- (u2) => (0, import_vue9.h)("tr", { key: u2.id }, [
733
+ (u2) => (0, import_vue10.h)("tr", { key: u2.id }, [
652
734
  td(
653
- (0, import_vue9.h)(
735
+ (0, import_vue10.h)(
654
736
  "button",
655
737
  {
656
738
  type: "button",
@@ -661,17 +743,17 @@ var UsersScreen = (0, import_vue9.defineComponent)({
661
743
  )
662
744
  ),
663
745
  td(
664
- `${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue9.h)("span", { style: styles.muted }, "\u2014")
746
+ `${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue10.h)("span", { style: styles.muted }, "\u2014")
665
747
  ),
666
748
  td(new Date(u2.createdAt).toLocaleDateString()),
667
749
  td(
668
- u2.suspended ? (0, import_vue9.h)("span", { style: styles.badge }, "suspended") : u2.deletedAt ? (0, import_vue9.h)("span", { style: styles.badge }, "deleted") : (0, import_vue9.h)("span", { style: styles.muted }, "active")
750
+ u2.suspended ? (0, import_vue10.h)("span", { style: styles.badge }, "suspended") : u2.deletedAt ? (0, import_vue10.h)("span", { style: styles.badge }, "deleted") : (0, import_vue10.h)("span", { style: styles.muted }, "active")
669
751
  )
670
752
  ])
671
753
  )
672
754
  ),
673
- list.isLoading.value ? (0, import_vue9.h)("p", { style: styles.status }, "Loading\u2026") : null,
674
- list.hasMore.value && !list.isLoading.value ? (0, import_vue9.h)(
755
+ list.isLoading.value ? (0, import_vue10.h)("p", { style: styles.status }, "Loading\u2026") : null,
756
+ list.hasMore.value && !list.isLoading.value ? (0, import_vue10.h)(
675
757
  "button",
676
758
  {
677
759
  type: "button",
@@ -681,21 +763,21 @@ var UsersScreen = (0, import_vue9.defineComponent)({
681
763
  "Load more"
682
764
  ) : null
683
765
  ]) : null,
684
- error.value ? (0, import_vue9.h)(
766
+ error.value ? (0, import_vue10.h)(
685
767
  "p",
686
768
  { style: styles.error, role: "alert" },
687
769
  error.value.status === 404 ? "No user with that email." : error.value.explanation
688
770
  ) : null,
689
771
  u ? [
690
- (0, import_vue9.h)("h2", { style: styles.subtitle }, [
772
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, [
691
773
  u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
692
774
  " ",
693
- u.suspended ? (0, import_vue9.h)("span", { style: styles.badge }, "suspended") : null,
694
- u.deletedAt ? (0, import_vue9.h)("span", { style: styles.badge }, "deleted") : null
775
+ u.suspended ? (0, import_vue10.h)("span", { style: styles.badge }, "suspended") : null,
776
+ u.deletedAt ? (0, import_vue10.h)("span", { style: styles.badge }, "deleted") : null
695
777
  ]),
696
- (0, import_vue9.h)("table", { style: styles.table }, [
697
- (0, import_vue9.h)("tbody", [
698
- row("id", (0, import_vue9.h)("span", { style: styles.mono }, u.id)),
778
+ (0, import_vue10.h)("table", { style: styles.table }, [
779
+ (0, import_vue10.h)("tbody", [
780
+ row("id", (0, import_vue10.h)("span", { style: styles.mono }, u.id)),
699
781
  row("email", [u.email, " \xB7 verified ", yesNo(u.isEmailVerified)]),
700
782
  row("MFA", yesNo(u.mfaEnabled)),
701
783
  row(
@@ -704,12 +786,12 @@ var UsersScreen = (0, import_vue9.defineComponent)({
704
786
  ),
705
787
  row(
706
788
  "last login",
707
- u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue9.h)("span", { style: styles.muted }, "never")
789
+ u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue10.h)("span", { style: styles.muted }, "never")
708
790
  ),
709
791
  row("created", new Date(u.createdAt).toLocaleString())
710
792
  ])
711
793
  ]),
712
- (0, import_vue9.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
794
+ (0, import_vue10.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
713
795
  u.suspended ? refreshButton("Unsuspend", isLoading.value, () => void unsuspend()) : refreshButton("Suspend", isLoading.value, () => void suspend()),
714
796
  refreshButton(
715
797
  "Sign out everywhere",
@@ -717,17 +799,17 @@ var UsersScreen = (0, import_vue9.defineComponent)({
717
799
  () => void revokeSessions().then(() => sessions.refresh())
718
800
  )
719
801
  ]),
720
- (0, import_vue9.h)("h2", { style: styles.subtitle }, "Live sessions"),
721
- sessions.sessions.value.length === 0 ? (0, import_vue9.h)("p", { style: styles.muted }, "None.") : (0, import_vue9.h)(
802
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, "Live sessions"),
803
+ sessions.sessions.value.length === 0 ? (0, import_vue10.h)("p", { style: styles.muted }, "None.") : (0, import_vue10.h)(
722
804
  "ul",
723
805
  { style: styles.list },
724
806
  sessions.sessions.value.map(
725
- (s) => (0, import_vue9.h)("li", { key: s.id, style: styles.row }, [
726
- (0, import_vue9.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
807
+ (s) => (0, import_vue10.h)("li", { key: s.id, style: styles.row }, [
808
+ (0, import_vue10.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
727
809
  " ",
728
- (0, import_vue9.h)("span", { style: styles.muted }, s.userAgent ?? ""),
810
+ (0, import_vue10.h)("span", { style: styles.muted }, s.userAgent ?? ""),
729
811
  " ",
730
- (0, import_vue9.h)(
812
+ (0, import_vue10.h)(
731
813
  "span",
732
814
  { style: styles.muted },
733
815
  `since ${new Date(s.createdAt).toLocaleString()}`
@@ -735,25 +817,25 @@ var UsersScreen = (0, import_vue9.defineComponent)({
735
817
  ])
736
818
  )
737
819
  ),
738
- (0, import_vue9.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
739
- history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue9.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue9.h)(
820
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
821
+ history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue10.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue10.h)(
740
822
  "ul",
741
823
  { style: styles.list },
742
824
  history.events.value.map(
743
- (e) => (0, import_vue9.h)("li", { key: e.id, style: styles.row }, [
744
- (0, import_vue9.h)(
825
+ (e) => (0, import_vue10.h)("li", { key: e.id, style: styles.row }, [
826
+ (0, import_vue10.h)(
745
827
  "span",
746
828
  { style: e.outcome === "success" ? styles.ok : styles.bad },
747
829
  e.outcome
748
830
  ),
749
831
  " ",
750
- (0, import_vue9.h)("span", { style: styles.muted }, e.method),
832
+ (0, import_vue10.h)("span", { style: styles.muted }, e.method),
751
833
  " ",
752
- (0, import_vue9.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
834
+ (0, import_vue10.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
753
835
  ])
754
836
  )
755
837
  ),
756
- history.hasMore.value && !history.isLoading.value ? (0, import_vue9.h)(
838
+ history.hasMore.value && !history.isLoading.value ? (0, import_vue10.h)(
757
839
  "button",
758
840
  {
759
841
  type: "button",
@@ -769,26 +851,26 @@ var UsersScreen = (0, import_vue9.defineComponent)({
769
851
  });
770
852
 
771
853
  // src/screens/CatalogScreen.ts
772
- var import_vue_admin9 = require("@fonderie/vue-admin");
773
- var import_vue10 = require("vue");
774
- var CatalogScreen = (0, import_vue10.defineComponent)({
854
+ var import_vue_admin10 = require("@fonderie/vue-admin");
855
+ var import_vue11 = require("vue");
856
+ var CatalogScreen = (0, import_vue11.defineComponent)({
775
857
  name: "FonderieCatalogScreen",
776
858
  props: { client: { type: Object, required: true } },
777
859
  setup(props) {
778
- const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin9.useAdminCatalog)(props.client);
860
+ const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin10.useAdminCatalog)(props.client);
779
861
  const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
780
862
  return () => {
781
863
  const c = catalog.value;
782
- return (0, import_vue10.h)("div", { style: styles.container }, [
783
- (0, import_vue10.h)("div", { style: styles.toolbar }, [
784
- (0, import_vue10.h)("h1", { style: { ...styles.title, marginBottom: 0 } }, "Catalog"),
864
+ return (0, import_vue11.h)("div", { style: styles.container }, [
865
+ (0, import_vue11.h)("div", { style: styles.toolbar }, [
866
+ (0, import_vue11.h)("h1", { style: { ...styles.title, marginBottom: 0 } }, "Catalog"),
785
867
  refreshButton("Refresh", isLoading.value, () => void refresh())
786
868
  ]),
787
- error.value ? (0, import_vue10.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
788
- isLoading.value && !c ? (0, import_vue10.h)("p", { style: styles.status }, "Loading\u2026") : null,
869
+ error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
870
+ isLoading.value && !c ? (0, import_vue11.h)("p", { style: styles.status }, "Loading\u2026") : null,
789
871
  c ? [
790
- (0, import_vue10.h)("h2", { style: styles.subtitle }, "Configured (code)"),
791
- (0, import_vue10.h)(
872
+ (0, import_vue11.h)("h2", { style: styles.subtitle }, "Configured (code)"),
873
+ (0, import_vue11.h)(
792
874
  "pre",
793
875
  {
794
876
  style: {
@@ -800,14 +882,14 @@ var CatalogScreen = (0, import_vue10.defineComponent)({
800
882
  },
801
883
  JSON.stringify(c.configured, null, 2)
802
884
  ),
803
- (0, import_vue10.h)("h2", { style: styles.subtitle }, "Stored (database)"),
804
- c.stored.length === 0 ? (0, import_vue10.h)("p", { style: styles.muted }, "No stored plans.") : table(
885
+ (0, import_vue11.h)("h2", { style: styles.subtitle }, "Stored (database)"),
886
+ c.stored.length === 0 ? (0, import_vue11.h)("p", { style: styles.muted }, "No stored plans.") : table(
805
887
  ["Plan", "Tier", "Seats", "Monthly", "Yearly", "Trial", ""],
806
888
  c.stored.map(
807
- (p) => (0, import_vue10.h)("tr", { key: p.id }, [
889
+ (p) => (0, import_vue11.h)("tr", { key: p.id }, [
808
890
  td([
809
- (0, import_vue10.h)("strong", p.name),
810
- p.description ? (0, import_vue10.h)("div", { style: styles.muted }, p.description) : ""
891
+ (0, import_vue11.h)("strong", p.name),
892
+ p.description ? (0, import_vue11.h)("div", { style: styles.muted }, p.description) : ""
811
893
  ]),
812
894
  td(String(p.tier)),
813
895
  td(p.seats == null ? "\u221E" : String(p.seats)),
@@ -815,7 +897,7 @@ var CatalogScreen = (0, import_vue10.defineComponent)({
815
897
  td(money(p.pricing?.yearly)),
816
898
  td(p.trialDays ? `${p.trialDays} d` : "\u2014"),
817
899
  td(
818
- (0, import_vue10.h)(
900
+ (0, import_vue11.h)(
819
901
  "button",
820
902
  {
821
903
  type: "button",
@@ -839,30 +921,30 @@ var CatalogScreen = (0, import_vue10.defineComponent)({
839
921
  });
840
922
 
841
923
  // src/screens/SubscriberScreen.ts
842
- var import_vue_admin10 = require("@fonderie/vue-admin");
843
- var import_vue11 = require("vue");
844
- var SubscriberScreen = (0, import_vue11.defineComponent)({
924
+ var import_vue_admin11 = require("@fonderie/vue-admin");
925
+ var import_vue12 = require("vue");
926
+ var SubscriberScreen = (0, import_vue12.defineComponent)({
845
927
  name: "FonderieSubscriberScreen",
846
928
  props: {
847
929
  client: { type: Object, required: true },
848
930
  pageSize: { type: Number, default: 50 }
849
931
  },
850
932
  setup(props) {
851
- const type = (0, import_vue11.ref)("user");
852
- const idInput = (0, import_vue11.ref)("");
853
- const subscriber = (0, import_vue11.ref)(null);
854
- const list = (0, import_vue_admin10.useAdminSubscribers)(props.client, { limit: props.pageSize });
855
- const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
933
+ const type = (0, import_vue12.ref)("user");
934
+ const idInput = (0, import_vue12.ref)("");
935
+ const subscriber = (0, import_vue12.ref)(null);
936
+ const list = (0, import_vue_admin11.useAdminSubscribers)(props.client, { limit: props.pageSize });
937
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin11.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
856
938
  const open = (t, id) => {
857
939
  granted.value = null;
858
940
  type.value = t;
859
941
  idInput.value = id;
860
942
  subscriber.value = { type: t, id };
861
943
  };
862
- const amount = (0, import_vue11.ref)("");
863
- const note = (0, import_vue11.ref)("");
864
- const granted = (0, import_vue11.ref)(null);
865
- const input = (model, placeholder, extra = {}) => (0, import_vue11.h)("input", {
944
+ const amount = (0, import_vue12.ref)("");
945
+ const note = (0, import_vue12.ref)("");
946
+ const granted = (0, import_vue12.ref)(null);
947
+ const input = (model, placeholder, extra = {}) => (0, import_vue12.h)("input", {
866
948
  value: model.value,
867
949
  onInput: (e) => model.value = e.target.value,
868
950
  placeholder,
@@ -872,9 +954,9 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
872
954
  return () => {
873
955
  const s = subscription.value;
874
956
  const w = wallet.value;
875
- return (0, import_vue11.h)("div", { style: styles.container }, [
876
- (0, import_vue11.h)("h1", { style: styles.title }, "Subscriber"),
877
- (0, import_vue11.h)(
957
+ return (0, import_vue12.h)("div", { style: styles.container }, [
958
+ (0, import_vue12.h)("h1", { style: styles.title }, "Subscriber"),
959
+ (0, import_vue12.h)(
878
960
  "form",
879
961
  {
880
962
  style: styles.toolbar,
@@ -885,7 +967,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
885
967
  }
886
968
  },
887
969
  [
888
- (0, import_vue11.h)(
970
+ (0, import_vue12.h)(
889
971
  "select",
890
972
  {
891
973
  value: type.value,
@@ -894,17 +976,17 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
894
976
  "aria-label": "Type"
895
977
  },
896
978
  [
897
- (0, import_vue11.h)("option", { value: "user" }, "user"),
898
- (0, import_vue11.h)("option", { value: "workspace" }, "workspace")
979
+ (0, import_vue12.h)("option", { value: "user" }, "user"),
980
+ (0, import_vue12.h)("option", { value: "workspace" }, "workspace")
899
981
  ]
900
982
  ),
901
983
  input(idInput, "subscriber id", { minWidth: "280px" }),
902
- (0, import_vue11.h)(
984
+ (0, import_vue12.h)(
903
985
  "button",
904
986
  { type: "submit", style: styles.button, disabled: isLoading.value },
905
987
  "Look up"
906
988
  ),
907
- subscriber.value ? (0, import_vue11.h)(
989
+ subscriber.value ? (0, import_vue12.h)(
908
990
  "button",
909
991
  {
910
992
  type: "button",
@@ -916,7 +998,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
916
998
  }
917
999
  },
918
1000
  "\u2190 All subscribers"
919
- ) : (0, import_vue11.h)(
1001
+ ) : (0, import_vue12.h)(
920
1002
  "button",
921
1003
  {
922
1004
  type: "button",
@@ -928,14 +1010,14 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
928
1010
  )
929
1011
  ]
930
1012
  ),
931
- !subscriber.value ? (0, import_vue11.h)("div", [
932
- list.error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
933
- list.subscriptions.value.length === 0 && !list.isLoading.value ? (0, import_vue11.h)("p", { style: styles.muted }, "No subscribers yet.") : table(
1013
+ !subscriber.value ? (0, import_vue12.h)("div", [
1014
+ list.error.value ? (0, import_vue12.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
1015
+ list.subscriptions.value.length === 0 && !list.isLoading.value ? (0, import_vue12.h)("p", { style: styles.muted }, "No subscribers yet.") : table(
934
1016
  ["Subscriber", "Plan", "Status", "Renews"],
935
1017
  list.subscriptions.value.map(
936
- (sub) => (0, import_vue11.h)("tr", { key: sub.id }, [
1018
+ (sub) => (0, import_vue12.h)("tr", { key: sub.id }, [
937
1019
  td(
938
- (0, import_vue11.h)(
1020
+ (0, import_vue12.h)(
939
1021
  "button",
940
1022
  {
941
1023
  type: "button",
@@ -947,16 +1029,16 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
947
1029
  ),
948
1030
  td(`${sub.plan} \xB7 ${sub.interval}`),
949
1031
  td(
950
- sub.cancelAtPeriodEnd ? [sub.status, " ", (0, import_vue11.h)("span", { style: styles.badge }, "cancels")] : sub.status
1032
+ sub.cancelAtPeriodEnd ? [sub.status, " ", (0, import_vue12.h)("span", { style: styles.badge }, "cancels")] : sub.status
951
1033
  ),
952
1034
  td(
953
- sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : (0, import_vue11.h)("span", { style: styles.muted }, "\u2014")
1035
+ sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : (0, import_vue12.h)("span", { style: styles.muted }, "\u2014")
954
1036
  )
955
1037
  ])
956
1038
  )
957
1039
  ),
958
- list.isLoading.value ? (0, import_vue11.h)("p", { style: styles.status }, "Loading\u2026") : null,
959
- list.hasMore.value && !list.isLoading.value ? (0, import_vue11.h)(
1040
+ list.isLoading.value ? (0, import_vue12.h)("p", { style: styles.status }, "Loading\u2026") : null,
1041
+ list.hasMore.value && !list.isLoading.value ? (0, import_vue12.h)(
960
1042
  "button",
961
1043
  {
962
1044
  type: "button",
@@ -966,37 +1048,37 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
966
1048
  "Load more"
967
1049
  ) : null
968
1050
  ]) : null,
969
- error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
1051
+ error.value ? (0, import_vue12.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
970
1052
  subscriber.value && !isLoading.value ? [
971
- (0, import_vue11.h)("h2", { style: styles.subtitle }, "Subscription"),
972
- s ? (0, import_vue11.h)("table", { style: styles.table }, [
973
- (0, import_vue11.h)("tbody", [
974
- (0, import_vue11.h)("tr", [td("plan"), td([(0, import_vue11.h)("strong", s.plan), ` \xB7 ${s.interval}`])]),
975
- (0, import_vue11.h)("tr", [
1053
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Subscription"),
1054
+ s ? (0, import_vue12.h)("table", { style: styles.table }, [
1055
+ (0, import_vue12.h)("tbody", [
1056
+ (0, import_vue12.h)("tr", [td("plan"), td([(0, import_vue12.h)("strong", s.plan), ` \xB7 ${s.interval}`])]),
1057
+ (0, import_vue12.h)("tr", [
976
1058
  td("status"),
977
1059
  td(`${s.status}${s.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""}`)
978
1060
  ]),
979
- (0, import_vue11.h)("tr", [
1061
+ (0, import_vue12.h)("tr", [
980
1062
  td("period"),
981
1063
  td(
982
1064
  `${s.currentPeriodStart ? new Date(s.currentPeriodStart).toLocaleDateString() : "\u2014"} \u2192 ${s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : "\u2014"}`
983
1065
  )
984
1066
  ]),
985
- (0, import_vue11.h)("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
1067
+ (0, import_vue12.h)("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
986
1068
  ])
987
- ]) : (0, import_vue11.h)("p", { style: styles.muted }, "No subscription."),
988
- (0, import_vue11.h)("h2", { style: styles.subtitle }, "Wallet"),
1069
+ ]) : (0, import_vue12.h)("p", { style: styles.muted }, "No subscription."),
1070
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Wallet"),
989
1071
  w ? [
990
- (0, import_vue11.h)("p", [
991
- (0, import_vue11.h)("strong", w.balance),
1072
+ (0, import_vue12.h)("p", [
1073
+ (0, import_vue12.h)("strong", w.balance),
992
1074
  ` ${w.currency} `,
993
- (0, import_vue11.h)(
1075
+ (0, import_vue12.h)(
994
1076
  "span",
995
1077
  { style: styles.muted },
996
1078
  `(granted ${w.granted ?? "0"} \xB7 purchased ${w.purchased ?? "0"}) \xB7 minor units, precision ${w.precision}`
997
1079
  )
998
1080
  ]),
999
- (0, import_vue11.h)(
1081
+ (0, import_vue12.h)(
1000
1082
  "form",
1001
1083
  {
1002
1084
  style: styles.toolbar,
@@ -1021,7 +1103,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1021
1103
  [
1022
1104
  input(amount, "amount (minor units)"),
1023
1105
  input(note, "reason (optional)", { minWidth: "220px" }),
1024
- (0, import_vue11.h)(
1106
+ (0, import_vue12.h)(
1025
1107
  "button",
1026
1108
  {
1027
1109
  type: "submit",
@@ -1030,14 +1112,14 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1030
1112
  },
1031
1113
  "Grant"
1032
1114
  ),
1033
- granted.value ? (0, import_vue11.h)("span", { style: styles.ok }, granted.value) : null
1115
+ granted.value ? (0, import_vue12.h)("span", { style: styles.ok }, granted.value) : null
1034
1116
  ]
1035
1117
  ),
1036
- (0, import_vue11.h)("h2", { style: styles.subtitle }, "Ledger"),
1037
- ledger.value.length === 0 ? (0, import_vue11.h)("p", { style: styles.muted }, "Nothing moved yet.") : table(
1118
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Ledger"),
1119
+ ledger.value.length === 0 ? (0, import_vue12.h)("p", { style: styles.muted }, "Nothing moved yet.") : table(
1038
1120
  ["When", "Kind", "Amount", "Note"],
1039
1121
  ledger.value.map(
1040
- (t) => (0, import_vue11.h)("tr", { key: t.id }, [
1122
+ (t) => (0, import_vue12.h)("tr", { key: t.id }, [
1041
1123
  td(new Date(t.createdAt).toLocaleString(), styles.muted),
1042
1124
  td(t.type),
1043
1125
  td(t.amount, styles.mono),
@@ -1045,7 +1127,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1045
1127
  ])
1046
1128
  )
1047
1129
  ),
1048
- hasMoreLedger.value ? (0, import_vue11.h)(
1130
+ hasMoreLedger.value ? (0, import_vue12.h)(
1049
1131
  "button",
1050
1132
  {
1051
1133
  type: "button",
@@ -1054,7 +1136,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1054
1136
  },
1055
1137
  "Load more"
1056
1138
  ) : null
1057
- ] : (0, import_vue11.h)(
1139
+ ] : (0, import_vue12.h)(
1058
1140
  "p",
1059
1141
  { style: styles.muted },
1060
1142
  "No wallet \u2014 billing has no wallet configuration."
@@ -1066,31 +1148,31 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1066
1148
  });
1067
1149
 
1068
1150
  // src/screens/AuditScreen.ts
1069
- var import_vue_admin11 = require("@fonderie/vue-admin");
1070
- var import_vue12 = require("vue");
1071
- var AuditScreen = (0, import_vue12.defineComponent)({
1151
+ var import_vue_admin12 = require("@fonderie/vue-admin");
1152
+ var import_vue13 = require("vue");
1153
+ var AuditScreen = (0, import_vue13.defineComponent)({
1072
1154
  name: "FonderieAuditScreen",
1073
1155
  props: {
1074
1156
  client: { type: Object, required: true },
1075
1157
  pageSize: { type: Number, default: 50 }
1076
1158
  },
1077
1159
  setup(props) {
1078
- const draft = (0, import_vue12.ref)({ workspaceId: "", type: "", actorId: "" });
1079
- const query = (0, import_vue12.ref)({ limit: props.pageSize });
1080
- const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin11.useAdminAudit)(
1160
+ const draft = (0, import_vue13.ref)({ workspaceId: "", type: "", actorId: "" });
1161
+ const query = (0, import_vue13.ref)({ limit: props.pageSize });
1162
+ const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin12.useAdminAudit)(
1081
1163
  props.client,
1082
1164
  query
1083
1165
  );
1084
- const field = (key, placeholder) => (0, import_vue12.h)("input", {
1166
+ const field = (key, placeholder) => (0, import_vue13.h)("input", {
1085
1167
  value: draft.value[key],
1086
1168
  onInput: (e) => draft.value = { ...draft.value, [key]: e.target.value },
1087
1169
  placeholder,
1088
1170
  style: { ...styles.button, cursor: "text" },
1089
1171
  "aria-label": placeholder
1090
1172
  });
1091
- return () => (0, import_vue12.h)("div", { style: styles.container }, [
1092
- (0, import_vue12.h)("h1", { style: styles.title }, "Audit"),
1093
- (0, import_vue12.h)(
1173
+ return () => (0, import_vue13.h)("div", { style: styles.container }, [
1174
+ (0, import_vue13.h)("h1", { style: styles.title }, "Audit"),
1175
+ (0, import_vue13.h)(
1094
1176
  "form",
1095
1177
  {
1096
1178
  style: styles.toolbar,
@@ -1109,7 +1191,7 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1109
1191
  field("workspaceId", "workspace id (all if empty)"),
1110
1192
  field("type", "event type"),
1111
1193
  field("actorId", "actor id"),
1112
- (0, import_vue12.h)(
1194
+ (0, import_vue13.h)(
1113
1195
  "button",
1114
1196
  { type: "submit", style: styles.button, disabled: isLoading.value },
1115
1197
  "Filter"
@@ -1117,11 +1199,11 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1117
1199
  refreshButton("Refresh", isLoading.value, () => void refresh())
1118
1200
  ]
1119
1201
  ),
1120
- error.value ? (0, import_vue12.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
1121
- events.value.length === 0 && !isLoading.value ? (0, import_vue12.h)("p", { style: styles.muted }, "Nothing recorded for this filter.") : table(
1202
+ error.value ? (0, import_vue13.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
1203
+ events.value.length === 0 && !isLoading.value ? (0, import_vue13.h)("p", { style: styles.muted }, "Nothing recorded for this filter.") : table(
1122
1204
  ["When", "Type", "Workspace", "Actor", "Request"],
1123
1205
  events.value.map(
1124
- (e) => (0, import_vue12.h)("tr", { key: e.id }, [
1206
+ (e) => (0, import_vue13.h)("tr", { key: e.id }, [
1125
1207
  td(new Date(e.createdAt).toLocaleString(), styles.muted),
1126
1208
  td(e.type, styles.mono),
1127
1209
  td(String(e.payload["workspaceId"] ?? "\u2014"), styles.mono),
@@ -1130,8 +1212,8 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1130
1212
  ])
1131
1213
  )
1132
1214
  ),
1133
- isLoading.value ? (0, import_vue12.h)("p", { style: styles.status }, "Loading\u2026") : null,
1134
- hasMore.value && !isLoading.value ? (0, import_vue12.h)(
1215
+ isLoading.value ? (0, import_vue13.h)("p", { style: styles.status }, "Loading\u2026") : null,
1216
+ hasMore.value && !isLoading.value ? (0, import_vue13.h)(
1135
1217
  "button",
1136
1218
  {
1137
1219
  type: "button",
@@ -1171,11 +1253,12 @@ var NAV = [
1171
1253
  items: [
1172
1254
  { page: "audit", label: "Audit", needs: "audit" },
1173
1255
  { page: "log", label: "Admin log" },
1174
- { page: "tokens", label: "Access" }
1256
+ { page: "tokens", label: "Access" },
1257
+ { page: "migrations", label: "Migrations" }
1175
1258
  ]
1176
1259
  }
1177
1260
  ];
1178
- var AdminShell = (0, import_vue13.defineComponent)({
1261
+ var AdminShell = (0, import_vue14.defineComponent)({
1179
1262
  name: "FonderieAdminShell",
1180
1263
  props: {
1181
1264
  client: { type: Object, required: true },
@@ -1196,15 +1279,15 @@ var AdminShell = (0, import_vue13.defineComponent)({
1196
1279
  },
1197
1280
  emits: { navigate: (_page) => true },
1198
1281
  setup(props, { emit }) {
1199
- const own = (0, import_vue13.ref)("attention");
1200
- const current = (0, import_vue13.computed)(() => props.page ?? own.value);
1201
- const editing = (0, import_vue13.ref)(null);
1282
+ const own = (0, import_vue14.ref)("attention");
1283
+ const current = (0, import_vue14.computed)(() => props.page ?? own.value);
1284
+ const editing = (0, import_vue14.ref)(null);
1202
1285
  const go = (p) => {
1203
1286
  editing.value = null;
1204
1287
  emit("navigate", p);
1205
1288
  if (props.page === void 0) own.value = p;
1206
1289
  };
1207
- const back = () => (0, import_vue13.h)(
1290
+ const back = () => (0, import_vue14.h)(
1208
1291
  "button",
1209
1292
  { type: "button", style: styles.button, onClick: () => editing.value = null },
1210
1293
  "\u2190 Back"
@@ -1213,43 +1296,45 @@ var AdminShell = (0, import_vue13.defineComponent)({
1213
1296
  const c = props.client;
1214
1297
  switch (current.value) {
1215
1298
  case "attention":
1216
- return (0, import_vue13.h)(AttentionScreen, { client: c });
1299
+ return (0, import_vue14.h)(AttentionScreen, { client: c });
1217
1300
  case "modules":
1218
- return (0, import_vue13.h)(ModulesScreen, { client: c });
1301
+ return (0, import_vue14.h)(ModulesScreen, { client: c });
1219
1302
  case "doctor":
1220
- return (0, import_vue13.h)(DoctorScreen, { client: c });
1303
+ return (0, import_vue14.h)(DoctorScreen, { client: c });
1221
1304
  case "config":
1222
- return (0, import_vue13.h)(ConfigScreen, { client: c });
1305
+ return (0, import_vue14.h)(ConfigScreen, { client: c });
1223
1306
  case "routes":
1224
- return (0, import_vue13.h)(RoutesScreen, { client: c });
1307
+ return (0, import_vue14.h)(RoutesScreen, { client: c });
1225
1308
  case "tokens":
1226
- return (0, import_vue13.h)(TokensScreen, { client: c });
1309
+ return (0, import_vue14.h)(TokensScreen, { client: c });
1310
+ case "migrations":
1311
+ return (0, import_vue14.h)(MigrationsScreen, { client: c });
1227
1312
  case "log":
1228
- return (0, import_vue13.h)(AdminLogScreen, { client: c });
1313
+ return (0, import_vue14.h)(AdminLogScreen, { client: c });
1229
1314
  case "catalog":
1230
- return props.billingClient ? (0, import_vue13.h)(CatalogScreen, { client: props.billingClient }) : (0, import_vue13.h)(
1315
+ return props.billingClient ? (0, import_vue14.h)(CatalogScreen, { client: props.billingClient }) : (0, import_vue14.h)(
1231
1316
  "p",
1232
1317
  { style: styles.status },
1233
1318
  "Pass a BillingAdminClient to see the catalog here."
1234
1319
  );
1235
1320
  case "subscriber":
1236
- return props.billingClient ? (0, import_vue13.h)(SubscriberScreen, { client: props.billingClient }) : (0, import_vue13.h)(
1321
+ return props.billingClient ? (0, import_vue14.h)(SubscriberScreen, { client: props.billingClient }) : (0, import_vue14.h)(
1237
1322
  "p",
1238
1323
  { style: styles.status },
1239
1324
  "Pass a BillingAdminClient to look up subscribers here."
1240
1325
  );
1241
1326
  case "audit":
1242
- return props.auditClient ? (0, import_vue13.h)(AuditScreen, { client: props.auditClient }) : (0, import_vue13.h)(
1327
+ return props.auditClient ? (0, import_vue14.h)(AuditScreen, { client: props.auditClient }) : (0, import_vue14.h)(
1243
1328
  "p",
1244
1329
  { style: styles.status },
1245
1330
  "Pass an AuditAdminClient to see the audit trail here."
1246
1331
  );
1247
1332
  case "users":
1248
- return props.authClient ? (0, import_vue13.h)(UsersScreen, { client: props.authClient }) : (0, import_vue13.h)("p", { style: styles.status }, "Pass an AuthAdminClient to look up users here.");
1333
+ return props.authClient ? (0, import_vue14.h)(UsersScreen, { client: props.authClient }) : (0, import_vue14.h)("p", { style: styles.status }, "Pass an AuthAdminClient to look up users here.");
1249
1334
  case "settings": {
1250
1335
  const cc = props.configClient;
1251
1336
  if (!cc)
1252
- return (0, import_vue13.h)(
1337
+ return (0, import_vue14.h)(
1253
1338
  "p",
1254
1339
  { style: styles.status },
1255
1340
  "Pass a ConfigAdminClient to manage config and secrets here."
@@ -1258,7 +1343,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1258
1343
  if (e && e.kind !== "template")
1259
1344
  return [
1260
1345
  back(),
1261
- (0, import_vue13.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
1346
+ (0, import_vue14.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
1262
1347
  client: cc,
1263
1348
  kind: e.kind,
1264
1349
  configKey: e.key,
@@ -1266,7 +1351,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1266
1351
  onSaved: () => editing.value = null
1267
1352
  })
1268
1353
  ];
1269
- return (0, import_vue13.h)(import_vue_config_admin_screens.ConfigListScreen, {
1354
+ return (0, import_vue14.h)(import_vue_config_admin_screens.ConfigListScreen, {
1270
1355
  client: cc,
1271
1356
  ...props.environment !== void 0 ? { environment: props.environment } : {},
1272
1357
  "onSelect-config": (key) => editing.value = { kind: "config", key },
@@ -1276,7 +1361,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1276
1361
  case "templates": {
1277
1362
  const kc = props.courierClient;
1278
1363
  if (!kc)
1279
- return (0, import_vue13.h)(
1364
+ return (0, import_vue14.h)(
1280
1365
  "p",
1281
1366
  { style: styles.status },
1282
1367
  "Pass a CourierAdminClient to manage templates here."
@@ -1285,21 +1370,21 @@ var AdminShell = (0, import_vue13.defineComponent)({
1285
1370
  if (e?.kind === "template")
1286
1371
  return [
1287
1372
  back(),
1288
- (0, import_vue13.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
1373
+ (0, import_vue14.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
1289
1374
  client: kc,
1290
1375
  type: e.type,
1291
1376
  onSaved: () => editing.value = null
1292
1377
  })
1293
1378
  ];
1294
- return (0, import_vue13.h)(import_vue_courier_admin_screens.TemplateListScreen, {
1379
+ return (0, import_vue14.h)(import_vue_courier_admin_screens.TemplateListScreen, {
1295
1380
  client: kc,
1296
1381
  "onSelect-template": (t) => editing.value = { kind: "template", type: t.type }
1297
1382
  });
1298
1383
  }
1299
1384
  }
1300
1385
  };
1301
- return () => (0, import_vue13.h)("div", { style: styles.shell }, [
1302
- (0, import_vue13.h)(
1386
+ return () => (0, import_vue14.h)("div", { style: styles.shell }, [
1387
+ (0, import_vue14.h)(
1303
1388
  "nav",
1304
1389
  { style: styles.nav, "aria-label": "Admin" },
1305
1390
  NAV.map((g) => {
@@ -1307,10 +1392,10 @@ var AdminShell = (0, import_vue13.defineComponent)({
1307
1392
  (i) => !i.needs || (i.needs === "config" ? props.configClient : i.needs === "courier" ? props.courierClient : i.needs === "auth" ? props.authClient : i.needs === "billing" ? props.billingClient : props.auditClient)
1308
1393
  );
1309
1394
  if (items.length === 0) return null;
1310
- return (0, import_vue13.h)("div", { key: g.group }, [
1311
- (0, import_vue13.h)("div", { style: styles.navGroup }, g.group),
1395
+ return (0, import_vue14.h)("div", { key: g.group }, [
1396
+ (0, import_vue14.h)("div", { style: styles.navGroup }, g.group),
1312
1397
  ...items.map(
1313
- (i) => (0, import_vue13.h)(
1398
+ (i) => (0, import_vue14.h)(
1314
1399
  "button",
1315
1400
  {
1316
1401
  key: i.page,
@@ -1328,7 +1413,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1328
1413
  ]);
1329
1414
  })
1330
1415
  ),
1331
- (0, import_vue13.h)("main", { style: styles.main }, body())
1416
+ (0, import_vue14.h)("main", { style: styles.main }, body())
1332
1417
  ]);
1333
1418
  }
1334
1419
  });
@@ -1341,6 +1426,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1341
1426
  CatalogScreen,
1342
1427
  ConfigScreen,
1343
1428
  DoctorScreen,
1429
+ MigrationsScreen,
1344
1430
  ModulesScreen,
1345
1431
  RoutesScreen,
1346
1432
  SubscriberScreen,