@fonderie/vue-admin-screens 1.4.0 → 1.6.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 +922 -546
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +164 -11
- package/dist/index.d.ts +164 -11
- package/dist/index.js +956 -545
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
40
|
|
|
41
41
|
// src/screens/AdminShell.ts
|
|
42
|
+
var import_client16 = require("@fonderie/client");
|
|
42
43
|
var import_vue_config_admin_screens = require("@fonderie/vue-config-admin-screens");
|
|
43
44
|
var import_vue_courier_admin_screens = require("@fonderie/vue-courier-admin-screens");
|
|
44
45
|
var import_vue18 = require("vue");
|
|
@@ -597,10 +598,10 @@ function empty(title, body, iconName = "check") {
|
|
|
597
598
|
body ? (0, import_vue.h)("div", body) : null
|
|
598
599
|
]);
|
|
599
600
|
}
|
|
600
|
-
function refreshButton(onClick, busy, label = "Refresh") {
|
|
601
|
+
function refreshButton(onClick, busy, label = "Refresh", busyLabel = "Working\u2026") {
|
|
601
602
|
return (0, import_vue.h)("button", { type: "button", style: styles.button, onClick, disabled: busy }, [
|
|
602
603
|
icon("refresh", 14),
|
|
603
|
-
busy ?
|
|
604
|
+
busy ? busyLabel : label
|
|
604
605
|
]);
|
|
605
606
|
}
|
|
606
607
|
function methodChip(method) {
|
|
@@ -627,6 +628,7 @@ function methodChip(method) {
|
|
|
627
628
|
}
|
|
628
629
|
|
|
629
630
|
// src/screens/AdminLogScreen.ts
|
|
631
|
+
var import_client = require("@fonderie/client");
|
|
630
632
|
var import_vue_admin = require("@fonderie/vue-admin");
|
|
631
633
|
var import_vue3 = require("vue");
|
|
632
634
|
|
|
@@ -654,15 +656,11 @@ function table(headers, rows) {
|
|
|
654
656
|
]);
|
|
655
657
|
}
|
|
656
658
|
var td = (children, extra = {}) => (0, import_vue2.h)("td", { style: { ...styles.td, ...extra } }, children);
|
|
657
|
-
var refreshButton2 = (label, disabled, onClick) => (0, import_vue2.h)("button", { type: "button", style: styles.button, disabled, onClick }, [
|
|
659
|
+
var refreshButton2 = (label, disabled, onClick, busyLabel = "Working\u2026") => (0, import_vue2.h)("button", { type: "button", style: styles.button, disabled, onClick }, [
|
|
658
660
|
icon("refresh", 14),
|
|
659
|
-
disabled ?
|
|
661
|
+
disabled ? busyLabel : label
|
|
660
662
|
]);
|
|
661
|
-
var loadMoreButton = (onClick) => (0, import_vue2.h)(
|
|
662
|
-
"button",
|
|
663
|
-
{ type: "button", style: { ...styles.button, marginTop: "12px" }, onClick },
|
|
664
|
-
"Load more"
|
|
665
|
-
);
|
|
663
|
+
var loadMoreButton = (onClick, label = "Load more") => (0, import_vue2.h)("button", { type: "button", style: { ...styles.button, marginTop: "12px" }, onClick }, label);
|
|
666
664
|
var actionButton = (label, disabled, onClick, style = styles.button) => (0, import_vue2.h)("button", { type: "button", style, disabled, onClick }, label);
|
|
667
665
|
|
|
668
666
|
// src/screens/AdminLogScreen.ts
|
|
@@ -670,69 +668,87 @@ var AdminLogScreen = (0, import_vue3.defineComponent)({
|
|
|
670
668
|
name: "FonderieAdminLogScreen",
|
|
671
669
|
props: {
|
|
672
670
|
client: { type: Object, required: true },
|
|
673
|
-
pageSize: { type: Number, default: 50 }
|
|
671
|
+
pageSize: { type: Number, default: 50 },
|
|
672
|
+
locale: { type: String, default: void 0 }
|
|
674
673
|
},
|
|
675
674
|
setup(props) {
|
|
676
675
|
const { entries, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin.useAdminLog)(props.client, {
|
|
677
676
|
limit: props.pageSize
|
|
678
677
|
});
|
|
679
|
-
return () =>
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
"
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
error.value.status === 404 ? "The admin log is off \u2014 give AdminModule a store." : error.value.explanation
|
|
689
|
-
) : entries.value.length === 0 && !isLoading.value ? empty("No requests yet", void 0, "log") : [
|
|
690
|
-
table(
|
|
691
|
-
["When", "Actor", "Request", "Status", "Module"],
|
|
692
|
-
entries.value.map(
|
|
693
|
-
(e) => (0, import_vue3.h)("tr", { key: e.id }, [
|
|
694
|
-
td(new Date(e.at).toLocaleString(), {
|
|
695
|
-
...styles.muted,
|
|
696
|
-
whiteSpace: "nowrap"
|
|
697
|
-
}),
|
|
698
|
-
td(e.actor),
|
|
699
|
-
td([
|
|
700
|
-
methodChip(e.method),
|
|
701
|
-
" ",
|
|
702
|
-
(0, import_vue3.h)("span", { style: { ...styles.mono, wordBreak: "break-all" } }, e.path)
|
|
703
|
-
]),
|
|
704
|
-
td(
|
|
705
|
-
[
|
|
706
|
-
pill(
|
|
707
|
-
e.status >= 500 ? "bad" : e.status >= 400 ? "warn" : "ok",
|
|
708
|
-
String(e.status)
|
|
709
|
-
),
|
|
710
|
-
" ",
|
|
711
|
-
(0, import_vue3.h)("span", { style: styles.muted }, `${e.durationMs} ms`)
|
|
712
|
-
],
|
|
713
|
-
{ whiteSpace: "nowrap" }
|
|
714
|
-
),
|
|
715
|
-
td(e.module, styles.muted)
|
|
716
|
-
])
|
|
678
|
+
return () => {
|
|
679
|
+
const t2 = (0, import_client.createAdminT)(props.locale);
|
|
680
|
+
return (0, import_vue3.h)("div", { style: styles.container }, [
|
|
681
|
+
pageHeader(t2("log.title"), t2("log.lead"), [
|
|
682
|
+
refreshButton2(
|
|
683
|
+
t2("common.refresh"),
|
|
684
|
+
isLoading.value,
|
|
685
|
+
() => void refresh(),
|
|
686
|
+
t2("common.working")
|
|
717
687
|
)
|
|
718
|
-
),
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
688
|
+
]),
|
|
689
|
+
error.value ? (0, import_vue3.h)(
|
|
690
|
+
"p",
|
|
691
|
+
{ style: styles.error, role: "alert" },
|
|
692
|
+
error.value.status === 404 ? t2("log.off") : error.value.explanation
|
|
693
|
+
) : entries.value.length === 0 && !isLoading.value ? empty(t2("log.empty"), void 0, "log") : [
|
|
694
|
+
table(
|
|
695
|
+
[
|
|
696
|
+
t2("log.colWhen"),
|
|
697
|
+
t2("log.colActor"),
|
|
698
|
+
t2("log.colRequest"),
|
|
699
|
+
t2("log.colStatus"),
|
|
700
|
+
t2("log.colModule")
|
|
701
|
+
],
|
|
702
|
+
entries.value.map(
|
|
703
|
+
(e) => (0, import_vue3.h)("tr", { key: e.id }, [
|
|
704
|
+
td((0, import_client.formatAdminDate)(e.at, props.locale), {
|
|
705
|
+
...styles.muted,
|
|
706
|
+
whiteSpace: "nowrap"
|
|
707
|
+
}),
|
|
708
|
+
td(e.actor),
|
|
709
|
+
td([
|
|
710
|
+
methodChip(e.method),
|
|
711
|
+
" ",
|
|
712
|
+
(0, import_vue3.h)("span", { style: { ...styles.mono, wordBreak: "break-all" } }, e.path)
|
|
713
|
+
]),
|
|
714
|
+
td(
|
|
715
|
+
[
|
|
716
|
+
pill(
|
|
717
|
+
e.status >= 500 ? "bad" : e.status >= 400 ? "warn" : "ok",
|
|
718
|
+
String(e.status)
|
|
719
|
+
),
|
|
720
|
+
" ",
|
|
721
|
+
(0, import_vue3.h)("span", { style: styles.muted }, `${e.durationMs} ms`)
|
|
722
|
+
],
|
|
723
|
+
{ whiteSpace: "nowrap" }
|
|
724
|
+
),
|
|
725
|
+
td(e.module, styles.muted)
|
|
726
|
+
])
|
|
727
|
+
)
|
|
728
|
+
),
|
|
729
|
+
isLoading.value ? (0, import_vue3.h)("p", { style: styles.status }, t2("common.loading")) : null,
|
|
730
|
+
hasMore.value && !isLoading.value ? loadMoreButton(() => void loadMore(), t2("common.loadMore")) : null
|
|
731
|
+
]
|
|
732
|
+
]);
|
|
733
|
+
};
|
|
723
734
|
}
|
|
724
735
|
});
|
|
725
736
|
|
|
726
737
|
// src/screens/AttentionScreen.ts
|
|
738
|
+
var import_client2 = require("@fonderie/client");
|
|
727
739
|
var import_vue_admin2 = require("@fonderie/vue-admin");
|
|
728
740
|
var import_vue4 = require("vue");
|
|
729
741
|
var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
730
742
|
name: "FonderieAttentionScreen",
|
|
731
|
-
props: {
|
|
743
|
+
props: {
|
|
744
|
+
client: { type: Object, required: true },
|
|
745
|
+
locale: { type: String, default: void 0 }
|
|
746
|
+
},
|
|
732
747
|
setup(props) {
|
|
733
748
|
const { attention, isLoading, error, refresh } = (0, import_vue_admin2.useAttention)(props.client);
|
|
734
749
|
const { manifest } = (0, import_vue_admin2.useManifest)(props.client);
|
|
735
750
|
return () => {
|
|
751
|
+
const t2 = (0, import_client2.createAdminT)(props.locale);
|
|
736
752
|
const a = attention.value;
|
|
737
753
|
const m = manifest.value;
|
|
738
754
|
const errors = a?.items.filter((i) => i.severity === "error") ?? [];
|
|
@@ -741,34 +757,39 @@ var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
|
741
757
|
const total = m?.modules.length ?? 0;
|
|
742
758
|
const tiles = (0, import_vue4.h)("div", { style: styles.grid }, [
|
|
743
759
|
stat(
|
|
744
|
-
"
|
|
760
|
+
t2("attention.needsAction"),
|
|
745
761
|
a ? errors.length : "\u2014",
|
|
746
|
-
"
|
|
762
|
+
t2("attention.needsActionHint"),
|
|
747
763
|
errors.length ? "bad" : a ? "ok" : void 0
|
|
748
764
|
),
|
|
749
|
-
stat("Advice", a ? advice.length : "\u2014", "worth a look", advice.length ? "warn" : void 0),
|
|
750
765
|
stat(
|
|
751
|
-
"
|
|
766
|
+
t2("attention.advice"),
|
|
767
|
+
a ? advice.length : "\u2014",
|
|
768
|
+
t2("attention.adviceHint"),
|
|
769
|
+
advice.length ? "warn" : void 0
|
|
770
|
+
),
|
|
771
|
+
stat(
|
|
772
|
+
t2("attention.modulesReady"),
|
|
752
773
|
m ? `${ready}/${total}` : "\u2014",
|
|
753
|
-
m ?
|
|
774
|
+
m ? t2("attention.modulesReadyHint", { version: m.admin.version }) : void 0,
|
|
754
775
|
m && ready < total ? "warn" : m ? "ok" : void 0
|
|
755
776
|
),
|
|
756
|
-
stat("
|
|
777
|
+
stat(t2("attention.routes"), m ? m.routes.length : "\u2014", m ? m.env : void 0)
|
|
757
778
|
]);
|
|
758
779
|
return page(
|
|
759
|
-
"
|
|
780
|
+
t2("attention.title"),
|
|
760
781
|
// Tiles show while the checks run; only the list waits.
|
|
761
782
|
{ isLoading: { value: false }, error },
|
|
762
783
|
() => {
|
|
763
784
|
if (isLoading.value && !a)
|
|
764
|
-
return [tiles, (0, import_vue4.h)("p", { style: styles.status }, "
|
|
785
|
+
return [tiles, (0, import_vue4.h)("p", { style: styles.status }, t2("attention.running"))];
|
|
765
786
|
if (!a) return tiles;
|
|
766
787
|
if (a.items.length === 0)
|
|
767
788
|
return [
|
|
768
789
|
tiles,
|
|
769
790
|
empty(
|
|
770
|
-
"
|
|
771
|
-
|
|
791
|
+
t2("attention.emptyTitle"),
|
|
792
|
+
t2("attention.emptyBody", { time: (0, import_client2.formatAdminDate)(a.generatedAt, props.locale) })
|
|
772
793
|
)
|
|
773
794
|
];
|
|
774
795
|
return [
|
|
@@ -808,7 +829,7 @@ var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
|
808
829
|
[
|
|
809
830
|
pill(
|
|
810
831
|
item.severity === "error" ? "bad" : "warn",
|
|
811
|
-
item.severity === "error" ? "error" : "advice"
|
|
832
|
+
item.severity === "error" ? t2("common.status.error") : t2("common.status.advice")
|
|
812
833
|
),
|
|
813
834
|
(0, import_vue4.h)("span", { style: styles.mono }, item.source)
|
|
814
835
|
]
|
|
@@ -821,9 +842,18 @@ var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
|
821
842
|
)
|
|
822
843
|
];
|
|
823
844
|
},
|
|
824
|
-
[
|
|
845
|
+
[
|
|
846
|
+
refreshButton2(
|
|
847
|
+
t2("common.refresh"),
|
|
848
|
+
isLoading.value,
|
|
849
|
+
() => void refresh(),
|
|
850
|
+
t2("common.working")
|
|
851
|
+
)
|
|
852
|
+
],
|
|
825
853
|
{
|
|
826
|
-
lead: a ?
|
|
854
|
+
lead: a ? t2("attention.leadChecked", {
|
|
855
|
+
time: (0, import_client2.formatAdminDate)(a.generatedAt, props.locale, "time")
|
|
856
|
+
}) : t2("attention.lead")
|
|
827
857
|
}
|
|
828
858
|
);
|
|
829
859
|
};
|
|
@@ -831,28 +861,29 @@ var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
|
831
861
|
});
|
|
832
862
|
|
|
833
863
|
// src/screens/EnvironmentScreen.ts
|
|
864
|
+
var import_client3 = require("@fonderie/client");
|
|
834
865
|
var import_vue_admin3 = require("@fonderie/vue-admin");
|
|
835
866
|
var import_vue5 = require("vue");
|
|
836
867
|
var EnvironmentScreen = (0, import_vue5.defineComponent)({
|
|
837
868
|
name: "FonderieEnvironmentScreen",
|
|
838
|
-
props: {
|
|
869
|
+
props: {
|
|
870
|
+
client: { type: Object, required: true },
|
|
871
|
+
locale: { type: String, default: void 0 }
|
|
872
|
+
},
|
|
839
873
|
setup(props) {
|
|
840
874
|
const { report, isLoading, error } = (0, import_vue_admin3.useAdminEnvironment)(props.client);
|
|
841
875
|
return () => {
|
|
876
|
+
const t2 = (0, import_client3.createAdminT)(props.locale);
|
|
842
877
|
const r = report.value;
|
|
843
878
|
const missing = r?.env.filter((e) => !e.set).length ?? 0;
|
|
844
879
|
return page(
|
|
845
|
-
"
|
|
880
|
+
t2("environment.title"),
|
|
846
881
|
{ isLoading, error },
|
|
847
882
|
() => {
|
|
848
883
|
if (!r) return null;
|
|
849
884
|
return [
|
|
850
|
-
(0, import_vue5.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, "
|
|
851
|
-
r.env.length === 0 ? (0, import_vue5.h)(
|
|
852
|
-
"p",
|
|
853
|
-
{ style: styles.muted },
|
|
854
|
-
"No variables declared \u2014 pass `env` to AdminModule."
|
|
855
|
-
) : (0, import_vue5.h)(
|
|
885
|
+
(0, import_vue5.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, t2("environment.variables")),
|
|
886
|
+
r.env.length === 0 ? (0, import_vue5.h)("p", { style: styles.muted }, t2("environment.noVariables")) : (0, import_vue5.h)(
|
|
856
887
|
"div",
|
|
857
888
|
{
|
|
858
889
|
style: {
|
|
@@ -877,12 +908,15 @@ var EnvironmentScreen = (0, import_vue5.defineComponent)({
|
|
|
877
908
|
},
|
|
878
909
|
[
|
|
879
910
|
(0, import_vue5.h)("span", { style: { ...styles.mono, wordBreak: "break-all" } }, e.name),
|
|
880
|
-
pill(
|
|
911
|
+
pill(
|
|
912
|
+
e.set ? "ok" : "bad",
|
|
913
|
+
e.set ? t2("common.status.set") : t2("common.status.missing")
|
|
914
|
+
)
|
|
881
915
|
]
|
|
882
916
|
)
|
|
883
917
|
)
|
|
884
918
|
),
|
|
885
|
-
(0, import_vue5.h)("h2", { style: styles.subtitle }, "
|
|
919
|
+
(0, import_vue5.h)("h2", { style: styles.subtitle }, t2("environment.moduleReadiness")),
|
|
886
920
|
(0, import_vue5.h)(
|
|
887
921
|
"ul",
|
|
888
922
|
{ style: styles.list },
|
|
@@ -902,7 +936,7 @@ var EnvironmentScreen = (0, import_vue5.defineComponent)({
|
|
|
902
936
|
(0, import_vue5.h)("span", { style: { ...styles.mono, fontWeight: 600 } }, m.name),
|
|
903
937
|
pill(
|
|
904
938
|
m.problems.length === 0 ? "ok" : m.problems.some((p) => p.severity === "error") ? "bad" : "warn",
|
|
905
|
-
m.problems.length === 0 ? "ready" :
|
|
939
|
+
m.problems.length === 0 ? t2("common.status.ready") : m.problems.length === 1 ? t2("environment.problemOne") : t2("environment.problemMany", { n: m.problems.length })
|
|
906
940
|
)
|
|
907
941
|
]
|
|
908
942
|
),
|
|
@@ -926,10 +960,14 @@ var EnvironmentScreen = (0, import_vue5.defineComponent)({
|
|
|
926
960
|
];
|
|
927
961
|
},
|
|
928
962
|
[
|
|
929
|
-
r && r.env.length > 0 ? pill(
|
|
963
|
+
r && r.env.length > 0 ? pill(
|
|
964
|
+
missing ? "bad" : "ok",
|
|
965
|
+
missing ? t2("environment.missingCount", { n: missing }) : t2("environment.allSet")
|
|
966
|
+
) : null
|
|
930
967
|
],
|
|
931
968
|
{
|
|
932
|
-
|
|
969
|
+
loadingText: t2("common.loading"),
|
|
970
|
+
lead: t2("environment.lead")
|
|
933
971
|
}
|
|
934
972
|
);
|
|
935
973
|
};
|
|
@@ -937,103 +975,131 @@ var EnvironmentScreen = (0, import_vue5.defineComponent)({
|
|
|
937
975
|
});
|
|
938
976
|
|
|
939
977
|
// src/screens/DoctorScreen.ts
|
|
978
|
+
var import_client4 = require("@fonderie/client");
|
|
940
979
|
var import_vue_admin4 = require("@fonderie/vue-admin");
|
|
941
980
|
var import_vue6 = require("vue");
|
|
942
981
|
var DoctorScreen = (0, import_vue6.defineComponent)({
|
|
943
982
|
name: "FonderieDoctorScreen",
|
|
944
|
-
props: {
|
|
983
|
+
props: {
|
|
984
|
+
client: { type: Object, required: true },
|
|
985
|
+
locale: { type: String, default: void 0 }
|
|
986
|
+
},
|
|
945
987
|
setup(props) {
|
|
946
988
|
const { report, isLoading, error, refresh } = (0, import_vue_admin4.useDoctor)(props.client);
|
|
947
|
-
return () =>
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
td([
|
|
968
|
-
(0, import_vue6.h)("div", [
|
|
969
|
-
(0, import_vue6.h)("span", { style: { ...styles.mono, fontWeight: 600 } }, c.name),
|
|
970
|
-
" ",
|
|
971
|
-
(0, import_vue6.h)("span", { style: styles.muted }, c.module)
|
|
972
|
-
]),
|
|
973
|
-
c.skipped ? (0, import_vue6.h)("div", { style: { ...styles.muted, marginTop: "4px" } }, c.skipped) : null,
|
|
974
|
-
...c.findings.map(
|
|
975
|
-
(f) => (0, import_vue6.h)(
|
|
976
|
-
"div",
|
|
977
|
-
{
|
|
978
|
-
key: f,
|
|
979
|
-
style: {
|
|
980
|
-
marginTop: "4px",
|
|
981
|
-
fontSize: "13px",
|
|
982
|
-
lineHeight: 1.55,
|
|
983
|
-
color: c.ok ? "var(--fonderie-text,#171717)" : "var(--fonderie-danger,#e00)"
|
|
984
|
-
}
|
|
985
|
-
},
|
|
986
|
-
f
|
|
989
|
+
return () => {
|
|
990
|
+
const t2 = (0, import_client4.createAdminT)(props.locale);
|
|
991
|
+
return page(
|
|
992
|
+
t2("doctor.title"),
|
|
993
|
+
{ isLoading: { value: isLoading.value && !report.value }, error },
|
|
994
|
+
() => {
|
|
995
|
+
const r = report.value;
|
|
996
|
+
if (!r) return null;
|
|
997
|
+
return table(
|
|
998
|
+
[
|
|
999
|
+
{ label: t2("doctor.colResult"), style: { width: "110px" } },
|
|
1000
|
+
t2("doctor.colCheck"),
|
|
1001
|
+
{ label: t2("doctor.colTime"), style: { textAlign: "right", width: "90px" } }
|
|
1002
|
+
],
|
|
1003
|
+
r.checks.map(
|
|
1004
|
+
(c) => (0, import_vue6.h)("tr", { key: c.name }, [
|
|
1005
|
+
td(
|
|
1006
|
+
pill(
|
|
1007
|
+
c.skipped ? "neutral" : c.ok ? c.findings.length ? "warn" : "ok" : "bad",
|
|
1008
|
+
c.skipped ? t2("common.status.skipped") : c.ok ? c.findings.length ? t2("common.status.advice") : t2("common.status.ok") : t2("common.status.failed")
|
|
987
1009
|
)
|
|
988
|
-
)
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1010
|
+
),
|
|
1011
|
+
td([
|
|
1012
|
+
(0, import_vue6.h)("div", [
|
|
1013
|
+
(0, import_vue6.h)("span", { style: { ...styles.mono, fontWeight: 600 } }, c.name),
|
|
1014
|
+
" ",
|
|
1015
|
+
(0, import_vue6.h)("span", { style: styles.muted }, c.module)
|
|
1016
|
+
]),
|
|
1017
|
+
c.skipped ? (0, import_vue6.h)("div", { style: { ...styles.muted, marginTop: "4px" } }, c.skipped) : null,
|
|
1018
|
+
...c.findings.map(
|
|
1019
|
+
(f) => (0, import_vue6.h)(
|
|
1020
|
+
"div",
|
|
1021
|
+
{
|
|
1022
|
+
key: f,
|
|
1023
|
+
style: {
|
|
1024
|
+
marginTop: "4px",
|
|
1025
|
+
fontSize: "13px",
|
|
1026
|
+
lineHeight: 1.55,
|
|
1027
|
+
color: c.ok ? "var(--fonderie-text,#171717)" : "var(--fonderie-danger,#e00)"
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
f
|
|
1031
|
+
)
|
|
1032
|
+
)
|
|
1033
|
+
]),
|
|
1034
|
+
td(`${c.durationMs} ms`, {
|
|
1035
|
+
...styles.muted,
|
|
1036
|
+
textAlign: "right",
|
|
1037
|
+
whiteSpace: "nowrap"
|
|
1038
|
+
})
|
|
1039
|
+
])
|
|
1040
|
+
)
|
|
1041
|
+
);
|
|
1042
|
+
},
|
|
1043
|
+
[
|
|
1044
|
+
report.value && !isLoading.value ? pill(
|
|
1045
|
+
report.value.ok ? "ok" : "bad",
|
|
1046
|
+
report.value.ok ? t2("doctor.allPass") : t2("doctor.oneFailed")
|
|
1047
|
+
) : null,
|
|
1048
|
+
refreshButton2(
|
|
1049
|
+
t2("doctor.runAgain"),
|
|
1050
|
+
isLoading.value,
|
|
1051
|
+
() => void refresh(),
|
|
1052
|
+
t2("common.working")
|
|
996
1053
|
)
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
refreshButton2("Run again", isLoading.value, () => void refresh())
|
|
1005
|
-
],
|
|
1006
|
-
{
|
|
1007
|
-
loadingText: "Running the checks\u2026",
|
|
1008
|
-
lead: "Every reconciliation check across the installed modules, run on demand."
|
|
1009
|
-
}
|
|
1010
|
-
);
|
|
1054
|
+
],
|
|
1055
|
+
{
|
|
1056
|
+
loadingText: t2("doctor.running"),
|
|
1057
|
+
lead: t2("doctor.lead")
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
};
|
|
1011
1061
|
}
|
|
1012
1062
|
});
|
|
1013
1063
|
|
|
1014
1064
|
// src/screens/ModulesScreen.ts
|
|
1065
|
+
var import_client5 = require("@fonderie/client");
|
|
1015
1066
|
var import_vue_admin5 = require("@fonderie/vue-admin");
|
|
1016
1067
|
var import_vue7 = require("vue");
|
|
1017
1068
|
var ModulesScreen = (0, import_vue7.defineComponent)({
|
|
1018
1069
|
name: "FonderieModulesScreen",
|
|
1019
|
-
props: {
|
|
1070
|
+
props: {
|
|
1071
|
+
client: { type: Object, required: true },
|
|
1072
|
+
locale: { type: String, default: void 0 }
|
|
1073
|
+
},
|
|
1020
1074
|
setup(props) {
|
|
1021
1075
|
const { manifest, isLoading, error } = (0, import_vue_admin5.useManifest)(props.client);
|
|
1022
1076
|
return () => {
|
|
1077
|
+
const t2 = (0, import_client5.createAdminT)(props.locale);
|
|
1023
1078
|
const m = manifest.value;
|
|
1024
1079
|
return page(
|
|
1025
|
-
"
|
|
1080
|
+
t2("modules.title"),
|
|
1026
1081
|
{ isLoading, error },
|
|
1027
1082
|
() => {
|
|
1028
1083
|
if (!m) return null;
|
|
1029
1084
|
return table(
|
|
1030
|
-
[
|
|
1085
|
+
[
|
|
1086
|
+
t2("modules.colModule"),
|
|
1087
|
+
t2("modules.colVersion"),
|
|
1088
|
+
t2("modules.colReadiness"),
|
|
1089
|
+
t2("modules.colAdmin")
|
|
1090
|
+
],
|
|
1031
1091
|
m.modules.map(
|
|
1032
1092
|
(mod) => (0, import_vue7.h)("tr", { key: mod.name }, [
|
|
1033
1093
|
td(mod.name, { ...styles.mono, fontWeight: 600 }),
|
|
1034
|
-
td(
|
|
1094
|
+
td(
|
|
1095
|
+
mod.version ?? (0, import_vue7.h)("span", { style: styles.muted }, t2("modules.notReported")),
|
|
1096
|
+
styles.mono
|
|
1097
|
+
),
|
|
1035
1098
|
td([
|
|
1036
|
-
pill(
|
|
1099
|
+
pill(
|
|
1100
|
+
mod.readiness.ok ? "ok" : "bad",
|
|
1101
|
+
mod.readiness.ok ? t2("common.status.ready") : t2("common.status.error")
|
|
1102
|
+
),
|
|
1037
1103
|
...mod.readiness.problems.map(
|
|
1038
1104
|
(p) => (0, import_vue7.h)(
|
|
1039
1105
|
"div",
|
|
@@ -1050,7 +1116,7 @@ var ModulesScreen = (0, import_vue7.defineComponent)({
|
|
|
1050
1116
|
)
|
|
1051
1117
|
]),
|
|
1052
1118
|
td(
|
|
1053
|
-
mod.describesAdmin ? (0, import_vue7.h)("span", { style: styles.badge }, "
|
|
1119
|
+
mod.describesAdmin ? (0, import_vue7.h)("span", { style: styles.badge }, t2("modules.describesAdmin")) : (0, import_vue7.h)("span", { style: styles.muted }, "\u2014")
|
|
1054
1120
|
)
|
|
1055
1121
|
])
|
|
1056
1122
|
)
|
|
@@ -1058,7 +1124,13 @@ var ModulesScreen = (0, import_vue7.defineComponent)({
|
|
|
1058
1124
|
},
|
|
1059
1125
|
[],
|
|
1060
1126
|
{
|
|
1061
|
-
|
|
1127
|
+
loadingText: t2("common.loading"),
|
|
1128
|
+
lead: m ? t2("modules.leadSummary", {
|
|
1129
|
+
env: m.env,
|
|
1130
|
+
version: m.admin.version,
|
|
1131
|
+
log: m.admin.log ? t2("modules.logOn") : t2("modules.logOff"),
|
|
1132
|
+
routes: m.routes.length
|
|
1133
|
+
}) : t2("modules.lead")
|
|
1062
1134
|
}
|
|
1063
1135
|
);
|
|
1064
1136
|
};
|
|
@@ -1066,24 +1138,34 @@ var ModulesScreen = (0, import_vue7.defineComponent)({
|
|
|
1066
1138
|
});
|
|
1067
1139
|
|
|
1068
1140
|
// src/screens/RoutesScreen.ts
|
|
1141
|
+
var import_client6 = require("@fonderie/client");
|
|
1069
1142
|
var import_vue_admin6 = require("@fonderie/vue-admin");
|
|
1070
1143
|
var import_vue8 = require("vue");
|
|
1071
1144
|
var RoutesScreen = (0, import_vue8.defineComponent)({
|
|
1072
1145
|
name: "FonderieRoutesScreen",
|
|
1073
|
-
props: {
|
|
1146
|
+
props: {
|
|
1147
|
+
client: { type: Object, required: true },
|
|
1148
|
+
locale: { type: String, default: void 0 }
|
|
1149
|
+
},
|
|
1074
1150
|
setup(props) {
|
|
1075
1151
|
const { report, isLoading, error } = (0, import_vue_admin6.useAdminRoutes)(props.client);
|
|
1076
1152
|
const q = (0, import_vue8.ref)("");
|
|
1077
1153
|
return () => {
|
|
1154
|
+
const t2 = (0, import_client6.createAdminT)(props.locale);
|
|
1078
1155
|
const needle = q.value.trim().toLowerCase();
|
|
1079
1156
|
const routes = (report.value?.routes ?? []).filter(
|
|
1080
1157
|
(r) => !needle || `${r.method} ${r.path} ${r.module ?? "application"} ${r.guard}`.toLowerCase().includes(needle)
|
|
1081
1158
|
);
|
|
1082
1159
|
return page(
|
|
1083
|
-
"
|
|
1160
|
+
t2("routes.title"),
|
|
1084
1161
|
{ isLoading, error },
|
|
1085
1162
|
() => table(
|
|
1086
|
-
[
|
|
1163
|
+
[
|
|
1164
|
+
{ label: t2("routes.colMethod"), style: { width: "80px" } },
|
|
1165
|
+
t2("routes.colPath"),
|
|
1166
|
+
t2("routes.colGuard"),
|
|
1167
|
+
t2("routes.colModule")
|
|
1168
|
+
],
|
|
1087
1169
|
[
|
|
1088
1170
|
...routes.map(
|
|
1089
1171
|
(r) => (0, import_vue8.h)("tr", { key: `${r.method} ${r.path}` }, [
|
|
@@ -1096,7 +1178,7 @@ var RoutesScreen = (0, import_vue8.defineComponent)({
|
|
|
1096
1178
|
false
|
|
1097
1179
|
)
|
|
1098
1180
|
),
|
|
1099
|
-
td(r.module ?? "application", styles.muted)
|
|
1181
|
+
td(r.module ?? t2("routes.application"), styles.muted)
|
|
1100
1182
|
])
|
|
1101
1183
|
),
|
|
1102
1184
|
...routes.length === 0 ? [
|
|
@@ -1104,7 +1186,7 @@ var RoutesScreen = (0, import_vue8.defineComponent)({
|
|
|
1104
1186
|
(0, import_vue8.h)(
|
|
1105
1187
|
"td",
|
|
1106
1188
|
{ style: { ...styles.td, ...styles.muted }, colspan: 4 },
|
|
1107
|
-
|
|
1189
|
+
t2("routes.noMatch", { q: q.value })
|
|
1108
1190
|
)
|
|
1109
1191
|
])
|
|
1110
1192
|
] : []
|
|
@@ -1114,16 +1196,17 @@ var RoutesScreen = (0, import_vue8.defineComponent)({
|
|
|
1114
1196
|
(0, import_vue8.h)("input", {
|
|
1115
1197
|
type: "search",
|
|
1116
1198
|
value: q.value,
|
|
1117
|
-
placeholder: "
|
|
1199
|
+
placeholder: t2("routes.filterPlaceholder"),
|
|
1118
1200
|
style: { ...styles.input, width: "280px" },
|
|
1119
|
-
"aria-label": "
|
|
1201
|
+
"aria-label": t2("routes.filterLabel"),
|
|
1120
1202
|
onInput: (e) => {
|
|
1121
1203
|
q.value = e.target.value;
|
|
1122
1204
|
}
|
|
1123
1205
|
})
|
|
1124
1206
|
],
|
|
1125
1207
|
{
|
|
1126
|
-
|
|
1208
|
+
loadingText: t2("common.loading"),
|
|
1209
|
+
lead: report.value ? t2("routes.leadCount", { n: report.value.routes.length }) : t2("routes.lead")
|
|
1127
1210
|
}
|
|
1128
1211
|
);
|
|
1129
1212
|
};
|
|
@@ -1131,27 +1214,31 @@ var RoutesScreen = (0, import_vue8.defineComponent)({
|
|
|
1131
1214
|
});
|
|
1132
1215
|
|
|
1133
1216
|
// src/screens/MigrationsScreen.ts
|
|
1217
|
+
var import_client7 = require("@fonderie/client");
|
|
1134
1218
|
var import_vue_admin7 = require("@fonderie/vue-admin");
|
|
1135
1219
|
var import_vue9 = require("vue");
|
|
1136
|
-
function why(m, everApplied) {
|
|
1220
|
+
function why(t2, m, everApplied) {
|
|
1137
1221
|
if (m.pending.length === 0) return null;
|
|
1138
|
-
if (m.blockedBy) return
|
|
1222
|
+
if (m.blockedBy) return t2("migrations.blockedBy", { module: m.blockedBy });
|
|
1139
1223
|
if (everApplied && m.pending.some((p) => p.impact === "destructive"))
|
|
1140
|
-
return "
|
|
1224
|
+
return t2("migrations.destructiveBlocked");
|
|
1141
1225
|
return null;
|
|
1142
1226
|
}
|
|
1143
1227
|
var MigrationsScreen = (0, import_vue9.defineComponent)({
|
|
1144
1228
|
name: "FonderieMigrationsScreen",
|
|
1145
|
-
props: {
|
|
1229
|
+
props: {
|
|
1230
|
+
client: { type: Object, required: true },
|
|
1231
|
+
locale: { type: String, default: void 0 }
|
|
1232
|
+
},
|
|
1146
1233
|
setup(props) {
|
|
1147
1234
|
const { report, isLoading, error, refresh, apply } = (0, import_vue_admin7.useAdminMigrations)(props.client);
|
|
1148
|
-
const section = (m, everApplied) => {
|
|
1149
|
-
const blocked = why(m, everApplied);
|
|
1235
|
+
const section = (t2, m, everApplied) => {
|
|
1236
|
+
const blocked = why(t2, m, everApplied);
|
|
1150
1237
|
const files = m.pending.map((p) => p.file);
|
|
1151
1238
|
return (0, import_vue9.h)("section", { style: { ...styles.card, marginBottom: "16px" } }, [
|
|
1152
1239
|
(0, import_vue9.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, [
|
|
1153
1240
|
(0, import_vue9.h)("span", { style: styles.mono }, m.name),
|
|
1154
|
-
pill("warn",
|
|
1241
|
+
pill("warn", t2("migrations.pendingCount", { n: m.pending.length }))
|
|
1155
1242
|
]),
|
|
1156
1243
|
(0, import_vue9.h)(
|
|
1157
1244
|
"ul",
|
|
@@ -1160,7 +1247,7 @@ var MigrationsScreen = (0, import_vue9.defineComponent)({
|
|
|
1160
1247
|
(p) => (0, import_vue9.h)("li", [
|
|
1161
1248
|
(0, import_vue9.h)("code", { style: styles.code }, p.file),
|
|
1162
1249
|
" ",
|
|
1163
|
-
p.impact === "destructive" ? pill("bad", "destructive") : pill("neutral", "additive"),
|
|
1250
|
+
p.impact === "destructive" ? pill("bad", t2("migrations.destructive")) : pill("neutral", t2("migrations.additive")),
|
|
1164
1251
|
p.destructive.length > 0 ? (0, import_vue9.h)(
|
|
1165
1252
|
"ul",
|
|
1166
1253
|
p.destructive.map((s) => (0, import_vue9.h)("li", [(0, import_vue9.h)("code", s.slice(0, 120))]))
|
|
@@ -1176,57 +1263,61 @@ var MigrationsScreen = (0, import_vue9.defineComponent)({
|
|
|
1176
1263
|
disabled: isLoading.value || !m.appliable,
|
|
1177
1264
|
onClick: () => {
|
|
1178
1265
|
if (window.confirm(
|
|
1179
|
-
|
|
1266
|
+
t2("migrations.confirmApply", { n: m.pending.length, module: m.name })
|
|
1180
1267
|
))
|
|
1181
1268
|
void apply(m.name, files).catch(() => {
|
|
1182
1269
|
});
|
|
1183
1270
|
}
|
|
1184
1271
|
},
|
|
1185
|
-
|
|
1272
|
+
m.pending.length === 1 ? t2("migrations.applyOne") : t2("migrations.applyMany", { n: m.pending.length })
|
|
1186
1273
|
)
|
|
1187
1274
|
]);
|
|
1188
1275
|
};
|
|
1189
|
-
return () =>
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
{ style: styles.notice },
|
|
1202
|
-
|
|
1203
|
-
)
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1276
|
+
return () => {
|
|
1277
|
+
const t2 = (0, import_client7.createAdminT)(props.locale);
|
|
1278
|
+
return page(
|
|
1279
|
+
t2("migrations.title"),
|
|
1280
|
+
{ isLoading, error },
|
|
1281
|
+
() => {
|
|
1282
|
+
const r = report.value;
|
|
1283
|
+
if (!r) return null;
|
|
1284
|
+
const behind = r.modules.filter((m) => m.pending.length > 0);
|
|
1285
|
+
if (behind.length === 0)
|
|
1286
|
+
return empty(t2("migrations.upToDate"), t2("migrations.noPending"), "migrations");
|
|
1287
|
+
return [
|
|
1288
|
+
r.everApplied ? null : (0, import_vue9.h)("p", { style: styles.notice }, t2("migrations.firstInstall")),
|
|
1289
|
+
...behind.map((m) => section(t2, m, r.everApplied))
|
|
1290
|
+
].filter(Boolean);
|
|
1291
|
+
},
|
|
1292
|
+
[
|
|
1293
|
+
refreshButton2(
|
|
1294
|
+
t2("common.refresh"),
|
|
1295
|
+
isLoading.value,
|
|
1296
|
+
() => void refresh(),
|
|
1297
|
+
t2("common.working")
|
|
1298
|
+
)
|
|
1299
|
+
],
|
|
1300
|
+
{
|
|
1301
|
+
loadingText: t2("common.loading"),
|
|
1302
|
+
lead: t2("migrations.lead"),
|
|
1303
|
+
errorText: (e) => e.status === 403 ? t2("migrations.needsWrite") : e.explanation
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
};
|
|
1213
1307
|
}
|
|
1214
1308
|
});
|
|
1215
1309
|
|
|
1216
1310
|
// src/screens/OperatorsScreen.ts
|
|
1311
|
+
var import_client8 = require("@fonderie/client");
|
|
1217
1312
|
var import_vue_admin8 = require("@fonderie/vue-admin");
|
|
1218
1313
|
var import_vue10 = require("vue");
|
|
1219
1314
|
var LEVELS = [
|
|
1220
|
-
{
|
|
1221
|
-
{
|
|
1222
|
-
{
|
|
1223
|
-
label: "Owner",
|
|
1224
|
-
scopes: ["read", "write", "secrets"],
|
|
1225
|
-
hint: "secrets, tokens and operators too"
|
|
1226
|
-
}
|
|
1315
|
+
{ id: "read", scopes: ["read"] },
|
|
1316
|
+
{ id: "editor", scopes: ["read", "write"] },
|
|
1317
|
+
{ id: "owner", scopes: ["read", "write", "secrets"] }
|
|
1227
1318
|
];
|
|
1228
|
-
var levelOf = (scopes) => scopes.includes("secrets") ? "
|
|
1229
|
-
var scopesFor = (
|
|
1319
|
+
var levelOf = (scopes) => scopes.includes("secrets") ? "owner" : scopes.includes("write") ? "editor" : "read";
|
|
1320
|
+
var scopesFor = (id) => LEVELS.find((l) => l.id === id)?.scopes ?? ["read"];
|
|
1230
1321
|
var small = { height: "28px" };
|
|
1231
1322
|
var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
1232
1323
|
name: "FonderieOperatorsScreen",
|
|
@@ -1234,18 +1325,19 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1234
1325
|
client: { type: Object, required: true },
|
|
1235
1326
|
// The signed-in operator's email: their own row offers no disable or
|
|
1236
1327
|
// recovery (the server refuses both for yourself anyway).
|
|
1237
|
-
me: { type: String, default: void 0 }
|
|
1328
|
+
me: { type: String, default: void 0 },
|
|
1329
|
+
locale: { type: String, default: void 0 }
|
|
1238
1330
|
},
|
|
1239
1331
|
setup(props) {
|
|
1240
1332
|
const { report, isLoading, error, invite, recover, update, revokeLink } = (0, import_vue_admin8.useAdminOperators)(
|
|
1241
1333
|
props.client
|
|
1242
1334
|
);
|
|
1243
1335
|
const email = (0, import_vue10.ref)("");
|
|
1244
|
-
const level = (0, import_vue10.ref)("
|
|
1336
|
+
const level = (0, import_vue10.ref)("editor");
|
|
1245
1337
|
const minted = (0, import_vue10.ref)(null);
|
|
1246
1338
|
const copied = (0, import_vue10.ref)(false);
|
|
1247
|
-
const status = (o) => o.disabledAt ? pill("neutral", "disabled") : o.locked ? pill("warn", "locked") : !o.enrolled ? pill("warn", "
|
|
1248
|
-
const mintedNotice = (link) => {
|
|
1339
|
+
const status = (t2, o) => o.disabledAt ? pill("neutral", t2("common.status.disabled")) : o.locked ? pill("warn", t2("common.status.locked")) : !o.enrolled ? pill("warn", t2("operators.settingUp")) : pill("ok", t2("common.status.active"));
|
|
1340
|
+
const mintedNotice = (t2, link) => {
|
|
1249
1341
|
const url = `${window.location.origin}${link.url}`;
|
|
1250
1342
|
return (0, import_vue10.h)(
|
|
1251
1343
|
"div",
|
|
@@ -1257,8 +1349,8 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1257
1349
|
}
|
|
1258
1350
|
},
|
|
1259
1351
|
[
|
|
1260
|
-
(0, import_vue10.h)("strong",
|
|
1261
|
-
`
|
|
1352
|
+
(0, import_vue10.h)("strong", t2("operators.mintedTitle", { email: link.email })),
|
|
1353
|
+
` ${t2("operators.mintedBody", { date: (0, import_client8.formatAdminDate)(link.expiresAt, props.locale) })}`,
|
|
1262
1354
|
(0, import_vue10.h)(
|
|
1263
1355
|
"div",
|
|
1264
1356
|
{ style: { display: "flex", gap: "8px", marginTop: "8px", alignItems: "center" } },
|
|
@@ -1278,14 +1370,14 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1278
1370
|
copied.value = true;
|
|
1279
1371
|
}
|
|
1280
1372
|
},
|
|
1281
|
-
copied.value ? "
|
|
1373
|
+
copied.value ? t2("common.copied") : t2("common.copy")
|
|
1282
1374
|
),
|
|
1283
1375
|
(0, import_vue10.h)(
|
|
1284
1376
|
"button",
|
|
1285
1377
|
{
|
|
1286
1378
|
type: "button",
|
|
1287
1379
|
style: styles.buttonGhost,
|
|
1288
|
-
"aria-label": "
|
|
1380
|
+
"aria-label": t2("common.dismiss"),
|
|
1289
1381
|
onClick: () => {
|
|
1290
1382
|
minted.value = null;
|
|
1291
1383
|
copied.value = false;
|
|
@@ -1303,19 +1395,17 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1303
1395
|
minted.value = l;
|
|
1304
1396
|
};
|
|
1305
1397
|
return () => {
|
|
1398
|
+
const t2 = (0, import_client8.createAdminT)(props.locale);
|
|
1306
1399
|
const r = report.value;
|
|
1307
1400
|
const e = error.value;
|
|
1308
1401
|
return (0, import_vue10.h)("div", { style: styles.container }, [
|
|
1309
|
-
pageHeader(
|
|
1310
|
-
"Operators",
|
|
1311
|
-
"The people who can sign in to this console. Each one uses a password and an authenticator app; there is no sign-up."
|
|
1312
|
-
),
|
|
1402
|
+
pageHeader(t2("operators.title"), t2("operators.lead")),
|
|
1313
1403
|
e ? (0, import_vue10.h)(
|
|
1314
1404
|
"p",
|
|
1315
1405
|
{ style: styles.error, role: "alert" },
|
|
1316
|
-
e.status === 403 && e.reason === "FORBIDDEN" ? "
|
|
1406
|
+
e.status === 403 && e.reason === "FORBIDDEN" ? t2("operators.needsOwner") : e.explanation
|
|
1317
1407
|
) : null,
|
|
1318
|
-
minted.value ? mintedNotice(minted.value) : null,
|
|
1408
|
+
minted.value ? mintedNotice(t2, minted.value) : null,
|
|
1319
1409
|
(0, import_vue10.h)(
|
|
1320
1410
|
"form",
|
|
1321
1411
|
{
|
|
@@ -1338,15 +1428,19 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1338
1428
|
},
|
|
1339
1429
|
[
|
|
1340
1430
|
icon("users", 16),
|
|
1341
|
-
(0, import_vue10.h)(
|
|
1431
|
+
(0, import_vue10.h)(
|
|
1432
|
+
"strong",
|
|
1433
|
+
{ style: { fontSize: "13.5px", marginRight: "4px" } },
|
|
1434
|
+
t2("operators.invite")
|
|
1435
|
+
),
|
|
1342
1436
|
(0, import_vue10.h)("input", {
|
|
1343
1437
|
type: "email",
|
|
1344
1438
|
required: true,
|
|
1345
1439
|
value: email.value,
|
|
1346
1440
|
onInput: (ev) => email.value = ev.target.value,
|
|
1347
|
-
placeholder: "
|
|
1441
|
+
placeholder: t2("operators.invitePlaceholder"),
|
|
1348
1442
|
style: { ...styles.input, flex: 1, minWidth: "220px" },
|
|
1349
|
-
"aria-label": "
|
|
1443
|
+
"aria-label": t2("operators.inviteLabel")
|
|
1350
1444
|
}),
|
|
1351
1445
|
(0, import_vue10.h)(
|
|
1352
1446
|
"select",
|
|
@@ -1354,21 +1448,31 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1354
1448
|
value: level.value,
|
|
1355
1449
|
onChange: (ev) => level.value = ev.target.value,
|
|
1356
1450
|
style: styles.input,
|
|
1357
|
-
"aria-label": "
|
|
1451
|
+
"aria-label": t2("operators.accessLevelLabel")
|
|
1358
1452
|
},
|
|
1359
1453
|
LEVELS.map(
|
|
1360
|
-
(l) => (0, import_vue10.h)(
|
|
1454
|
+
(l) => (0, import_vue10.h)(
|
|
1455
|
+
"option",
|
|
1456
|
+
{ key: l.id, value: l.id },
|
|
1457
|
+
`${t2(`operators.level.${l.id}`)} \u2014 ${t2(`operators.levelHint.${l.id}`)}`
|
|
1458
|
+
)
|
|
1361
1459
|
)
|
|
1362
1460
|
),
|
|
1363
1461
|
(0, import_vue10.h)(
|
|
1364
1462
|
"button",
|
|
1365
1463
|
{ type: "submit", style: styles.buttonPrimary, disabled: !email.value.trim() },
|
|
1366
|
-
[icon("plus", 14), "
|
|
1464
|
+
[icon("plus", 14), t2("operators.createInvite")]
|
|
1367
1465
|
)
|
|
1368
1466
|
]
|
|
1369
1467
|
),
|
|
1370
|
-
isLoading.value && !r ? (0, import_vue10.h)("p", { style: styles.status }, "
|
|
1371
|
-
[
|
|
1468
|
+
isLoading.value && !r ? (0, import_vue10.h)("p", { style: styles.status }, t2("common.loading")) : r && r.operators.length === 0 ? empty(t2("operators.emptyTitle"), void 0, "users") : r ? table(
|
|
1469
|
+
[
|
|
1470
|
+
t2("operators.col.operator"),
|
|
1471
|
+
t2("operators.col.access"),
|
|
1472
|
+
t2("operators.col.status"),
|
|
1473
|
+
t2("operators.col.lastSignIn"),
|
|
1474
|
+
""
|
|
1475
|
+
],
|
|
1372
1476
|
r.operators.map((o) => {
|
|
1373
1477
|
const mine = o.email === props.me;
|
|
1374
1478
|
return (0, import_vue10.h)("tr", { key: o.id }, [
|
|
@@ -1387,25 +1491,33 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1387
1491
|
}).catch(() => {
|
|
1388
1492
|
}),
|
|
1389
1493
|
style: { ...styles.input, ...small, fontSize: "12.5px" },
|
|
1390
|
-
"aria-label":
|
|
1494
|
+
"aria-label": t2("operators.accessLevelFor", { email: o.email })
|
|
1391
1495
|
},
|
|
1392
|
-
LEVELS.map(
|
|
1496
|
+
LEVELS.map(
|
|
1497
|
+
(l) => (0, import_vue10.h)("option", { key: l.id, value: l.id }, t2(`operators.level.${l.id}`))
|
|
1498
|
+
)
|
|
1393
1499
|
)
|
|
1394
1500
|
),
|
|
1395
1501
|
td([
|
|
1396
|
-
status(o),
|
|
1502
|
+
status(t2, o),
|
|
1397
1503
|
o.enrolled && o.backupCodesLeft <= 2 && !o.disabledAt ? (0, import_vue10.h)(
|
|
1398
1504
|
"div",
|
|
1399
1505
|
{ style: { ...styles.muted, marginTop: "4px" } },
|
|
1400
|
-
|
|
1506
|
+
t2(
|
|
1507
|
+
o.backupCodesLeft === 1 ? "operators.backupCodesLeftOne" : "operators.backupCodesLeftMany",
|
|
1508
|
+
{ n: o.backupCodesLeft }
|
|
1509
|
+
)
|
|
1401
1510
|
) : null
|
|
1402
1511
|
]),
|
|
1403
|
-
td(o.lastLoginAt ? new Date(o.lastLoginAt).toLocaleString() : "never", {
|
|
1404
|
-
...styles.muted,
|
|
1405
|
-
whiteSpace: "nowrap"
|
|
1406
|
-
}),
|
|
1407
1512
|
td(
|
|
1408
|
-
|
|
1513
|
+
o.lastLoginAt ? (0, import_client8.formatAdminDate)(o.lastLoginAt, props.locale) : t2("common.never"),
|
|
1514
|
+
{
|
|
1515
|
+
...styles.muted,
|
|
1516
|
+
whiteSpace: "nowrap"
|
|
1517
|
+
}
|
|
1518
|
+
),
|
|
1519
|
+
td(
|
|
1520
|
+
mine ? (0, import_vue10.h)("span", { style: styles.muted }, t2("common.you")) : [
|
|
1409
1521
|
(0, import_vue10.h)(
|
|
1410
1522
|
"button",
|
|
1411
1523
|
{
|
|
@@ -1413,13 +1525,13 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1413
1525
|
style: { ...styles.button, ...small, marginRight: "6px" },
|
|
1414
1526
|
onClick: () => {
|
|
1415
1527
|
if (window.confirm(
|
|
1416
|
-
|
|
1528
|
+
t2("operators.recoveryConfirm", { email: o.email })
|
|
1417
1529
|
))
|
|
1418
1530
|
void recover(o.id).then(setMinted).catch(() => {
|
|
1419
1531
|
});
|
|
1420
1532
|
}
|
|
1421
1533
|
},
|
|
1422
|
-
"
|
|
1534
|
+
t2("operators.recoveryLink")
|
|
1423
1535
|
),
|
|
1424
1536
|
(0, import_vue10.h)(
|
|
1425
1537
|
"button",
|
|
@@ -1431,7 +1543,7 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1431
1543
|
},
|
|
1432
1544
|
onClick: () => {
|
|
1433
1545
|
if (o.disabledAt || window.confirm(
|
|
1434
|
-
|
|
1546
|
+
t2("operators.disableConfirm", { email: o.email })
|
|
1435
1547
|
))
|
|
1436
1548
|
void update(o.id, { disabled: !o.disabledAt }).catch(
|
|
1437
1549
|
() => {
|
|
@@ -1439,7 +1551,7 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1439
1551
|
);
|
|
1440
1552
|
}
|
|
1441
1553
|
},
|
|
1442
|
-
o.disabledAt ? "
|
|
1554
|
+
o.disabledAt ? t2("operators.enable") : t2("operators.disable")
|
|
1443
1555
|
)
|
|
1444
1556
|
],
|
|
1445
1557
|
{ textAlign: "right", whiteSpace: "nowrap" }
|
|
@@ -1448,7 +1560,7 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1448
1560
|
})
|
|
1449
1561
|
) : null,
|
|
1450
1562
|
r && r.links.length > 0 ? [
|
|
1451
|
-
(0, import_vue10.h)("h2", { style: styles.subtitle }, "
|
|
1563
|
+
(0, import_vue10.h)("h2", { style: styles.subtitle }, t2("operators.pendingLinks")),
|
|
1452
1564
|
(0, import_vue10.h)(
|
|
1453
1565
|
"ul",
|
|
1454
1566
|
{ style: styles.list },
|
|
@@ -1460,15 +1572,25 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1460
1572
|
style: { ...styles.row, display: "flex", gap: "12px", alignItems: "center" }
|
|
1461
1573
|
},
|
|
1462
1574
|
[
|
|
1463
|
-
pill(
|
|
1575
|
+
pill(
|
|
1576
|
+
l.kind === "invite" ? "info" : "warn",
|
|
1577
|
+
l.kind === "invite" ? t2("operators.linkKind.invite") : t2("operators.linkKind.recovery"),
|
|
1578
|
+
false
|
|
1579
|
+
),
|
|
1464
1580
|
(0, import_vue10.h)("span", { style: { flex: 1 } }, [
|
|
1465
1581
|
l.email,
|
|
1466
|
-
l.kind === "invite" ? (0, import_vue10.h)(
|
|
1582
|
+
l.kind === "invite" ? (0, import_vue10.h)(
|
|
1583
|
+
"span",
|
|
1584
|
+
{ style: styles.muted },
|
|
1585
|
+
` \xB7 ${t2(`operators.level.${levelOf(l.scopes)}`)}`
|
|
1586
|
+
) : null
|
|
1467
1587
|
]),
|
|
1468
1588
|
(0, import_vue10.h)(
|
|
1469
1589
|
"span",
|
|
1470
1590
|
{ style: styles.muted },
|
|
1471
|
-
|
|
1591
|
+
t2("operators.expiresOn", {
|
|
1592
|
+
date: (0, import_client8.formatAdminDate)(l.expiresAt, props.locale)
|
|
1593
|
+
})
|
|
1472
1594
|
),
|
|
1473
1595
|
(0, import_vue10.h)(
|
|
1474
1596
|
"button",
|
|
@@ -1478,7 +1600,7 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1478
1600
|
onClick: () => void revokeLink(l.id).catch(() => {
|
|
1479
1601
|
})
|
|
1480
1602
|
},
|
|
1481
|
-
"
|
|
1603
|
+
t2("operators.revoke")
|
|
1482
1604
|
)
|
|
1483
1605
|
]
|
|
1484
1606
|
)
|
|
@@ -1491,12 +1613,16 @@ var OperatorsScreen = (0, import_vue10.defineComponent)({
|
|
|
1491
1613
|
});
|
|
1492
1614
|
|
|
1493
1615
|
// src/screens/TokensScreen.ts
|
|
1616
|
+
var import_client9 = require("@fonderie/client");
|
|
1494
1617
|
var import_vue_admin9 = require("@fonderie/vue-admin");
|
|
1495
1618
|
var import_vue11 = require("vue");
|
|
1496
1619
|
var ALL = ["read", "write", "secrets"];
|
|
1497
1620
|
var TokensScreen = (0, import_vue11.defineComponent)({
|
|
1498
1621
|
name: "FonderieTokensScreen",
|
|
1499
|
-
props: {
|
|
1622
|
+
props: {
|
|
1623
|
+
client: { type: Object, required: true },
|
|
1624
|
+
locale: { type: String, default: void 0 }
|
|
1625
|
+
},
|
|
1500
1626
|
setup(props) {
|
|
1501
1627
|
const { report, isLoading, error, issue, revoke } = (0, import_vue_admin9.useAdminTokens)(props.client);
|
|
1502
1628
|
const name = (0, import_vue11.ref)("");
|
|
@@ -1505,25 +1631,27 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1505
1631
|
const minted = (0, import_vue11.ref)(null);
|
|
1506
1632
|
const toggle = (s) => scopes.value = scopes.value.includes(s) ? scopes.value.filter((x) => x !== s) : [...scopes.value, s];
|
|
1507
1633
|
return () => {
|
|
1634
|
+
const t2 = (0, import_client9.createAdminT)(props.locale);
|
|
1635
|
+
const loc = props.locale;
|
|
1508
1636
|
const r = report.value;
|
|
1509
1637
|
return (0, import_vue11.h)("div", { style: styles.container }, [
|
|
1510
|
-
pageHeader(
|
|
1511
|
-
"Tokens",
|
|
1512
|
-
"Credentials for machines: the root token's strength, scoped tokens for the CLI and CI, and any legacy per-module tokens. People sign in as operators."
|
|
1513
|
-
),
|
|
1638
|
+
pageHeader(t2("tokens.title"), t2("tokens.lead")),
|
|
1514
1639
|
error.value ? (0, import_vue11.h)(
|
|
1515
1640
|
"p",
|
|
1516
1641
|
{ style: styles.error, role: "alert" },
|
|
1517
|
-
error.value.status === 401 ? "
|
|
1642
|
+
error.value.status === 401 ? t2("tokens.needsRoot") : error.value.explanation
|
|
1518
1643
|
) : null,
|
|
1519
|
-
(0, import_vue11.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, "
|
|
1644
|
+
(0, import_vue11.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, t2("tokens.rootToken")),
|
|
1520
1645
|
r ? (0, import_vue11.h)(
|
|
1521
1646
|
"div",
|
|
1522
1647
|
{ style: { ...styles.card, display: "flex", gap: "12px", alignItems: "flex-start" } },
|
|
1523
1648
|
[
|
|
1524
1649
|
icon("lock", 18, { marginTop: "2px" }),
|
|
1525
1650
|
(0, import_vue11.h)("div", [
|
|
1526
|
-
pill(
|
|
1651
|
+
pill(
|
|
1652
|
+
r.admin.ok ? "ok" : "bad",
|
|
1653
|
+
r.admin.ok ? t2("tokens.strong") : t2("tokens.weak")
|
|
1654
|
+
),
|
|
1527
1655
|
...r.admin.problems.map(
|
|
1528
1656
|
(p) => (0, import_vue11.h)(
|
|
1529
1657
|
"div",
|
|
@@ -1541,12 +1669,8 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1541
1669
|
])
|
|
1542
1670
|
]
|
|
1543
1671
|
) : null,
|
|
1544
|
-
(0, import_vue11.h)("h2", { style: styles.subtitle }, "
|
|
1545
|
-
r?.issued === null ? (0, import_vue11.h)(
|
|
1546
|
-
"p",
|
|
1547
|
-
{ style: styles.muted },
|
|
1548
|
-
"Issuing is off \u2014 give AdminModule a store and run its migrations."
|
|
1549
|
-
) : [
|
|
1672
|
+
(0, import_vue11.h)("h2", { style: styles.subtitle }, t2("tokens.issuedTokens")),
|
|
1673
|
+
r?.issued === null ? (0, import_vue11.h)("p", { style: styles.muted }, t2("tokens.issuingOff")) : [
|
|
1550
1674
|
minted.value ? (0, import_vue11.h)(
|
|
1551
1675
|
"div",
|
|
1552
1676
|
{
|
|
@@ -1557,7 +1681,7 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1557
1681
|
}
|
|
1558
1682
|
},
|
|
1559
1683
|
[
|
|
1560
|
-
(0, import_vue11.h)("strong", "
|
|
1684
|
+
(0, import_vue11.h)("strong", t2("tokens.copyNow")),
|
|
1561
1685
|
` (${minted.value.name})`,
|
|
1562
1686
|
(0, import_vue11.h)(
|
|
1563
1687
|
"div",
|
|
@@ -1585,8 +1709,8 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1585
1709
|
name: name.value.trim(),
|
|
1586
1710
|
scopes: scopes.value,
|
|
1587
1711
|
...days.value && Number.isInteger(d) && d > 0 ? { expiresInDays: d } : {}
|
|
1588
|
-
}).then((
|
|
1589
|
-
minted.value = { name:
|
|
1712
|
+
}).then((tk) => {
|
|
1713
|
+
minted.value = { name: tk.name, token: tk.token };
|
|
1590
1714
|
name.value = "";
|
|
1591
1715
|
days.value = "";
|
|
1592
1716
|
}).catch(() => {
|
|
@@ -1597,9 +1721,9 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1597
1721
|
(0, import_vue11.h)("input", {
|
|
1598
1722
|
value: name.value,
|
|
1599
1723
|
onInput: (e) => name.value = e.target.value,
|
|
1600
|
-
placeholder: "
|
|
1724
|
+
placeholder: t2("tokens.namePlaceholder"),
|
|
1601
1725
|
style: styles.input,
|
|
1602
|
-
"aria-label": "
|
|
1726
|
+
"aria-label": t2("tokens.nameLabel")
|
|
1603
1727
|
}),
|
|
1604
1728
|
...ALL.map(
|
|
1605
1729
|
(s) => (0, import_vue11.h)(
|
|
@@ -1621,16 +1745,16 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1621
1745
|
checked: scopes.value.includes(s),
|
|
1622
1746
|
onChange: () => toggle(s)
|
|
1623
1747
|
}),
|
|
1624
|
-
` ${s}`
|
|
1748
|
+
` ${t2(`tokens.scope.${s}`)}`
|
|
1625
1749
|
]
|
|
1626
1750
|
)
|
|
1627
1751
|
),
|
|
1628
1752
|
(0, import_vue11.h)("input", {
|
|
1629
1753
|
value: days.value,
|
|
1630
1754
|
onInput: (e) => days.value = e.target.value,
|
|
1631
|
-
placeholder: "
|
|
1755
|
+
placeholder: t2("tokens.daysPlaceholder"),
|
|
1632
1756
|
style: { ...styles.input, width: "140px" },
|
|
1633
|
-
"aria-label": "
|
|
1757
|
+
"aria-label": t2("tokens.daysLabel")
|
|
1634
1758
|
}),
|
|
1635
1759
|
(0, import_vue11.h)(
|
|
1636
1760
|
"button",
|
|
@@ -1639,70 +1763,66 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1639
1763
|
style: styles.buttonPrimary,
|
|
1640
1764
|
disabled: !name.value.trim() || scopes.value.length === 0 || isLoading.value
|
|
1641
1765
|
},
|
|
1642
|
-
[icon("plus", 14), "
|
|
1766
|
+
[icon("plus", 14), t2("tokens.issue")]
|
|
1643
1767
|
)
|
|
1644
1768
|
]
|
|
1645
1769
|
),
|
|
1646
1770
|
r?.issued && r.issued.length > 0 ? table(
|
|
1647
|
-
[
|
|
1771
|
+
[
|
|
1772
|
+
t2("tokens.col.name"),
|
|
1773
|
+
t2("tokens.col.scopes"),
|
|
1774
|
+
t2("tokens.col.created"),
|
|
1775
|
+
t2("tokens.col.expires"),
|
|
1776
|
+
t2("tokens.col.lastUsed"),
|
|
1777
|
+
""
|
|
1778
|
+
],
|
|
1648
1779
|
r.issued.map(
|
|
1649
|
-
(
|
|
1650
|
-
td([
|
|
1651
|
-
|
|
1780
|
+
(tk) => (0, import_vue11.h)("tr", { key: tk.id }, [
|
|
1781
|
+
td([
|
|
1782
|
+
tk.name,
|
|
1783
|
+
tk.revokedAt ? [" ", pill("neutral", t2("tokens.revoked"))] : ""
|
|
1784
|
+
]),
|
|
1785
|
+
td(tk.scopes.map((sc) => t2(`tokens.scope.${sc}`)).join(", "), styles.mono),
|
|
1652
1786
|
td(
|
|
1653
|
-
`${
|
|
1787
|
+
`${(0, import_client9.formatAdminDate)(tk.createdAt, loc, "date")} \xB7 ${tk.createdBy}`,
|
|
1654
1788
|
styles.muted
|
|
1655
1789
|
),
|
|
1656
1790
|
td(
|
|
1657
|
-
|
|
1791
|
+
tk.expiresAt ? (0, import_client9.formatAdminDate)(tk.expiresAt, loc, "date") : t2("common.never"),
|
|
1658
1792
|
styles.muted
|
|
1659
1793
|
),
|
|
1660
1794
|
td(
|
|
1661
|
-
|
|
1795
|
+
tk.lastUsedAt ? (0, import_client9.formatAdminDate)(tk.lastUsedAt, loc) : t2("common.never"),
|
|
1662
1796
|
styles.muted
|
|
1663
1797
|
),
|
|
1664
1798
|
td(
|
|
1665
|
-
|
|
1799
|
+
tk.revokedAt ? "" : (0, import_vue11.h)(
|
|
1666
1800
|
"button",
|
|
1667
1801
|
{
|
|
1668
1802
|
type: "button",
|
|
1669
1803
|
style: styles.buttonDanger,
|
|
1670
1804
|
onClick: () => {
|
|
1671
|
-
if (window.confirm(
|
|
1672
|
-
|
|
1673
|
-
))
|
|
1674
|
-
void revoke(t2.id).catch(() => {
|
|
1805
|
+
if (window.confirm(t2("tokens.revokeConfirm", { name: tk.name })))
|
|
1806
|
+
void revoke(tk.id).catch(() => {
|
|
1675
1807
|
});
|
|
1676
1808
|
}
|
|
1677
1809
|
},
|
|
1678
|
-
"
|
|
1810
|
+
t2("tokens.revoke")
|
|
1679
1811
|
)
|
|
1680
1812
|
)
|
|
1681
1813
|
])
|
|
1682
1814
|
)
|
|
1683
|
-
) : empty(
|
|
1684
|
-
"No scoped tokens yet",
|
|
1685
|
-
"Issue a read-only token for a dashboard or a teammate instead of sharing the root.",
|
|
1686
|
-
"tokens"
|
|
1687
|
-
)
|
|
1815
|
+
) : empty(t2("tokens.emptyTitle"), t2("tokens.emptyBody"), "tokens")
|
|
1688
1816
|
],
|
|
1689
|
-
(0, import_vue11.h)("h2", { style: styles.subtitle }, "
|
|
1690
|
-
r && r.legacy.length === 0 ? (0, import_vue11.h)(
|
|
1691
|
-
"p",
|
|
1692
|
-
{ style: styles.muted },
|
|
1693
|
-
"None \u2014 every brick's admin surface goes through this token."
|
|
1694
|
-
) : (0, import_vue11.h)(
|
|
1817
|
+
(0, import_vue11.h)("h2", { style: styles.subtitle }, t2("tokens.legacyTitle")),
|
|
1818
|
+
r && r.legacy.length === 0 ? (0, import_vue11.h)("p", { style: styles.muted }, t2("tokens.legacyNone")) : (0, import_vue11.h)(
|
|
1695
1819
|
"ul",
|
|
1696
1820
|
{ style: styles.list },
|
|
1697
1821
|
(r?.legacy ?? []).map(
|
|
1698
1822
|
(l) => (0, import_vue11.h)("li", { key: l.module, style: styles.row }, [
|
|
1699
1823
|
(0, import_vue11.h)("span", { style: styles.mono }, l.module),
|
|
1700
1824
|
" ",
|
|
1701
|
-
(0, import_vue11.h)(
|
|
1702
|
-
"span",
|
|
1703
|
-
{ style: styles.muted },
|
|
1704
|
-
"still registers its standalone routes with its own token (deprecated)"
|
|
1705
|
-
)
|
|
1825
|
+
(0, import_vue11.h)("span", { style: styles.muted }, t2("tokens.legacyRow"))
|
|
1706
1826
|
])
|
|
1707
1827
|
)
|
|
1708
1828
|
)
|
|
@@ -1712,19 +1832,40 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1712
1832
|
});
|
|
1713
1833
|
|
|
1714
1834
|
// src/screens/UsersScreen.ts
|
|
1715
|
-
var
|
|
1835
|
+
var import_client12 = require("@fonderie/client");
|
|
1716
1836
|
var import_vue_admin11 = require("@fonderie/vue-admin");
|
|
1717
1837
|
var import_vue14 = require("vue");
|
|
1718
1838
|
|
|
1719
1839
|
// src/billing.ts
|
|
1840
|
+
var import_client10 = require("@fonderie/client");
|
|
1720
1841
|
var import_vue12 = require("vue");
|
|
1721
|
-
function periodEnd(s) {
|
|
1842
|
+
function periodEnd(s, locale) {
|
|
1722
1843
|
if (!s.currentPeriodEnd) return null;
|
|
1723
|
-
const
|
|
1844
|
+
const t2 = (0, import_client10.createAdminT)(locale);
|
|
1845
|
+
const date = (0, import_client10.formatAdminDate)(s.currentPeriodEnd, locale, "date");
|
|
1724
1846
|
if (s.status === "canceled")
|
|
1725
|
-
return {
|
|
1726
|
-
|
|
1727
|
-
|
|
1847
|
+
return {
|
|
1848
|
+
label: new Date(s.currentPeriodEnd) < /* @__PURE__ */ new Date() ? t2("billing.ended") : t2("billing.ends"),
|
|
1849
|
+
date
|
|
1850
|
+
};
|
|
1851
|
+
if (s.cancelAtPeriodEnd) return { label: t2("billing.ends"), date };
|
|
1852
|
+
return { label: t2("billing.renews"), date };
|
|
1853
|
+
}
|
|
1854
|
+
var STATUS_KEYS = [
|
|
1855
|
+
"active",
|
|
1856
|
+
"trialing",
|
|
1857
|
+
"past_due",
|
|
1858
|
+
"unpaid",
|
|
1859
|
+
"canceled",
|
|
1860
|
+
"incomplete",
|
|
1861
|
+
"incomplete_expired",
|
|
1862
|
+
"paused"
|
|
1863
|
+
];
|
|
1864
|
+
function statusLabel(t2, status) {
|
|
1865
|
+
return STATUS_KEYS.includes(status) ? t2(`billing.statusLabel.${status}`) : status;
|
|
1866
|
+
}
|
|
1867
|
+
function intervalLabel(t2, interval) {
|
|
1868
|
+
return interval === "month" || interval === "year" ? t2(`billing.intervalLabel.${interval}`) : interval;
|
|
1728
1869
|
}
|
|
1729
1870
|
var statusTone = (status) => status === "active" ? "ok" : status === "trialing" ? "info" : status === "past_due" || status === "unpaid" ? "warn" : "neutral";
|
|
1730
1871
|
function useSubscriptionIndex(client) {
|
|
@@ -1753,31 +1894,43 @@ function useSubscriptionIndex(client) {
|
|
|
1753
1894
|
}
|
|
1754
1895
|
|
|
1755
1896
|
// src/screens/SubscriberBilling.ts
|
|
1897
|
+
var import_client11 = require("@fonderie/client");
|
|
1756
1898
|
var import_vue_admin10 = require("@fonderie/vue-admin");
|
|
1757
1899
|
var import_vue13 = require("vue");
|
|
1758
|
-
var
|
|
1759
|
-
|
|
1900
|
+
var LEDGER_KINDS = ["purchase", "grant", "usage", "refund", "adjustment", "expiry"];
|
|
1901
|
+
var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
1902
|
+
name: "FonderieSubscriberBillingInner",
|
|
1760
1903
|
props: {
|
|
1761
1904
|
client: { type: Object, required: true },
|
|
1762
1905
|
subscriberType: { type: String, required: true },
|
|
1763
|
-
subscriberId: { type: String, required: true }
|
|
1906
|
+
subscriberId: { type: String, required: true },
|
|
1907
|
+
currency: { type: String, default: "" },
|
|
1908
|
+
locale: { type: String, default: void 0 }
|
|
1764
1909
|
},
|
|
1765
|
-
|
|
1910
|
+
emits: { "change-currency": (_currency) => true },
|
|
1911
|
+
setup(props, { emit }) {
|
|
1766
1912
|
const subscriber = (0, import_vue13.computed)(() => ({ type: props.subscriberType, id: props.subscriberId }));
|
|
1767
|
-
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, {
|
|
1913
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, {
|
|
1914
|
+
limit: 20,
|
|
1915
|
+
...props.currency ? { currency: props.currency } : {}
|
|
1916
|
+
});
|
|
1917
|
+
const currencyDraft = (0, import_vue13.ref)(props.currency);
|
|
1768
1918
|
const amount = (0, import_vue13.ref)("");
|
|
1769
1919
|
const note = (0, import_vue13.ref)("");
|
|
1770
1920
|
const granted = (0, import_vue13.ref)(null);
|
|
1771
1921
|
return () => {
|
|
1922
|
+
const t2 = (0, import_client11.createAdminT)(props.locale);
|
|
1923
|
+
const ledgerKind = (kind) => LEDGER_KINDS.includes(kind) ? t2(`billing.ledgerKind.${kind}`) : kind;
|
|
1772
1924
|
const s = subscription.value;
|
|
1773
1925
|
const w = wallet.value;
|
|
1774
|
-
if (isLoading.value && !w && !s)
|
|
1775
|
-
|
|
1926
|
+
if (isLoading.value && !w && !s)
|
|
1927
|
+
return (0, import_vue13.h)("p", { style: styles.status }, t2("billing.loading"));
|
|
1928
|
+
const end = s ? periodEnd(s, props.locale) : null;
|
|
1776
1929
|
return (0, import_vue13.h)("div", [
|
|
1777
1930
|
error.value ? (0, import_vue13.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
1778
1931
|
(0, import_vue13.h)("div", { style: { ...styles.grid, marginBottom: "16px" } }, [
|
|
1779
1932
|
(0, import_vue13.h)("div", { style: styles.card }, [
|
|
1780
|
-
(0, import_vue13.h)("div", { style: styles.statLabel }, "
|
|
1933
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, t2("billing.plan")),
|
|
1781
1934
|
(0, import_vue13.h)(
|
|
1782
1935
|
"div",
|
|
1783
1936
|
{
|
|
@@ -1790,11 +1943,11 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1790
1943
|
}
|
|
1791
1944
|
},
|
|
1792
1945
|
[
|
|
1793
|
-
(0, import_vue13.h)("strong", { style: { fontSize: "18px" } }, s ? s.plan : "
|
|
1946
|
+
(0, import_vue13.h)("strong", { style: { fontSize: "18px" } }, s ? s.plan : t2("billing.free")),
|
|
1794
1947
|
...s ? [
|
|
1795
|
-
(0, import_vue13.h)("span", { style: styles.muted }, `\xB7 ${s.interval}`),
|
|
1796
|
-
pill(statusTone(s.status), s.status)
|
|
1797
|
-
] : [(0, import_vue13.h)("span", { style: styles.muted }, "
|
|
1948
|
+
(0, import_vue13.h)("span", { style: styles.muted }, `\xB7 ${intervalLabel(t2, s.interval)}`),
|
|
1949
|
+
pill(statusTone(s.status), statusLabel(t2, s.status))
|
|
1950
|
+
] : [(0, import_vue13.h)("span", { style: styles.muted }, t2("billing.noSubscription"))]
|
|
1798
1951
|
]
|
|
1799
1952
|
),
|
|
1800
1953
|
end ? (0, import_vue13.h)("div", { style: styles.statHint }, `${end.label} ${end.date}`) : null,
|
|
@@ -1805,7 +1958,43 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1805
1958
|
) : null
|
|
1806
1959
|
]),
|
|
1807
1960
|
(0, import_vue13.h)("div", { style: styles.card }, [
|
|
1808
|
-
(0, import_vue13.h)(
|
|
1961
|
+
(0, import_vue13.h)(
|
|
1962
|
+
"div",
|
|
1963
|
+
{
|
|
1964
|
+
style: {
|
|
1965
|
+
display: "flex",
|
|
1966
|
+
justifyContent: "space-between",
|
|
1967
|
+
alignItems: "center",
|
|
1968
|
+
gap: "8px"
|
|
1969
|
+
}
|
|
1970
|
+
},
|
|
1971
|
+
[
|
|
1972
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, t2("billing.credits")),
|
|
1973
|
+
(0, import_vue13.h)(
|
|
1974
|
+
"form",
|
|
1975
|
+
{
|
|
1976
|
+
style: { display: "flex", gap: "4px" },
|
|
1977
|
+
onSubmit: (e) => {
|
|
1978
|
+
e.preventDefault();
|
|
1979
|
+
emit("change-currency", currencyDraft.value.trim().toUpperCase());
|
|
1980
|
+
}
|
|
1981
|
+
},
|
|
1982
|
+
[
|
|
1983
|
+
(0, import_vue13.h)("input", {
|
|
1984
|
+
value: currencyDraft.value,
|
|
1985
|
+
onInput: (e) => {
|
|
1986
|
+
currencyDraft.value = e.target.value;
|
|
1987
|
+
},
|
|
1988
|
+
placeholder: w?.currency ?? t2("billing.currencyPlaceholder"),
|
|
1989
|
+
maxlength: 8,
|
|
1990
|
+
style: { ...styles.input, height: "26px", width: "84px", fontSize: "12px" },
|
|
1991
|
+
"aria-label": t2("billing.currencyLabel"),
|
|
1992
|
+
title: t2("billing.currencyTitle")
|
|
1993
|
+
})
|
|
1994
|
+
]
|
|
1995
|
+
)
|
|
1996
|
+
]
|
|
1997
|
+
),
|
|
1809
1998
|
...w ? [
|
|
1810
1999
|
(0, import_vue13.h)("div", { style: styles.statValue }, [
|
|
1811
2000
|
`${w.balance} `,
|
|
@@ -1814,15 +2003,11 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1814
2003
|
(0, import_vue13.h)(
|
|
1815
2004
|
"div",
|
|
1816
2005
|
{ style: styles.statHint },
|
|
1817
|
-
`${w.purchased ?? "0"
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
(0, import_vue13.h)(
|
|
1821
|
-
"div",
|
|
1822
|
-
{ style: styles.statHint },
|
|
1823
|
-
"No wallet \u2014 billing has no wallet configuration."
|
|
2006
|
+
`${t2("billing.breakdown", { permanent: w.purchased ?? "0", allowance: w.granted ?? "0" })}${w.grantedExpiresAt ? t2("billing.expires", {
|
|
2007
|
+
date: (0, import_client11.formatAdminDate)(w.grantedExpiresAt, props.locale, "date")
|
|
2008
|
+
}) : ""}`
|
|
1824
2009
|
)
|
|
1825
|
-
]
|
|
2010
|
+
] : [(0, import_vue13.h)("div", { style: styles.statHint }, t2("billing.noWallet"))]
|
|
1826
2011
|
])
|
|
1827
2012
|
]),
|
|
1828
2013
|
w ? (0, import_vue13.h)(
|
|
@@ -1845,7 +2030,7 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1845
2030
|
...note.value ? { description: note.value } : {},
|
|
1846
2031
|
idempotencyKey: key
|
|
1847
2032
|
}).then(() => {
|
|
1848
|
-
granted.value =
|
|
2033
|
+
granted.value = t2("billing.granted", { amount: a, currency: w.currency });
|
|
1849
2034
|
amount.value = "";
|
|
1850
2035
|
note.value = "";
|
|
1851
2036
|
}).catch(() => {
|
|
@@ -1853,21 +2038,25 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1853
2038
|
}
|
|
1854
2039
|
},
|
|
1855
2040
|
[
|
|
1856
|
-
(0, import_vue13.h)(
|
|
2041
|
+
(0, import_vue13.h)(
|
|
2042
|
+
"strong",
|
|
2043
|
+
{ style: { fontSize: "13.5px", marginRight: "4px" } },
|
|
2044
|
+
t2("billing.grantCredits")
|
|
2045
|
+
),
|
|
1857
2046
|
(0, import_vue13.h)("input", {
|
|
1858
2047
|
value: amount.value,
|
|
1859
2048
|
onInput: (e) => amount.value = e.target.value,
|
|
1860
|
-
placeholder:
|
|
2049
|
+
placeholder: t2("billing.amountPlaceholder", { currency: w.currency }),
|
|
1861
2050
|
inputmode: "numeric",
|
|
1862
2051
|
style: { ...styles.input, width: "150px" },
|
|
1863
|
-
"aria-label": "
|
|
2052
|
+
"aria-label": t2("billing.amountLabel")
|
|
1864
2053
|
}),
|
|
1865
2054
|
(0, import_vue13.h)("input", {
|
|
1866
2055
|
value: note.value,
|
|
1867
2056
|
onInput: (e) => note.value = e.target.value,
|
|
1868
|
-
placeholder: "
|
|
2057
|
+
placeholder: t2("billing.reasonPlaceholder"),
|
|
1869
2058
|
style: { ...styles.input, flex: 1, minWidth: "180px" },
|
|
1870
|
-
"aria-label": "
|
|
2059
|
+
"aria-label": t2("billing.reasonLabel")
|
|
1871
2060
|
}),
|
|
1872
2061
|
(0, import_vue13.h)(
|
|
1873
2062
|
"button",
|
|
@@ -1876,32 +2065,60 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1876
2065
|
style: styles.buttonPrimary,
|
|
1877
2066
|
disabled: !/^\d+$/.test(amount.value.trim())
|
|
1878
2067
|
},
|
|
1879
|
-
[icon("plus", 14), "
|
|
2068
|
+
[icon("plus", 14), t2("billing.grant")]
|
|
1880
2069
|
),
|
|
1881
2070
|
granted.value ? (0, import_vue13.h)("span", { style: styles.ok }, granted.value) : null
|
|
1882
2071
|
]
|
|
1883
2072
|
) : null,
|
|
1884
|
-
w ? ledger.value.length === 0 ? empty("
|
|
2073
|
+
w ? ledger.value.length === 0 ? empty(t2("billing.noMovements"), void 0, "subscriber") : (0, import_vue13.h)("div", [
|
|
1885
2074
|
table(
|
|
1886
|
-
[
|
|
2075
|
+
[
|
|
2076
|
+
t2("billing.col.when"),
|
|
2077
|
+
t2("billing.col.kind"),
|
|
2078
|
+
t2("billing.col.amount"),
|
|
2079
|
+
t2("billing.col.note")
|
|
2080
|
+
],
|
|
1887
2081
|
ledger.value.map(
|
|
1888
|
-
(
|
|
1889
|
-
td(
|
|
2082
|
+
(tx) => (0, import_vue13.h)("tr", { key: tx.id }, [
|
|
2083
|
+
td((0, import_client11.formatAdminDate)(tx.createdAt, props.locale), {
|
|
1890
2084
|
...styles.muted,
|
|
1891
2085
|
whiteSpace: "nowrap"
|
|
1892
2086
|
}),
|
|
1893
|
-
td(
|
|
1894
|
-
td(
|
|
1895
|
-
td(
|
|
2087
|
+
td(ledgerKind(tx.type)),
|
|
2088
|
+
td(tx.amount, styles.mono),
|
|
2089
|
+
td(tx.description ?? "", styles.muted)
|
|
1896
2090
|
])
|
|
1897
2091
|
)
|
|
1898
2092
|
),
|
|
1899
|
-
hasMoreLedger.value ? loadMoreButton(() => void loadMoreLedger()) : null
|
|
2093
|
+
hasMoreLedger.value ? loadMoreButton(() => void loadMoreLedger(), t2("common.loadMore")) : null
|
|
1900
2094
|
]) : null
|
|
1901
2095
|
]);
|
|
1902
2096
|
};
|
|
1903
2097
|
}
|
|
1904
2098
|
});
|
|
2099
|
+
var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
2100
|
+
name: "FonderieSubscriberBilling",
|
|
2101
|
+
props: {
|
|
2102
|
+
client: { type: Object, required: true },
|
|
2103
|
+
subscriberType: { type: String, required: true },
|
|
2104
|
+
subscriberId: { type: String, required: true },
|
|
2105
|
+
locale: { type: String, default: void 0 }
|
|
2106
|
+
},
|
|
2107
|
+
setup(props) {
|
|
2108
|
+
const currency = (0, import_vue13.ref)("");
|
|
2109
|
+
return () => (0, import_vue13.h)(SubscriberBillingInner, {
|
|
2110
|
+
key: `${props.subscriberType}:${props.subscriberId}:${currency.value}`,
|
|
2111
|
+
client: props.client,
|
|
2112
|
+
subscriberType: props.subscriberType,
|
|
2113
|
+
subscriberId: props.subscriberId,
|
|
2114
|
+
currency: currency.value,
|
|
2115
|
+
...props.locale ? { locale: props.locale } : {},
|
|
2116
|
+
"onChange-currency": (c) => {
|
|
2117
|
+
currency.value = c;
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
1905
2122
|
|
|
1906
2123
|
// src/screens/UsersScreen.ts
|
|
1907
2124
|
var UsersScreen = (0, import_vue14.defineComponent)({
|
|
@@ -1913,7 +2130,8 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1913
2130
|
// their page. Everyone has a wallet; no subscription means the free tier.
|
|
1914
2131
|
billingClient: { type: Object, default: void 0 },
|
|
1915
2132
|
// Open this user on arrival (e.g. from the Subscriptions list).
|
|
1916
|
-
openUserId: { type: String, default: void 0 }
|
|
2133
|
+
openUserId: { type: String, default: void 0 },
|
|
2134
|
+
locale: { type: String, default: void 0 }
|
|
1917
2135
|
},
|
|
1918
2136
|
setup(props) {
|
|
1919
2137
|
const input = (0, import_vue14.ref)("");
|
|
@@ -1931,16 +2149,18 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1931
2149
|
const sessions = (0, import_vue_admin11.useAdminUserSessions)(props.client, userId);
|
|
1932
2150
|
const history = (0, import_vue_admin11.useAdminLoginHistory)(props.client, userId, { limit: 20 });
|
|
1933
2151
|
const showList = (0, import_vue14.computed)(() => !email.value && !selectedId.value);
|
|
1934
|
-
const
|
|
2152
|
+
const tr = (0, import_vue14.computed)(() => (0, import_client12.createAdminT)(props.locale));
|
|
2153
|
+
const yesNo = (v) => v ? pill("ok", tr.value("common.yes")) : pill("neutral", tr.value("common.no"));
|
|
1935
2154
|
const row = (k, v) => (0, import_vue14.h)("tr", [td(k), td(v)]);
|
|
1936
2155
|
const planCell = (id) => {
|
|
1937
2156
|
if (!plans.value) return (0, import_vue14.h)("span", { style: styles.muted }, "\u2026");
|
|
1938
2157
|
const sub = plans.value.get(`user/${id}`);
|
|
1939
|
-
if (!sub || sub.status === "canceled")
|
|
2158
|
+
if (!sub || sub.status === "canceled")
|
|
2159
|
+
return (0, import_vue14.h)("span", { style: styles.muted }, tr.value("common.status.free"));
|
|
1940
2160
|
return [
|
|
1941
2161
|
(0, import_vue14.h)("strong", sub.plan),
|
|
1942
2162
|
" ",
|
|
1943
|
-
sub.status !== "active" ? pill(statusTone(sub.status), sub.status) : null
|
|
2163
|
+
sub.status !== "active" ? pill(statusTone(sub.status), statusLabel(tr.value, sub.status)) : null
|
|
1944
2164
|
];
|
|
1945
2165
|
};
|
|
1946
2166
|
const clear = () => {
|
|
@@ -1949,13 +2169,12 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1949
2169
|
input.value = "";
|
|
1950
2170
|
};
|
|
1951
2171
|
return () => {
|
|
2172
|
+
const t2 = tr.value;
|
|
2173
|
+
const loc = props.locale;
|
|
1952
2174
|
const u = user.value;
|
|
1953
2175
|
const list = showDeleted.value ? deletedList : activeList;
|
|
1954
2176
|
return (0, import_vue14.h)("div", { style: styles.container }, [
|
|
1955
|
-
pageHeader(
|
|
1956
|
-
"Users",
|
|
1957
|
-
"Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
1958
|
-
),
|
|
2177
|
+
pageHeader(t2("users.title"), t2("users.lead")),
|
|
1959
2178
|
(0, import_vue14.h)(
|
|
1960
2179
|
"form",
|
|
1961
2180
|
{
|
|
@@ -1971,16 +2190,26 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1971
2190
|
type: "email",
|
|
1972
2191
|
value: input.value,
|
|
1973
2192
|
onInput: (e) => input.value = e.target.value,
|
|
1974
|
-
placeholder: "
|
|
2193
|
+
placeholder: t2("users.emailPlaceholder"),
|
|
1975
2194
|
style: { ...styles.input, minWidth: "280px" },
|
|
1976
|
-
"aria-label": "
|
|
2195
|
+
"aria-label": t2("users.emailLabel")
|
|
1977
2196
|
}),
|
|
1978
2197
|
(0, import_vue14.h)(
|
|
1979
2198
|
"button",
|
|
1980
2199
|
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
1981
|
-
[icon("search", 14), "
|
|
2200
|
+
[icon("search", 14), t2("common.lookUp")]
|
|
2201
|
+
),
|
|
2202
|
+
showList.value ? refreshButton2(
|
|
2203
|
+
t2("common.refresh"),
|
|
2204
|
+
list.isLoading.value,
|
|
2205
|
+
() => void list.refresh(),
|
|
2206
|
+
t2("common.working")
|
|
2207
|
+
) : actionButton(
|
|
2208
|
+
[icon("back", 14), t2("users.allUsers")],
|
|
2209
|
+
false,
|
|
2210
|
+
clear,
|
|
2211
|
+
styles.buttonGhost
|
|
1982
2212
|
),
|
|
1983
|
-
showList.value ? refreshButton2("Refresh", list.isLoading.value, () => void list.refresh()) : actionButton([icon("back", 14), "All users"], false, clear, styles.buttonGhost),
|
|
1984
2213
|
showList.value ? (0, import_vue14.h)(
|
|
1985
2214
|
"fieldset",
|
|
1986
2215
|
{
|
|
@@ -1992,7 +2221,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1992
2221
|
padding: "0",
|
|
1993
2222
|
minWidth: "0"
|
|
1994
2223
|
},
|
|
1995
|
-
"aria-label": "
|
|
2224
|
+
"aria-label": t2("users.whichAccounts")
|
|
1996
2225
|
},
|
|
1997
2226
|
[false, true].map(
|
|
1998
2227
|
(d) => (0, import_vue14.h)(
|
|
@@ -2007,7 +2236,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2007
2236
|
height: "28px"
|
|
2008
2237
|
}
|
|
2009
2238
|
},
|
|
2010
|
-
d ? "
|
|
2239
|
+
d ? t2("users.deletedAccounts") : t2("users.activeAccounts")
|
|
2011
2240
|
)
|
|
2012
2241
|
)
|
|
2013
2242
|
) : null
|
|
@@ -2015,8 +2244,19 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2015
2244
|
),
|
|
2016
2245
|
showList.value ? (0, import_vue14.h)("div", [
|
|
2017
2246
|
list.error.value ? (0, import_vue14.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
2018
|
-
list.users.value.length === 0 && !list.isLoading.value ? empty("
|
|
2019
|
-
props.billingClient ? [
|
|
2247
|
+
list.users.value.length === 0 && !list.isLoading.value ? empty(t2("users.emptyTitle"), t2("users.emptyBody"), "users") : table(
|
|
2248
|
+
props.billingClient ? [
|
|
2249
|
+
t2("users.col.email"),
|
|
2250
|
+
t2("users.col.name"),
|
|
2251
|
+
t2("users.col.plan"),
|
|
2252
|
+
t2("users.col.created"),
|
|
2253
|
+
t2("users.col.status")
|
|
2254
|
+
] : [
|
|
2255
|
+
t2("users.col.email"),
|
|
2256
|
+
t2("users.col.name"),
|
|
2257
|
+
t2("users.col.created"),
|
|
2258
|
+
t2("users.col.status")
|
|
2259
|
+
],
|
|
2020
2260
|
list.users.value.map(
|
|
2021
2261
|
(u2) => (0, import_vue14.h)("tr", { key: u2.id }, [
|
|
2022
2262
|
td(
|
|
@@ -2034,88 +2274,94 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2034
2274
|
`${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue14.h)("span", { style: styles.muted }, "\u2014")
|
|
2035
2275
|
),
|
|
2036
2276
|
props.billingClient ? td(planCell(u2.id)) : null,
|
|
2037
|
-
td(
|
|
2277
|
+
td((0, import_client12.formatAdminDate)(u2.createdAt, loc, "date")),
|
|
2038
2278
|
td(
|
|
2039
|
-
u2.suspended ? pill("warn", "suspended") : u2.deletedAt ? pill("neutral", "deleted") : pill("ok", "active")
|
|
2279
|
+
u2.suspended ? pill("warn", t2("common.status.suspended")) : u2.deletedAt ? pill("neutral", t2("common.status.deleted")) : pill("ok", t2("common.status.active"))
|
|
2040
2280
|
)
|
|
2041
2281
|
])
|
|
2042
2282
|
)
|
|
2043
2283
|
),
|
|
2044
|
-
list.isLoading.value ? (0, import_vue14.h)("p", { style: styles.status }, "
|
|
2045
|
-
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore()) : null
|
|
2284
|
+
list.isLoading.value ? (0, import_vue14.h)("p", { style: styles.status }, t2("common.loading")) : null,
|
|
2285
|
+
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore(), t2("common.loadMore")) : null
|
|
2046
2286
|
]) : null,
|
|
2047
2287
|
error.value && !(error.value.status === 404 && selectedId.value) ? (0, import_vue14.h)(
|
|
2048
2288
|
"p",
|
|
2049
2289
|
{ style: styles.error, role: "alert" },
|
|
2050
|
-
error.value.status === 404 ? "
|
|
2290
|
+
error.value.status === 404 ? t2("users.noUserWithEmail") : error.value.explanation
|
|
2051
2291
|
) : null,
|
|
2052
2292
|
// Opened by id (e.g. from Subscriptions) and the account is gone —
|
|
2053
2293
|
// deleted or purged — while its billing rows remain. Say so, and
|
|
2054
2294
|
// still show the money.
|
|
2055
2295
|
error.value?.status === 404 && selectedId.value ? [
|
|
2056
2296
|
(0, import_vue14.h)("div", { style: styles.notice, role: "status" }, [
|
|
2057
|
-
(0, import_vue14.h)("strong", "
|
|
2297
|
+
(0, import_vue14.h)("strong", `${t2("users.missingIdPrefix")} `),
|
|
2058
2298
|
(0, import_vue14.h)("code", { style: styles.code }, selectedId.value),
|
|
2059
2299
|
(0, import_vue14.h)("strong", "."),
|
|
2060
|
-
"
|
|
2300
|
+
` ${t2("users.missingIdBody")}`
|
|
2061
2301
|
]),
|
|
2062
|
-
props.billingClient ? (0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2302
|
+
props.billingClient ? (0, import_vue14.h)("h2", { style: styles.subtitle }, t2("users.planCredits")) : null,
|
|
2063
2303
|
props.billingClient ? (0, import_vue14.h)(SubscriberBilling, {
|
|
2064
2304
|
key: `missing-${selectedId.value}`,
|
|
2065
2305
|
client: props.billingClient,
|
|
2066
2306
|
subscriberType: "user",
|
|
2067
|
-
subscriberId: selectedId.value
|
|
2307
|
+
subscriberId: selectedId.value,
|
|
2308
|
+
...loc ? { locale: loc } : {}
|
|
2068
2309
|
}) : null
|
|
2069
2310
|
] : null,
|
|
2070
2311
|
u ? [
|
|
2071
2312
|
(0, import_vue14.h)("h2", { style: styles.subtitle }, [
|
|
2072
2313
|
u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
|
|
2073
2314
|
" ",
|
|
2074
|
-
u.suspended ? pill("warn", "suspended") : null,
|
|
2075
|
-
u.deletedAt ? pill("neutral", "deleted") : null
|
|
2315
|
+
u.suspended ? pill("warn", t2("common.status.suspended")) : null,
|
|
2316
|
+
u.deletedAt ? pill("neutral", t2("common.status.deleted")) : null
|
|
2076
2317
|
]),
|
|
2077
2318
|
(0, import_vue14.h)("table", { style: styles.table }, [
|
|
2078
2319
|
(0, import_vue14.h)("tbody", [
|
|
2079
|
-
row("id", (0, import_vue14.h)("span", { style: styles.mono }, u.id)),
|
|
2080
|
-
row("email", [
|
|
2081
|
-
|
|
2320
|
+
row(t2("users.field.id"), (0, import_vue14.h)("span", { style: styles.mono }, u.id)),
|
|
2321
|
+
row(t2("users.field.email"), [
|
|
2322
|
+
u.email,
|
|
2323
|
+
` \xB7 ${t2("users.field.verified")} `,
|
|
2324
|
+
yesNo(u.isEmailVerified)
|
|
2325
|
+
]),
|
|
2326
|
+
row(t2("users.field.mfa"), yesNo(u.mfaEnabled)),
|
|
2082
2327
|
row(
|
|
2083
|
-
"provider",
|
|
2084
|
-
`${u.provider || "
|
|
2328
|
+
t2("users.field.provider"),
|
|
2329
|
+
`${u.provider || t2("users.passwordProvider")}${u.hasPassword ? "" : ` \xB7 ${t2("users.noPasswordSet")}`}`
|
|
2085
2330
|
),
|
|
2086
2331
|
row(
|
|
2087
|
-
"
|
|
2088
|
-
u.lastLogin ?
|
|
2332
|
+
t2("users.field.lastLogin"),
|
|
2333
|
+
u.lastLogin ? (0, import_client12.formatAdminDate)(u.lastLogin, loc) : (0, import_vue14.h)("span", { style: styles.muted }, t2("common.never"))
|
|
2089
2334
|
),
|
|
2090
|
-
row("created",
|
|
2335
|
+
row(t2("users.field.created"), (0, import_client12.formatAdminDate)(u.createdAt, loc))
|
|
2091
2336
|
])
|
|
2092
2337
|
]),
|
|
2093
2338
|
u.deletedAt ? (0, import_vue14.h)("div", { style: { ...styles.notice, marginTop: "12px" }, role: "status" }, [
|
|
2094
|
-
(0, import_vue14.h)("strong",
|
|
2095
|
-
"
|
|
2339
|
+
(0, import_vue14.h)("strong", t2("users.deletedOn", { date: (0, import_client12.formatAdminDate)(u.deletedAt, loc) })),
|
|
2340
|
+
` ${t2("users.deletedBody")}`
|
|
2096
2341
|
]) : (0, import_vue14.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
|
|
2097
|
-
u.suspended ? actionButton("
|
|
2098
|
-
"
|
|
2342
|
+
u.suspended ? actionButton(t2("users.unsuspend"), isLoading.value, () => void unsuspend()) : actionButton(
|
|
2343
|
+
t2("users.suspend"),
|
|
2099
2344
|
isLoading.value,
|
|
2100
2345
|
() => void suspend(),
|
|
2101
2346
|
styles.buttonDanger
|
|
2102
2347
|
),
|
|
2103
2348
|
actionButton(
|
|
2104
|
-
"
|
|
2349
|
+
t2("users.signOutEverywhere"),
|
|
2105
2350
|
isLoading.value,
|
|
2106
2351
|
() => void revokeSessions().then(() => sessions.refresh())
|
|
2107
2352
|
)
|
|
2108
2353
|
]),
|
|
2109
|
-
props.billingClient ? (0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2354
|
+
props.billingClient ? (0, import_vue14.h)("h2", { style: styles.subtitle }, t2("users.planCredits")) : null,
|
|
2110
2355
|
props.billingClient ? (0, import_vue14.h)(SubscriberBilling, {
|
|
2111
2356
|
key: u.id,
|
|
2112
2357
|
client: props.billingClient,
|
|
2113
2358
|
subscriberType: "user",
|
|
2114
|
-
subscriberId: u.id
|
|
2359
|
+
subscriberId: u.id,
|
|
2360
|
+
...loc ? { locale: loc } : {}
|
|
2115
2361
|
}) : null,
|
|
2116
2362
|
u.deletedAt ? null : [
|
|
2117
|
-
(0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2118
|
-
sessions.sessions.value.length === 0 ? (0, import_vue14.h)("p", { style: styles.muted }, "
|
|
2363
|
+
(0, import_vue14.h)("h2", { style: styles.subtitle }, t2("users.liveSessions")),
|
|
2364
|
+
sessions.sessions.value.length === 0 ? (0, import_vue14.h)("p", { style: styles.muted }, t2("common.none")) : (0, import_vue14.h)(
|
|
2119
2365
|
"ul",
|
|
2120
2366
|
{ style: styles.list },
|
|
2121
2367
|
sessions.sessions.value.map(
|
|
@@ -2125,7 +2371,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2125
2371
|
(0, import_vue14.h)(
|
|
2126
2372
|
"span",
|
|
2127
2373
|
{ style: styles.muted },
|
|
2128
|
-
(0,
|
|
2374
|
+
(0, import_client12.describeLocation)(s.location) ?? ""
|
|
2129
2375
|
),
|
|
2130
2376
|
" ",
|
|
2131
2377
|
(0, import_vue14.h)("span", { style: styles.muted }, s.userAgent ?? ""),
|
|
@@ -2133,18 +2379,21 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2133
2379
|
(0, import_vue14.h)(
|
|
2134
2380
|
"span",
|
|
2135
2381
|
{ style: styles.muted },
|
|
2136
|
-
|
|
2382
|
+
t2("users.since", { date: (0, import_client12.formatAdminDate)(s.createdAt, loc) })
|
|
2137
2383
|
)
|
|
2138
2384
|
])
|
|
2139
2385
|
)
|
|
2140
2386
|
),
|
|
2141
|
-
(0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2142
|
-
history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue14.h)("p", { style: styles.muted }, "
|
|
2387
|
+
(0, import_vue14.h)("h2", { style: styles.subtitle }, t2("users.recentSignIns")),
|
|
2388
|
+
history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue14.h)("p", { style: styles.muted }, t2("users.noneRecorded")) : (0, import_vue14.h)(
|
|
2143
2389
|
"ul",
|
|
2144
2390
|
{ style: styles.list },
|
|
2145
2391
|
history.events.value.map(
|
|
2146
2392
|
(e) => (0, import_vue14.h)("li", { key: e.id, style: styles.row }, [
|
|
2147
|
-
pill(
|
|
2393
|
+
pill(
|
|
2394
|
+
e.outcome === "success" ? "ok" : "bad",
|
|
2395
|
+
e.outcome === "success" ? t2("users.outcome.success") : e.outcome === "failure" ? t2("users.outcome.failure") : e.outcome
|
|
2396
|
+
),
|
|
2148
2397
|
" ",
|
|
2149
2398
|
(0, import_vue14.h)("span", { style: styles.muted }, e.method),
|
|
2150
2399
|
" ",
|
|
@@ -2153,19 +2402,21 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2153
2402
|
(0, import_vue14.h)(
|
|
2154
2403
|
"span",
|
|
2155
2404
|
{ style: styles.muted },
|
|
2156
|
-
(0,
|
|
2405
|
+
(0, import_client12.describeLocation)(e.location) ?? ""
|
|
2157
2406
|
),
|
|
2158
|
-
e.location?.proxy || e.location?.hosting ? [
|
|
2407
|
+
e.location?.proxy || e.location?.hosting ? [
|
|
2408
|
+
" ",
|
|
2409
|
+
pill(
|
|
2410
|
+
"warn",
|
|
2411
|
+
e.location.proxy ? t2("users.proxyVpn") : t2("users.hosting")
|
|
2412
|
+
)
|
|
2413
|
+
] : "",
|
|
2159
2414
|
" ",
|
|
2160
|
-
(0, import_vue14.h)(
|
|
2161
|
-
"span",
|
|
2162
|
-
{ style: styles.muted },
|
|
2163
|
-
new Date(e.createdAt).toLocaleString()
|
|
2164
|
-
)
|
|
2415
|
+
(0, import_vue14.h)("span", { style: styles.muted }, (0, import_client12.formatAdminDate)(e.createdAt, loc))
|
|
2165
2416
|
])
|
|
2166
2417
|
)
|
|
2167
2418
|
),
|
|
2168
|
-
history.hasMore.value && !history.isLoading.value ? loadMoreButton(() => void history.loadMore()) : null
|
|
2419
|
+
history.hasMore.value && !history.isLoading.value ? loadMoreButton(() => void history.loadMore(), t2("common.loadMore")) : null
|
|
2169
2420
|
]
|
|
2170
2421
|
] : null
|
|
2171
2422
|
]);
|
|
@@ -2174,26 +2425,34 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2174
2425
|
});
|
|
2175
2426
|
|
|
2176
2427
|
// src/screens/CatalogScreen.ts
|
|
2428
|
+
var import_client13 = require("@fonderie/client");
|
|
2177
2429
|
var import_vue_admin12 = require("@fonderie/vue-admin");
|
|
2178
2430
|
var import_vue15 = require("vue");
|
|
2179
2431
|
var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
2180
2432
|
name: "FonderieCatalogScreen",
|
|
2181
|
-
props: {
|
|
2433
|
+
props: {
|
|
2434
|
+
client: { type: Object, required: true },
|
|
2435
|
+
locale: { type: String, default: void 0 }
|
|
2436
|
+
},
|
|
2182
2437
|
setup(props) {
|
|
2183
2438
|
const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin12.useAdminCatalog)(props.client);
|
|
2184
2439
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
2185
2440
|
return () => {
|
|
2441
|
+
const t2 = (0, import_client13.createAdminT)(props.locale);
|
|
2186
2442
|
const c = catalog.value;
|
|
2187
2443
|
return (0, import_vue15.h)("div", { style: styles.container }, [
|
|
2188
|
-
pageHeader(
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2444
|
+
pageHeader(t2("catalog.title"), t2("catalog.lead"), [
|
|
2445
|
+
refreshButton2(
|
|
2446
|
+
t2("common.refresh"),
|
|
2447
|
+
isLoading.value,
|
|
2448
|
+
() => void refresh(),
|
|
2449
|
+
t2("common.working")
|
|
2450
|
+
)
|
|
2451
|
+
]),
|
|
2193
2452
|
error.value ? (0, import_vue15.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
2194
|
-
isLoading.value && !c ? (0, import_vue15.h)("p", { style: styles.status }, "
|
|
2453
|
+
isLoading.value && !c ? (0, import_vue15.h)("p", { style: styles.status }, t2("common.loading")) : null,
|
|
2195
2454
|
c ? [
|
|
2196
|
-
(0, import_vue15.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } },
|
|
2455
|
+
(0, import_vue15.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, t2("catalog.configured")),
|
|
2197
2456
|
(0, import_vue15.h)(
|
|
2198
2457
|
"pre",
|
|
2199
2458
|
{
|
|
@@ -2208,13 +2467,17 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2208
2467
|
},
|
|
2209
2468
|
JSON.stringify(c.configured, null, 2)
|
|
2210
2469
|
),
|
|
2211
|
-
(0, import_vue15.h)("h2", { style: styles.subtitle },
|
|
2212
|
-
c.stored.length === 0 ? empty(
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2470
|
+
(0, import_vue15.h)("h2", { style: styles.subtitle }, t2("catalog.stored")),
|
|
2471
|
+
c.stored.length === 0 ? empty(t2("catalog.emptyTitle"), t2("catalog.emptyBody"), "catalog") : table(
|
|
2472
|
+
[
|
|
2473
|
+
t2("catalog.col.plan"),
|
|
2474
|
+
t2("catalog.col.tier"),
|
|
2475
|
+
t2("catalog.col.seats"),
|
|
2476
|
+
t2("catalog.col.monthly"),
|
|
2477
|
+
t2("catalog.col.yearly"),
|
|
2478
|
+
t2("catalog.col.trial"),
|
|
2479
|
+
""
|
|
2480
|
+
],
|
|
2218
2481
|
c.stored.map(
|
|
2219
2482
|
(p) => (0, import_vue15.h)("tr", { key: p.id }, [
|
|
2220
2483
|
td([
|
|
@@ -2225,7 +2488,7 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2225
2488
|
td(p.seats == null ? "\u221E" : String(p.seats)),
|
|
2226
2489
|
td(`${money(p.pricing?.monthly)} ${p.pricing?.currency ?? ""}`),
|
|
2227
2490
|
td(money(p.pricing?.yearly)),
|
|
2228
|
-
td(p.trialDays ?
|
|
2491
|
+
td(p.trialDays ? t2("catalog.trialDays", { n: p.trialDays }) : "\u2014"),
|
|
2229
2492
|
td(
|
|
2230
2493
|
(0, import_vue15.h)(
|
|
2231
2494
|
"button",
|
|
@@ -2233,12 +2496,12 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2233
2496
|
type: "button",
|
|
2234
2497
|
style: styles.buttonDanger,
|
|
2235
2498
|
onClick: () => {
|
|
2236
|
-
if (window.confirm(
|
|
2499
|
+
if (window.confirm(t2("catalog.deleteConfirm", { name: p.name })))
|
|
2237
2500
|
void deletePlan(p.id).catch(() => {
|
|
2238
2501
|
});
|
|
2239
2502
|
}
|
|
2240
2503
|
},
|
|
2241
|
-
"
|
|
2504
|
+
t2("common.delete")
|
|
2242
2505
|
)
|
|
2243
2506
|
)
|
|
2244
2507
|
])
|
|
@@ -2251,14 +2514,15 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2251
2514
|
});
|
|
2252
2515
|
|
|
2253
2516
|
// src/screens/SubscriberScreen.ts
|
|
2517
|
+
var import_client14 = require("@fonderie/client");
|
|
2254
2518
|
var import_vue_admin13 = require("@fonderie/vue-admin");
|
|
2255
2519
|
var import_vue16 = require("vue");
|
|
2256
2520
|
var FILTERS = [
|
|
2257
|
-
{
|
|
2258
|
-
{
|
|
2259
|
-
{
|
|
2260
|
-
{
|
|
2261
|
-
{
|
|
2521
|
+
{ id: "all", match: () => true },
|
|
2522
|
+
{ id: "active", match: (s) => s === "active" },
|
|
2523
|
+
{ id: "trialing", match: (s) => s === "trialing" },
|
|
2524
|
+
{ id: "pastDue", match: (s) => s === "past_due" || s === "unpaid" },
|
|
2525
|
+
{ id: "canceled", match: (s) => s === "canceled" }
|
|
2262
2526
|
];
|
|
2263
2527
|
var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
2264
2528
|
name: "FonderieSubscriberScreen",
|
|
@@ -2269,11 +2533,12 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2269
2533
|
* A user subscriber opens on the Users page, where their plan and credits
|
|
2270
2534
|
* live beside their account. Omit to open everything here.
|
|
2271
2535
|
*/
|
|
2272
|
-
onOpenUser: { type: Function, default: void 0 }
|
|
2536
|
+
onOpenUser: { type: Function, default: void 0 },
|
|
2537
|
+
locale: { type: String, default: void 0 }
|
|
2273
2538
|
},
|
|
2274
2539
|
setup(props) {
|
|
2275
2540
|
const list = (0, import_vue_admin13.useAdminSubscribers)(props.client, { limit: props.pageSize });
|
|
2276
|
-
const filter = (0, import_vue16.ref)("
|
|
2541
|
+
const filter = (0, import_vue16.ref)("all");
|
|
2277
2542
|
const open = (0, import_vue16.ref)(null);
|
|
2278
2543
|
const type = (0, import_vue16.ref)("workspace");
|
|
2279
2544
|
const idInput = (0, import_vue16.ref)("");
|
|
@@ -2282,32 +2547,38 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2282
2547
|
else open.value = { type: t2, id };
|
|
2283
2548
|
};
|
|
2284
2549
|
return () => {
|
|
2550
|
+
const t2 = (0, import_client14.createAdminT)(props.locale);
|
|
2551
|
+
const loc = props.locale ? { locale: props.locale } : {};
|
|
2285
2552
|
const o = open.value;
|
|
2286
2553
|
if (o) {
|
|
2287
2554
|
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2288
|
-
pageHeader(`${o.type}/${o.id}`, "
|
|
2555
|
+
pageHeader(`${o.type}/${o.id}`, t2("billing.subscriptions.detailLead"), [
|
|
2289
2556
|
(0, import_vue16.h)(
|
|
2290
2557
|
"button",
|
|
2291
2558
|
{ type: "button", style: styles.buttonGhost, onClick: () => open.value = null },
|
|
2292
|
-
[icon("back", 14), "
|
|
2559
|
+
[icon("back", 14), t2("billing.subscriptions.back")]
|
|
2293
2560
|
)
|
|
2294
2561
|
]),
|
|
2295
2562
|
(0, import_vue16.h)(SubscriberBilling, {
|
|
2296
2563
|
key: `${o.type}/${o.id}`,
|
|
2297
2564
|
client: props.client,
|
|
2298
2565
|
subscriberType: o.type,
|
|
2299
|
-
subscriberId: o.id
|
|
2566
|
+
subscriberId: o.id,
|
|
2567
|
+
...loc
|
|
2300
2568
|
})
|
|
2301
2569
|
]);
|
|
2302
2570
|
}
|
|
2303
|
-
const match = FILTERS.find((f) => f.
|
|
2571
|
+
const match = FILTERS.find((f) => f.id === filter.value)?.match ?? (() => true);
|
|
2304
2572
|
const rows = list.subscriptions.value.filter((s) => match(s.status));
|
|
2305
2573
|
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2306
|
-
pageHeader(
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2574
|
+
pageHeader(t2("billing.subscriptions.title"), t2("billing.subscriptions.lead"), [
|
|
2575
|
+
refreshButton(
|
|
2576
|
+
() => void list.refresh(),
|
|
2577
|
+
list.isLoading.value,
|
|
2578
|
+
t2("common.refresh"),
|
|
2579
|
+
t2("common.working")
|
|
2580
|
+
)
|
|
2581
|
+
]),
|
|
2311
2582
|
(0, import_vue16.h)("div", { style: { ...styles.toolbar, justifyContent: "space-between" } }, [
|
|
2312
2583
|
(0, import_vue16.h)(
|
|
2313
2584
|
"fieldset",
|
|
@@ -2321,21 +2592,24 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2321
2592
|
padding: 0,
|
|
2322
2593
|
minWidth: 0
|
|
2323
2594
|
},
|
|
2324
|
-
"aria-label": "
|
|
2595
|
+
"aria-label": t2("billing.subscriptions.filterLabel")
|
|
2325
2596
|
},
|
|
2326
2597
|
FILTERS.map((f) => {
|
|
2327
2598
|
const n = list.subscriptions.value.filter((s) => f.match(s.status)).length;
|
|
2328
|
-
const on = filter.value === f.
|
|
2599
|
+
const on = filter.value === f.id;
|
|
2329
2600
|
return (0, import_vue16.h)(
|
|
2330
2601
|
"button",
|
|
2331
2602
|
{
|
|
2332
|
-
key: f.
|
|
2603
|
+
key: f.id,
|
|
2333
2604
|
type: "button",
|
|
2334
2605
|
"aria-pressed": on,
|
|
2335
|
-
onClick: () => filter.value = f.
|
|
2606
|
+
onClick: () => filter.value = f.id,
|
|
2336
2607
|
style: { ...on ? styles.buttonPrimary : styles.button, height: "28px" }
|
|
2337
2608
|
},
|
|
2338
|
-
[
|
|
2609
|
+
[
|
|
2610
|
+
t2(`billing.subscriptions.filter.${f.id}`),
|
|
2611
|
+
(0, import_vue16.h)("span", { style: { opacity: 0.7 } }, String(n))
|
|
2612
|
+
]
|
|
2339
2613
|
);
|
|
2340
2614
|
})
|
|
2341
2615
|
),
|
|
@@ -2355,37 +2629,42 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2355
2629
|
value: type.value,
|
|
2356
2630
|
onChange: (e) => type.value = e.target.value,
|
|
2357
2631
|
style: styles.input,
|
|
2358
|
-
"aria-label": "
|
|
2632
|
+
"aria-label": t2("billing.subscriptions.typeLabel")
|
|
2359
2633
|
},
|
|
2360
2634
|
[
|
|
2361
|
-
(0, import_vue16.h)("option", { value: "workspace" }, "
|
|
2362
|
-
(0, import_vue16.h)("option", { value: "user" }, "
|
|
2635
|
+
(0, import_vue16.h)("option", { value: "workspace" }, t2("billing.subscriptions.typeWorkspace")),
|
|
2636
|
+
(0, import_vue16.h)("option", { value: "user" }, t2("billing.subscriptions.typeUser"))
|
|
2363
2637
|
]
|
|
2364
2638
|
),
|
|
2365
2639
|
(0, import_vue16.h)("input", {
|
|
2366
2640
|
value: idInput.value,
|
|
2367
2641
|
onInput: (e) => idInput.value = e.target.value,
|
|
2368
|
-
placeholder: "
|
|
2642
|
+
placeholder: t2("billing.subscriptions.idPlaceholder"),
|
|
2369
2643
|
style: { ...styles.input, width: "220px" },
|
|
2370
|
-
"aria-label": "
|
|
2644
|
+
"aria-label": t2("billing.subscriptions.idLabel")
|
|
2371
2645
|
}),
|
|
2372
2646
|
(0, import_vue16.h)(
|
|
2373
2647
|
"button",
|
|
2374
2648
|
{ type: "submit", style: styles.button, disabled: !idInput.value.trim() },
|
|
2375
|
-
[icon("search", 14), "
|
|
2649
|
+
[icon("search", 14), t2("common.open")]
|
|
2376
2650
|
)
|
|
2377
2651
|
]
|
|
2378
2652
|
)
|
|
2379
2653
|
]),
|
|
2380
2654
|
list.error.value ? (0, import_vue16.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
2381
2655
|
rows.length === 0 && !list.isLoading.value ? empty(
|
|
2382
|
-
|
|
2383
|
-
"
|
|
2656
|
+
t2(`billing.subscriptions.empty.${filter.value}`),
|
|
2657
|
+
t2("billing.subscriptions.emptyHint"),
|
|
2384
2658
|
"subscriber"
|
|
2385
2659
|
) : table(
|
|
2386
|
-
[
|
|
2660
|
+
[
|
|
2661
|
+
t2("billing.subscriptions.col.subscriber"),
|
|
2662
|
+
t2("billing.subscriptions.col.plan"),
|
|
2663
|
+
t2("billing.subscriptions.col.status"),
|
|
2664
|
+
t2("billing.subscriptions.col.renewsEnds")
|
|
2665
|
+
],
|
|
2387
2666
|
rows.map((s) => {
|
|
2388
|
-
const end = periodEnd(s);
|
|
2667
|
+
const end = periodEnd(s, props.locale);
|
|
2389
2668
|
return (0, import_vue16.h)("tr", { key: s.id }, [
|
|
2390
2669
|
td(
|
|
2391
2670
|
(0, import_vue16.h)(
|
|
@@ -2401,11 +2680,11 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2401
2680
|
td([
|
|
2402
2681
|
(0, import_vue16.h)("strong", s.plan),
|
|
2403
2682
|
" ",
|
|
2404
|
-
(0, import_vue16.h)("span", { style: styles.muted }, `\xB7 ${s.interval}`)
|
|
2683
|
+
(0, import_vue16.h)("span", { style: styles.muted }, `\xB7 ${intervalLabel(t2, s.interval)}`)
|
|
2405
2684
|
]),
|
|
2406
2685
|
td([
|
|
2407
|
-
pill(statusTone(s.status), s.status),
|
|
2408
|
-
s.cancelAtPeriodEnd && s.status !== "canceled" ? (0, import_vue16.h)("span", [" ", pill("warn", "cancels", false)]) : null
|
|
2686
|
+
pill(statusTone(s.status), statusLabel(t2, s.status)),
|
|
2687
|
+
s.cancelAtPeriodEnd && s.status !== "canceled" ? (0, import_vue16.h)("span", [" ", pill("warn", t2("billing.subscriptions.cancels"), false)]) : null
|
|
2409
2688
|
]),
|
|
2410
2689
|
td(
|
|
2411
2690
|
end ? [(0, import_vue16.h)("span", { style: styles.muted }, end.label), ` ${end.date}`] : (0, import_vue16.h)("span", { style: styles.muted }, "\u2014"),
|
|
@@ -2414,24 +2693,26 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2414
2693
|
]);
|
|
2415
2694
|
})
|
|
2416
2695
|
),
|
|
2417
|
-
list.isLoading.value ? (0, import_vue16.h)("p", { style: styles.status }, "
|
|
2418
|
-
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore()) : null
|
|
2696
|
+
list.isLoading.value ? (0, import_vue16.h)("p", { style: styles.status }, t2("common.loading")) : null,
|
|
2697
|
+
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore(), t2("common.loadMore")) : null
|
|
2419
2698
|
]);
|
|
2420
2699
|
};
|
|
2421
2700
|
}
|
|
2422
2701
|
});
|
|
2423
2702
|
|
|
2424
2703
|
// src/screens/AuditScreen.ts
|
|
2704
|
+
var import_client15 = require("@fonderie/client");
|
|
2425
2705
|
var import_vue_admin14 = require("@fonderie/vue-admin");
|
|
2426
2706
|
var import_vue17 = require("vue");
|
|
2427
2707
|
var AuditScreen = (0, import_vue17.defineComponent)({
|
|
2428
2708
|
name: "FonderieAuditScreen",
|
|
2429
2709
|
props: {
|
|
2430
2710
|
client: { type: Object, required: true },
|
|
2431
|
-
pageSize: { type: Number, default: 50 }
|
|
2711
|
+
pageSize: { type: Number, default: 50 },
|
|
2712
|
+
locale: { type: String, default: void 0 }
|
|
2432
2713
|
},
|
|
2433
2714
|
setup(props) {
|
|
2434
|
-
const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "" });
|
|
2715
|
+
const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "", from: "", to: "" });
|
|
2435
2716
|
const query = (0, import_vue17.ref)({ limit: props.pageSize });
|
|
2436
2717
|
const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin14.useAdminAudit)(
|
|
2437
2718
|
props.client,
|
|
@@ -2444,87 +2725,120 @@ var AuditScreen = (0, import_vue17.defineComponent)({
|
|
|
2444
2725
|
style: styles.input,
|
|
2445
2726
|
"aria-label": placeholder
|
|
2446
2727
|
});
|
|
2447
|
-
return () =>
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
"
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
"
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2728
|
+
return () => {
|
|
2729
|
+
const t2 = (0, import_client15.createAdminT)(props.locale);
|
|
2730
|
+
return (0, import_vue17.h)("div", { style: styles.container }, [
|
|
2731
|
+
pageHeader(t2("audit.title"), t2("audit.lead")),
|
|
2732
|
+
(0, import_vue17.h)(
|
|
2733
|
+
"form",
|
|
2734
|
+
{
|
|
2735
|
+
style: styles.toolbar,
|
|
2736
|
+
onSubmit: (e) => {
|
|
2737
|
+
e.preventDefault();
|
|
2738
|
+
const d = draft.value;
|
|
2739
|
+
query.value = {
|
|
2740
|
+
limit: props.pageSize,
|
|
2741
|
+
...d.workspaceId.trim() ? { workspaceId: d.workspaceId.trim() } : {},
|
|
2742
|
+
...d.type.trim() ? { type: d.type.trim() } : {},
|
|
2743
|
+
...d.actorId.trim() ? { actorId: d.actorId.trim() } : {},
|
|
2744
|
+
// Dates are whole days in the operator's time zone: "to" includes that day.
|
|
2745
|
+
...d.from ? { from: /* @__PURE__ */ new Date(`${d.from}T00:00:00`) } : {},
|
|
2746
|
+
...d.to ? { to: /* @__PURE__ */ new Date(`${d.to}T23:59:59.999`) } : {}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
},
|
|
2750
|
+
[
|
|
2751
|
+
field("workspaceId", t2("audit.workspacePlaceholder")),
|
|
2752
|
+
field("type", t2("audit.typePlaceholder")),
|
|
2753
|
+
field("actorId", t2("audit.actorPlaceholder")),
|
|
2754
|
+
...["from", "to"].map(
|
|
2755
|
+
(k) => (0, import_vue17.h)("input", {
|
|
2756
|
+
type: "date",
|
|
2757
|
+
value: draft.value[k],
|
|
2758
|
+
onInput: (e) => draft.value = { ...draft.value, [k]: e.target.value },
|
|
2759
|
+
style: styles.input,
|
|
2760
|
+
"aria-label": k === "from" ? t2("audit.fromLabel") : t2("audit.toLabel"),
|
|
2761
|
+
title: k === "from" ? t2("audit.fromTitle") : t2("audit.toTitle")
|
|
2762
|
+
})
|
|
2763
|
+
),
|
|
2764
|
+
(0, import_vue17.h)(
|
|
2765
|
+
"button",
|
|
2766
|
+
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
2767
|
+
[icon("search", 14), t2("common.filter")]
|
|
2768
|
+
),
|
|
2769
|
+
refreshButton2(
|
|
2770
|
+
t2("common.refresh"),
|
|
2771
|
+
isLoading.value,
|
|
2772
|
+
() => void refresh(),
|
|
2773
|
+
t2("common.working")
|
|
2774
|
+
)
|
|
2775
|
+
]
|
|
2776
|
+
),
|
|
2777
|
+
error.value ? (0, import_vue17.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
2778
|
+
events.value.length === 0 && !isLoading.value ? empty(t2("audit.empty"), void 0, "audit") : table(
|
|
2779
|
+
[
|
|
2780
|
+
t2("audit.col.when"),
|
|
2781
|
+
t2("audit.col.type"),
|
|
2782
|
+
t2("audit.col.workspace"),
|
|
2783
|
+
t2("audit.col.actor"),
|
|
2784
|
+
t2("audit.col.request")
|
|
2785
|
+
],
|
|
2786
|
+
events.value.map(
|
|
2787
|
+
(e) => (0, import_vue17.h)("tr", { key: e.id }, [
|
|
2788
|
+
td((0, import_client15.formatAdminDate)(e.createdAt, props.locale), styles.muted),
|
|
2789
|
+
td(e.type, styles.mono),
|
|
2790
|
+
td(String(e.payload["workspaceId"] ?? "\u2014"), styles.mono),
|
|
2791
|
+
td(e.actorId ?? "\u2014", styles.mono),
|
|
2792
|
+
td(e.requestId ?? "", styles.muted)
|
|
2793
|
+
])
|
|
2794
|
+
)
|
|
2795
|
+
),
|
|
2796
|
+
isLoading.value ? (0, import_vue17.h)("p", { style: styles.status }, t2("common.loading")) : null,
|
|
2797
|
+
hasMore.value && !isLoading.value ? loadMoreButton(() => void loadMore(), t2("common.loadMore")) : null
|
|
2798
|
+
]);
|
|
2799
|
+
};
|
|
2495
2800
|
}
|
|
2496
2801
|
});
|
|
2497
2802
|
|
|
2498
2803
|
// src/screens/AdminShell.ts
|
|
2499
2804
|
var NAV = [
|
|
2500
|
-
{ group: "
|
|
2805
|
+
{ group: "nav.groups.today", items: [{ page: "attention", label: "nav.items.attention" }] },
|
|
2501
2806
|
{
|
|
2502
|
-
group: "
|
|
2807
|
+
group: "nav.groups.system",
|
|
2503
2808
|
items: [
|
|
2504
|
-
{ page: "modules", label: "
|
|
2505
|
-
{ page: "environment", label: "
|
|
2506
|
-
{ page: "doctor", label: "
|
|
2507
|
-
{ page: "routes", label: "
|
|
2809
|
+
{ page: "modules", label: "nav.items.modules" },
|
|
2810
|
+
{ page: "environment", label: "nav.items.environment" },
|
|
2811
|
+
{ page: "doctor", label: "nav.items.doctor" },
|
|
2812
|
+
{ page: "routes", label: "nav.items.routes" }
|
|
2508
2813
|
]
|
|
2509
2814
|
},
|
|
2510
|
-
{ group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
|
|
2511
2815
|
{
|
|
2512
|
-
group: "
|
|
2816
|
+
group: "nav.groups.people",
|
|
2817
|
+
items: [{ page: "users", label: "nav.items.users", needs: "auth" }]
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
group: "nav.groups.money",
|
|
2513
2821
|
items: [
|
|
2514
|
-
{ page: "catalog", label: "
|
|
2515
|
-
{ page: "subscriber", label: "
|
|
2822
|
+
{ page: "catalog", label: "nav.items.catalog", needs: "billing" },
|
|
2823
|
+
{ page: "subscriber", label: "nav.items.subscriptions", needs: "billing" }
|
|
2516
2824
|
]
|
|
2517
2825
|
},
|
|
2518
|
-
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
2519
|
-
{ group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
|
|
2520
2826
|
{
|
|
2521
|
-
group: "
|
|
2827
|
+
group: "nav.groups.settings",
|
|
2828
|
+
items: [{ page: "settings", label: "nav.items.settings", needs: "config" }]
|
|
2829
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
group: "nav.groups.messaging",
|
|
2832
|
+
items: [{ page: "templates", label: "nav.items.templates", needs: "courier" }]
|
|
2833
|
+
},
|
|
2834
|
+
{
|
|
2835
|
+
group: "nav.groups.activity",
|
|
2522
2836
|
items: [
|
|
2523
|
-
{ page: "audit", label: "
|
|
2524
|
-
{ page: "log", label: "
|
|
2525
|
-
{ page: "operators", label: "
|
|
2526
|
-
{ page: "tokens", label: "
|
|
2527
|
-
{ page: "migrations", label: "
|
|
2837
|
+
{ page: "audit", label: "nav.items.audit", needs: "audit" },
|
|
2838
|
+
{ page: "log", label: "nav.items.log" },
|
|
2839
|
+
{ page: "operators", label: "nav.items.operators", needs: "operators" },
|
|
2840
|
+
{ page: "tokens", label: "nav.items.tokens" },
|
|
2841
|
+
{ page: "migrations", label: "nav.items.migrations" }
|
|
2528
2842
|
]
|
|
2529
2843
|
}
|
|
2530
2844
|
];
|
|
@@ -2573,12 +2887,19 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2573
2887
|
// shows the Operators page.
|
|
2574
2888
|
operators: { type: Boolean, default: false },
|
|
2575
2889
|
// The signed-in operator's email, when a person (not a token) is signed in.
|
|
2576
|
-
currentOperator: { type: String, default: void 0 }
|
|
2890
|
+
currentOperator: { type: String, default: void 0 },
|
|
2891
|
+
// Where the app serves GET /config/public — marks public keys on the
|
|
2892
|
+
// Config page and previews exactly what frontends receive.
|
|
2893
|
+
publicConfigUrl: { type: String, default: void 0 },
|
|
2894
|
+
// The console's own language (the operator's choice, independent of the
|
|
2895
|
+
// locales the app serves). Default English.
|
|
2896
|
+
locale: { type: String, default: void 0 }
|
|
2577
2897
|
},
|
|
2578
2898
|
// `footer` slot: pinned to the bottom of the sidebar — session controls
|
|
2579
2899
|
// (theme, sign out). The React shell takes the same thing as a `footer` prop.
|
|
2580
2900
|
emits: { navigate: (_page) => true },
|
|
2581
2901
|
setup(props, { emit, slots }) {
|
|
2902
|
+
const tr = (0, import_vue18.computed)(() => (0, import_client16.createAdminT)(props.locale));
|
|
2582
2903
|
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
2583
2904
|
const narrow = (0, import_vue18.ref)(query?.matches ?? false);
|
|
2584
2905
|
const drawer = (0, import_vue18.ref)(false);
|
|
@@ -2605,90 +2926,141 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2605
2926
|
const missing = (text) => (0, import_vue18.h)("p", { style: { ...styles.status, padding: "32px 40px" } }, text);
|
|
2606
2927
|
const body = () => {
|
|
2607
2928
|
const c = props.client;
|
|
2929
|
+
const t2 = tr.value;
|
|
2930
|
+
const loc = props.locale ? { locale: props.locale } : {};
|
|
2931
|
+
const missingClient = (client) => missing(t2("shell.missingClient", { client }));
|
|
2608
2932
|
switch (current.value) {
|
|
2609
2933
|
case "attention":
|
|
2610
|
-
return (0, import_vue18.h)(AttentionScreen, { client: c });
|
|
2934
|
+
return (0, import_vue18.h)(AttentionScreen, { client: c, ...loc });
|
|
2611
2935
|
case "modules":
|
|
2612
|
-
return (0, import_vue18.h)(ModulesScreen, { client: c });
|
|
2936
|
+
return (0, import_vue18.h)(ModulesScreen, { client: c, ...loc });
|
|
2613
2937
|
case "doctor":
|
|
2614
|
-
return (0, import_vue18.h)(DoctorScreen, { client: c });
|
|
2938
|
+
return (0, import_vue18.h)(DoctorScreen, { client: c, ...loc });
|
|
2615
2939
|
case "environment":
|
|
2616
|
-
return (0, import_vue18.h)(EnvironmentScreen, { client: c });
|
|
2940
|
+
return (0, import_vue18.h)(EnvironmentScreen, { client: c, ...loc });
|
|
2617
2941
|
case "routes":
|
|
2618
|
-
return (0, import_vue18.h)(RoutesScreen, { client: c });
|
|
2942
|
+
return (0, import_vue18.h)(RoutesScreen, { client: c, ...loc });
|
|
2619
2943
|
case "tokens":
|
|
2620
|
-
return (0, import_vue18.h)(TokensScreen, { client: c });
|
|
2944
|
+
return (0, import_vue18.h)(TokensScreen, { client: c, ...loc });
|
|
2621
2945
|
case "operators":
|
|
2622
2946
|
return (0, import_vue18.h)(OperatorsScreen, {
|
|
2623
2947
|
client: c,
|
|
2948
|
+
...loc,
|
|
2624
2949
|
...props.currentOperator ? { me: props.currentOperator } : {}
|
|
2625
2950
|
});
|
|
2626
2951
|
case "migrations":
|
|
2627
|
-
return (0, import_vue18.h)(MigrationsScreen, { client: c });
|
|
2952
|
+
return (0, import_vue18.h)(MigrationsScreen, { client: c, ...loc });
|
|
2628
2953
|
case "log":
|
|
2629
|
-
return (0, import_vue18.h)(AdminLogScreen, { client: c });
|
|
2954
|
+
return (0, import_vue18.h)(AdminLogScreen, { client: c, ...loc });
|
|
2630
2955
|
case "catalog":
|
|
2631
|
-
return props.billingClient ? (0, import_vue18.h)(CatalogScreen, { client: props.billingClient }) :
|
|
2956
|
+
return props.billingClient ? (0, import_vue18.h)(CatalogScreen, { client: props.billingClient, ...loc }) : missingClient("BillingAdminClient");
|
|
2632
2957
|
case "subscriber":
|
|
2633
2958
|
return props.billingClient ? (0, import_vue18.h)(SubscriberScreen, {
|
|
2634
2959
|
client: props.billingClient,
|
|
2960
|
+
...loc,
|
|
2635
2961
|
...props.authClient ? {
|
|
2636
2962
|
onOpenUser: (id) => {
|
|
2637
2963
|
openUser.value = id;
|
|
2638
2964
|
go("users");
|
|
2639
2965
|
}
|
|
2640
2966
|
} : {}
|
|
2641
|
-
}) :
|
|
2967
|
+
}) : missingClient("BillingAdminClient");
|
|
2642
2968
|
case "audit":
|
|
2643
|
-
return props.auditClient ? (0, import_vue18.h)(AuditScreen, { client: props.auditClient }) :
|
|
2969
|
+
return props.auditClient ? (0, import_vue18.h)(AuditScreen, { client: props.auditClient, ...loc }) : missingClient("AuditAdminClient");
|
|
2644
2970
|
case "users":
|
|
2645
2971
|
return props.authClient ? (0, import_vue18.h)(UsersScreen, {
|
|
2646
2972
|
key: openUser.value ?? "list",
|
|
2647
2973
|
client: props.authClient,
|
|
2974
|
+
...loc,
|
|
2648
2975
|
...props.billingClient ? { billingClient: props.billingClient } : {},
|
|
2649
2976
|
...openUser.value ? { openUserId: openUser.value } : {}
|
|
2650
|
-
}) :
|
|
2977
|
+
}) : missingClient("AuthAdminClient");
|
|
2651
2978
|
case "settings": {
|
|
2652
2979
|
const cc = props.configClient;
|
|
2653
|
-
if (!cc) return
|
|
2980
|
+
if (!cc) return missingClient("ConfigAdminClient");
|
|
2654
2981
|
const e = editing.value;
|
|
2655
|
-
if (e && e.kind
|
|
2982
|
+
if (e && (e.kind === "config" || e.kind === "secret")) {
|
|
2983
|
+
const env = e.environment ?? props.environment;
|
|
2656
2984
|
return [
|
|
2657
|
-
back("
|
|
2985
|
+
back(t2("nav.items.settings")),
|
|
2658
2986
|
(0, import_vue18.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
|
|
2987
|
+
key: `${e.kind}:${e.key}:${e.environment ?? ""}`,
|
|
2659
2988
|
client: cc,
|
|
2989
|
+
...loc,
|
|
2660
2990
|
kind: e.kind,
|
|
2661
2991
|
configKey: e.key,
|
|
2662
|
-
...
|
|
2663
|
-
|
|
2992
|
+
...env !== void 0 ? { environment: env } : {},
|
|
2993
|
+
...e.environments ? { environments: e.environments } : {},
|
|
2994
|
+
onSaved: () => editing.value = null,
|
|
2995
|
+
onDeleted: () => editing.value = null
|
|
2664
2996
|
})
|
|
2665
2997
|
];
|
|
2998
|
+
}
|
|
2999
|
+
const create = (kind) => (c2) => editing.value = {
|
|
3000
|
+
kind,
|
|
3001
|
+
key: "",
|
|
3002
|
+
environments: c2.environments,
|
|
3003
|
+
...c2.environment ? { environment: c2.environment } : {}
|
|
3004
|
+
};
|
|
2666
3005
|
return (0, import_vue18.h)(import_vue_config_admin_screens.ConfigListScreen, {
|
|
2667
3006
|
client: cc,
|
|
3007
|
+
...loc,
|
|
2668
3008
|
...props.environment !== void 0 ? { environment: props.environment } : {},
|
|
2669
|
-
|
|
2670
|
-
"onSelect-
|
|
3009
|
+
...props.publicConfigUrl ? { publicConfigUrl: props.publicConfigUrl } : {},
|
|
3010
|
+
"onSelect-config": (key, environment) => editing.value = { kind: "config", key, environment },
|
|
3011
|
+
"onSelect-secret": (key, environment) => editing.value = { kind: "secret", key, environment },
|
|
2671
3012
|
allowCreate: true,
|
|
2672
|
-
"onCreate-config": (
|
|
2673
|
-
"onCreate-secret": (
|
|
3013
|
+
"onCreate-config": create("config"),
|
|
3014
|
+
"onCreate-secret": create("secret")
|
|
2674
3015
|
});
|
|
2675
3016
|
}
|
|
2676
3017
|
case "templates": {
|
|
2677
3018
|
const kc = props.courierClient;
|
|
2678
|
-
if (!kc) return
|
|
3019
|
+
if (!kc) return missingClient("CourierAdminClient");
|
|
2679
3020
|
const e = editing.value;
|
|
3021
|
+
if (e?.kind === "template-new")
|
|
3022
|
+
return [
|
|
3023
|
+
back(t2("nav.items.templates")),
|
|
3024
|
+
(0, import_vue18.h)(import_vue_courier_admin_screens.TemplateCreateScreen, {
|
|
3025
|
+
client: kc,
|
|
3026
|
+
...loc,
|
|
3027
|
+
...e.type ? { type: e.type } : {},
|
|
3028
|
+
...e.locales ? { locales: e.locales } : {},
|
|
3029
|
+
onCreated: (c2) => editing.value = {
|
|
3030
|
+
kind: "template",
|
|
3031
|
+
type: c2.type,
|
|
3032
|
+
locale: c2.locale,
|
|
3033
|
+
system: false
|
|
3034
|
+
}
|
|
3035
|
+
})
|
|
3036
|
+
];
|
|
2680
3037
|
if (e?.kind === "template")
|
|
2681
3038
|
return [
|
|
2682
|
-
back("
|
|
3039
|
+
back(t2("nav.items.templates")),
|
|
2683
3040
|
(0, import_vue18.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
|
|
3041
|
+
key: `${e.type}:${e.locale ?? ""}`,
|
|
2684
3042
|
client: kc,
|
|
3043
|
+
...props.locale ? { uiLocale: props.locale } : {},
|
|
2685
3044
|
type: e.type,
|
|
2686
|
-
|
|
3045
|
+
locale: e.locale,
|
|
3046
|
+
system: e.system,
|
|
3047
|
+
allowAddLocale: true,
|
|
3048
|
+
onSaved: () => editing.value = null,
|
|
3049
|
+
onDeleted: () => editing.value = null,
|
|
3050
|
+
"onAdd-locale": (type) => editing.value = { kind: "template-new", type }
|
|
2687
3051
|
})
|
|
2688
3052
|
];
|
|
2689
3053
|
return (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateListScreen, {
|
|
2690
3054
|
client: kc,
|
|
2691
|
-
|
|
3055
|
+
...loc,
|
|
3056
|
+
allowCreate: true,
|
|
3057
|
+
"onSelect-template": (t3) => editing.value = {
|
|
3058
|
+
kind: "template",
|
|
3059
|
+
type: t3.type,
|
|
3060
|
+
locale: t3.locale ?? null,
|
|
3061
|
+
system: t3.system === true
|
|
3062
|
+
},
|
|
3063
|
+
"onCreate-template": (c2) => editing.value = { kind: "template-new", locales: c2.locales }
|
|
2692
3064
|
});
|
|
2693
3065
|
}
|
|
2694
3066
|
}
|
|
@@ -2722,7 +3094,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2722
3094
|
{
|
|
2723
3095
|
type: "button",
|
|
2724
3096
|
style: styles.buttonGhost,
|
|
2725
|
-
"aria-label": "
|
|
3097
|
+
"aria-label": tr.value("shell.closeMenu"),
|
|
2726
3098
|
onClick: () => drawer.value = false
|
|
2727
3099
|
},
|
|
2728
3100
|
[icon("close")]
|
|
@@ -2736,7 +3108,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2736
3108
|
...styles.nav,
|
|
2737
3109
|
...narrow.value ? { width: "280px", maxWidth: "85vw", boxShadow: "0 10px 40px rgba(0,0,0,.25)" } : {}
|
|
2738
3110
|
},
|
|
2739
|
-
"aria-label": "
|
|
3111
|
+
"aria-label": tr.value("shell.navLabel")
|
|
2740
3112
|
},
|
|
2741
3113
|
[
|
|
2742
3114
|
brand(),
|
|
@@ -2747,7 +3119,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2747
3119
|
const items = g.items.filter(visible);
|
|
2748
3120
|
if (items.length === 0) return null;
|
|
2749
3121
|
return (0, import_vue18.h)("div", { key: g.group }, [
|
|
2750
|
-
(0, import_vue18.h)("div", { style: styles.navGroup }, g.group),
|
|
3122
|
+
(0, import_vue18.h)("div", { style: styles.navGroup }, tr.value(g.group)),
|
|
2751
3123
|
...items.map((i) => {
|
|
2752
3124
|
const active = current.value === i.page;
|
|
2753
3125
|
return (0, import_vue18.h)(
|
|
@@ -2763,7 +3135,10 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2763
3135
|
go(i.page);
|
|
2764
3136
|
}
|
|
2765
3137
|
},
|
|
2766
|
-
[
|
|
3138
|
+
[
|
|
3139
|
+
icon(ICON[i.page], 16, active ? styles.navIconActive : void 0),
|
|
3140
|
+
tr.value(i.label)
|
|
3141
|
+
]
|
|
2767
3142
|
);
|
|
2768
3143
|
})
|
|
2769
3144
|
]);
|
|
@@ -2773,7 +3148,8 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2773
3148
|
]
|
|
2774
3149
|
);
|
|
2775
3150
|
return () => {
|
|
2776
|
-
const
|
|
3151
|
+
const titleKey = NAV.flatMap((g) => g.items).find((i) => i.page === current.value)?.label;
|
|
3152
|
+
const title = titleKey ? tr.value(titleKey) : "";
|
|
2777
3153
|
return (0, import_vue18.h)(
|
|
2778
3154
|
"div",
|
|
2779
3155
|
{ style: { ...styles.shell, ...narrow.value ? { flexDirection: "column" } : {} } },
|
|
@@ -2785,7 +3161,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2785
3161
|
{
|
|
2786
3162
|
type: "button",
|
|
2787
3163
|
style: styles.buttonGhost,
|
|
2788
|
-
"aria-label": "
|
|
3164
|
+
"aria-label": tr.value("shell.openMenu"),
|
|
2789
3165
|
"aria-expanded": drawer.value,
|
|
2790
3166
|
onClick: () => drawer.value = true
|
|
2791
3167
|
},
|