@fonderie/vue-admin-screens 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/index.cjs +421 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +417 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/index.d.cts +0 -169
- package/dist/index.d.ts +0 -169
package/README.md
CHANGED
|
@@ -10,12 +10,14 @@ Status: **experimental** (0.x).
|
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| Today | Attention | what needs me today — empty is green |
|
|
12
12
|
| System | Modules · Configuration · Doctor · Routes | what did I deploy · is it configured · is it working · what is exposed |
|
|
13
|
+
| People | Users | why can't this person log in — lookup, sessions, sign-ins, suspend, sign out everywhere (`authClient`) |
|
|
14
|
+
| Money | Catalog · Subscriber | what am I selling (configured vs stored) · what is this subscriber on, their wallet, ledger, a manual grant (`billingClient`) |
|
|
13
15
|
| Settings | Config & secrets | the `@fonderie/config` admin screens, as a sub-page |
|
|
14
16
|
| Messaging | Templates | the `@fonderie/courier` admin screens, as a sub-page |
|
|
15
17
|
| Activity | Admin log · Access | who did what · who can be here |
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
|
-
import { AdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
|
|
20
|
+
import { AdminClient, AuthAdminClient, BillingAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
|
|
19
21
|
import { AdminShell } from '@fonderie/vue-admin-screens';
|
|
20
22
|
|
|
21
23
|
const base = { baseUrl: 'https://api.example.com', adminToken, actor: 'louis' };
|
|
@@ -23,10 +25,12 @@ const client = new AdminClient(base);
|
|
|
23
25
|
// prefix: the composed surface under /_admin — one token for everything.
|
|
24
26
|
const configClient = new ConfigAdminClient({ ...base, prefix: '/_admin' });
|
|
25
27
|
const courierClient = new CourierAdminClient({ ...base, prefix: '/_admin' });
|
|
28
|
+
const authClient = new AuthAdminClient(base); // @fonderie/auth ≥ 7.8
|
|
29
|
+
const billingClient = new BillingAdminClient(base); // @fonderie/billing ≥ 9.10
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
```ts
|
|
29
|
-
h(AdminShell, { client, configClient, courierClient })
|
|
33
|
+
h(AdminShell, { client, configClient, courierClient, authClient, billingClient })
|
|
30
34
|
```
|
|
31
35
|
|
|
32
36
|
Pass `page` and listen to `navigate` to own the URL (one route per page);
|
package/dist/index.cjs
CHANGED
|
@@ -23,18 +23,21 @@ __export(index_exports, {
|
|
|
23
23
|
AdminLogScreen: () => AdminLogScreen,
|
|
24
24
|
AdminShell: () => AdminShell,
|
|
25
25
|
AttentionScreen: () => AttentionScreen,
|
|
26
|
+
CatalogScreen: () => CatalogScreen,
|
|
26
27
|
ConfigScreen: () => ConfigScreen,
|
|
27
28
|
DoctorScreen: () => DoctorScreen,
|
|
28
29
|
ModulesScreen: () => ModulesScreen,
|
|
29
30
|
RoutesScreen: () => RoutesScreen,
|
|
30
|
-
|
|
31
|
+
SubscriberScreen: () => SubscriberScreen,
|
|
32
|
+
TokensScreen: () => TokensScreen,
|
|
33
|
+
UsersScreen: () => UsersScreen
|
|
31
34
|
});
|
|
32
35
|
module.exports = __toCommonJS(index_exports);
|
|
33
36
|
|
|
34
37
|
// src/screens/AdminShell.ts
|
|
35
38
|
var import_vue_config_admin_screens = require("@fonderie/vue-config-admin-screens");
|
|
36
39
|
var import_vue_courier_admin_screens = require("@fonderie/vue-courier-admin-screens");
|
|
37
|
-
var
|
|
40
|
+
var import_vue12 = require("vue");
|
|
38
41
|
|
|
39
42
|
// src/styles.ts
|
|
40
43
|
var styles = {
|
|
@@ -460,6 +463,367 @@ var TokensScreen = (0, import_vue8.defineComponent)({
|
|
|
460
463
|
}
|
|
461
464
|
});
|
|
462
465
|
|
|
466
|
+
// src/screens/UsersScreen.ts
|
|
467
|
+
var import_vue_admin8 = require("@fonderie/vue-admin");
|
|
468
|
+
var import_vue9 = require("vue");
|
|
469
|
+
var UsersScreen = (0, import_vue9.defineComponent)({
|
|
470
|
+
name: "FonderieUsersScreen",
|
|
471
|
+
props: { client: { type: Object, required: true } },
|
|
472
|
+
setup(props) {
|
|
473
|
+
const input = (0, import_vue9.ref)("");
|
|
474
|
+
const email = (0, import_vue9.ref)("");
|
|
475
|
+
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin8.useAdminUser)(
|
|
476
|
+
props.client,
|
|
477
|
+
{ email }
|
|
478
|
+
);
|
|
479
|
+
const userId = (0, import_vue9.computed)(() => user.value?.id ?? null);
|
|
480
|
+
const sessions = (0, import_vue_admin8.useAdminUserSessions)(props.client, userId);
|
|
481
|
+
const history = (0, import_vue_admin8.useAdminLoginHistory)(props.client, userId, { limit: 20 });
|
|
482
|
+
const yesNo = (v) => (0, import_vue9.h)("span", { style: v ? styles.ok : styles.muted }, v ? "yes" : "no");
|
|
483
|
+
const row = (k, v) => (0, import_vue9.h)("tr", [td(k), td(v)]);
|
|
484
|
+
return () => {
|
|
485
|
+
const u = user.value;
|
|
486
|
+
return (0, import_vue9.h)("div", { style: styles.container }, [
|
|
487
|
+
(0, import_vue9.h)("h1", { style: styles.title }, "Users"),
|
|
488
|
+
(0, import_vue9.h)(
|
|
489
|
+
"form",
|
|
490
|
+
{
|
|
491
|
+
style: styles.toolbar,
|
|
492
|
+
onSubmit: (e) => {
|
|
493
|
+
e.preventDefault();
|
|
494
|
+
email.value = input.value.trim();
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
[
|
|
498
|
+
(0, import_vue9.h)("input", {
|
|
499
|
+
type: "email",
|
|
500
|
+
value: input.value,
|
|
501
|
+
onInput: (e) => input.value = e.target.value,
|
|
502
|
+
placeholder: "email address",
|
|
503
|
+
style: { ...styles.button, cursor: "text", minWidth: "280px" },
|
|
504
|
+
"aria-label": "Email"
|
|
505
|
+
}),
|
|
506
|
+
(0, import_vue9.h)(
|
|
507
|
+
"button",
|
|
508
|
+
{ type: "submit", style: styles.button, disabled: isLoading.value },
|
|
509
|
+
"Look up"
|
|
510
|
+
)
|
|
511
|
+
]
|
|
512
|
+
),
|
|
513
|
+
error.value ? (0, import_vue9.h)(
|
|
514
|
+
"p",
|
|
515
|
+
{ style: styles.error, role: "alert" },
|
|
516
|
+
error.value.status === 404 ? "No user with that email." : error.value.explanation
|
|
517
|
+
) : null,
|
|
518
|
+
u ? [
|
|
519
|
+
(0, import_vue9.h)("h2", { style: styles.subtitle }, [
|
|
520
|
+
u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
|
|
521
|
+
" ",
|
|
522
|
+
u.suspended ? (0, import_vue9.h)("span", { style: styles.badge }, "suspended") : null,
|
|
523
|
+
u.deletedAt ? (0, import_vue9.h)("span", { style: styles.badge }, "deleted") : null
|
|
524
|
+
]),
|
|
525
|
+
(0, import_vue9.h)("table", { style: styles.table }, [
|
|
526
|
+
(0, import_vue9.h)("tbody", [
|
|
527
|
+
row("id", (0, import_vue9.h)("span", { style: styles.mono }, u.id)),
|
|
528
|
+
row("email", [u.email, " \xB7 verified ", yesNo(u.isEmailVerified)]),
|
|
529
|
+
row("MFA", yesNo(u.mfaEnabled)),
|
|
530
|
+
row(
|
|
531
|
+
"provider",
|
|
532
|
+
`${u.provider || "password"}${u.hasPassword ? "" : " \xB7 no password set"}`
|
|
533
|
+
),
|
|
534
|
+
row(
|
|
535
|
+
"last login",
|
|
536
|
+
u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue9.h)("span", { style: styles.muted }, "never")
|
|
537
|
+
),
|
|
538
|
+
row("created", new Date(u.createdAt).toLocaleString())
|
|
539
|
+
])
|
|
540
|
+
]),
|
|
541
|
+
(0, import_vue9.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
|
|
542
|
+
u.suspended ? refreshButton("Unsuspend", isLoading.value, () => void unsuspend()) : refreshButton("Suspend", isLoading.value, () => void suspend()),
|
|
543
|
+
refreshButton(
|
|
544
|
+
"Sign out everywhere",
|
|
545
|
+
isLoading.value,
|
|
546
|
+
() => void revokeSessions().then(() => sessions.refresh())
|
|
547
|
+
)
|
|
548
|
+
]),
|
|
549
|
+
(0, import_vue9.h)("h2", { style: styles.subtitle }, "Live sessions"),
|
|
550
|
+
sessions.sessions.value.length === 0 ? (0, import_vue9.h)("p", { style: styles.muted }, "None.") : (0, import_vue9.h)(
|
|
551
|
+
"ul",
|
|
552
|
+
{ style: styles.list },
|
|
553
|
+
sessions.sessions.value.map(
|
|
554
|
+
(s) => (0, import_vue9.h)("li", { key: s.id, style: styles.row }, [
|
|
555
|
+
(0, import_vue9.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
|
|
556
|
+
" ",
|
|
557
|
+
(0, import_vue9.h)("span", { style: styles.muted }, s.userAgent ?? ""),
|
|
558
|
+
" ",
|
|
559
|
+
(0, import_vue9.h)(
|
|
560
|
+
"span",
|
|
561
|
+
{ style: styles.muted },
|
|
562
|
+
`since ${new Date(s.createdAt).toLocaleString()}`
|
|
563
|
+
)
|
|
564
|
+
])
|
|
565
|
+
)
|
|
566
|
+
),
|
|
567
|
+
(0, import_vue9.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
|
|
568
|
+
history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue9.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue9.h)(
|
|
569
|
+
"ul",
|
|
570
|
+
{ style: styles.list },
|
|
571
|
+
history.events.value.map(
|
|
572
|
+
(e) => (0, import_vue9.h)("li", { key: e.id, style: styles.row }, [
|
|
573
|
+
(0, import_vue9.h)(
|
|
574
|
+
"span",
|
|
575
|
+
{ style: e.outcome === "success" ? styles.ok : styles.bad },
|
|
576
|
+
e.outcome
|
|
577
|
+
),
|
|
578
|
+
" ",
|
|
579
|
+
(0, import_vue9.h)("span", { style: styles.muted }, e.method),
|
|
580
|
+
" ",
|
|
581
|
+
(0, import_vue9.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
|
|
582
|
+
])
|
|
583
|
+
)
|
|
584
|
+
),
|
|
585
|
+
history.hasMore.value && !history.isLoading.value ? (0, import_vue9.h)(
|
|
586
|
+
"button",
|
|
587
|
+
{
|
|
588
|
+
type: "button",
|
|
589
|
+
style: { ...styles.button, marginTop: "8px" },
|
|
590
|
+
onClick: () => void history.loadMore()
|
|
591
|
+
},
|
|
592
|
+
"Load more"
|
|
593
|
+
) : null
|
|
594
|
+
] : null
|
|
595
|
+
]);
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// src/screens/CatalogScreen.ts
|
|
601
|
+
var import_vue_admin9 = require("@fonderie/vue-admin");
|
|
602
|
+
var import_vue10 = require("vue");
|
|
603
|
+
var CatalogScreen = (0, import_vue10.defineComponent)({
|
|
604
|
+
name: "FonderieCatalogScreen",
|
|
605
|
+
props: { client: { type: Object, required: true } },
|
|
606
|
+
setup(props) {
|
|
607
|
+
const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin9.useAdminCatalog)(props.client);
|
|
608
|
+
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
609
|
+
return () => {
|
|
610
|
+
const c = catalog.value;
|
|
611
|
+
return (0, import_vue10.h)("div", { style: styles.container }, [
|
|
612
|
+
(0, import_vue10.h)("div", { style: styles.toolbar }, [
|
|
613
|
+
(0, import_vue10.h)("h1", { style: { ...styles.title, marginBottom: 0 } }, "Catalog"),
|
|
614
|
+
refreshButton("Refresh", isLoading.value, () => void refresh())
|
|
615
|
+
]),
|
|
616
|
+
error.value ? (0, import_vue10.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
617
|
+
isLoading.value && !c ? (0, import_vue10.h)("p", { style: styles.status }, "Loading\u2026") : null,
|
|
618
|
+
c ? [
|
|
619
|
+
(0, import_vue10.h)("h2", { style: styles.subtitle }, "Configured (code)"),
|
|
620
|
+
(0, import_vue10.h)(
|
|
621
|
+
"pre",
|
|
622
|
+
{
|
|
623
|
+
style: {
|
|
624
|
+
...styles.mono,
|
|
625
|
+
background: "#f9fafb",
|
|
626
|
+
padding: "12px",
|
|
627
|
+
overflowX: "auto"
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
JSON.stringify(c.configured, null, 2)
|
|
631
|
+
),
|
|
632
|
+
(0, import_vue10.h)("h2", { style: styles.subtitle }, "Stored (database)"),
|
|
633
|
+
c.stored.length === 0 ? (0, import_vue10.h)("p", { style: styles.muted }, "No stored plans.") : table(
|
|
634
|
+
["Plan", "Tier", "Seats", "Monthly", "Yearly", "Trial", ""],
|
|
635
|
+
c.stored.map(
|
|
636
|
+
(p) => (0, import_vue10.h)("tr", { key: p.id }, [
|
|
637
|
+
td([
|
|
638
|
+
(0, import_vue10.h)("strong", p.name),
|
|
639
|
+
p.description ? (0, import_vue10.h)("div", { style: styles.muted }, p.description) : ""
|
|
640
|
+
]),
|
|
641
|
+
td(String(p.tier)),
|
|
642
|
+
td(p.seats == null ? "\u221E" : String(p.seats)),
|
|
643
|
+
td(`${money(p.pricing?.monthly)} ${p.pricing?.currency ?? ""}`),
|
|
644
|
+
td(money(p.pricing?.yearly)),
|
|
645
|
+
td(p.trialDays ? `${p.trialDays} d` : "\u2014"),
|
|
646
|
+
td(
|
|
647
|
+
(0, import_vue10.h)(
|
|
648
|
+
"button",
|
|
649
|
+
{
|
|
650
|
+
type: "button",
|
|
651
|
+
style: styles.button,
|
|
652
|
+
onClick: () => {
|
|
653
|
+
if (window.confirm(`Delete stored plan "${p.name}"?`))
|
|
654
|
+
void deletePlan(p.id).catch(() => {
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
"Delete"
|
|
659
|
+
)
|
|
660
|
+
)
|
|
661
|
+
])
|
|
662
|
+
)
|
|
663
|
+
)
|
|
664
|
+
] : null
|
|
665
|
+
]);
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// src/screens/SubscriberScreen.ts
|
|
671
|
+
var import_vue_admin10 = require("@fonderie/vue-admin");
|
|
672
|
+
var import_vue11 = require("vue");
|
|
673
|
+
var SubscriberScreen = (0, import_vue11.defineComponent)({
|
|
674
|
+
name: "FonderieSubscriberScreen",
|
|
675
|
+
props: { client: { type: Object, required: true } },
|
|
676
|
+
setup(props) {
|
|
677
|
+
const type = (0, import_vue11.ref)("user");
|
|
678
|
+
const idInput = (0, import_vue11.ref)("");
|
|
679
|
+
const subscriber = (0, import_vue11.ref)(null);
|
|
680
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
|
|
681
|
+
const amount = (0, import_vue11.ref)("");
|
|
682
|
+
const note = (0, import_vue11.ref)("");
|
|
683
|
+
const granted = (0, import_vue11.ref)(null);
|
|
684
|
+
const input = (model, placeholder, extra = {}) => (0, import_vue11.h)("input", {
|
|
685
|
+
value: model.value,
|
|
686
|
+
onInput: (e) => model.value = e.target.value,
|
|
687
|
+
placeholder,
|
|
688
|
+
style: { ...styles.button, cursor: "text", ...extra },
|
|
689
|
+
"aria-label": placeholder
|
|
690
|
+
});
|
|
691
|
+
return () => {
|
|
692
|
+
const s = subscription.value;
|
|
693
|
+
const w = wallet.value;
|
|
694
|
+
return (0, import_vue11.h)("div", { style: styles.container }, [
|
|
695
|
+
(0, import_vue11.h)("h1", { style: styles.title }, "Subscriber"),
|
|
696
|
+
(0, import_vue11.h)(
|
|
697
|
+
"form",
|
|
698
|
+
{
|
|
699
|
+
style: styles.toolbar,
|
|
700
|
+
onSubmit: (e) => {
|
|
701
|
+
e.preventDefault();
|
|
702
|
+
granted.value = null;
|
|
703
|
+
subscriber.value = idInput.value.trim() ? { type: type.value, id: idInput.value.trim() } : null;
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
[
|
|
707
|
+
(0, import_vue11.h)(
|
|
708
|
+
"select",
|
|
709
|
+
{
|
|
710
|
+
value: type.value,
|
|
711
|
+
onChange: (e) => type.value = e.target.value,
|
|
712
|
+
style: styles.button,
|
|
713
|
+
"aria-label": "Type"
|
|
714
|
+
},
|
|
715
|
+
[
|
|
716
|
+
(0, import_vue11.h)("option", { value: "user" }, "user"),
|
|
717
|
+
(0, import_vue11.h)("option", { value: "workspace" }, "workspace")
|
|
718
|
+
]
|
|
719
|
+
),
|
|
720
|
+
input(idInput, "subscriber id", { minWidth: "280px" }),
|
|
721
|
+
(0, import_vue11.h)(
|
|
722
|
+
"button",
|
|
723
|
+
{ type: "submit", style: styles.button, disabled: isLoading.value },
|
|
724
|
+
"Look up"
|
|
725
|
+
)
|
|
726
|
+
]
|
|
727
|
+
),
|
|
728
|
+
error.value ? (0, import_vue11.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
729
|
+
subscriber.value && !isLoading.value ? [
|
|
730
|
+
(0, import_vue11.h)("h2", { style: styles.subtitle }, "Subscription"),
|
|
731
|
+
s ? (0, import_vue11.h)("table", { style: styles.table }, [
|
|
732
|
+
(0, import_vue11.h)("tbody", [
|
|
733
|
+
(0, import_vue11.h)("tr", [td("plan"), td([(0, import_vue11.h)("strong", s.plan), ` \xB7 ${s.interval}`])]),
|
|
734
|
+
(0, import_vue11.h)("tr", [
|
|
735
|
+
td("status"),
|
|
736
|
+
td(`${s.status}${s.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""}`)
|
|
737
|
+
]),
|
|
738
|
+
(0, import_vue11.h)("tr", [
|
|
739
|
+
td("period"),
|
|
740
|
+
td(
|
|
741
|
+
`${s.currentPeriodStart ? new Date(s.currentPeriodStart).toLocaleDateString() : "\u2014"} \u2192 ${s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : "\u2014"}`
|
|
742
|
+
)
|
|
743
|
+
]),
|
|
744
|
+
(0, import_vue11.h)("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
|
|
745
|
+
])
|
|
746
|
+
]) : (0, import_vue11.h)("p", { style: styles.muted }, "No subscription."),
|
|
747
|
+
(0, import_vue11.h)("h2", { style: styles.subtitle }, "Wallet"),
|
|
748
|
+
w ? [
|
|
749
|
+
(0, import_vue11.h)("p", [
|
|
750
|
+
(0, import_vue11.h)("strong", w.balance),
|
|
751
|
+
` ${w.currency} `,
|
|
752
|
+
(0, import_vue11.h)(
|
|
753
|
+
"span",
|
|
754
|
+
{ style: styles.muted },
|
|
755
|
+
`(granted ${w.granted ?? "0"} \xB7 purchased ${w.purchased ?? "0"}) \xB7 minor units, precision ${w.precision}`
|
|
756
|
+
)
|
|
757
|
+
]),
|
|
758
|
+
(0, import_vue11.h)(
|
|
759
|
+
"form",
|
|
760
|
+
{
|
|
761
|
+
style: styles.toolbar,
|
|
762
|
+
onSubmit: (e) => {
|
|
763
|
+
e.preventDefault();
|
|
764
|
+
const sub = subscriber.value;
|
|
765
|
+
if (!sub) return;
|
|
766
|
+
const key = `admin-grant-${sub.type}-${sub.id}-${Date.now()}`;
|
|
767
|
+
const amt = amount.value.trim();
|
|
768
|
+
void grant({
|
|
769
|
+
amount: amt,
|
|
770
|
+
...note.value ? { description: note.value } : {},
|
|
771
|
+
idempotencyKey: key
|
|
772
|
+
}).then(() => {
|
|
773
|
+
granted.value = `Granted ${amt} ${w.currency}.`;
|
|
774
|
+
amount.value = "";
|
|
775
|
+
note.value = "";
|
|
776
|
+
}).catch(() => {
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
[
|
|
781
|
+
input(amount, "amount (minor units)"),
|
|
782
|
+
input(note, "reason (optional)", { minWidth: "220px" }),
|
|
783
|
+
(0, import_vue11.h)(
|
|
784
|
+
"button",
|
|
785
|
+
{
|
|
786
|
+
type: "submit",
|
|
787
|
+
style: styles.button,
|
|
788
|
+
disabled: !/^\d+$/.test(amount.value.trim())
|
|
789
|
+
},
|
|
790
|
+
"Grant"
|
|
791
|
+
),
|
|
792
|
+
granted.value ? (0, import_vue11.h)("span", { style: styles.ok }, granted.value) : null
|
|
793
|
+
]
|
|
794
|
+
),
|
|
795
|
+
(0, import_vue11.h)("h2", { style: styles.subtitle }, "Ledger"),
|
|
796
|
+
ledger.value.length === 0 ? (0, import_vue11.h)("p", { style: styles.muted }, "Nothing moved yet.") : table(
|
|
797
|
+
["When", "Kind", "Amount", "Note"],
|
|
798
|
+
ledger.value.map(
|
|
799
|
+
(t) => (0, import_vue11.h)("tr", { key: t.id }, [
|
|
800
|
+
td(new Date(t.createdAt).toLocaleString(), styles.muted),
|
|
801
|
+
td(t.type),
|
|
802
|
+
td(t.amount, styles.mono),
|
|
803
|
+
td(t.description ?? "", styles.muted)
|
|
804
|
+
])
|
|
805
|
+
)
|
|
806
|
+
),
|
|
807
|
+
hasMoreLedger.value ? (0, import_vue11.h)(
|
|
808
|
+
"button",
|
|
809
|
+
{
|
|
810
|
+
type: "button",
|
|
811
|
+
style: { ...styles.button, marginTop: "8px" },
|
|
812
|
+
onClick: () => void loadMoreLedger()
|
|
813
|
+
},
|
|
814
|
+
"Load more"
|
|
815
|
+
) : null
|
|
816
|
+
] : (0, import_vue11.h)(
|
|
817
|
+
"p",
|
|
818
|
+
{ style: styles.muted },
|
|
819
|
+
"No wallet \u2014 billing has no wallet configuration."
|
|
820
|
+
)
|
|
821
|
+
] : null
|
|
822
|
+
]);
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
463
827
|
// src/screens/AdminShell.ts
|
|
464
828
|
var NAV = [
|
|
465
829
|
{ group: "Today", items: [{ page: "attention", label: "Attention" }] },
|
|
@@ -472,6 +836,14 @@ var NAV = [
|
|
|
472
836
|
{ page: "routes", label: "Routes" }
|
|
473
837
|
]
|
|
474
838
|
},
|
|
839
|
+
{ group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
|
|
840
|
+
{
|
|
841
|
+
group: "Money",
|
|
842
|
+
items: [
|
|
843
|
+
{ page: "catalog", label: "Catalog", needs: "billing" },
|
|
844
|
+
{ page: "subscriber", label: "Subscriber", needs: "billing" }
|
|
845
|
+
]
|
|
846
|
+
},
|
|
475
847
|
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
476
848
|
{ group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
|
|
477
849
|
{
|
|
@@ -482,7 +854,7 @@ var NAV = [
|
|
|
482
854
|
]
|
|
483
855
|
}
|
|
484
856
|
];
|
|
485
|
-
var AdminShell = (0,
|
|
857
|
+
var AdminShell = (0, import_vue12.defineComponent)({
|
|
486
858
|
name: "FonderieAdminShell",
|
|
487
859
|
props: {
|
|
488
860
|
client: { type: Object, required: true },
|
|
@@ -490,6 +862,10 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
490
862
|
// `prefix: '/_admin'` to go through the one token.
|
|
491
863
|
configClient: { type: Object, default: void 0 },
|
|
492
864
|
courierClient: { type: Object, default: void 0 },
|
|
865
|
+
// Given ⇒ the People page (users) appears; needs @fonderie/auth ≥ 7.8.
|
|
866
|
+
authClient: { type: Object, default: void 0 },
|
|
867
|
+
// Given ⇒ the Money pages (catalog, subscriber) appear; needs @fonderie/billing ≥ 9.10.
|
|
868
|
+
billingClient: { type: Object, default: void 0 },
|
|
493
869
|
environment: { type: String, default: void 0 },
|
|
494
870
|
// Controlled navigation: pass `page` and listen to `navigate` to own the
|
|
495
871
|
// URL. Omit `page` and the shell keeps it itself.
|
|
@@ -497,15 +873,15 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
497
873
|
},
|
|
498
874
|
emits: { navigate: (_page) => true },
|
|
499
875
|
setup(props, { emit }) {
|
|
500
|
-
const own = (0,
|
|
501
|
-
const current = (0,
|
|
502
|
-
const editing = (0,
|
|
876
|
+
const own = (0, import_vue12.ref)("attention");
|
|
877
|
+
const current = (0, import_vue12.computed)(() => props.page ?? own.value);
|
|
878
|
+
const editing = (0, import_vue12.ref)(null);
|
|
503
879
|
const go = (p) => {
|
|
504
880
|
editing.value = null;
|
|
505
881
|
emit("navigate", p);
|
|
506
882
|
if (props.page === void 0) own.value = p;
|
|
507
883
|
};
|
|
508
|
-
const back = () => (0,
|
|
884
|
+
const back = () => (0, import_vue12.h)(
|
|
509
885
|
"button",
|
|
510
886
|
{ type: "button", style: styles.button, onClick: () => editing.value = null },
|
|
511
887
|
"\u2190 Back"
|
|
@@ -514,23 +890,37 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
514
890
|
const c = props.client;
|
|
515
891
|
switch (current.value) {
|
|
516
892
|
case "attention":
|
|
517
|
-
return (0,
|
|
893
|
+
return (0, import_vue12.h)(AttentionScreen, { client: c });
|
|
518
894
|
case "modules":
|
|
519
|
-
return (0,
|
|
895
|
+
return (0, import_vue12.h)(ModulesScreen, { client: c });
|
|
520
896
|
case "doctor":
|
|
521
|
-
return (0,
|
|
897
|
+
return (0, import_vue12.h)(DoctorScreen, { client: c });
|
|
522
898
|
case "config":
|
|
523
|
-
return (0,
|
|
899
|
+
return (0, import_vue12.h)(ConfigScreen, { client: c });
|
|
524
900
|
case "routes":
|
|
525
|
-
return (0,
|
|
901
|
+
return (0, import_vue12.h)(RoutesScreen, { client: c });
|
|
526
902
|
case "tokens":
|
|
527
|
-
return (0,
|
|
903
|
+
return (0, import_vue12.h)(TokensScreen, { client: c });
|
|
528
904
|
case "log":
|
|
529
|
-
return (0,
|
|
905
|
+
return (0, import_vue12.h)(AdminLogScreen, { client: c });
|
|
906
|
+
case "catalog":
|
|
907
|
+
return props.billingClient ? (0, import_vue12.h)(CatalogScreen, { client: props.billingClient }) : (0, import_vue12.h)(
|
|
908
|
+
"p",
|
|
909
|
+
{ style: styles.status },
|
|
910
|
+
"Pass a BillingAdminClient to see the catalog here."
|
|
911
|
+
);
|
|
912
|
+
case "subscriber":
|
|
913
|
+
return props.billingClient ? (0, import_vue12.h)(SubscriberScreen, { client: props.billingClient }) : (0, import_vue12.h)(
|
|
914
|
+
"p",
|
|
915
|
+
{ style: styles.status },
|
|
916
|
+
"Pass a BillingAdminClient to look up subscribers here."
|
|
917
|
+
);
|
|
918
|
+
case "users":
|
|
919
|
+
return props.authClient ? (0, import_vue12.h)(UsersScreen, { client: props.authClient }) : (0, import_vue12.h)("p", { style: styles.status }, "Pass an AuthAdminClient to look up users here.");
|
|
530
920
|
case "settings": {
|
|
531
921
|
const cc = props.configClient;
|
|
532
922
|
if (!cc)
|
|
533
|
-
return (0,
|
|
923
|
+
return (0, import_vue12.h)(
|
|
534
924
|
"p",
|
|
535
925
|
{ style: styles.status },
|
|
536
926
|
"Pass a ConfigAdminClient to manage config and secrets here."
|
|
@@ -539,7 +929,7 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
539
929
|
if (e && e.kind !== "template")
|
|
540
930
|
return [
|
|
541
931
|
back(),
|
|
542
|
-
(0,
|
|
932
|
+
(0, import_vue12.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
|
|
543
933
|
client: cc,
|
|
544
934
|
kind: e.kind,
|
|
545
935
|
configKey: e.key,
|
|
@@ -547,7 +937,7 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
547
937
|
onSaved: () => editing.value = null
|
|
548
938
|
})
|
|
549
939
|
];
|
|
550
|
-
return (0,
|
|
940
|
+
return (0, import_vue12.h)(import_vue_config_admin_screens.ConfigListScreen, {
|
|
551
941
|
client: cc,
|
|
552
942
|
...props.environment !== void 0 ? { environment: props.environment } : {},
|
|
553
943
|
"onSelect-config": (key) => editing.value = { kind: "config", key },
|
|
@@ -557,7 +947,7 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
557
947
|
case "templates": {
|
|
558
948
|
const kc = props.courierClient;
|
|
559
949
|
if (!kc)
|
|
560
|
-
return (0,
|
|
950
|
+
return (0, import_vue12.h)(
|
|
561
951
|
"p",
|
|
562
952
|
{ style: styles.status },
|
|
563
953
|
"Pass a CourierAdminClient to manage templates here."
|
|
@@ -566,32 +956,32 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
566
956
|
if (e?.kind === "template")
|
|
567
957
|
return [
|
|
568
958
|
back(),
|
|
569
|
-
(0,
|
|
959
|
+
(0, import_vue12.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
|
|
570
960
|
client: kc,
|
|
571
961
|
type: e.type,
|
|
572
962
|
onSaved: () => editing.value = null
|
|
573
963
|
})
|
|
574
964
|
];
|
|
575
|
-
return (0,
|
|
965
|
+
return (0, import_vue12.h)(import_vue_courier_admin_screens.TemplateListScreen, {
|
|
576
966
|
client: kc,
|
|
577
967
|
"onSelect-template": (t) => editing.value = { kind: "template", type: t.type }
|
|
578
968
|
});
|
|
579
969
|
}
|
|
580
970
|
}
|
|
581
971
|
};
|
|
582
|
-
return () => (0,
|
|
583
|
-
(0,
|
|
972
|
+
return () => (0, import_vue12.h)("div", { style: styles.shell }, [
|
|
973
|
+
(0, import_vue12.h)(
|
|
584
974
|
"nav",
|
|
585
975
|
{ style: styles.nav, "aria-label": "Admin" },
|
|
586
976
|
NAV.map((g) => {
|
|
587
977
|
const items = g.items.filter(
|
|
588
|
-
(i) => !i.needs || (i.needs === "config" ? props.configClient : props.courierClient)
|
|
978
|
+
(i) => !i.needs || (i.needs === "config" ? props.configClient : i.needs === "courier" ? props.courierClient : i.needs === "auth" ? props.authClient : props.billingClient)
|
|
589
979
|
);
|
|
590
980
|
if (items.length === 0) return null;
|
|
591
|
-
return (0,
|
|
592
|
-
(0,
|
|
981
|
+
return (0, import_vue12.h)("div", { key: g.group }, [
|
|
982
|
+
(0, import_vue12.h)("div", { style: styles.navGroup }, g.group),
|
|
593
983
|
...items.map(
|
|
594
|
-
(i) => (0,
|
|
984
|
+
(i) => (0, import_vue12.h)(
|
|
595
985
|
"button",
|
|
596
986
|
{
|
|
597
987
|
key: i.page,
|
|
@@ -609,7 +999,7 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
609
999
|
]);
|
|
610
1000
|
})
|
|
611
1001
|
),
|
|
612
|
-
(0,
|
|
1002
|
+
(0, import_vue12.h)("main", { style: styles.main }, body())
|
|
613
1003
|
]);
|
|
614
1004
|
}
|
|
615
1005
|
});
|
|
@@ -618,10 +1008,13 @@ var AdminShell = (0, import_vue9.defineComponent)({
|
|
|
618
1008
|
AdminLogScreen,
|
|
619
1009
|
AdminShell,
|
|
620
1010
|
AttentionScreen,
|
|
1011
|
+
CatalogScreen,
|
|
621
1012
|
ConfigScreen,
|
|
622
1013
|
DoctorScreen,
|
|
623
1014
|
ModulesScreen,
|
|
624
1015
|
RoutesScreen,
|
|
625
|
-
|
|
1016
|
+
SubscriberScreen,
|
|
1017
|
+
TokensScreen,
|
|
1018
|
+
UsersScreen
|
|
626
1019
|
});
|
|
627
1020
|
//# sourceMappingURL=index.cjs.map
|