@fonderie/vue-admin-screens 1.5.0 → 1.6.1
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 +819 -555
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +155 -11
- package/dist/index.d.ts +155 -11
- package/dist/index.js +848 -553
- 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,15 +1894,18 @@ 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");
|
|
1900
|
+
var LEDGER_KINDS = ["purchase", "grant", "usage", "refund", "adjustment", "expiry"];
|
|
1758
1901
|
var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
1759
1902
|
name: "FonderieSubscriberBillingInner",
|
|
1760
1903
|
props: {
|
|
1761
1904
|
client: { type: Object, required: true },
|
|
1762
1905
|
subscriberType: { type: String, required: true },
|
|
1763
1906
|
subscriberId: { type: String, required: true },
|
|
1764
|
-
currency: { type: String, default: "" }
|
|
1907
|
+
currency: { type: String, default: "" },
|
|
1908
|
+
locale: { type: String, default: void 0 }
|
|
1765
1909
|
},
|
|
1766
1910
|
emits: { "change-currency": (_currency) => true },
|
|
1767
1911
|
setup(props, { emit }) {
|
|
@@ -1775,15 +1919,18 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1775
1919
|
const note = (0, import_vue13.ref)("");
|
|
1776
1920
|
const granted = (0, import_vue13.ref)(null);
|
|
1777
1921
|
return () => {
|
|
1922
|
+
const t2 = (0, import_client11.createAdminT)(props.locale);
|
|
1923
|
+
const ledgerKind = (kind) => LEDGER_KINDS.includes(kind) ? t2(`billing.ledgerKind.${kind}`) : kind;
|
|
1778
1924
|
const s = subscription.value;
|
|
1779
1925
|
const w = wallet.value;
|
|
1780
|
-
if (isLoading.value && !w && !s)
|
|
1781
|
-
|
|
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;
|
|
1782
1929
|
return (0, import_vue13.h)("div", [
|
|
1783
1930
|
error.value ? (0, import_vue13.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
1784
1931
|
(0, import_vue13.h)("div", { style: { ...styles.grid, marginBottom: "16px" } }, [
|
|
1785
1932
|
(0, import_vue13.h)("div", { style: styles.card }, [
|
|
1786
|
-
(0, import_vue13.h)("div", { style: styles.statLabel }, "
|
|
1933
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, t2("billing.plan")),
|
|
1787
1934
|
(0, import_vue13.h)(
|
|
1788
1935
|
"div",
|
|
1789
1936
|
{
|
|
@@ -1796,11 +1943,11 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1796
1943
|
}
|
|
1797
1944
|
},
|
|
1798
1945
|
[
|
|
1799
|
-
(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")),
|
|
1800
1947
|
...s ? [
|
|
1801
|
-
(0, import_vue13.h)("span", { style: styles.muted }, `\xB7 ${s.interval}`),
|
|
1802
|
-
pill(statusTone(s.status), s.status)
|
|
1803
|
-
] : [(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"))]
|
|
1804
1951
|
]
|
|
1805
1952
|
),
|
|
1806
1953
|
end ? (0, import_vue13.h)("div", { style: styles.statHint }, `${end.label} ${end.date}`) : null,
|
|
@@ -1822,7 +1969,7 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1822
1969
|
}
|
|
1823
1970
|
},
|
|
1824
1971
|
[
|
|
1825
|
-
(0, import_vue13.h)("div", { style: styles.statLabel }, "
|
|
1972
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, t2("billing.credits")),
|
|
1826
1973
|
(0, import_vue13.h)(
|
|
1827
1974
|
"form",
|
|
1828
1975
|
{
|
|
@@ -1838,11 +1985,11 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1838
1985
|
onInput: (e) => {
|
|
1839
1986
|
currencyDraft.value = e.target.value;
|
|
1840
1987
|
},
|
|
1841
|
-
placeholder: w?.currency ?? "
|
|
1988
|
+
placeholder: w?.currency ?? t2("billing.currencyPlaceholder"),
|
|
1842
1989
|
maxlength: 8,
|
|
1843
1990
|
style: { ...styles.input, height: "26px", width: "84px", fontSize: "12px" },
|
|
1844
|
-
"aria-label": "
|
|
1845
|
-
title: "
|
|
1991
|
+
"aria-label": t2("billing.currencyLabel"),
|
|
1992
|
+
title: t2("billing.currencyTitle")
|
|
1846
1993
|
})
|
|
1847
1994
|
]
|
|
1848
1995
|
)
|
|
@@ -1856,15 +2003,11 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1856
2003
|
(0, import_vue13.h)(
|
|
1857
2004
|
"div",
|
|
1858
2005
|
{ style: styles.statHint },
|
|
1859
|
-
`${w.purchased ?? "0"
|
|
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
|
+
}) : ""}`
|
|
1860
2009
|
)
|
|
1861
|
-
] : [
|
|
1862
|
-
(0, import_vue13.h)(
|
|
1863
|
-
"div",
|
|
1864
|
-
{ style: styles.statHint },
|
|
1865
|
-
"No wallet \u2014 billing has no wallet configuration."
|
|
1866
|
-
)
|
|
1867
|
-
]
|
|
2010
|
+
] : [(0, import_vue13.h)("div", { style: styles.statHint }, t2("billing.noWallet"))]
|
|
1868
2011
|
])
|
|
1869
2012
|
]),
|
|
1870
2013
|
w ? (0, import_vue13.h)(
|
|
@@ -1887,7 +2030,7 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1887
2030
|
...note.value ? { description: note.value } : {},
|
|
1888
2031
|
idempotencyKey: key
|
|
1889
2032
|
}).then(() => {
|
|
1890
|
-
granted.value =
|
|
2033
|
+
granted.value = t2("billing.granted", { amount: a, currency: w.currency });
|
|
1891
2034
|
amount.value = "";
|
|
1892
2035
|
note.value = "";
|
|
1893
2036
|
}).catch(() => {
|
|
@@ -1895,21 +2038,25 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1895
2038
|
}
|
|
1896
2039
|
},
|
|
1897
2040
|
[
|
|
1898
|
-
(0, import_vue13.h)(
|
|
2041
|
+
(0, import_vue13.h)(
|
|
2042
|
+
"strong",
|
|
2043
|
+
{ style: { fontSize: "13.5px", marginRight: "4px" } },
|
|
2044
|
+
t2("billing.grantCredits")
|
|
2045
|
+
),
|
|
1899
2046
|
(0, import_vue13.h)("input", {
|
|
1900
2047
|
value: amount.value,
|
|
1901
2048
|
onInput: (e) => amount.value = e.target.value,
|
|
1902
|
-
placeholder:
|
|
2049
|
+
placeholder: t2("billing.amountPlaceholder", { currency: w.currency }),
|
|
1903
2050
|
inputmode: "numeric",
|
|
1904
2051
|
style: { ...styles.input, width: "150px" },
|
|
1905
|
-
"aria-label": "
|
|
2052
|
+
"aria-label": t2("billing.amountLabel")
|
|
1906
2053
|
}),
|
|
1907
2054
|
(0, import_vue13.h)("input", {
|
|
1908
2055
|
value: note.value,
|
|
1909
2056
|
onInput: (e) => note.value = e.target.value,
|
|
1910
|
-
placeholder: "
|
|
2057
|
+
placeholder: t2("billing.reasonPlaceholder"),
|
|
1911
2058
|
style: { ...styles.input, flex: 1, minWidth: "180px" },
|
|
1912
|
-
"aria-label": "
|
|
2059
|
+
"aria-label": t2("billing.reasonLabel")
|
|
1913
2060
|
}),
|
|
1914
2061
|
(0, import_vue13.h)(
|
|
1915
2062
|
"button",
|
|
@@ -1918,27 +2065,32 @@ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
|
|
|
1918
2065
|
style: styles.buttonPrimary,
|
|
1919
2066
|
disabled: !/^\d+$/.test(amount.value.trim())
|
|
1920
2067
|
},
|
|
1921
|
-
[icon("plus", 14), "
|
|
2068
|
+
[icon("plus", 14), t2("billing.grant")]
|
|
1922
2069
|
),
|
|
1923
2070
|
granted.value ? (0, import_vue13.h)("span", { style: styles.ok }, granted.value) : null
|
|
1924
2071
|
]
|
|
1925
2072
|
) : null,
|
|
1926
|
-
w ? ledger.value.length === 0 ? empty("
|
|
2073
|
+
w ? ledger.value.length === 0 ? empty(t2("billing.noMovements"), void 0, "subscriber") : (0, import_vue13.h)("div", [
|
|
1927
2074
|
table(
|
|
1928
|
-
[
|
|
2075
|
+
[
|
|
2076
|
+
t2("billing.col.when"),
|
|
2077
|
+
t2("billing.col.kind"),
|
|
2078
|
+
t2("billing.col.amount"),
|
|
2079
|
+
t2("billing.col.note")
|
|
2080
|
+
],
|
|
1929
2081
|
ledger.value.map(
|
|
1930
|
-
(
|
|
1931
|
-
td(
|
|
2082
|
+
(tx) => (0, import_vue13.h)("tr", { key: tx.id }, [
|
|
2083
|
+
td((0, import_client11.formatAdminDate)(tx.createdAt, props.locale), {
|
|
1932
2084
|
...styles.muted,
|
|
1933
2085
|
whiteSpace: "nowrap"
|
|
1934
2086
|
}),
|
|
1935
|
-
td(
|
|
1936
|
-
td(
|
|
1937
|
-
td(
|
|
2087
|
+
td(ledgerKind(tx.type)),
|
|
2088
|
+
td(tx.amount, styles.mono),
|
|
2089
|
+
td(tx.description ?? "", styles.muted)
|
|
1938
2090
|
])
|
|
1939
2091
|
)
|
|
1940
2092
|
),
|
|
1941
|
-
hasMoreLedger.value ? loadMoreButton(() => void loadMoreLedger()) : null
|
|
2093
|
+
hasMoreLedger.value ? loadMoreButton(() => void loadMoreLedger(), t2("common.loadMore")) : null
|
|
1942
2094
|
]) : null
|
|
1943
2095
|
]);
|
|
1944
2096
|
};
|
|
@@ -1949,7 +2101,8 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1949
2101
|
props: {
|
|
1950
2102
|
client: { type: Object, required: true },
|
|
1951
2103
|
subscriberType: { type: String, required: true },
|
|
1952
|
-
subscriberId: { type: String, required: true }
|
|
2104
|
+
subscriberId: { type: String, required: true },
|
|
2105
|
+
locale: { type: String, default: void 0 }
|
|
1953
2106
|
},
|
|
1954
2107
|
setup(props) {
|
|
1955
2108
|
const currency = (0, import_vue13.ref)("");
|
|
@@ -1959,6 +2112,7 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
|
1959
2112
|
subscriberType: props.subscriberType,
|
|
1960
2113
|
subscriberId: props.subscriberId,
|
|
1961
2114
|
currency: currency.value,
|
|
2115
|
+
...props.locale ? { locale: props.locale } : {},
|
|
1962
2116
|
"onChange-currency": (c) => {
|
|
1963
2117
|
currency.value = c;
|
|
1964
2118
|
}
|
|
@@ -1976,7 +2130,8 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1976
2130
|
// their page. Everyone has a wallet; no subscription means the free tier.
|
|
1977
2131
|
billingClient: { type: Object, default: void 0 },
|
|
1978
2132
|
// Open this user on arrival (e.g. from the Subscriptions list).
|
|
1979
|
-
openUserId: { type: String, default: void 0 }
|
|
2133
|
+
openUserId: { type: String, default: void 0 },
|
|
2134
|
+
locale: { type: String, default: void 0 }
|
|
1980
2135
|
},
|
|
1981
2136
|
setup(props) {
|
|
1982
2137
|
const input = (0, import_vue14.ref)("");
|
|
@@ -1994,16 +2149,18 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
1994
2149
|
const sessions = (0, import_vue_admin11.useAdminUserSessions)(props.client, userId);
|
|
1995
2150
|
const history = (0, import_vue_admin11.useAdminLoginHistory)(props.client, userId, { limit: 20 });
|
|
1996
2151
|
const showList = (0, import_vue14.computed)(() => !email.value && !selectedId.value);
|
|
1997
|
-
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"));
|
|
1998
2154
|
const row = (k, v) => (0, import_vue14.h)("tr", [td(k), td(v)]);
|
|
1999
2155
|
const planCell = (id) => {
|
|
2000
2156
|
if (!plans.value) return (0, import_vue14.h)("span", { style: styles.muted }, "\u2026");
|
|
2001
2157
|
const sub = plans.value.get(`user/${id}`);
|
|
2002
|
-
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"));
|
|
2003
2160
|
return [
|
|
2004
2161
|
(0, import_vue14.h)("strong", sub.plan),
|
|
2005
2162
|
" ",
|
|
2006
|
-
sub.status !== "active" ? pill(statusTone(sub.status), sub.status) : null
|
|
2163
|
+
sub.status !== "active" ? pill(statusTone(sub.status), statusLabel(tr.value, sub.status)) : null
|
|
2007
2164
|
];
|
|
2008
2165
|
};
|
|
2009
2166
|
const clear = () => {
|
|
@@ -2012,13 +2169,12 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2012
2169
|
input.value = "";
|
|
2013
2170
|
};
|
|
2014
2171
|
return () => {
|
|
2172
|
+
const t2 = tr.value;
|
|
2173
|
+
const loc = props.locale;
|
|
2015
2174
|
const u = user.value;
|
|
2016
2175
|
const list = showDeleted.value ? deletedList : activeList;
|
|
2017
2176
|
return (0, import_vue14.h)("div", { style: styles.container }, [
|
|
2018
|
-
pageHeader(
|
|
2019
|
-
"Users",
|
|
2020
|
-
"Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
2021
|
-
),
|
|
2177
|
+
pageHeader(t2("users.title"), t2("users.lead")),
|
|
2022
2178
|
(0, import_vue14.h)(
|
|
2023
2179
|
"form",
|
|
2024
2180
|
{
|
|
@@ -2034,16 +2190,26 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2034
2190
|
type: "email",
|
|
2035
2191
|
value: input.value,
|
|
2036
2192
|
onInput: (e) => input.value = e.target.value,
|
|
2037
|
-
placeholder: "
|
|
2193
|
+
placeholder: t2("users.emailPlaceholder"),
|
|
2038
2194
|
style: { ...styles.input, minWidth: "280px" },
|
|
2039
|
-
"aria-label": "
|
|
2195
|
+
"aria-label": t2("users.emailLabel")
|
|
2040
2196
|
}),
|
|
2041
2197
|
(0, import_vue14.h)(
|
|
2042
2198
|
"button",
|
|
2043
2199
|
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
2044
|
-
[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
|
|
2045
2212
|
),
|
|
2046
|
-
showList.value ? refreshButton2("Refresh", list.isLoading.value, () => void list.refresh()) : actionButton([icon("back", 14), "All users"], false, clear, styles.buttonGhost),
|
|
2047
2213
|
showList.value ? (0, import_vue14.h)(
|
|
2048
2214
|
"fieldset",
|
|
2049
2215
|
{
|
|
@@ -2055,7 +2221,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2055
2221
|
padding: "0",
|
|
2056
2222
|
minWidth: "0"
|
|
2057
2223
|
},
|
|
2058
|
-
"aria-label": "
|
|
2224
|
+
"aria-label": t2("users.whichAccounts")
|
|
2059
2225
|
},
|
|
2060
2226
|
[false, true].map(
|
|
2061
2227
|
(d) => (0, import_vue14.h)(
|
|
@@ -2070,7 +2236,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2070
2236
|
height: "28px"
|
|
2071
2237
|
}
|
|
2072
2238
|
},
|
|
2073
|
-
d ? "
|
|
2239
|
+
d ? t2("users.deletedAccounts") : t2("users.activeAccounts")
|
|
2074
2240
|
)
|
|
2075
2241
|
)
|
|
2076
2242
|
) : null
|
|
@@ -2078,8 +2244,19 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2078
2244
|
),
|
|
2079
2245
|
showList.value ? (0, import_vue14.h)("div", [
|
|
2080
2246
|
list.error.value ? (0, import_vue14.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
2081
|
-
list.users.value.length === 0 && !list.isLoading.value ? empty("
|
|
2082
|
-
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
|
+
],
|
|
2083
2260
|
list.users.value.map(
|
|
2084
2261
|
(u2) => (0, import_vue14.h)("tr", { key: u2.id }, [
|
|
2085
2262
|
td(
|
|
@@ -2097,88 +2274,94 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2097
2274
|
`${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue14.h)("span", { style: styles.muted }, "\u2014")
|
|
2098
2275
|
),
|
|
2099
2276
|
props.billingClient ? td(planCell(u2.id)) : null,
|
|
2100
|
-
td(
|
|
2277
|
+
td((0, import_client12.formatAdminDate)(u2.createdAt, loc, "date")),
|
|
2101
2278
|
td(
|
|
2102
|
-
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"))
|
|
2103
2280
|
)
|
|
2104
2281
|
])
|
|
2105
2282
|
)
|
|
2106
2283
|
),
|
|
2107
|
-
list.isLoading.value ? (0, import_vue14.h)("p", { style: styles.status }, "
|
|
2108
|
-
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
|
|
2109
2286
|
]) : null,
|
|
2110
2287
|
error.value && !(error.value.status === 404 && selectedId.value) ? (0, import_vue14.h)(
|
|
2111
2288
|
"p",
|
|
2112
2289
|
{ style: styles.error, role: "alert" },
|
|
2113
|
-
error.value.status === 404 ? "
|
|
2290
|
+
error.value.status === 404 ? t2("users.noUserWithEmail") : error.value.explanation
|
|
2114
2291
|
) : null,
|
|
2115
2292
|
// Opened by id (e.g. from Subscriptions) and the account is gone —
|
|
2116
2293
|
// deleted or purged — while its billing rows remain. Say so, and
|
|
2117
2294
|
// still show the money.
|
|
2118
2295
|
error.value?.status === 404 && selectedId.value ? [
|
|
2119
2296
|
(0, import_vue14.h)("div", { style: styles.notice, role: "status" }, [
|
|
2120
|
-
(0, import_vue14.h)("strong", "
|
|
2297
|
+
(0, import_vue14.h)("strong", `${t2("users.missingIdPrefix")} `),
|
|
2121
2298
|
(0, import_vue14.h)("code", { style: styles.code }, selectedId.value),
|
|
2122
2299
|
(0, import_vue14.h)("strong", "."),
|
|
2123
|
-
"
|
|
2300
|
+
` ${t2("users.missingIdBody")}`
|
|
2124
2301
|
]),
|
|
2125
|
-
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,
|
|
2126
2303
|
props.billingClient ? (0, import_vue14.h)(SubscriberBilling, {
|
|
2127
2304
|
key: `missing-${selectedId.value}`,
|
|
2128
2305
|
client: props.billingClient,
|
|
2129
2306
|
subscriberType: "user",
|
|
2130
|
-
subscriberId: selectedId.value
|
|
2307
|
+
subscriberId: selectedId.value,
|
|
2308
|
+
...loc ? { locale: loc } : {}
|
|
2131
2309
|
}) : null
|
|
2132
2310
|
] : null,
|
|
2133
2311
|
u ? [
|
|
2134
2312
|
(0, import_vue14.h)("h2", { style: styles.subtitle }, [
|
|
2135
2313
|
u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
|
|
2136
2314
|
" ",
|
|
2137
|
-
u.suspended ? pill("warn", "suspended") : null,
|
|
2138
|
-
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
|
|
2139
2317
|
]),
|
|
2140
2318
|
(0, import_vue14.h)("table", { style: styles.table }, [
|
|
2141
2319
|
(0, import_vue14.h)("tbody", [
|
|
2142
|
-
row("id", (0, import_vue14.h)("span", { style: styles.mono }, u.id)),
|
|
2143
|
-
row("email", [
|
|
2144
|
-
|
|
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)),
|
|
2145
2327
|
row(
|
|
2146
|
-
"provider",
|
|
2147
|
-
`${u.provider || "
|
|
2328
|
+
t2("users.field.provider"),
|
|
2329
|
+
`${u.provider || t2("users.passwordProvider")}${u.hasPassword ? "" : ` \xB7 ${t2("users.noPasswordSet")}`}`
|
|
2148
2330
|
),
|
|
2149
2331
|
row(
|
|
2150
|
-
"
|
|
2151
|
-
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"))
|
|
2152
2334
|
),
|
|
2153
|
-
row("created",
|
|
2335
|
+
row(t2("users.field.created"), (0, import_client12.formatAdminDate)(u.createdAt, loc))
|
|
2154
2336
|
])
|
|
2155
2337
|
]),
|
|
2156
2338
|
u.deletedAt ? (0, import_vue14.h)("div", { style: { ...styles.notice, marginTop: "12px" }, role: "status" }, [
|
|
2157
|
-
(0, import_vue14.h)("strong",
|
|
2158
|
-
"
|
|
2339
|
+
(0, import_vue14.h)("strong", t2("users.deletedOn", { date: (0, import_client12.formatAdminDate)(u.deletedAt, loc) })),
|
|
2340
|
+
` ${t2("users.deletedBody")}`
|
|
2159
2341
|
]) : (0, import_vue14.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
|
|
2160
|
-
u.suspended ? actionButton("
|
|
2161
|
-
"
|
|
2342
|
+
u.suspended ? actionButton(t2("users.unsuspend"), isLoading.value, () => void unsuspend()) : actionButton(
|
|
2343
|
+
t2("users.suspend"),
|
|
2162
2344
|
isLoading.value,
|
|
2163
2345
|
() => void suspend(),
|
|
2164
2346
|
styles.buttonDanger
|
|
2165
2347
|
),
|
|
2166
2348
|
actionButton(
|
|
2167
|
-
"
|
|
2349
|
+
t2("users.signOutEverywhere"),
|
|
2168
2350
|
isLoading.value,
|
|
2169
2351
|
() => void revokeSessions().then(() => sessions.refresh())
|
|
2170
2352
|
)
|
|
2171
2353
|
]),
|
|
2172
|
-
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,
|
|
2173
2355
|
props.billingClient ? (0, import_vue14.h)(SubscriberBilling, {
|
|
2174
2356
|
key: u.id,
|
|
2175
2357
|
client: props.billingClient,
|
|
2176
2358
|
subscriberType: "user",
|
|
2177
|
-
subscriberId: u.id
|
|
2359
|
+
subscriberId: u.id,
|
|
2360
|
+
...loc ? { locale: loc } : {}
|
|
2178
2361
|
}) : null,
|
|
2179
2362
|
u.deletedAt ? null : [
|
|
2180
|
-
(0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2181
|
-
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)(
|
|
2182
2365
|
"ul",
|
|
2183
2366
|
{ style: styles.list },
|
|
2184
2367
|
sessions.sessions.value.map(
|
|
@@ -2188,7 +2371,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2188
2371
|
(0, import_vue14.h)(
|
|
2189
2372
|
"span",
|
|
2190
2373
|
{ style: styles.muted },
|
|
2191
|
-
(0,
|
|
2374
|
+
(0, import_client12.describeLocation)(s.location) ?? ""
|
|
2192
2375
|
),
|
|
2193
2376
|
" ",
|
|
2194
2377
|
(0, import_vue14.h)("span", { style: styles.muted }, s.userAgent ?? ""),
|
|
@@ -2196,18 +2379,21 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2196
2379
|
(0, import_vue14.h)(
|
|
2197
2380
|
"span",
|
|
2198
2381
|
{ style: styles.muted },
|
|
2199
|
-
|
|
2382
|
+
t2("users.since", { date: (0, import_client12.formatAdminDate)(s.createdAt, loc) })
|
|
2200
2383
|
)
|
|
2201
2384
|
])
|
|
2202
2385
|
)
|
|
2203
2386
|
),
|
|
2204
|
-
(0, import_vue14.h)("h2", { style: styles.subtitle }, "
|
|
2205
|
-
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)(
|
|
2206
2389
|
"ul",
|
|
2207
2390
|
{ style: styles.list },
|
|
2208
2391
|
history.events.value.map(
|
|
2209
2392
|
(e) => (0, import_vue14.h)("li", { key: e.id, style: styles.row }, [
|
|
2210
|
-
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
|
+
),
|
|
2211
2397
|
" ",
|
|
2212
2398
|
(0, import_vue14.h)("span", { style: styles.muted }, e.method),
|
|
2213
2399
|
" ",
|
|
@@ -2216,19 +2402,21 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2216
2402
|
(0, import_vue14.h)(
|
|
2217
2403
|
"span",
|
|
2218
2404
|
{ style: styles.muted },
|
|
2219
|
-
(0,
|
|
2405
|
+
(0, import_client12.describeLocation)(e.location) ?? ""
|
|
2220
2406
|
),
|
|
2221
|
-
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
|
+
] : "",
|
|
2222
2414
|
" ",
|
|
2223
|
-
(0, import_vue14.h)(
|
|
2224
|
-
"span",
|
|
2225
|
-
{ style: styles.muted },
|
|
2226
|
-
new Date(e.createdAt).toLocaleString()
|
|
2227
|
-
)
|
|
2415
|
+
(0, import_vue14.h)("span", { style: styles.muted }, (0, import_client12.formatAdminDate)(e.createdAt, loc))
|
|
2228
2416
|
])
|
|
2229
2417
|
)
|
|
2230
2418
|
),
|
|
2231
|
-
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
|
|
2232
2420
|
]
|
|
2233
2421
|
] : null
|
|
2234
2422
|
]);
|
|
@@ -2237,26 +2425,34 @@ var UsersScreen = (0, import_vue14.defineComponent)({
|
|
|
2237
2425
|
});
|
|
2238
2426
|
|
|
2239
2427
|
// src/screens/CatalogScreen.ts
|
|
2428
|
+
var import_client13 = require("@fonderie/client");
|
|
2240
2429
|
var import_vue_admin12 = require("@fonderie/vue-admin");
|
|
2241
2430
|
var import_vue15 = require("vue");
|
|
2242
2431
|
var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
2243
2432
|
name: "FonderieCatalogScreen",
|
|
2244
|
-
props: {
|
|
2433
|
+
props: {
|
|
2434
|
+
client: { type: Object, required: true },
|
|
2435
|
+
locale: { type: String, default: void 0 }
|
|
2436
|
+
},
|
|
2245
2437
|
setup(props) {
|
|
2246
2438
|
const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin12.useAdminCatalog)(props.client);
|
|
2247
2439
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
2248
2440
|
return () => {
|
|
2441
|
+
const t2 = (0, import_client13.createAdminT)(props.locale);
|
|
2249
2442
|
const c = catalog.value;
|
|
2250
2443
|
return (0, import_vue15.h)("div", { style: styles.container }, [
|
|
2251
|
-
pageHeader(
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
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
|
+
]),
|
|
2256
2452
|
error.value ? (0, import_vue15.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
2257
|
-
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,
|
|
2258
2454
|
c ? [
|
|
2259
|
-
(0, import_vue15.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } },
|
|
2455
|
+
(0, import_vue15.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, t2("catalog.configured")),
|
|
2260
2456
|
(0, import_vue15.h)(
|
|
2261
2457
|
"pre",
|
|
2262
2458
|
{
|
|
@@ -2271,13 +2467,17 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2271
2467
|
},
|
|
2272
2468
|
JSON.stringify(c.configured, null, 2)
|
|
2273
2469
|
),
|
|
2274
|
-
(0, import_vue15.h)("h2", { style: styles.subtitle },
|
|
2275
|
-
c.stored.length === 0 ? empty(
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
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
|
+
],
|
|
2281
2481
|
c.stored.map(
|
|
2282
2482
|
(p) => (0, import_vue15.h)("tr", { key: p.id }, [
|
|
2283
2483
|
td([
|
|
@@ -2288,7 +2488,7 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2288
2488
|
td(p.seats == null ? "\u221E" : String(p.seats)),
|
|
2289
2489
|
td(`${money(p.pricing?.monthly)} ${p.pricing?.currency ?? ""}`),
|
|
2290
2490
|
td(money(p.pricing?.yearly)),
|
|
2291
|
-
td(p.trialDays ?
|
|
2491
|
+
td(p.trialDays ? t2("catalog.trialDays", { n: p.trialDays }) : "\u2014"),
|
|
2292
2492
|
td(
|
|
2293
2493
|
(0, import_vue15.h)(
|
|
2294
2494
|
"button",
|
|
@@ -2296,12 +2496,12 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2296
2496
|
type: "button",
|
|
2297
2497
|
style: styles.buttonDanger,
|
|
2298
2498
|
onClick: () => {
|
|
2299
|
-
if (window.confirm(
|
|
2499
|
+
if (window.confirm(t2("catalog.deleteConfirm", { name: p.name })))
|
|
2300
2500
|
void deletePlan(p.id).catch(() => {
|
|
2301
2501
|
});
|
|
2302
2502
|
}
|
|
2303
2503
|
},
|
|
2304
|
-
"
|
|
2504
|
+
t2("common.delete")
|
|
2305
2505
|
)
|
|
2306
2506
|
)
|
|
2307
2507
|
])
|
|
@@ -2314,14 +2514,15 @@ var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
|
2314
2514
|
});
|
|
2315
2515
|
|
|
2316
2516
|
// src/screens/SubscriberScreen.ts
|
|
2517
|
+
var import_client14 = require("@fonderie/client");
|
|
2317
2518
|
var import_vue_admin13 = require("@fonderie/vue-admin");
|
|
2318
2519
|
var import_vue16 = require("vue");
|
|
2319
2520
|
var FILTERS = [
|
|
2320
|
-
{
|
|
2321
|
-
{
|
|
2322
|
-
{
|
|
2323
|
-
{
|
|
2324
|
-
{
|
|
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" }
|
|
2325
2526
|
];
|
|
2326
2527
|
var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
2327
2528
|
name: "FonderieSubscriberScreen",
|
|
@@ -2332,11 +2533,12 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2332
2533
|
* A user subscriber opens on the Users page, where their plan and credits
|
|
2333
2534
|
* live beside their account. Omit to open everything here.
|
|
2334
2535
|
*/
|
|
2335
|
-
onOpenUser: { type: Function, default: void 0 }
|
|
2536
|
+
onOpenUser: { type: Function, default: void 0 },
|
|
2537
|
+
locale: { type: String, default: void 0 }
|
|
2336
2538
|
},
|
|
2337
2539
|
setup(props) {
|
|
2338
2540
|
const list = (0, import_vue_admin13.useAdminSubscribers)(props.client, { limit: props.pageSize });
|
|
2339
|
-
const filter = (0, import_vue16.ref)("
|
|
2541
|
+
const filter = (0, import_vue16.ref)("all");
|
|
2340
2542
|
const open = (0, import_vue16.ref)(null);
|
|
2341
2543
|
const type = (0, import_vue16.ref)("workspace");
|
|
2342
2544
|
const idInput = (0, import_vue16.ref)("");
|
|
@@ -2345,32 +2547,38 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2345
2547
|
else open.value = { type: t2, id };
|
|
2346
2548
|
};
|
|
2347
2549
|
return () => {
|
|
2550
|
+
const t2 = (0, import_client14.createAdminT)(props.locale);
|
|
2551
|
+
const loc = props.locale ? { locale: props.locale } : {};
|
|
2348
2552
|
const o = open.value;
|
|
2349
2553
|
if (o) {
|
|
2350
2554
|
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2351
|
-
pageHeader(`${o.type}/${o.id}`, "
|
|
2555
|
+
pageHeader(`${o.type}/${o.id}`, t2("billing.subscriptions.detailLead"), [
|
|
2352
2556
|
(0, import_vue16.h)(
|
|
2353
2557
|
"button",
|
|
2354
2558
|
{ type: "button", style: styles.buttonGhost, onClick: () => open.value = null },
|
|
2355
|
-
[icon("back", 14), "
|
|
2559
|
+
[icon("back", 14), t2("billing.subscriptions.back")]
|
|
2356
2560
|
)
|
|
2357
2561
|
]),
|
|
2358
2562
|
(0, import_vue16.h)(SubscriberBilling, {
|
|
2359
2563
|
key: `${o.type}/${o.id}`,
|
|
2360
2564
|
client: props.client,
|
|
2361
2565
|
subscriberType: o.type,
|
|
2362
|
-
subscriberId: o.id
|
|
2566
|
+
subscriberId: o.id,
|
|
2567
|
+
...loc
|
|
2363
2568
|
})
|
|
2364
2569
|
]);
|
|
2365
2570
|
}
|
|
2366
|
-
const match = FILTERS.find((f) => f.
|
|
2571
|
+
const match = FILTERS.find((f) => f.id === filter.value)?.match ?? (() => true);
|
|
2367
2572
|
const rows = list.subscriptions.value.filter((s) => match(s.status));
|
|
2368
2573
|
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2369
|
-
pageHeader(
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
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
|
+
]),
|
|
2374
2582
|
(0, import_vue16.h)("div", { style: { ...styles.toolbar, justifyContent: "space-between" } }, [
|
|
2375
2583
|
(0, import_vue16.h)(
|
|
2376
2584
|
"fieldset",
|
|
@@ -2384,21 +2592,24 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2384
2592
|
padding: 0,
|
|
2385
2593
|
minWidth: 0
|
|
2386
2594
|
},
|
|
2387
|
-
"aria-label": "
|
|
2595
|
+
"aria-label": t2("billing.subscriptions.filterLabel")
|
|
2388
2596
|
},
|
|
2389
2597
|
FILTERS.map((f) => {
|
|
2390
2598
|
const n = list.subscriptions.value.filter((s) => f.match(s.status)).length;
|
|
2391
|
-
const on = filter.value === f.
|
|
2599
|
+
const on = filter.value === f.id;
|
|
2392
2600
|
return (0, import_vue16.h)(
|
|
2393
2601
|
"button",
|
|
2394
2602
|
{
|
|
2395
|
-
key: f.
|
|
2603
|
+
key: f.id,
|
|
2396
2604
|
type: "button",
|
|
2397
2605
|
"aria-pressed": on,
|
|
2398
|
-
onClick: () => filter.value = f.
|
|
2606
|
+
onClick: () => filter.value = f.id,
|
|
2399
2607
|
style: { ...on ? styles.buttonPrimary : styles.button, height: "28px" }
|
|
2400
2608
|
},
|
|
2401
|
-
[
|
|
2609
|
+
[
|
|
2610
|
+
t2(`billing.subscriptions.filter.${f.id}`),
|
|
2611
|
+
(0, import_vue16.h)("span", { style: { opacity: 0.7 } }, String(n))
|
|
2612
|
+
]
|
|
2402
2613
|
);
|
|
2403
2614
|
})
|
|
2404
2615
|
),
|
|
@@ -2418,37 +2629,42 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2418
2629
|
value: type.value,
|
|
2419
2630
|
onChange: (e) => type.value = e.target.value,
|
|
2420
2631
|
style: styles.input,
|
|
2421
|
-
"aria-label": "
|
|
2632
|
+
"aria-label": t2("billing.subscriptions.typeLabel")
|
|
2422
2633
|
},
|
|
2423
2634
|
[
|
|
2424
|
-
(0, import_vue16.h)("option", { value: "workspace" }, "
|
|
2425
|
-
(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"))
|
|
2426
2637
|
]
|
|
2427
2638
|
),
|
|
2428
2639
|
(0, import_vue16.h)("input", {
|
|
2429
2640
|
value: idInput.value,
|
|
2430
2641
|
onInput: (e) => idInput.value = e.target.value,
|
|
2431
|
-
placeholder: "
|
|
2642
|
+
placeholder: t2("billing.subscriptions.idPlaceholder"),
|
|
2432
2643
|
style: { ...styles.input, width: "220px" },
|
|
2433
|
-
"aria-label": "
|
|
2644
|
+
"aria-label": t2("billing.subscriptions.idLabel")
|
|
2434
2645
|
}),
|
|
2435
2646
|
(0, import_vue16.h)(
|
|
2436
2647
|
"button",
|
|
2437
2648
|
{ type: "submit", style: styles.button, disabled: !idInput.value.trim() },
|
|
2438
|
-
[icon("search", 14), "
|
|
2649
|
+
[icon("search", 14), t2("common.open")]
|
|
2439
2650
|
)
|
|
2440
2651
|
]
|
|
2441
2652
|
)
|
|
2442
2653
|
]),
|
|
2443
2654
|
list.error.value ? (0, import_vue16.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
2444
2655
|
rows.length === 0 && !list.isLoading.value ? empty(
|
|
2445
|
-
|
|
2446
|
-
"
|
|
2656
|
+
t2(`billing.subscriptions.empty.${filter.value}`),
|
|
2657
|
+
t2("billing.subscriptions.emptyHint"),
|
|
2447
2658
|
"subscriber"
|
|
2448
2659
|
) : table(
|
|
2449
|
-
[
|
|
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
|
+
],
|
|
2450
2666
|
rows.map((s) => {
|
|
2451
|
-
const end = periodEnd(s);
|
|
2667
|
+
const end = periodEnd(s, props.locale);
|
|
2452
2668
|
return (0, import_vue16.h)("tr", { key: s.id }, [
|
|
2453
2669
|
td(
|
|
2454
2670
|
(0, import_vue16.h)(
|
|
@@ -2464,11 +2680,11 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2464
2680
|
td([
|
|
2465
2681
|
(0, import_vue16.h)("strong", s.plan),
|
|
2466
2682
|
" ",
|
|
2467
|
-
(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)}`)
|
|
2468
2684
|
]),
|
|
2469
2685
|
td([
|
|
2470
|
-
pill(statusTone(s.status), s.status),
|
|
2471
|
-
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
|
|
2472
2688
|
]),
|
|
2473
2689
|
td(
|
|
2474
2690
|
end ? [(0, import_vue16.h)("span", { style: styles.muted }, end.label), ` ${end.date}`] : (0, import_vue16.h)("span", { style: styles.muted }, "\u2014"),
|
|
@@ -2477,21 +2693,23 @@ var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
|
2477
2693
|
]);
|
|
2478
2694
|
})
|
|
2479
2695
|
),
|
|
2480
|
-
list.isLoading.value ? (0, import_vue16.h)("p", { style: styles.status }, "
|
|
2481
|
-
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
|
|
2482
2698
|
]);
|
|
2483
2699
|
};
|
|
2484
2700
|
}
|
|
2485
2701
|
});
|
|
2486
2702
|
|
|
2487
2703
|
// src/screens/AuditScreen.ts
|
|
2704
|
+
var import_client15 = require("@fonderie/client");
|
|
2488
2705
|
var import_vue_admin14 = require("@fonderie/vue-admin");
|
|
2489
2706
|
var import_vue17 = require("vue");
|
|
2490
2707
|
var AuditScreen = (0, import_vue17.defineComponent)({
|
|
2491
2708
|
name: "FonderieAuditScreen",
|
|
2492
2709
|
props: {
|
|
2493
2710
|
client: { type: Object, required: true },
|
|
2494
|
-
pageSize: { type: Number, default: 50 }
|
|
2711
|
+
pageSize: { type: Number, default: 50 },
|
|
2712
|
+
locale: { type: String, default: void 0 }
|
|
2495
2713
|
},
|
|
2496
2714
|
setup(props) {
|
|
2497
2715
|
const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "", from: "", to: "" });
|
|
@@ -2507,100 +2725,120 @@ var AuditScreen = (0, import_vue17.defineComponent)({
|
|
|
2507
2725
|
style: styles.input,
|
|
2508
2726
|
"aria-label": placeholder
|
|
2509
2727
|
});
|
|
2510
|
-
return () =>
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
"
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
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
|
+
};
|
|
2571
2800
|
}
|
|
2572
2801
|
});
|
|
2573
2802
|
|
|
2574
2803
|
// src/screens/AdminShell.ts
|
|
2575
2804
|
var NAV = [
|
|
2576
|
-
{ group: "
|
|
2805
|
+
{ group: "nav.groups.today", items: [{ page: "attention", label: "nav.items.attention" }] },
|
|
2577
2806
|
{
|
|
2578
|
-
group: "
|
|
2807
|
+
group: "nav.groups.system",
|
|
2579
2808
|
items: [
|
|
2580
|
-
{ page: "modules", label: "
|
|
2581
|
-
{ page: "environment", label: "
|
|
2582
|
-
{ page: "doctor", label: "
|
|
2583
|
-
{ 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" }
|
|
2584
2813
|
]
|
|
2585
2814
|
},
|
|
2586
|
-
{ group: "People", items: [{ page: "users", label: "Users", needs: "auth" }] },
|
|
2587
2815
|
{
|
|
2588
|
-
group: "
|
|
2816
|
+
group: "nav.groups.people",
|
|
2817
|
+
items: [{ page: "users", label: "nav.items.users", needs: "auth" }]
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
group: "nav.groups.money",
|
|
2589
2821
|
items: [
|
|
2590
|
-
{ page: "catalog", label: "
|
|
2591
|
-
{ page: "subscriber", label: "
|
|
2822
|
+
{ page: "catalog", label: "nav.items.catalog", needs: "billing" },
|
|
2823
|
+
{ page: "subscriber", label: "nav.items.subscriptions", needs: "billing" }
|
|
2592
2824
|
]
|
|
2593
2825
|
},
|
|
2594
|
-
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
2595
|
-
{ group: "Messaging", items: [{ page: "templates", label: "Templates", needs: "courier" }] },
|
|
2596
2826
|
{
|
|
2597
|
-
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",
|
|
2598
2836
|
items: [
|
|
2599
|
-
{ page: "audit", label: "
|
|
2600
|
-
{ page: "log", label: "
|
|
2601
|
-
{ page: "operators", label: "
|
|
2602
|
-
{ page: "tokens", label: "
|
|
2603
|
-
{ 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" }
|
|
2604
2842
|
]
|
|
2605
2843
|
}
|
|
2606
2844
|
];
|
|
@@ -2652,12 +2890,16 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2652
2890
|
currentOperator: { type: String, default: void 0 },
|
|
2653
2891
|
// Where the app serves GET /config/public — marks public keys on the
|
|
2654
2892
|
// Config page and previews exactly what frontends receive.
|
|
2655
|
-
publicConfigUrl: { type: String, default: void 0 }
|
|
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 }
|
|
2656
2897
|
},
|
|
2657
2898
|
// `footer` slot: pinned to the bottom of the sidebar — session controls
|
|
2658
2899
|
// (theme, sign out). The React shell takes the same thing as a `footer` prop.
|
|
2659
2900
|
emits: { navigate: (_page) => true },
|
|
2660
2901
|
setup(props, { emit, slots }) {
|
|
2902
|
+
const tr = (0, import_vue18.computed)(() => (0, import_client16.createAdminT)(props.locale));
|
|
2661
2903
|
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
2662
2904
|
const narrow = (0, import_vue18.ref)(query?.matches ?? false);
|
|
2663
2905
|
const drawer = (0, import_vue18.ref)(false);
|
|
@@ -2684,60 +2926,67 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2684
2926
|
const missing = (text) => (0, import_vue18.h)("p", { style: { ...styles.status, padding: "32px 40px" } }, text);
|
|
2685
2927
|
const body = () => {
|
|
2686
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 }));
|
|
2687
2932
|
switch (current.value) {
|
|
2688
2933
|
case "attention":
|
|
2689
|
-
return (0, import_vue18.h)(AttentionScreen, { client: c });
|
|
2934
|
+
return (0, import_vue18.h)(AttentionScreen, { client: c, ...loc });
|
|
2690
2935
|
case "modules":
|
|
2691
|
-
return (0, import_vue18.h)(ModulesScreen, { client: c });
|
|
2936
|
+
return (0, import_vue18.h)(ModulesScreen, { client: c, ...loc });
|
|
2692
2937
|
case "doctor":
|
|
2693
|
-
return (0, import_vue18.h)(DoctorScreen, { client: c });
|
|
2938
|
+
return (0, import_vue18.h)(DoctorScreen, { client: c, ...loc });
|
|
2694
2939
|
case "environment":
|
|
2695
|
-
return (0, import_vue18.h)(EnvironmentScreen, { client: c });
|
|
2940
|
+
return (0, import_vue18.h)(EnvironmentScreen, { client: c, ...loc });
|
|
2696
2941
|
case "routes":
|
|
2697
|
-
return (0, import_vue18.h)(RoutesScreen, { client: c });
|
|
2942
|
+
return (0, import_vue18.h)(RoutesScreen, { client: c, ...loc });
|
|
2698
2943
|
case "tokens":
|
|
2699
|
-
return (0, import_vue18.h)(TokensScreen, { client: c });
|
|
2944
|
+
return (0, import_vue18.h)(TokensScreen, { client: c, ...loc });
|
|
2700
2945
|
case "operators":
|
|
2701
2946
|
return (0, import_vue18.h)(OperatorsScreen, {
|
|
2702
2947
|
client: c,
|
|
2948
|
+
...loc,
|
|
2703
2949
|
...props.currentOperator ? { me: props.currentOperator } : {}
|
|
2704
2950
|
});
|
|
2705
2951
|
case "migrations":
|
|
2706
|
-
return (0, import_vue18.h)(MigrationsScreen, { client: c });
|
|
2952
|
+
return (0, import_vue18.h)(MigrationsScreen, { client: c, ...loc });
|
|
2707
2953
|
case "log":
|
|
2708
|
-
return (0, import_vue18.h)(AdminLogScreen, { client: c });
|
|
2954
|
+
return (0, import_vue18.h)(AdminLogScreen, { client: c, ...loc });
|
|
2709
2955
|
case "catalog":
|
|
2710
|
-
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");
|
|
2711
2957
|
case "subscriber":
|
|
2712
2958
|
return props.billingClient ? (0, import_vue18.h)(SubscriberScreen, {
|
|
2713
2959
|
client: props.billingClient,
|
|
2960
|
+
...loc,
|
|
2714
2961
|
...props.authClient ? {
|
|
2715
2962
|
onOpenUser: (id) => {
|
|
2716
2963
|
openUser.value = id;
|
|
2717
2964
|
go("users");
|
|
2718
2965
|
}
|
|
2719
2966
|
} : {}
|
|
2720
|
-
}) :
|
|
2967
|
+
}) : missingClient("BillingAdminClient");
|
|
2721
2968
|
case "audit":
|
|
2722
|
-
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");
|
|
2723
2970
|
case "users":
|
|
2724
2971
|
return props.authClient ? (0, import_vue18.h)(UsersScreen, {
|
|
2725
2972
|
key: openUser.value ?? "list",
|
|
2726
2973
|
client: props.authClient,
|
|
2974
|
+
...loc,
|
|
2727
2975
|
...props.billingClient ? { billingClient: props.billingClient } : {},
|
|
2728
2976
|
...openUser.value ? { openUserId: openUser.value } : {}
|
|
2729
|
-
}) :
|
|
2977
|
+
}) : missingClient("AuthAdminClient");
|
|
2730
2978
|
case "settings": {
|
|
2731
2979
|
const cc = props.configClient;
|
|
2732
|
-
if (!cc) return
|
|
2980
|
+
if (!cc) return missingClient("ConfigAdminClient");
|
|
2733
2981
|
const e = editing.value;
|
|
2734
2982
|
if (e && (e.kind === "config" || e.kind === "secret")) {
|
|
2735
2983
|
const env = e.environment ?? props.environment;
|
|
2736
2984
|
return [
|
|
2737
|
-
back("
|
|
2985
|
+
back(t2("nav.items.settings")),
|
|
2738
2986
|
(0, import_vue18.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
|
|
2739
2987
|
key: `${e.kind}:${e.key}:${e.environment ?? ""}`,
|
|
2740
2988
|
client: cc,
|
|
2989
|
+
...loc,
|
|
2741
2990
|
kind: e.kind,
|
|
2742
2991
|
configKey: e.key,
|
|
2743
2992
|
...env !== void 0 ? { environment: env } : {},
|
|
@@ -2755,6 +3004,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2755
3004
|
};
|
|
2756
3005
|
return (0, import_vue18.h)(import_vue_config_admin_screens.ConfigListScreen, {
|
|
2757
3006
|
client: cc,
|
|
3007
|
+
...loc,
|
|
2758
3008
|
...props.environment !== void 0 ? { environment: props.environment } : {},
|
|
2759
3009
|
...props.publicConfigUrl ? { publicConfigUrl: props.publicConfigUrl } : {},
|
|
2760
3010
|
"onSelect-config": (key, environment) => editing.value = { kind: "config", key, environment },
|
|
@@ -2766,13 +3016,14 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2766
3016
|
}
|
|
2767
3017
|
case "templates": {
|
|
2768
3018
|
const kc = props.courierClient;
|
|
2769
|
-
if (!kc) return
|
|
3019
|
+
if (!kc) return missingClient("CourierAdminClient");
|
|
2770
3020
|
const e = editing.value;
|
|
2771
3021
|
if (e?.kind === "template-new")
|
|
2772
3022
|
return [
|
|
2773
|
-
back("
|
|
3023
|
+
back(t2("nav.items.templates")),
|
|
2774
3024
|
(0, import_vue18.h)(import_vue_courier_admin_screens.TemplateCreateScreen, {
|
|
2775
3025
|
client: kc,
|
|
3026
|
+
...loc,
|
|
2776
3027
|
...e.type ? { type: e.type } : {},
|
|
2777
3028
|
...e.locales ? { locales: e.locales } : {},
|
|
2778
3029
|
onCreated: (c2) => editing.value = {
|
|
@@ -2785,27 +3036,36 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2785
3036
|
];
|
|
2786
3037
|
if (e?.kind === "template")
|
|
2787
3038
|
return [
|
|
2788
|
-
back("
|
|
3039
|
+
back(t2("nav.items.templates")),
|
|
2789
3040
|
(0, import_vue18.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
|
|
2790
3041
|
key: `${e.type}:${e.locale ?? ""}`,
|
|
2791
3042
|
client: kc,
|
|
3043
|
+
...props.locale ? { uiLocale: props.locale } : {},
|
|
2792
3044
|
type: e.type,
|
|
2793
3045
|
locale: e.locale,
|
|
2794
3046
|
system: e.system,
|
|
2795
3047
|
allowAddLocale: true,
|
|
3048
|
+
localeTabs: true,
|
|
2796
3049
|
onSaved: () => editing.value = null,
|
|
2797
3050
|
onDeleted: () => editing.value = null,
|
|
2798
|
-
"onAdd-locale": (type) => editing.value = { kind: "template-new", type }
|
|
3051
|
+
"onAdd-locale": (type, c2) => editing.value = { kind: "template-new", type, locales: c2.locales },
|
|
3052
|
+
"onSelect-locale": (tpl) => editing.value = {
|
|
3053
|
+
kind: "template",
|
|
3054
|
+
type: tpl.type,
|
|
3055
|
+
locale: tpl.locale,
|
|
3056
|
+
system: tpl.system === true
|
|
3057
|
+
}
|
|
2799
3058
|
})
|
|
2800
3059
|
];
|
|
2801
3060
|
return (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateListScreen, {
|
|
2802
3061
|
client: kc,
|
|
3062
|
+
...loc,
|
|
2803
3063
|
allowCreate: true,
|
|
2804
|
-
"onSelect-template": (
|
|
3064
|
+
"onSelect-template": (t3) => editing.value = {
|
|
2805
3065
|
kind: "template",
|
|
2806
|
-
type:
|
|
2807
|
-
locale:
|
|
2808
|
-
system:
|
|
3066
|
+
type: t3.type,
|
|
3067
|
+
locale: t3.locale ?? null,
|
|
3068
|
+
system: t3.system === true
|
|
2809
3069
|
},
|
|
2810
3070
|
"onCreate-template": (c2) => editing.value = { kind: "template-new", locales: c2.locales }
|
|
2811
3071
|
});
|
|
@@ -2841,7 +3101,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2841
3101
|
{
|
|
2842
3102
|
type: "button",
|
|
2843
3103
|
style: styles.buttonGhost,
|
|
2844
|
-
"aria-label": "
|
|
3104
|
+
"aria-label": tr.value("shell.closeMenu"),
|
|
2845
3105
|
onClick: () => drawer.value = false
|
|
2846
3106
|
},
|
|
2847
3107
|
[icon("close")]
|
|
@@ -2855,7 +3115,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2855
3115
|
...styles.nav,
|
|
2856
3116
|
...narrow.value ? { width: "280px", maxWidth: "85vw", boxShadow: "0 10px 40px rgba(0,0,0,.25)" } : {}
|
|
2857
3117
|
},
|
|
2858
|
-
"aria-label": "
|
|
3118
|
+
"aria-label": tr.value("shell.navLabel")
|
|
2859
3119
|
},
|
|
2860
3120
|
[
|
|
2861
3121
|
brand(),
|
|
@@ -2866,7 +3126,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2866
3126
|
const items = g.items.filter(visible);
|
|
2867
3127
|
if (items.length === 0) return null;
|
|
2868
3128
|
return (0, import_vue18.h)("div", { key: g.group }, [
|
|
2869
|
-
(0, import_vue18.h)("div", { style: styles.navGroup }, g.group),
|
|
3129
|
+
(0, import_vue18.h)("div", { style: styles.navGroup }, tr.value(g.group)),
|
|
2870
3130
|
...items.map((i) => {
|
|
2871
3131
|
const active = current.value === i.page;
|
|
2872
3132
|
return (0, import_vue18.h)(
|
|
@@ -2882,7 +3142,10 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2882
3142
|
go(i.page);
|
|
2883
3143
|
}
|
|
2884
3144
|
},
|
|
2885
|
-
[
|
|
3145
|
+
[
|
|
3146
|
+
icon(ICON[i.page], 16, active ? styles.navIconActive : void 0),
|
|
3147
|
+
tr.value(i.label)
|
|
3148
|
+
]
|
|
2886
3149
|
);
|
|
2887
3150
|
})
|
|
2888
3151
|
]);
|
|
@@ -2892,7 +3155,8 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2892
3155
|
]
|
|
2893
3156
|
);
|
|
2894
3157
|
return () => {
|
|
2895
|
-
const
|
|
3158
|
+
const titleKey = NAV.flatMap((g) => g.items).find((i) => i.page === current.value)?.label;
|
|
3159
|
+
const title = titleKey ? tr.value(titleKey) : "";
|
|
2896
3160
|
return (0, import_vue18.h)(
|
|
2897
3161
|
"div",
|
|
2898
3162
|
{ style: { ...styles.shell, ...narrow.value ? { flexDirection: "column" } : {} } },
|
|
@@ -2904,7 +3168,7 @@ var AdminShell = (0, import_vue18.defineComponent)({
|
|
|
2904
3168
|
{
|
|
2905
3169
|
type: "button",
|
|
2906
3170
|
style: styles.buttonGhost,
|
|
2907
|
-
"aria-label": "
|
|
3171
|
+
"aria-label": tr.value("shell.openMenu"),
|
|
2908
3172
|
"aria-expanded": drawer.value,
|
|
2909
3173
|
onClick: () => drawer.value = true
|
|
2910
3174
|
},
|