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