@fonderie/vue-admin-screens 0.6.0 → 0.8.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,23 +39,52 @@ 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
45
+ var t = {
46
+ text: "var(--fonderie-text,#171717)",
47
+ muted: "var(--fonderie-text-muted,#5c5c5c)",
48
+ bg: "var(--fonderie-bg,#fafafa)",
49
+ surface: "var(--fonderie-surface,#fff)",
50
+ surfaceAlt: "var(--fonderie-surface-alt,#fafafa)",
51
+ border: "var(--fonderie-border,#e0e0e0)",
52
+ borderLight: "var(--fonderie-border-light,#f5f5f5)",
53
+ accent: "var(--fonderie-accent,#00d294)",
54
+ accentStrong: "var(--fonderie-accent-strong,#009767)",
55
+ warning: "var(--fonderie-warning,#f5a623)",
56
+ danger: "var(--fonderie-danger,#e00)",
57
+ font: 'var(--fonderie-font,Inter,"Inter Fallback",-apple-system,BlinkMacSystemFont,"Helvetica Neue",system-ui,sans-serif)',
58
+ mono: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
59
+ radius: "var(--fonderie-radius,4px)",
60
+ radiusLg: "var(--fonderie-radius-lg,8px)",
61
+ trackingDisplay: "var(--fonderie-tracking-display,-0.05em)",
62
+ shadowCard: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
63
+ };
44
64
  var styles = {
45
65
  shell: {
46
66
  display: "flex",
47
67
  minHeight: "100vh",
48
- fontFamily: "system-ui, sans-serif",
49
- color: "#111"
68
+ fontFamily: t.font,
69
+ color: t.text,
70
+ background: t.bg
71
+ },
72
+ // The sidebar sits on the raised surface against the page canvas, which is
73
+ // what separates the two without a heavy rule between them.
74
+ nav: {
75
+ width: 220,
76
+ borderRight: `1px solid ${t.border}`,
77
+ padding: 16,
78
+ flexShrink: 0,
79
+ background: t.surface
50
80
  },
51
- nav: { width: "220px", borderRight: "1px solid #eee", padding: "16px", flexShrink: 0 },
52
81
  navGroup: {
53
- fontSize: "11px",
82
+ fontSize: 11,
54
83
  textTransform: "uppercase",
55
- letterSpacing: "0.6px",
56
- color: "#888",
57
- margin: "16px 0 6px"
84
+ letterSpacing: 0.6,
85
+ color: t.muted,
86
+ margin: "16px 0 6px",
87
+ fontWeight: 600
58
88
  },
59
89
  navItem: {
60
90
  display: "block",
@@ -62,51 +92,79 @@ var styles = {
62
92
  textAlign: "left",
63
93
  background: "none",
64
94
  border: "none",
65
- borderRadius: "6px",
95
+ borderRadius: t.radius,
66
96
  padding: "6px 8px",
67
97
  cursor: "pointer",
68
- fontSize: "14px",
69
- color: "#111"
98
+ fontSize: 14,
99
+ color: t.text,
100
+ fontFamily: "inherit"
101
+ },
102
+ navItemActive: { background: t.surfaceAlt, fontWeight: 600, color: t.accentStrong },
103
+ main: { flex: 1, padding: 24, maxWidth: 960 },
104
+ container: { padding: 24, maxWidth: 960 },
105
+ title: {
106
+ fontSize: 20,
107
+ fontWeight: 600,
108
+ marginTop: 0,
109
+ marginBottom: 12,
110
+ letterSpacing: t.trackingDisplay
70
111
  },
71
- navItemActive: { background: "#f3f4f6", fontWeight: 600 },
72
- main: { flex: 1, padding: "24px", maxWidth: "960px" },
73
- container: { padding: "24px", maxWidth: "960px" },
74
- title: { fontSize: "20px", fontWeight: 700, marginTop: 0, marginBottom: "12px" },
75
- subtitle: { fontSize: "15px", fontWeight: 600, marginTop: "24px", marginBottom: "8px" },
76
- status: { padding: "12px", color: "#666" },
77
- error: { color: "#e11d48", marginBottom: "12px", fontSize: "14px" },
78
- ok: { color: "#15803d", fontWeight: 600 },
79
- bad: { color: "#e11d48", fontWeight: 600 },
80
- advice: { color: "#b45309" },
81
- muted: { color: "#666", fontSize: "13px" },
82
- mono: { fontFamily: "ui-monospace, monospace", fontSize: "13px" },
83
- table: { width: "100%", borderCollapse: "collapse", fontSize: "14px" },
112
+ subtitle: {
113
+ fontSize: 15,
114
+ fontWeight: 600,
115
+ marginTop: 24,
116
+ marginBottom: 8,
117
+ letterSpacing: t.trackingDisplay
118
+ },
119
+ status: { padding: 12, color: t.muted },
120
+ error: { color: t.danger, marginBottom: 12, fontSize: 14 },
121
+ ok: { color: t.accentStrong, fontWeight: 600 },
122
+ bad: { color: t.danger, fontWeight: 600 },
123
+ advice: { color: t.warning },
124
+ muted: { color: t.muted, fontSize: 13 },
125
+ mono: { fontFamily: t.mono, fontSize: 13 },
126
+ table: { width: "100%", borderCollapse: "collapse", fontSize: 14 },
84
127
  th: {
85
128
  textAlign: "left",
86
- borderBottom: "1px solid #ddd",
129
+ borderBottom: `1px solid ${t.border}`,
87
130
  padding: "6px 8px",
88
131
  fontWeight: 600,
89
- color: "#444"
132
+ color: t.muted
90
133
  },
91
- td: { borderBottom: "1px solid #eee", padding: "6px 8px", verticalAlign: "top" },
134
+ td: { borderBottom: `1px solid ${t.borderLight}`, padding: "6px 8px", verticalAlign: "top" },
92
135
  list: { listStyle: "none", padding: 0, margin: 0 },
93
- row: { borderBottom: "1px solid #eee", padding: "8px 0" },
136
+ row: { borderBottom: `1px solid ${t.borderLight}`, padding: "8px 0" },
94
137
  badge: {
95
138
  display: "inline-block",
96
- borderRadius: "999px",
139
+ borderRadius: 999,
97
140
  padding: "1px 8px",
98
- fontSize: "12px",
99
- background: "#f3f4f6"
141
+ fontSize: 12,
142
+ background: t.surfaceAlt,
143
+ border: `1px solid ${t.border}`,
144
+ color: t.muted
100
145
  },
101
146
  button: {
102
- background: "none",
103
- border: "1px solid #ddd",
104
- borderRadius: "8px",
147
+ background: t.surface,
148
+ border: `1px solid ${t.border}`,
149
+ borderRadius: t.radius,
105
150
  padding: "4px 10px",
106
151
  cursor: "pointer",
107
- fontSize: "13px"
152
+ fontSize: 13,
153
+ color: t.text,
154
+ fontFamily: "inherit",
155
+ boxShadow: t.shadowCard
108
156
  },
109
- toolbar: { display: "flex", gap: "8px", alignItems: "center", marginBottom: "12px" }
157
+ toolbar: { display: "flex", gap: 8, alignItems: "center", marginBottom: 12 },
158
+ // Surfaces the screens compose themselves, kept here so a card looks the
159
+ // same on every page rather than being re-invented per screen.
160
+ card: {
161
+ background: t.surface,
162
+ border: `1px solid ${t.border}`,
163
+ borderRadius: t.radiusLg,
164
+ padding: 16,
165
+ boxShadow: t.shadowCard
166
+ },
167
+ accent: { color: t.accent }
110
168
  };
111
169
 
112
170
  // src/screens/AdminLogScreen.ts
@@ -134,7 +192,7 @@ function table(headers, rows) {
134
192
  (0, import_vue.h)("thead", [
135
193
  (0, import_vue.h)(
136
194
  "tr",
137
- headers.map((t) => (0, import_vue.h)("th", { style: styles.th }, t))
195
+ headers.map((t2) => (0, import_vue.h)("th", { style: styles.th }, t2))
138
196
  )
139
197
  ]),
140
198
  (0, import_vue.h)("tbody", rows)
@@ -416,52 +474,133 @@ var RoutesScreen = (0, import_vue7.defineComponent)({
416
474
  }
417
475
  });
418
476
 
419
- // src/screens/TokensScreen.ts
477
+ // src/screens/MigrationsScreen.ts
420
478
  var import_vue_admin7 = require("@fonderie/vue-admin");
421
479
  var import_vue8 = require("vue");
480
+ function why(m, everApplied) {
481
+ if (m.pending.length === 0) return null;
482
+ if (m.blockedBy) return `Apply "${m.blockedBy}" first \u2014 it runs before this one and is behind.`;
483
+ if (everApplied && m.pending.some((p) => p.impact === "destructive"))
484
+ return "Contains a migration that deletes data. No down-migration brings it back \u2014 apply this one through CI or `npm run migrate`.";
485
+ return null;
486
+ }
487
+ var MigrationsScreen = (0, import_vue8.defineComponent)({
488
+ name: "FonderieMigrationsScreen",
489
+ props: { client: { type: Object, required: true } },
490
+ setup(props) {
491
+ const { report, isLoading, error, refresh, apply } = (0, import_vue_admin7.useAdminMigrations)(props.client);
492
+ const section = (m, everApplied) => {
493
+ const blocked = why(m, everApplied);
494
+ const files = m.pending.map((p) => p.file);
495
+ return (0, import_vue8.h)("section", { style: { marginBottom: "24px" } }, [
496
+ (0, import_vue8.h)("h2", { style: styles.subtitle }, `${m.name} \u2014 ${m.pending.length} pending`),
497
+ (0, import_vue8.h)(
498
+ "ul",
499
+ m.pending.map(
500
+ (p) => (0, import_vue8.h)("li", [
501
+ (0, import_vue8.h)("code", p.file),
502
+ " ",
503
+ (0, import_vue8.h)(
504
+ "span",
505
+ { style: p.impact === "destructive" ? styles.bad : styles.advice },
506
+ p.impact
507
+ ),
508
+ p.destructive.length > 0 ? (0, import_vue8.h)(
509
+ "ul",
510
+ p.destructive.map((s) => (0, import_vue8.h)("li", [(0, import_vue8.h)("code", s.slice(0, 120))]))
511
+ ) : null
512
+ ])
513
+ )
514
+ ),
515
+ blocked ? (0, import_vue8.h)("p", { style: styles.advice }, blocked) : (0, import_vue8.h)(
516
+ "button",
517
+ {
518
+ type: "button",
519
+ style: styles.button,
520
+ disabled: isLoading.value || !m.appliable,
521
+ onClick: () => {
522
+ if (window.confirm(
523
+ `Apply ${m.pending.length} migration(s) to "${m.name}"? This changes the database schema.`
524
+ ))
525
+ void apply(m.name, files).catch(() => {
526
+ });
527
+ }
528
+ },
529
+ `Apply ${m.pending.length} migration${m.pending.length === 1 ? "" : "s"}`
530
+ )
531
+ ]);
532
+ };
533
+ return () => page(
534
+ "Migrations",
535
+ { isLoading, error },
536
+ () => {
537
+ const r = report.value;
538
+ if (!r) return null;
539
+ const behind = r.modules.filter((m) => m.pending.length > 0);
540
+ if (behind.length === 0) return (0, import_vue8.h)("p", { style: styles.ok }, "Every module is up to date.");
541
+ return [
542
+ r.everApplied ? null : (0, import_vue8.h)(
543
+ "p",
544
+ { style: styles.advice },
545
+ "This database has never been migrated \u2014 treating it as a first install, so nothing is held back."
546
+ ),
547
+ ...behind.map((m) => section(m, r.everApplied))
548
+ ].filter(Boolean);
549
+ },
550
+ [refreshButton("Refresh", isLoading.value, () => void refresh())],
551
+ {
552
+ errorText: (e) => e.status === 403 ? "Applying migrations needs a token with the write scope." : e.explanation
553
+ }
554
+ );
555
+ }
556
+ });
557
+
558
+ // src/screens/TokensScreen.ts
559
+ var import_vue_admin8 = require("@fonderie/vue-admin");
560
+ var import_vue9 = require("vue");
422
561
  var ALL = ["read", "write", "secrets"];
423
- var TokensScreen = (0, import_vue8.defineComponent)({
562
+ var TokensScreen = (0, import_vue9.defineComponent)({
424
563
  name: "FonderieTokensScreen",
425
564
  props: { client: { type: Object, required: true } },
426
565
  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);
566
+ const { report, isLoading, error, issue, revoke } = (0, import_vue_admin8.useAdminTokens)(props.client);
567
+ const name = (0, import_vue9.ref)("");
568
+ const scopes = (0, import_vue9.ref)(["read"]);
569
+ const days = (0, import_vue9.ref)("");
570
+ const minted = (0, import_vue9.ref)(null);
432
571
  const toggle = (s) => scopes.value = scopes.value.includes(s) ? scopes.value.filter((x) => x !== s) : [...scopes.value, s];
433
572
  return () => {
434
573
  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)(
574
+ return (0, import_vue9.h)("div", { style: styles.container }, [
575
+ (0, import_vue9.h)("h1", { style: styles.title }, "Access"),
576
+ error.value ? (0, import_vue9.h)(
438
577
  "p",
439
578
  { style: styles.error, role: "alert" },
440
579
  error.value.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.value.explanation
441
580
  ) : null,
442
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Root token"),
443
- r ? (0, import_vue8.h)("p", [
444
- (0, import_vue8.h)(
581
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Root token"),
582
+ r ? (0, import_vue9.h)("p", [
583
+ (0, import_vue9.h)(
445
584
  "span",
446
585
  { style: r.admin.ok ? styles.ok : styles.bad },
447
586
  r.admin.ok ? "strong" : "weak"
448
587
  ),
449
588
  ...r.admin.problems.map(
450
- (p) => (0, import_vue8.h)("div", { key: p.message, style: styles.bad }, p.message)
589
+ (p) => (0, import_vue9.h)("div", { key: p.message, style: styles.bad }, p.message)
451
590
  )
452
591
  ]) : null,
453
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Issued tokens"),
454
- r?.issued === null ? (0, import_vue8.h)(
592
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Issued tokens"),
593
+ r?.issued === null ? (0, import_vue9.h)(
455
594
  "p",
456
595
  { style: styles.muted },
457
596
  "Issuing is off \u2014 give AdminModule a store and run its migrations."
458
597
  ) : [
459
- minted.value ? (0, import_vue8.h)("p", { style: styles.ok }, [
598
+ minted.value ? (0, import_vue9.h)("p", { style: styles.ok }, [
460
599
  "Copy this now \u2014 it is never shown again. ",
461
- (0, import_vue8.h)("span", { style: styles.mono }, minted.value.token),
600
+ (0, import_vue9.h)("span", { style: styles.mono }, minted.value.token),
462
601
  ` (${minted.value.name})`
463
602
  ]) : null,
464
- (0, import_vue8.h)(
603
+ (0, import_vue9.h)(
465
604
  "form",
466
605
  {
467
606
  style: styles.toolbar,
@@ -472,8 +611,8 @@ var TokensScreen = (0, import_vue8.defineComponent)({
472
611
  name: name.value.trim(),
473
612
  scopes: scopes.value,
474
613
  ...days.value && Number.isInteger(d) && d > 0 ? { expiresInDays: d } : {}
475
- }).then((t) => {
476
- minted.value = { name: t.name, token: t.token };
614
+ }).then((t2) => {
615
+ minted.value = { name: t2.name, token: t2.token };
477
616
  name.value = "";
478
617
  days.value = "";
479
618
  }).catch(() => {
@@ -481,7 +620,7 @@ var TokensScreen = (0, import_vue8.defineComponent)({
481
620
  }
482
621
  },
483
622
  [
484
- (0, import_vue8.h)("input", {
623
+ (0, import_vue9.h)("input", {
485
624
  value: name.value,
486
625
  onInput: (e) => name.value = e.target.value,
487
626
  placeholder: "name",
@@ -489,8 +628,8 @@ var TokensScreen = (0, import_vue8.defineComponent)({
489
628
  "aria-label": "Name"
490
629
  }),
491
630
  ...ALL.map(
492
- (s) => (0, import_vue8.h)("label", { key: s, style: { ...styles.badge, cursor: "pointer" } }, [
493
- (0, import_vue8.h)("input", {
631
+ (s) => (0, import_vue9.h)("label", { key: s, style: { ...styles.badge, cursor: "pointer" } }, [
632
+ (0, import_vue9.h)("input", {
494
633
  type: "checkbox",
495
634
  checked: scopes.value.includes(s),
496
635
  onChange: () => toggle(s)
@@ -498,14 +637,14 @@ var TokensScreen = (0, import_vue8.defineComponent)({
498
637
  ` ${s}`
499
638
  ])
500
639
  ),
501
- (0, import_vue8.h)("input", {
640
+ (0, import_vue9.h)("input", {
502
641
  value: days.value,
503
642
  onInput: (e) => days.value = e.target.value,
504
643
  placeholder: "days (optional)",
505
644
  style: { ...styles.button, cursor: "text", width: "140px" },
506
645
  "aria-label": "Days"
507
646
  }),
508
- (0, import_vue8.h)(
647
+ (0, import_vue9.h)(
509
648
  "button",
510
649
  {
511
650
  type: "submit",
@@ -519,35 +658,35 @@ var TokensScreen = (0, import_vue8.defineComponent)({
519
658
  r?.issued && r.issued.length > 0 ? table(
520
659
  ["Name", "Scopes", "Created", "Expires", "Last used", ""],
521
660
  r.issued.map(
522
- (t) => (0, import_vue8.h)("tr", { key: t.id }, [
661
+ (t2) => (0, import_vue9.h)("tr", { key: t2.id }, [
523
662
  td([
524
- t.name,
525
- t.revokedAt ? (0, import_vue8.h)("span", { style: styles.badge }, " revoked") : ""
663
+ t2.name,
664
+ t2.revokedAt ? (0, import_vue9.h)("span", { style: styles.badge }, " revoked") : ""
526
665
  ]),
527
- td(t.scopes.join(", "), styles.mono),
666
+ td(t2.scopes.join(", "), styles.mono),
528
667
  td(
529
- `${new Date(t.createdAt).toLocaleDateString()} \xB7 ${t.createdBy}`,
668
+ `${new Date(t2.createdAt).toLocaleDateString()} \xB7 ${t2.createdBy}`,
530
669
  styles.muted
531
670
  ),
532
671
  td(
533
- t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never",
672
+ t2.expiresAt ? new Date(t2.expiresAt).toLocaleDateString() : "never",
534
673
  styles.muted
535
674
  ),
536
675
  td(
537
- t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never",
676
+ t2.lastUsedAt ? new Date(t2.lastUsedAt).toLocaleString() : "never",
538
677
  styles.muted
539
678
  ),
540
679
  td(
541
- t.revokedAt ? "" : (0, import_vue8.h)(
680
+ t2.revokedAt ? "" : (0, import_vue9.h)(
542
681
  "button",
543
682
  {
544
683
  type: "button",
545
684
  style: styles.button,
546
685
  onClick: () => {
547
686
  if (window.confirm(
548
- `Revoke "${t.name}"? Anything using it stops working immediately.`
687
+ `Revoke "${t2.name}"? Anything using it stops working immediately.`
549
688
  ))
550
- void revoke(t.id).catch(() => {
689
+ void revoke(t2.id).catch(() => {
551
690
  });
552
691
  }
553
692
  },
@@ -556,21 +695,21 @@ var TokensScreen = (0, import_vue8.defineComponent)({
556
695
  )
557
696
  ])
558
697
  )
559
- ) : (0, import_vue8.h)("p", { style: styles.muted }, "None issued.")
698
+ ) : (0, import_vue9.h)("p", { style: styles.muted }, "None issued.")
560
699
  ],
561
- (0, import_vue8.h)("h2", { style: styles.subtitle }, "Legacy per-brick tokens"),
562
- r && r.legacy.length === 0 ? (0, import_vue8.h)(
700
+ (0, import_vue9.h)("h2", { style: styles.subtitle }, "Legacy per-brick tokens"),
701
+ r && r.legacy.length === 0 ? (0, import_vue9.h)(
563
702
  "p",
564
703
  { style: styles.muted },
565
704
  "None \u2014 every brick's admin surface goes through this token."
566
- ) : (0, import_vue8.h)(
705
+ ) : (0, import_vue9.h)(
567
706
  "ul",
568
707
  { style: styles.list },
569
708
  (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),
709
+ (l) => (0, import_vue9.h)("li", { key: l.module, style: styles.row }, [
710
+ (0, import_vue9.h)("span", { style: styles.mono }, l.module),
572
711
  " ",
573
- (0, import_vue8.h)(
712
+ (0, import_vue9.h)(
574
713
  "span",
575
714
  { style: styles.muted },
576
715
  "still registers its standalone routes with its own token (deprecated)"
@@ -584,29 +723,29 @@ var TokensScreen = (0, import_vue8.defineComponent)({
584
723
  });
585
724
 
586
725
  // 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)({
726
+ var import_vue_admin9 = require("@fonderie/vue-admin");
727
+ var import_vue10 = require("vue");
728
+ var UsersScreen = (0, import_vue10.defineComponent)({
590
729
  name: "FonderieUsersScreen",
591
730
  props: {
592
731
  client: { type: Object, required: true },
593
732
  pageSize: { type: Number, default: 50 }
594
733
  },
595
734
  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)(
735
+ const input = (0, import_vue10.ref)("");
736
+ const email = (0, import_vue10.ref)("");
737
+ const selectedId = (0, import_vue10.ref)("");
738
+ const list = (0, import_vue_admin9.useAdminUsers)(props.client, { limit: props.pageSize });
739
+ const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin9.useAdminUser)(
601
740
  props.client,
602
741
  { email, id: selectedId }
603
742
  );
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)]);
743
+ const userId = (0, import_vue10.computed)(() => user.value?.id ?? null);
744
+ const sessions = (0, import_vue_admin9.useAdminUserSessions)(props.client, userId);
745
+ const history = (0, import_vue_admin9.useAdminLoginHistory)(props.client, userId, { limit: 20 });
746
+ const showList = (0, import_vue10.computed)(() => !email.value && !selectedId.value);
747
+ const yesNo = (v) => (0, import_vue10.h)("span", { style: v ? styles.ok : styles.muted }, v ? "yes" : "no");
748
+ const row = (k, v) => (0, import_vue10.h)("tr", [td(k), td(v)]);
610
749
  const clear = () => {
611
750
  email.value = "";
612
751
  selectedId.value = "";
@@ -614,9 +753,9 @@ var UsersScreen = (0, import_vue9.defineComponent)({
614
753
  };
615
754
  return () => {
616
755
  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)(
756
+ return (0, import_vue10.h)("div", { style: styles.container }, [
757
+ (0, import_vue10.h)("h1", { style: styles.title }, "Users"),
758
+ (0, import_vue10.h)(
620
759
  "form",
621
760
  {
622
761
  style: styles.toolbar,
@@ -627,7 +766,7 @@ var UsersScreen = (0, import_vue9.defineComponent)({
627
766
  }
628
767
  },
629
768
  [
630
- (0, import_vue9.h)("input", {
769
+ (0, import_vue10.h)("input", {
631
770
  type: "email",
632
771
  value: input.value,
633
772
  onInput: (e) => input.value = e.target.value,
@@ -635,7 +774,7 @@ var UsersScreen = (0, import_vue9.defineComponent)({
635
774
  style: { ...styles.button, cursor: "text", minWidth: "280px" },
636
775
  "aria-label": "Email"
637
776
  }),
638
- (0, import_vue9.h)(
777
+ (0, import_vue10.h)(
639
778
  "button",
640
779
  { type: "submit", style: styles.button, disabled: isLoading.value },
641
780
  "Look up"
@@ -643,14 +782,14 @@ var UsersScreen = (0, import_vue9.defineComponent)({
643
782
  showList.value ? refreshButton("Refresh", list.isLoading.value, () => void list.refresh()) : refreshButton("\u2190 All users", false, clear)
644
783
  ]
645
784
  ),
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(
785
+ showList.value ? (0, import_vue10.h)("div", [
786
+ list.error.value ? (0, import_vue10.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
787
+ list.users.value.length === 0 && !list.isLoading.value ? (0, import_vue10.h)("p", { style: styles.muted }, "No users yet.") : table(
649
788
  ["Email", "Name", "Created", "Status"],
650
789
  list.users.value.map(
651
- (u2) => (0, import_vue9.h)("tr", { key: u2.id }, [
790
+ (u2) => (0, import_vue10.h)("tr", { key: u2.id }, [
652
791
  td(
653
- (0, import_vue9.h)(
792
+ (0, import_vue10.h)(
654
793
  "button",
655
794
  {
656
795
  type: "button",
@@ -661,17 +800,17 @@ var UsersScreen = (0, import_vue9.defineComponent)({
661
800
  )
662
801
  ),
663
802
  td(
664
- `${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue9.h)("span", { style: styles.muted }, "\u2014")
803
+ `${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue10.h)("span", { style: styles.muted }, "\u2014")
665
804
  ),
666
805
  td(new Date(u2.createdAt).toLocaleDateString()),
667
806
  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")
807
+ 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
808
  )
670
809
  ])
671
810
  )
672
811
  ),
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)(
812
+ list.isLoading.value ? (0, import_vue10.h)("p", { style: styles.status }, "Loading\u2026") : null,
813
+ list.hasMore.value && !list.isLoading.value ? (0, import_vue10.h)(
675
814
  "button",
676
815
  {
677
816
  type: "button",
@@ -681,21 +820,21 @@ var UsersScreen = (0, import_vue9.defineComponent)({
681
820
  "Load more"
682
821
  ) : null
683
822
  ]) : null,
684
- error.value ? (0, import_vue9.h)(
823
+ error.value ? (0, import_vue10.h)(
685
824
  "p",
686
825
  { style: styles.error, role: "alert" },
687
826
  error.value.status === 404 ? "No user with that email." : error.value.explanation
688
827
  ) : null,
689
828
  u ? [
690
- (0, import_vue9.h)("h2", { style: styles.subtitle }, [
829
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, [
691
830
  u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
692
831
  " ",
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
832
+ u.suspended ? (0, import_vue10.h)("span", { style: styles.badge }, "suspended") : null,
833
+ u.deletedAt ? (0, import_vue10.h)("span", { style: styles.badge }, "deleted") : null
695
834
  ]),
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)),
835
+ (0, import_vue10.h)("table", { style: styles.table }, [
836
+ (0, import_vue10.h)("tbody", [
837
+ row("id", (0, import_vue10.h)("span", { style: styles.mono }, u.id)),
699
838
  row("email", [u.email, " \xB7 verified ", yesNo(u.isEmailVerified)]),
700
839
  row("MFA", yesNo(u.mfaEnabled)),
701
840
  row(
@@ -704,12 +843,12 @@ var UsersScreen = (0, import_vue9.defineComponent)({
704
843
  ),
705
844
  row(
706
845
  "last login",
707
- u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue9.h)("span", { style: styles.muted }, "never")
846
+ u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue10.h)("span", { style: styles.muted }, "never")
708
847
  ),
709
848
  row("created", new Date(u.createdAt).toLocaleString())
710
849
  ])
711
850
  ]),
712
- (0, import_vue9.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
851
+ (0, import_vue10.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
713
852
  u.suspended ? refreshButton("Unsuspend", isLoading.value, () => void unsuspend()) : refreshButton("Suspend", isLoading.value, () => void suspend()),
714
853
  refreshButton(
715
854
  "Sign out everywhere",
@@ -717,17 +856,17 @@ var UsersScreen = (0, import_vue9.defineComponent)({
717
856
  () => void revokeSessions().then(() => sessions.refresh())
718
857
  )
719
858
  ]),
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)(
859
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, "Live sessions"),
860
+ sessions.sessions.value.length === 0 ? (0, import_vue10.h)("p", { style: styles.muted }, "None.") : (0, import_vue10.h)(
722
861
  "ul",
723
862
  { style: styles.list },
724
863
  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"),
864
+ (s) => (0, import_vue10.h)("li", { key: s.id, style: styles.row }, [
865
+ (0, import_vue10.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
727
866
  " ",
728
- (0, import_vue9.h)("span", { style: styles.muted }, s.userAgent ?? ""),
867
+ (0, import_vue10.h)("span", { style: styles.muted }, s.userAgent ?? ""),
729
868
  " ",
730
- (0, import_vue9.h)(
869
+ (0, import_vue10.h)(
731
870
  "span",
732
871
  { style: styles.muted },
733
872
  `since ${new Date(s.createdAt).toLocaleString()}`
@@ -735,25 +874,25 @@ var UsersScreen = (0, import_vue9.defineComponent)({
735
874
  ])
736
875
  )
737
876
  ),
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)(
877
+ (0, import_vue10.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
878
+ history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue10.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue10.h)(
740
879
  "ul",
741
880
  { style: styles.list },
742
881
  history.events.value.map(
743
- (e) => (0, import_vue9.h)("li", { key: e.id, style: styles.row }, [
744
- (0, import_vue9.h)(
882
+ (e) => (0, import_vue10.h)("li", { key: e.id, style: styles.row }, [
883
+ (0, import_vue10.h)(
745
884
  "span",
746
885
  { style: e.outcome === "success" ? styles.ok : styles.bad },
747
886
  e.outcome
748
887
  ),
749
888
  " ",
750
- (0, import_vue9.h)("span", { style: styles.muted }, e.method),
889
+ (0, import_vue10.h)("span", { style: styles.muted }, e.method),
751
890
  " ",
752
- (0, import_vue9.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
891
+ (0, import_vue10.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
753
892
  ])
754
893
  )
755
894
  ),
756
- history.hasMore.value && !history.isLoading.value ? (0, import_vue9.h)(
895
+ history.hasMore.value && !history.isLoading.value ? (0, import_vue10.h)(
757
896
  "button",
758
897
  {
759
898
  type: "button",
@@ -769,45 +908,45 @@ var UsersScreen = (0, import_vue9.defineComponent)({
769
908
  });
770
909
 
771
910
  // 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)({
911
+ var import_vue_admin10 = require("@fonderie/vue-admin");
912
+ var import_vue11 = require("vue");
913
+ var CatalogScreen = (0, import_vue11.defineComponent)({
775
914
  name: "FonderieCatalogScreen",
776
915
  props: { client: { type: Object, required: true } },
777
916
  setup(props) {
778
- const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin9.useAdminCatalog)(props.client);
917
+ const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin10.useAdminCatalog)(props.client);
779
918
  const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
780
919
  return () => {
781
920
  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"),
921
+ return (0, import_vue11.h)("div", { style: styles.container }, [
922
+ (0, import_vue11.h)("div", { style: styles.toolbar }, [
923
+ (0, import_vue11.h)("h1", { style: { ...styles.title, marginBottom: 0 } }, "Catalog"),
785
924
  refreshButton("Refresh", isLoading.value, () => void refresh())
786
925
  ]),
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,
926
+ error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
927
+ isLoading.value && !c ? (0, import_vue11.h)("p", { style: styles.status }, "Loading\u2026") : null,
789
928
  c ? [
790
- (0, import_vue10.h)("h2", { style: styles.subtitle }, "Configured (code)"),
791
- (0, import_vue10.h)(
929
+ (0, import_vue11.h)("h2", { style: styles.subtitle }, "Configured (code)"),
930
+ (0, import_vue11.h)(
792
931
  "pre",
793
932
  {
794
933
  style: {
795
934
  ...styles.mono,
796
- background: "#f9fafb",
935
+ background: "var(--fonderie-surface-alt,#fafafa)",
797
936
  padding: "12px",
798
937
  overflowX: "auto"
799
938
  }
800
939
  },
801
940
  JSON.stringify(c.configured, null, 2)
802
941
  ),
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(
942
+ (0, import_vue11.h)("h2", { style: styles.subtitle }, "Stored (database)"),
943
+ c.stored.length === 0 ? (0, import_vue11.h)("p", { style: styles.muted }, "No stored plans.") : table(
805
944
  ["Plan", "Tier", "Seats", "Monthly", "Yearly", "Trial", ""],
806
945
  c.stored.map(
807
- (p) => (0, import_vue10.h)("tr", { key: p.id }, [
946
+ (p) => (0, import_vue11.h)("tr", { key: p.id }, [
808
947
  td([
809
- (0, import_vue10.h)("strong", p.name),
810
- p.description ? (0, import_vue10.h)("div", { style: styles.muted }, p.description) : ""
948
+ (0, import_vue11.h)("strong", p.name),
949
+ p.description ? (0, import_vue11.h)("div", { style: styles.muted }, p.description) : ""
811
950
  ]),
812
951
  td(String(p.tier)),
813
952
  td(p.seats == null ? "\u221E" : String(p.seats)),
@@ -815,7 +954,7 @@ var CatalogScreen = (0, import_vue10.defineComponent)({
815
954
  td(money(p.pricing?.yearly)),
816
955
  td(p.trialDays ? `${p.trialDays} d` : "\u2014"),
817
956
  td(
818
- (0, import_vue10.h)(
957
+ (0, import_vue11.h)(
819
958
  "button",
820
959
  {
821
960
  type: "button",
@@ -839,30 +978,30 @@ var CatalogScreen = (0, import_vue10.defineComponent)({
839
978
  });
840
979
 
841
980
  // 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)({
981
+ var import_vue_admin11 = require("@fonderie/vue-admin");
982
+ var import_vue12 = require("vue");
983
+ var SubscriberScreen = (0, import_vue12.defineComponent)({
845
984
  name: "FonderieSubscriberScreen",
846
985
  props: {
847
986
  client: { type: Object, required: true },
848
987
  pageSize: { type: Number, default: 50 }
849
988
  },
850
989
  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 });
856
- const open = (t, id) => {
990
+ const type = (0, import_vue12.ref)("user");
991
+ const idInput = (0, import_vue12.ref)("");
992
+ const subscriber = (0, import_vue12.ref)(null);
993
+ const list = (0, import_vue_admin11.useAdminSubscribers)(props.client, { limit: props.pageSize });
994
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin11.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
995
+ const open = (t2, id) => {
857
996
  granted.value = null;
858
- type.value = t;
997
+ type.value = t2;
859
998
  idInput.value = id;
860
- subscriber.value = { type: t, id };
999
+ subscriber.value = { type: t2, id };
861
1000
  };
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", {
1001
+ const amount = (0, import_vue12.ref)("");
1002
+ const note = (0, import_vue12.ref)("");
1003
+ const granted = (0, import_vue12.ref)(null);
1004
+ const input = (model, placeholder, extra = {}) => (0, import_vue12.h)("input", {
866
1005
  value: model.value,
867
1006
  onInput: (e) => model.value = e.target.value,
868
1007
  placeholder,
@@ -872,9 +1011,9 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
872
1011
  return () => {
873
1012
  const s = subscription.value;
874
1013
  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)(
1014
+ return (0, import_vue12.h)("div", { style: styles.container }, [
1015
+ (0, import_vue12.h)("h1", { style: styles.title }, "Subscriber"),
1016
+ (0, import_vue12.h)(
878
1017
  "form",
879
1018
  {
880
1019
  style: styles.toolbar,
@@ -885,7 +1024,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
885
1024
  }
886
1025
  },
887
1026
  [
888
- (0, import_vue11.h)(
1027
+ (0, import_vue12.h)(
889
1028
  "select",
890
1029
  {
891
1030
  value: type.value,
@@ -894,17 +1033,17 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
894
1033
  "aria-label": "Type"
895
1034
  },
896
1035
  [
897
- (0, import_vue11.h)("option", { value: "user" }, "user"),
898
- (0, import_vue11.h)("option", { value: "workspace" }, "workspace")
1036
+ (0, import_vue12.h)("option", { value: "user" }, "user"),
1037
+ (0, import_vue12.h)("option", { value: "workspace" }, "workspace")
899
1038
  ]
900
1039
  ),
901
1040
  input(idInput, "subscriber id", { minWidth: "280px" }),
902
- (0, import_vue11.h)(
1041
+ (0, import_vue12.h)(
903
1042
  "button",
904
1043
  { type: "submit", style: styles.button, disabled: isLoading.value },
905
1044
  "Look up"
906
1045
  ),
907
- subscriber.value ? (0, import_vue11.h)(
1046
+ subscriber.value ? (0, import_vue12.h)(
908
1047
  "button",
909
1048
  {
910
1049
  type: "button",
@@ -916,7 +1055,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
916
1055
  }
917
1056
  },
918
1057
  "\u2190 All subscribers"
919
- ) : (0, import_vue11.h)(
1058
+ ) : (0, import_vue12.h)(
920
1059
  "button",
921
1060
  {
922
1061
  type: "button",
@@ -928,14 +1067,14 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
928
1067
  )
929
1068
  ]
930
1069
  ),
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(
1070
+ !subscriber.value ? (0, import_vue12.h)("div", [
1071
+ list.error.value ? (0, import_vue12.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
1072
+ list.subscriptions.value.length === 0 && !list.isLoading.value ? (0, import_vue12.h)("p", { style: styles.muted }, "No subscribers yet.") : table(
934
1073
  ["Subscriber", "Plan", "Status", "Renews"],
935
1074
  list.subscriptions.value.map(
936
- (sub) => (0, import_vue11.h)("tr", { key: sub.id }, [
1075
+ (sub) => (0, import_vue12.h)("tr", { key: sub.id }, [
937
1076
  td(
938
- (0, import_vue11.h)(
1077
+ (0, import_vue12.h)(
939
1078
  "button",
940
1079
  {
941
1080
  type: "button",
@@ -947,16 +1086,16 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
947
1086
  ),
948
1087
  td(`${sub.plan} \xB7 ${sub.interval}`),
949
1088
  td(
950
- sub.cancelAtPeriodEnd ? [sub.status, " ", (0, import_vue11.h)("span", { style: styles.badge }, "cancels")] : sub.status
1089
+ sub.cancelAtPeriodEnd ? [sub.status, " ", (0, import_vue12.h)("span", { style: styles.badge }, "cancels")] : sub.status
951
1090
  ),
952
1091
  td(
953
- sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : (0, import_vue11.h)("span", { style: styles.muted }, "\u2014")
1092
+ sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : (0, import_vue12.h)("span", { style: styles.muted }, "\u2014")
954
1093
  )
955
1094
  ])
956
1095
  )
957
1096
  ),
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)(
1097
+ list.isLoading.value ? (0, import_vue12.h)("p", { style: styles.status }, "Loading\u2026") : null,
1098
+ list.hasMore.value && !list.isLoading.value ? (0, import_vue12.h)(
960
1099
  "button",
961
1100
  {
962
1101
  type: "button",
@@ -966,37 +1105,37 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
966
1105
  "Load more"
967
1106
  ) : null
968
1107
  ]) : null,
969
- error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
1108
+ error.value ? (0, import_vue12.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
970
1109
  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", [
1110
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Subscription"),
1111
+ s ? (0, import_vue12.h)("table", { style: styles.table }, [
1112
+ (0, import_vue12.h)("tbody", [
1113
+ (0, import_vue12.h)("tr", [td("plan"), td([(0, import_vue12.h)("strong", s.plan), ` \xB7 ${s.interval}`])]),
1114
+ (0, import_vue12.h)("tr", [
976
1115
  td("status"),
977
1116
  td(`${s.status}${s.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""}`)
978
1117
  ]),
979
- (0, import_vue11.h)("tr", [
1118
+ (0, import_vue12.h)("tr", [
980
1119
  td("period"),
981
1120
  td(
982
1121
  `${s.currentPeriodStart ? new Date(s.currentPeriodStart).toLocaleDateString() : "\u2014"} \u2192 ${s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : "\u2014"}`
983
1122
  )
984
1123
  ]),
985
- (0, import_vue11.h)("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
1124
+ (0, import_vue12.h)("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
986
1125
  ])
987
- ]) : (0, import_vue11.h)("p", { style: styles.muted }, "No subscription."),
988
- (0, import_vue11.h)("h2", { style: styles.subtitle }, "Wallet"),
1126
+ ]) : (0, import_vue12.h)("p", { style: styles.muted }, "No subscription."),
1127
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Wallet"),
989
1128
  w ? [
990
- (0, import_vue11.h)("p", [
991
- (0, import_vue11.h)("strong", w.balance),
1129
+ (0, import_vue12.h)("p", [
1130
+ (0, import_vue12.h)("strong", w.balance),
992
1131
  ` ${w.currency} `,
993
- (0, import_vue11.h)(
1132
+ (0, import_vue12.h)(
994
1133
  "span",
995
1134
  { style: styles.muted },
996
1135
  `(granted ${w.granted ?? "0"} \xB7 purchased ${w.purchased ?? "0"}) \xB7 minor units, precision ${w.precision}`
997
1136
  )
998
1137
  ]),
999
- (0, import_vue11.h)(
1138
+ (0, import_vue12.h)(
1000
1139
  "form",
1001
1140
  {
1002
1141
  style: styles.toolbar,
@@ -1021,7 +1160,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1021
1160
  [
1022
1161
  input(amount, "amount (minor units)"),
1023
1162
  input(note, "reason (optional)", { minWidth: "220px" }),
1024
- (0, import_vue11.h)(
1163
+ (0, import_vue12.h)(
1025
1164
  "button",
1026
1165
  {
1027
1166
  type: "submit",
@@ -1030,22 +1169,22 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1030
1169
  },
1031
1170
  "Grant"
1032
1171
  ),
1033
- granted.value ? (0, import_vue11.h)("span", { style: styles.ok }, granted.value) : null
1172
+ granted.value ? (0, import_vue12.h)("span", { style: styles.ok }, granted.value) : null
1034
1173
  ]
1035
1174
  ),
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(
1175
+ (0, import_vue12.h)("h2", { style: styles.subtitle }, "Ledger"),
1176
+ ledger.value.length === 0 ? (0, import_vue12.h)("p", { style: styles.muted }, "Nothing moved yet.") : table(
1038
1177
  ["When", "Kind", "Amount", "Note"],
1039
1178
  ledger.value.map(
1040
- (t) => (0, import_vue11.h)("tr", { key: t.id }, [
1041
- td(new Date(t.createdAt).toLocaleString(), styles.muted),
1042
- td(t.type),
1043
- td(t.amount, styles.mono),
1044
- td(t.description ?? "", styles.muted)
1179
+ (t2) => (0, import_vue12.h)("tr", { key: t2.id }, [
1180
+ td(new Date(t2.createdAt).toLocaleString(), styles.muted),
1181
+ td(t2.type),
1182
+ td(t2.amount, styles.mono),
1183
+ td(t2.description ?? "", styles.muted)
1045
1184
  ])
1046
1185
  )
1047
1186
  ),
1048
- hasMoreLedger.value ? (0, import_vue11.h)(
1187
+ hasMoreLedger.value ? (0, import_vue12.h)(
1049
1188
  "button",
1050
1189
  {
1051
1190
  type: "button",
@@ -1054,7 +1193,7 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1054
1193
  },
1055
1194
  "Load more"
1056
1195
  ) : null
1057
- ] : (0, import_vue11.h)(
1196
+ ] : (0, import_vue12.h)(
1058
1197
  "p",
1059
1198
  { style: styles.muted },
1060
1199
  "No wallet \u2014 billing has no wallet configuration."
@@ -1066,31 +1205,31 @@ var SubscriberScreen = (0, import_vue11.defineComponent)({
1066
1205
  });
1067
1206
 
1068
1207
  // 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)({
1208
+ var import_vue_admin12 = require("@fonderie/vue-admin");
1209
+ var import_vue13 = require("vue");
1210
+ var AuditScreen = (0, import_vue13.defineComponent)({
1072
1211
  name: "FonderieAuditScreen",
1073
1212
  props: {
1074
1213
  client: { type: Object, required: true },
1075
1214
  pageSize: { type: Number, default: 50 }
1076
1215
  },
1077
1216
  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)(
1217
+ const draft = (0, import_vue13.ref)({ workspaceId: "", type: "", actorId: "" });
1218
+ const query = (0, import_vue13.ref)({ limit: props.pageSize });
1219
+ const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin12.useAdminAudit)(
1081
1220
  props.client,
1082
1221
  query
1083
1222
  );
1084
- const field = (key, placeholder) => (0, import_vue12.h)("input", {
1223
+ const field = (key, placeholder) => (0, import_vue13.h)("input", {
1085
1224
  value: draft.value[key],
1086
1225
  onInput: (e) => draft.value = { ...draft.value, [key]: e.target.value },
1087
1226
  placeholder,
1088
1227
  style: { ...styles.button, cursor: "text" },
1089
1228
  "aria-label": placeholder
1090
1229
  });
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)(
1230
+ return () => (0, import_vue13.h)("div", { style: styles.container }, [
1231
+ (0, import_vue13.h)("h1", { style: styles.title }, "Audit"),
1232
+ (0, import_vue13.h)(
1094
1233
  "form",
1095
1234
  {
1096
1235
  style: styles.toolbar,
@@ -1109,7 +1248,7 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1109
1248
  field("workspaceId", "workspace id (all if empty)"),
1110
1249
  field("type", "event type"),
1111
1250
  field("actorId", "actor id"),
1112
- (0, import_vue12.h)(
1251
+ (0, import_vue13.h)(
1113
1252
  "button",
1114
1253
  { type: "submit", style: styles.button, disabled: isLoading.value },
1115
1254
  "Filter"
@@ -1117,11 +1256,11 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1117
1256
  refreshButton("Refresh", isLoading.value, () => void refresh())
1118
1257
  ]
1119
1258
  ),
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(
1259
+ error.value ? (0, import_vue13.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
1260
+ events.value.length === 0 && !isLoading.value ? (0, import_vue13.h)("p", { style: styles.muted }, "Nothing recorded for this filter.") : table(
1122
1261
  ["When", "Type", "Workspace", "Actor", "Request"],
1123
1262
  events.value.map(
1124
- (e) => (0, import_vue12.h)("tr", { key: e.id }, [
1263
+ (e) => (0, import_vue13.h)("tr", { key: e.id }, [
1125
1264
  td(new Date(e.createdAt).toLocaleString(), styles.muted),
1126
1265
  td(e.type, styles.mono),
1127
1266
  td(String(e.payload["workspaceId"] ?? "\u2014"), styles.mono),
@@ -1130,8 +1269,8 @@ var AuditScreen = (0, import_vue12.defineComponent)({
1130
1269
  ])
1131
1270
  )
1132
1271
  ),
1133
- isLoading.value ? (0, import_vue12.h)("p", { style: styles.status }, "Loading\u2026") : null,
1134
- hasMore.value && !isLoading.value ? (0, import_vue12.h)(
1272
+ isLoading.value ? (0, import_vue13.h)("p", { style: styles.status }, "Loading\u2026") : null,
1273
+ hasMore.value && !isLoading.value ? (0, import_vue13.h)(
1135
1274
  "button",
1136
1275
  {
1137
1276
  type: "button",
@@ -1171,11 +1310,12 @@ var NAV = [
1171
1310
  items: [
1172
1311
  { page: "audit", label: "Audit", needs: "audit" },
1173
1312
  { page: "log", label: "Admin log" },
1174
- { page: "tokens", label: "Access" }
1313
+ { page: "tokens", label: "Access" },
1314
+ { page: "migrations", label: "Migrations" }
1175
1315
  ]
1176
1316
  }
1177
1317
  ];
1178
- var AdminShell = (0, import_vue13.defineComponent)({
1318
+ var AdminShell = (0, import_vue14.defineComponent)({
1179
1319
  name: "FonderieAdminShell",
1180
1320
  props: {
1181
1321
  client: { type: Object, required: true },
@@ -1196,15 +1336,15 @@ var AdminShell = (0, import_vue13.defineComponent)({
1196
1336
  },
1197
1337
  emits: { navigate: (_page) => true },
1198
1338
  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);
1339
+ const own = (0, import_vue14.ref)("attention");
1340
+ const current = (0, import_vue14.computed)(() => props.page ?? own.value);
1341
+ const editing = (0, import_vue14.ref)(null);
1202
1342
  const go = (p) => {
1203
1343
  editing.value = null;
1204
1344
  emit("navigate", p);
1205
1345
  if (props.page === void 0) own.value = p;
1206
1346
  };
1207
- const back = () => (0, import_vue13.h)(
1347
+ const back = () => (0, import_vue14.h)(
1208
1348
  "button",
1209
1349
  { type: "button", style: styles.button, onClick: () => editing.value = null },
1210
1350
  "\u2190 Back"
@@ -1213,43 +1353,45 @@ var AdminShell = (0, import_vue13.defineComponent)({
1213
1353
  const c = props.client;
1214
1354
  switch (current.value) {
1215
1355
  case "attention":
1216
- return (0, import_vue13.h)(AttentionScreen, { client: c });
1356
+ return (0, import_vue14.h)(AttentionScreen, { client: c });
1217
1357
  case "modules":
1218
- return (0, import_vue13.h)(ModulesScreen, { client: c });
1358
+ return (0, import_vue14.h)(ModulesScreen, { client: c });
1219
1359
  case "doctor":
1220
- return (0, import_vue13.h)(DoctorScreen, { client: c });
1360
+ return (0, import_vue14.h)(DoctorScreen, { client: c });
1221
1361
  case "config":
1222
- return (0, import_vue13.h)(ConfigScreen, { client: c });
1362
+ return (0, import_vue14.h)(ConfigScreen, { client: c });
1223
1363
  case "routes":
1224
- return (0, import_vue13.h)(RoutesScreen, { client: c });
1364
+ return (0, import_vue14.h)(RoutesScreen, { client: c });
1225
1365
  case "tokens":
1226
- return (0, import_vue13.h)(TokensScreen, { client: c });
1366
+ return (0, import_vue14.h)(TokensScreen, { client: c });
1367
+ case "migrations":
1368
+ return (0, import_vue14.h)(MigrationsScreen, { client: c });
1227
1369
  case "log":
1228
- return (0, import_vue13.h)(AdminLogScreen, { client: c });
1370
+ return (0, import_vue14.h)(AdminLogScreen, { client: c });
1229
1371
  case "catalog":
1230
- return props.billingClient ? (0, import_vue13.h)(CatalogScreen, { client: props.billingClient }) : (0, import_vue13.h)(
1372
+ return props.billingClient ? (0, import_vue14.h)(CatalogScreen, { client: props.billingClient }) : (0, import_vue14.h)(
1231
1373
  "p",
1232
1374
  { style: styles.status },
1233
1375
  "Pass a BillingAdminClient to see the catalog here."
1234
1376
  );
1235
1377
  case "subscriber":
1236
- return props.billingClient ? (0, import_vue13.h)(SubscriberScreen, { client: props.billingClient }) : (0, import_vue13.h)(
1378
+ return props.billingClient ? (0, import_vue14.h)(SubscriberScreen, { client: props.billingClient }) : (0, import_vue14.h)(
1237
1379
  "p",
1238
1380
  { style: styles.status },
1239
1381
  "Pass a BillingAdminClient to look up subscribers here."
1240
1382
  );
1241
1383
  case "audit":
1242
- return props.auditClient ? (0, import_vue13.h)(AuditScreen, { client: props.auditClient }) : (0, import_vue13.h)(
1384
+ return props.auditClient ? (0, import_vue14.h)(AuditScreen, { client: props.auditClient }) : (0, import_vue14.h)(
1243
1385
  "p",
1244
1386
  { style: styles.status },
1245
1387
  "Pass an AuditAdminClient to see the audit trail here."
1246
1388
  );
1247
1389
  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.");
1390
+ 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
1391
  case "settings": {
1250
1392
  const cc = props.configClient;
1251
1393
  if (!cc)
1252
- return (0, import_vue13.h)(
1394
+ return (0, import_vue14.h)(
1253
1395
  "p",
1254
1396
  { style: styles.status },
1255
1397
  "Pass a ConfigAdminClient to manage config and secrets here."
@@ -1258,7 +1400,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1258
1400
  if (e && e.kind !== "template")
1259
1401
  return [
1260
1402
  back(),
1261
- (0, import_vue13.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
1403
+ (0, import_vue14.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
1262
1404
  client: cc,
1263
1405
  kind: e.kind,
1264
1406
  configKey: e.key,
@@ -1266,7 +1408,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1266
1408
  onSaved: () => editing.value = null
1267
1409
  })
1268
1410
  ];
1269
- return (0, import_vue13.h)(import_vue_config_admin_screens.ConfigListScreen, {
1411
+ return (0, import_vue14.h)(import_vue_config_admin_screens.ConfigListScreen, {
1270
1412
  client: cc,
1271
1413
  ...props.environment !== void 0 ? { environment: props.environment } : {},
1272
1414
  "onSelect-config": (key) => editing.value = { kind: "config", key },
@@ -1276,7 +1418,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1276
1418
  case "templates": {
1277
1419
  const kc = props.courierClient;
1278
1420
  if (!kc)
1279
- return (0, import_vue13.h)(
1421
+ return (0, import_vue14.h)(
1280
1422
  "p",
1281
1423
  { style: styles.status },
1282
1424
  "Pass a CourierAdminClient to manage templates here."
@@ -1285,21 +1427,21 @@ var AdminShell = (0, import_vue13.defineComponent)({
1285
1427
  if (e?.kind === "template")
1286
1428
  return [
1287
1429
  back(),
1288
- (0, import_vue13.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
1430
+ (0, import_vue14.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
1289
1431
  client: kc,
1290
1432
  type: e.type,
1291
1433
  onSaved: () => editing.value = null
1292
1434
  })
1293
1435
  ];
1294
- return (0, import_vue13.h)(import_vue_courier_admin_screens.TemplateListScreen, {
1436
+ return (0, import_vue14.h)(import_vue_courier_admin_screens.TemplateListScreen, {
1295
1437
  client: kc,
1296
- "onSelect-template": (t) => editing.value = { kind: "template", type: t.type }
1438
+ "onSelect-template": (t2) => editing.value = { kind: "template", type: t2.type }
1297
1439
  });
1298
1440
  }
1299
1441
  }
1300
1442
  };
1301
- return () => (0, import_vue13.h)("div", { style: styles.shell }, [
1302
- (0, import_vue13.h)(
1443
+ return () => (0, import_vue14.h)("div", { style: styles.shell }, [
1444
+ (0, import_vue14.h)(
1303
1445
  "nav",
1304
1446
  { style: styles.nav, "aria-label": "Admin" },
1305
1447
  NAV.map((g) => {
@@ -1307,10 +1449,10 @@ var AdminShell = (0, import_vue13.defineComponent)({
1307
1449
  (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
1450
  );
1309
1451
  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),
1452
+ return (0, import_vue14.h)("div", { key: g.group }, [
1453
+ (0, import_vue14.h)("div", { style: styles.navGroup }, g.group),
1312
1454
  ...items.map(
1313
- (i) => (0, import_vue13.h)(
1455
+ (i) => (0, import_vue14.h)(
1314
1456
  "button",
1315
1457
  {
1316
1458
  key: i.page,
@@ -1328,7 +1470,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1328
1470
  ]);
1329
1471
  })
1330
1472
  ),
1331
- (0, import_vue13.h)("main", { style: styles.main }, body())
1473
+ (0, import_vue14.h)("main", { style: styles.main }, body())
1332
1474
  ]);
1333
1475
  }
1334
1476
  });
@@ -1341,6 +1483,7 @@ var AdminShell = (0, import_vue13.defineComponent)({
1341
1483
  CatalogScreen,
1342
1484
  ConfigScreen,
1343
1485
  DoctorScreen,
1486
+ MigrationsScreen,
1344
1487
  ModulesScreen,
1345
1488
  RoutesScreen,
1346
1489
  SubscriberScreen,