@fonderie/vue-admin-screens 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/index.cjs +279 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +276 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/screens/AdminShell.ts
|
|
2
2
|
import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/vue-config-admin-screens";
|
|
3
3
|
import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/vue-courier-admin-screens";
|
|
4
|
-
import { computed as computed2, defineComponent as
|
|
4
|
+
import { computed as computed2, defineComponent as defineComponent11, h as h12, ref as ref3 } from "vue";
|
|
5
5
|
|
|
6
6
|
// src/styles.ts
|
|
7
7
|
var styles = {
|
|
@@ -561,6 +561,233 @@ var UsersScreen = defineComponent8({
|
|
|
561
561
|
}
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
+
// src/screens/CatalogScreen.ts
|
|
565
|
+
import { useAdminCatalog } from "@fonderie/vue-admin";
|
|
566
|
+
import { defineComponent as defineComponent9, h as h10 } from "vue";
|
|
567
|
+
var CatalogScreen = defineComponent9({
|
|
568
|
+
name: "FonderieCatalogScreen",
|
|
569
|
+
props: { client: { type: Object, required: true } },
|
|
570
|
+
setup(props) {
|
|
571
|
+
const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(props.client);
|
|
572
|
+
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
573
|
+
return () => {
|
|
574
|
+
const c = catalog.value;
|
|
575
|
+
return h10("div", { style: styles.container }, [
|
|
576
|
+
h10("div", { style: styles.toolbar }, [
|
|
577
|
+
h10("h1", { style: { ...styles.title, marginBottom: 0 } }, "Catalog"),
|
|
578
|
+
refreshButton("Refresh", isLoading.value, () => void refresh())
|
|
579
|
+
]),
|
|
580
|
+
error.value ? h10("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
581
|
+
isLoading.value && !c ? h10("p", { style: styles.status }, "Loading\u2026") : null,
|
|
582
|
+
c ? [
|
|
583
|
+
h10("h2", { style: styles.subtitle }, "Configured (code)"),
|
|
584
|
+
h10(
|
|
585
|
+
"pre",
|
|
586
|
+
{
|
|
587
|
+
style: {
|
|
588
|
+
...styles.mono,
|
|
589
|
+
background: "#f9fafb",
|
|
590
|
+
padding: "12px",
|
|
591
|
+
overflowX: "auto"
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
JSON.stringify(c.configured, null, 2)
|
|
595
|
+
),
|
|
596
|
+
h10("h2", { style: styles.subtitle }, "Stored (database)"),
|
|
597
|
+
c.stored.length === 0 ? h10("p", { style: styles.muted }, "No stored plans.") : table(
|
|
598
|
+
["Plan", "Tier", "Seats", "Monthly", "Yearly", "Trial", ""],
|
|
599
|
+
c.stored.map(
|
|
600
|
+
(p) => h10("tr", { key: p.id }, [
|
|
601
|
+
td([
|
|
602
|
+
h10("strong", p.name),
|
|
603
|
+
p.description ? h10("div", { style: styles.muted }, p.description) : ""
|
|
604
|
+
]),
|
|
605
|
+
td(String(p.tier)),
|
|
606
|
+
td(p.seats == null ? "\u221E" : String(p.seats)),
|
|
607
|
+
td(`${money(p.pricing?.monthly)} ${p.pricing?.currency ?? ""}`),
|
|
608
|
+
td(money(p.pricing?.yearly)),
|
|
609
|
+
td(p.trialDays ? `${p.trialDays} d` : "\u2014"),
|
|
610
|
+
td(
|
|
611
|
+
h10(
|
|
612
|
+
"button",
|
|
613
|
+
{
|
|
614
|
+
type: "button",
|
|
615
|
+
style: styles.button,
|
|
616
|
+
onClick: () => {
|
|
617
|
+
if (window.confirm(`Delete stored plan "${p.name}"?`))
|
|
618
|
+
void deletePlan(p.id).catch(() => {
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
"Delete"
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
])
|
|
626
|
+
)
|
|
627
|
+
)
|
|
628
|
+
] : null
|
|
629
|
+
]);
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// src/screens/SubscriberScreen.ts
|
|
635
|
+
import { useAdminSubscriber } from "@fonderie/vue-admin";
|
|
636
|
+
import { defineComponent as defineComponent10, h as h11, ref as ref2 } from "vue";
|
|
637
|
+
var SubscriberScreen = defineComponent10({
|
|
638
|
+
name: "FonderieSubscriberScreen",
|
|
639
|
+
props: { client: { type: Object, required: true } },
|
|
640
|
+
setup(props) {
|
|
641
|
+
const type = ref2("user");
|
|
642
|
+
const idInput = ref2("");
|
|
643
|
+
const subscriber = ref2(null);
|
|
644
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(props.client, subscriber, { limit: 20 });
|
|
645
|
+
const amount = ref2("");
|
|
646
|
+
const note = ref2("");
|
|
647
|
+
const granted = ref2(null);
|
|
648
|
+
const input = (model, placeholder, extra = {}) => h11("input", {
|
|
649
|
+
value: model.value,
|
|
650
|
+
onInput: (e) => model.value = e.target.value,
|
|
651
|
+
placeholder,
|
|
652
|
+
style: { ...styles.button, cursor: "text", ...extra },
|
|
653
|
+
"aria-label": placeholder
|
|
654
|
+
});
|
|
655
|
+
return () => {
|
|
656
|
+
const s = subscription.value;
|
|
657
|
+
const w = wallet.value;
|
|
658
|
+
return h11("div", { style: styles.container }, [
|
|
659
|
+
h11("h1", { style: styles.title }, "Subscriber"),
|
|
660
|
+
h11(
|
|
661
|
+
"form",
|
|
662
|
+
{
|
|
663
|
+
style: styles.toolbar,
|
|
664
|
+
onSubmit: (e) => {
|
|
665
|
+
e.preventDefault();
|
|
666
|
+
granted.value = null;
|
|
667
|
+
subscriber.value = idInput.value.trim() ? { type: type.value, id: idInput.value.trim() } : null;
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
[
|
|
671
|
+
h11(
|
|
672
|
+
"select",
|
|
673
|
+
{
|
|
674
|
+
value: type.value,
|
|
675
|
+
onChange: (e) => type.value = e.target.value,
|
|
676
|
+
style: styles.button,
|
|
677
|
+
"aria-label": "Type"
|
|
678
|
+
},
|
|
679
|
+
[
|
|
680
|
+
h11("option", { value: "user" }, "user"),
|
|
681
|
+
h11("option", { value: "workspace" }, "workspace")
|
|
682
|
+
]
|
|
683
|
+
),
|
|
684
|
+
input(idInput, "subscriber id", { minWidth: "280px" }),
|
|
685
|
+
h11(
|
|
686
|
+
"button",
|
|
687
|
+
{ type: "submit", style: styles.button, disabled: isLoading.value },
|
|
688
|
+
"Look up"
|
|
689
|
+
)
|
|
690
|
+
]
|
|
691
|
+
),
|
|
692
|
+
error.value ? h11("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
693
|
+
subscriber.value && !isLoading.value ? [
|
|
694
|
+
h11("h2", { style: styles.subtitle }, "Subscription"),
|
|
695
|
+
s ? h11("table", { style: styles.table }, [
|
|
696
|
+
h11("tbody", [
|
|
697
|
+
h11("tr", [td("plan"), td([h11("strong", s.plan), ` \xB7 ${s.interval}`])]),
|
|
698
|
+
h11("tr", [
|
|
699
|
+
td("status"),
|
|
700
|
+
td(`${s.status}${s.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""}`)
|
|
701
|
+
]),
|
|
702
|
+
h11("tr", [
|
|
703
|
+
td("period"),
|
|
704
|
+
td(
|
|
705
|
+
`${s.currentPeriodStart ? new Date(s.currentPeriodStart).toLocaleDateString() : "\u2014"} \u2192 ${s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : "\u2014"}`
|
|
706
|
+
)
|
|
707
|
+
]),
|
|
708
|
+
h11("tr", [td("provider"), td(s.providerSubscriptionId ?? "\u2014", styles.mono)])
|
|
709
|
+
])
|
|
710
|
+
]) : h11("p", { style: styles.muted }, "No subscription."),
|
|
711
|
+
h11("h2", { style: styles.subtitle }, "Wallet"),
|
|
712
|
+
w ? [
|
|
713
|
+
h11("p", [
|
|
714
|
+
h11("strong", w.balance),
|
|
715
|
+
` ${w.currency} `,
|
|
716
|
+
h11(
|
|
717
|
+
"span",
|
|
718
|
+
{ style: styles.muted },
|
|
719
|
+
`(granted ${w.granted ?? "0"} \xB7 purchased ${w.purchased ?? "0"}) \xB7 minor units, precision ${w.precision}`
|
|
720
|
+
)
|
|
721
|
+
]),
|
|
722
|
+
h11(
|
|
723
|
+
"form",
|
|
724
|
+
{
|
|
725
|
+
style: styles.toolbar,
|
|
726
|
+
onSubmit: (e) => {
|
|
727
|
+
e.preventDefault();
|
|
728
|
+
const sub = subscriber.value;
|
|
729
|
+
if (!sub) return;
|
|
730
|
+
const key = `admin-grant-${sub.type}-${sub.id}-${Date.now()}`;
|
|
731
|
+
const amt = amount.value.trim();
|
|
732
|
+
void grant({
|
|
733
|
+
amount: amt,
|
|
734
|
+
...note.value ? { description: note.value } : {},
|
|
735
|
+
idempotencyKey: key
|
|
736
|
+
}).then(() => {
|
|
737
|
+
granted.value = `Granted ${amt} ${w.currency}.`;
|
|
738
|
+
amount.value = "";
|
|
739
|
+
note.value = "";
|
|
740
|
+
}).catch(() => {
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
[
|
|
745
|
+
input(amount, "amount (minor units)"),
|
|
746
|
+
input(note, "reason (optional)", { minWidth: "220px" }),
|
|
747
|
+
h11(
|
|
748
|
+
"button",
|
|
749
|
+
{
|
|
750
|
+
type: "submit",
|
|
751
|
+
style: styles.button,
|
|
752
|
+
disabled: !/^\d+$/.test(amount.value.trim())
|
|
753
|
+
},
|
|
754
|
+
"Grant"
|
|
755
|
+
),
|
|
756
|
+
granted.value ? h11("span", { style: styles.ok }, granted.value) : null
|
|
757
|
+
]
|
|
758
|
+
),
|
|
759
|
+
h11("h2", { style: styles.subtitle }, "Ledger"),
|
|
760
|
+
ledger.value.length === 0 ? h11("p", { style: styles.muted }, "Nothing moved yet.") : table(
|
|
761
|
+
["When", "Kind", "Amount", "Note"],
|
|
762
|
+
ledger.value.map(
|
|
763
|
+
(t) => h11("tr", { key: t.id }, [
|
|
764
|
+
td(new Date(t.createdAt).toLocaleString(), styles.muted),
|
|
765
|
+
td(t.type),
|
|
766
|
+
td(t.amount, styles.mono),
|
|
767
|
+
td(t.description ?? "", styles.muted)
|
|
768
|
+
])
|
|
769
|
+
)
|
|
770
|
+
),
|
|
771
|
+
hasMoreLedger.value ? h11(
|
|
772
|
+
"button",
|
|
773
|
+
{
|
|
774
|
+
type: "button",
|
|
775
|
+
style: { ...styles.button, marginTop: "8px" },
|
|
776
|
+
onClick: () => void loadMoreLedger()
|
|
777
|
+
},
|
|
778
|
+
"Load more"
|
|
779
|
+
) : null
|
|
780
|
+
] : h11(
|
|
781
|
+
"p",
|
|
782
|
+
{ style: styles.muted },
|
|
783
|
+
"No wallet \u2014 billing has no wallet configuration."
|
|
784
|
+
)
|
|
785
|
+
] : null
|
|
786
|
+
]);
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
|
|
564
791
|
// src/screens/AdminShell.ts
|
|
565
792
|
var NAV = [
|
|
566
793
|
{ group: "Today", items: [{ page: "attention", label: "Attention" }] },
|
|
@@ -574,6 +801,13 @@ var NAV = [
|
|
|
574
801
|
]
|
|
575
802
|
},
|
|
576
803
|
{ group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
|
|
804
|
+
{
|
|
805
|
+
group: "Money",
|
|
806
|
+
items: [
|
|
807
|
+
{ page: "catalog", label: "Catalog", needs: "billing" },
|
|
808
|
+
{ page: "subscriber", label: "Subscriber", needs: "billing" }
|
|
809
|
+
]
|
|
810
|
+
},
|
|
577
811
|
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
578
812
|
{ group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
|
|
579
813
|
{
|
|
@@ -584,7 +818,7 @@ var NAV = [
|
|
|
584
818
|
]
|
|
585
819
|
}
|
|
586
820
|
];
|
|
587
|
-
var AdminShell =
|
|
821
|
+
var AdminShell = defineComponent11({
|
|
588
822
|
name: "FonderieAdminShell",
|
|
589
823
|
props: {
|
|
590
824
|
client: { type: Object, required: true },
|
|
@@ -594,6 +828,8 @@ var AdminShell = defineComponent9({
|
|
|
594
828
|
courierClient: { type: Object, default: void 0 },
|
|
595
829
|
// Given ⇒ the People page (users) appears; needs @fonderie/auth ≥ 7.8.
|
|
596
830
|
authClient: { type: Object, default: void 0 },
|
|
831
|
+
// Given ⇒ the Money pages (catalog, subscriber) appear; needs @fonderie/billing ≥ 9.10.
|
|
832
|
+
billingClient: { type: Object, default: void 0 },
|
|
597
833
|
environment: { type: String, default: void 0 },
|
|
598
834
|
// Controlled navigation: pass `page` and listen to `navigate` to own the
|
|
599
835
|
// URL. Omit `page` and the shell keeps it itself.
|
|
@@ -601,15 +837,15 @@ var AdminShell = defineComponent9({
|
|
|
601
837
|
},
|
|
602
838
|
emits: { navigate: (_page) => true },
|
|
603
839
|
setup(props, { emit }) {
|
|
604
|
-
const own =
|
|
840
|
+
const own = ref3("attention");
|
|
605
841
|
const current = computed2(() => props.page ?? own.value);
|
|
606
|
-
const editing =
|
|
842
|
+
const editing = ref3(null);
|
|
607
843
|
const go = (p) => {
|
|
608
844
|
editing.value = null;
|
|
609
845
|
emit("navigate", p);
|
|
610
846
|
if (props.page === void 0) own.value = p;
|
|
611
847
|
};
|
|
612
|
-
const back = () =>
|
|
848
|
+
const back = () => h12(
|
|
613
849
|
"button",
|
|
614
850
|
{ type: "button", style: styles.button, onClick: () => editing.value = null },
|
|
615
851
|
"\u2190 Back"
|
|
@@ -618,25 +854,37 @@ var AdminShell = defineComponent9({
|
|
|
618
854
|
const c = props.client;
|
|
619
855
|
switch (current.value) {
|
|
620
856
|
case "attention":
|
|
621
|
-
return
|
|
857
|
+
return h12(AttentionScreen, { client: c });
|
|
622
858
|
case "modules":
|
|
623
|
-
return
|
|
859
|
+
return h12(ModulesScreen, { client: c });
|
|
624
860
|
case "doctor":
|
|
625
|
-
return
|
|
861
|
+
return h12(DoctorScreen, { client: c });
|
|
626
862
|
case "config":
|
|
627
|
-
return
|
|
863
|
+
return h12(ConfigScreen, { client: c });
|
|
628
864
|
case "routes":
|
|
629
|
-
return
|
|
865
|
+
return h12(RoutesScreen, { client: c });
|
|
630
866
|
case "tokens":
|
|
631
|
-
return
|
|
867
|
+
return h12(TokensScreen, { client: c });
|
|
632
868
|
case "log":
|
|
633
|
-
return
|
|
869
|
+
return h12(AdminLogScreen, { client: c });
|
|
870
|
+
case "catalog":
|
|
871
|
+
return props.billingClient ? h12(CatalogScreen, { client: props.billingClient }) : h12(
|
|
872
|
+
"p",
|
|
873
|
+
{ style: styles.status },
|
|
874
|
+
"Pass a BillingAdminClient to see the catalog here."
|
|
875
|
+
);
|
|
876
|
+
case "subscriber":
|
|
877
|
+
return props.billingClient ? h12(SubscriberScreen, { client: props.billingClient }) : h12(
|
|
878
|
+
"p",
|
|
879
|
+
{ style: styles.status },
|
|
880
|
+
"Pass a BillingAdminClient to look up subscribers here."
|
|
881
|
+
);
|
|
634
882
|
case "users":
|
|
635
|
-
return props.authClient ?
|
|
883
|
+
return props.authClient ? h12(UsersScreen, { client: props.authClient }) : h12("p", { style: styles.status }, "Pass an AuthAdminClient to look up users here.");
|
|
636
884
|
case "settings": {
|
|
637
885
|
const cc = props.configClient;
|
|
638
886
|
if (!cc)
|
|
639
|
-
return
|
|
887
|
+
return h12(
|
|
640
888
|
"p",
|
|
641
889
|
{ style: styles.status },
|
|
642
890
|
"Pass a ConfigAdminClient to manage config and secrets here."
|
|
@@ -645,7 +893,7 @@ var AdminShell = defineComponent9({
|
|
|
645
893
|
if (e && e.kind !== "template")
|
|
646
894
|
return [
|
|
647
895
|
back(),
|
|
648
|
-
|
|
896
|
+
h12(ConfigEditorScreen, {
|
|
649
897
|
client: cc,
|
|
650
898
|
kind: e.kind,
|
|
651
899
|
configKey: e.key,
|
|
@@ -653,7 +901,7 @@ var AdminShell = defineComponent9({
|
|
|
653
901
|
onSaved: () => editing.value = null
|
|
654
902
|
})
|
|
655
903
|
];
|
|
656
|
-
return
|
|
904
|
+
return h12(ConfigListScreen, {
|
|
657
905
|
client: cc,
|
|
658
906
|
...props.environment !== void 0 ? { environment: props.environment } : {},
|
|
659
907
|
"onSelect-config": (key) => editing.value = { kind: "config", key },
|
|
@@ -663,7 +911,7 @@ var AdminShell = defineComponent9({
|
|
|
663
911
|
case "templates": {
|
|
664
912
|
const kc = props.courierClient;
|
|
665
913
|
if (!kc)
|
|
666
|
-
return
|
|
914
|
+
return h12(
|
|
667
915
|
"p",
|
|
668
916
|
{ style: styles.status },
|
|
669
917
|
"Pass a CourierAdminClient to manage templates here."
|
|
@@ -672,32 +920,32 @@ var AdminShell = defineComponent9({
|
|
|
672
920
|
if (e?.kind === "template")
|
|
673
921
|
return [
|
|
674
922
|
back(),
|
|
675
|
-
|
|
923
|
+
h12(TemplateEditorScreen, {
|
|
676
924
|
client: kc,
|
|
677
925
|
type: e.type,
|
|
678
926
|
onSaved: () => editing.value = null
|
|
679
927
|
})
|
|
680
928
|
];
|
|
681
|
-
return
|
|
929
|
+
return h12(TemplateListScreen, {
|
|
682
930
|
client: kc,
|
|
683
931
|
"onSelect-template": (t) => editing.value = { kind: "template", type: t.type }
|
|
684
932
|
});
|
|
685
933
|
}
|
|
686
934
|
}
|
|
687
935
|
};
|
|
688
|
-
return () =>
|
|
689
|
-
|
|
936
|
+
return () => h12("div", { style: styles.shell }, [
|
|
937
|
+
h12(
|
|
690
938
|
"nav",
|
|
691
939
|
{ style: styles.nav, "aria-label": "Admin" },
|
|
692
940
|
NAV.map((g) => {
|
|
693
941
|
const items = g.items.filter(
|
|
694
|
-
(i) => !i.needs || (i.needs === "config" ? props.configClient : i.needs === "courier" ? props.courierClient : props.authClient)
|
|
942
|
+
(i) => !i.needs || (i.needs === "config" ? props.configClient : i.needs === "courier" ? props.courierClient : i.needs === "auth" ? props.authClient : props.billingClient)
|
|
695
943
|
);
|
|
696
944
|
if (items.length === 0) return null;
|
|
697
|
-
return
|
|
698
|
-
|
|
945
|
+
return h12("div", { key: g.group }, [
|
|
946
|
+
h12("div", { style: styles.navGroup }, g.group),
|
|
699
947
|
...items.map(
|
|
700
|
-
(i) =>
|
|
948
|
+
(i) => h12(
|
|
701
949
|
"button",
|
|
702
950
|
{
|
|
703
951
|
key: i.page,
|
|
@@ -715,7 +963,7 @@ var AdminShell = defineComponent9({
|
|
|
715
963
|
]);
|
|
716
964
|
})
|
|
717
965
|
),
|
|
718
|
-
|
|
966
|
+
h12("main", { style: styles.main }, body())
|
|
719
967
|
]);
|
|
720
968
|
}
|
|
721
969
|
});
|
|
@@ -723,10 +971,12 @@ export {
|
|
|
723
971
|
AdminLogScreen,
|
|
724
972
|
AdminShell,
|
|
725
973
|
AttentionScreen,
|
|
974
|
+
CatalogScreen,
|
|
726
975
|
ConfigScreen,
|
|
727
976
|
DoctorScreen,
|
|
728
977
|
ModulesScreen,
|
|
729
978
|
RoutesScreen,
|
|
979
|
+
SubscriberScreen,
|
|
730
980
|
TokensScreen,
|
|
731
981
|
UsersScreen
|
|
732
982
|
};
|