@fonderie/vue-admin-screens 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +517 -367
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -3
- package/dist/index.d.ts +68 -3
- package/dist/index.js +505 -356
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ __export(index_exports, {
|
|
|
31
31
|
ModulesScreen: () => ModulesScreen,
|
|
32
32
|
OperatorsScreen: () => OperatorsScreen,
|
|
33
33
|
RoutesScreen: () => RoutesScreen,
|
|
34
|
+
SubscriberBilling: () => SubscriberBilling,
|
|
34
35
|
SubscriberScreen: () => SubscriberScreen,
|
|
35
36
|
TokensScreen: () => TokensScreen,
|
|
36
37
|
UsersScreen: () => UsersScreen
|
|
@@ -40,7 +41,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
41
|
// src/screens/AdminShell.ts
|
|
41
42
|
var import_vue_config_admin_screens = require("@fonderie/vue-config-admin-screens");
|
|
42
43
|
var import_vue_courier_admin_screens = require("@fonderie/vue-courier-admin-screens");
|
|
43
|
-
var
|
|
44
|
+
var import_vue18 = require("vue");
|
|
44
45
|
|
|
45
46
|
// src/styles.ts
|
|
46
47
|
var t = {
|
|
@@ -596,6 +597,12 @@ function empty(title, body, iconName = "check") {
|
|
|
596
597
|
body ? (0, import_vue.h)("div", body) : null
|
|
597
598
|
]);
|
|
598
599
|
}
|
|
600
|
+
function refreshButton(onClick, busy, label = "Refresh") {
|
|
601
|
+
return (0, import_vue.h)("button", { type: "button", style: styles.button, onClick, disabled: busy }, [
|
|
602
|
+
icon("refresh", 14),
|
|
603
|
+
busy ? "Working\u2026" : label
|
|
604
|
+
]);
|
|
605
|
+
}
|
|
599
606
|
function methodChip(method) {
|
|
600
607
|
const tone = method === "GET" ? "ok" : method === "DELETE" ? "bad" : method === "POST" ? "info" : "warn";
|
|
601
608
|
const color = toneColor[tone];
|
|
@@ -647,7 +654,7 @@ function table(headers, rows) {
|
|
|
647
654
|
]);
|
|
648
655
|
}
|
|
649
656
|
var td = (children, extra = {}) => (0, import_vue2.h)("td", { style: { ...styles.td, ...extra } }, children);
|
|
650
|
-
var
|
|
657
|
+
var refreshButton2 = (label, disabled, onClick) => (0, import_vue2.h)("button", { type: "button", style: styles.button, disabled, onClick }, [
|
|
651
658
|
icon("refresh", 14),
|
|
652
659
|
disabled ? "Working\u2026" : label
|
|
653
660
|
]);
|
|
@@ -673,7 +680,7 @@ var AdminLogScreen = (0, import_vue3.defineComponent)({
|
|
|
673
680
|
pageHeader(
|
|
674
681
|
"Admin log",
|
|
675
682
|
"Every request made through this surface, newest first \u2014 refused ones included.",
|
|
676
|
-
[
|
|
683
|
+
[refreshButton2("Refresh", isLoading.value, () => void refresh())]
|
|
677
684
|
),
|
|
678
685
|
error.value ? (0, import_vue3.h)(
|
|
679
686
|
"p",
|
|
@@ -814,7 +821,7 @@ var AttentionScreen = (0, import_vue4.defineComponent)({
|
|
|
814
821
|
)
|
|
815
822
|
];
|
|
816
823
|
},
|
|
817
|
-
[
|
|
824
|
+
[refreshButton2("Refresh", isLoading.value, () => void refresh())],
|
|
818
825
|
{
|
|
819
826
|
lead: a ? `What needs you on this deployment \xB7 checked ${new Date(a.generatedAt).toLocaleTimeString()}` : "What needs you on this deployment"
|
|
820
827
|
}
|
|
@@ -994,7 +1001,7 @@ var DoctorScreen = (0, import_vue6.defineComponent)({
|
|
|
994
1001
|
report.value.ok ? "ok" : "bad",
|
|
995
1002
|
report.value.ok ? "all checks pass" : "a check failed"
|
|
996
1003
|
) : null,
|
|
997
|
-
|
|
1004
|
+
refreshButton2("Run again", isLoading.value, () => void refresh())
|
|
998
1005
|
],
|
|
999
1006
|
{
|
|
1000
1007
|
loadingText: "Running the checks\u2026",
|
|
@@ -1197,7 +1204,7 @@ var MigrationsScreen = (0, import_vue9.defineComponent)({
|
|
|
1197
1204
|
...behind.map((m) => section(m, r.everApplied))
|
|
1198
1205
|
].filter(Boolean);
|
|
1199
1206
|
},
|
|
1200
|
-
[
|
|
1207
|
+
[refreshButton2("Refresh", isLoading.value, () => void refresh())],
|
|
1201
1208
|
{
|
|
1202
1209
|
lead: "Schema changes each module ships, and whether this database has them.",
|
|
1203
1210
|
errorText: (e) => e.status === 403 ? "Applying migrations needs a token with the write scope." : e.explanation
|
|
@@ -1706,29 +1713,234 @@ var TokensScreen = (0, import_vue11.defineComponent)({
|
|
|
1706
1713
|
|
|
1707
1714
|
// src/screens/UsersScreen.ts
|
|
1708
1715
|
var import_client = require("@fonderie/client");
|
|
1709
|
-
var
|
|
1716
|
+
var import_vue_admin11 = require("@fonderie/vue-admin");
|
|
1717
|
+
var import_vue14 = require("vue");
|
|
1718
|
+
|
|
1719
|
+
// src/billing.ts
|
|
1710
1720
|
var import_vue12 = require("vue");
|
|
1711
|
-
|
|
1721
|
+
function periodEnd(s) {
|
|
1722
|
+
if (!s.currentPeriodEnd) return null;
|
|
1723
|
+
const date = new Date(s.currentPeriodEnd).toLocaleDateString();
|
|
1724
|
+
if (s.status === "canceled")
|
|
1725
|
+
return { label: new Date(s.currentPeriodEnd) < /* @__PURE__ */ new Date() ? "Ended" : "Ends", date };
|
|
1726
|
+
if (s.cancelAtPeriodEnd) return { label: "Ends", date };
|
|
1727
|
+
return { label: "Renews", date };
|
|
1728
|
+
}
|
|
1729
|
+
var statusTone = (status) => status === "active" ? "ok" : status === "trialing" ? "info" : status === "past_due" || status === "unpaid" ? "warn" : "neutral";
|
|
1730
|
+
function useSubscriptionIndex(client) {
|
|
1731
|
+
const index = (0, import_vue12.ref)(null);
|
|
1732
|
+
if (!client) return index;
|
|
1733
|
+
void (async () => {
|
|
1734
|
+
const map = /* @__PURE__ */ new Map();
|
|
1735
|
+
let cursor;
|
|
1736
|
+
for (let page2 = 0; page2 < 20; page2++) {
|
|
1737
|
+
const { result } = await client.listSubscriptions({
|
|
1738
|
+
limit: 100,
|
|
1739
|
+
...cursor ? { cursor } : {}
|
|
1740
|
+
});
|
|
1741
|
+
for (const s of result.subscriptions) {
|
|
1742
|
+
const key = `${s.subscriberType}/${s.subscriberId}`;
|
|
1743
|
+
if (!map.has(key)) map.set(key, s);
|
|
1744
|
+
}
|
|
1745
|
+
if (!result.nextCursor) break;
|
|
1746
|
+
cursor = result.nextCursor;
|
|
1747
|
+
}
|
|
1748
|
+
index.value = map;
|
|
1749
|
+
})().catch(() => {
|
|
1750
|
+
index.value = /* @__PURE__ */ new Map();
|
|
1751
|
+
});
|
|
1752
|
+
return index;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// src/screens/SubscriberBilling.ts
|
|
1756
|
+
var import_vue_admin10 = require("@fonderie/vue-admin");
|
|
1757
|
+
var import_vue13 = require("vue");
|
|
1758
|
+
var SubscriberBilling = (0, import_vue13.defineComponent)({
|
|
1759
|
+
name: "FonderieSubscriberBilling",
|
|
1760
|
+
props: {
|
|
1761
|
+
client: { type: Object, required: true },
|
|
1762
|
+
subscriberType: { type: String, required: true },
|
|
1763
|
+
subscriberId: { type: String, required: true }
|
|
1764
|
+
},
|
|
1765
|
+
setup(props) {
|
|
1766
|
+
const subscriber = (0, import_vue13.computed)(() => ({ type: props.subscriberType, id: props.subscriberId }));
|
|
1767
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
|
|
1768
|
+
const amount = (0, import_vue13.ref)("");
|
|
1769
|
+
const note = (0, import_vue13.ref)("");
|
|
1770
|
+
const granted = (0, import_vue13.ref)(null);
|
|
1771
|
+
return () => {
|
|
1772
|
+
const s = subscription.value;
|
|
1773
|
+
const w = wallet.value;
|
|
1774
|
+
if (isLoading.value && !w && !s) return (0, import_vue13.h)("p", { style: styles.status }, "Loading billing\u2026");
|
|
1775
|
+
const end = s ? periodEnd(s) : null;
|
|
1776
|
+
return (0, import_vue13.h)("div", [
|
|
1777
|
+
error.value ? (0, import_vue13.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
1778
|
+
(0, import_vue13.h)("div", { style: { ...styles.grid, marginBottom: "16px" } }, [
|
|
1779
|
+
(0, import_vue13.h)("div", { style: styles.card }, [
|
|
1780
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, "Plan"),
|
|
1781
|
+
(0, import_vue13.h)(
|
|
1782
|
+
"div",
|
|
1783
|
+
{
|
|
1784
|
+
style: {
|
|
1785
|
+
display: "flex",
|
|
1786
|
+
alignItems: "center",
|
|
1787
|
+
gap: "8px",
|
|
1788
|
+
marginTop: "6px",
|
|
1789
|
+
flexWrap: "wrap"
|
|
1790
|
+
}
|
|
1791
|
+
},
|
|
1792
|
+
[
|
|
1793
|
+
(0, import_vue13.h)("strong", { style: { fontSize: "18px" } }, s ? s.plan : "Free"),
|
|
1794
|
+
...s ? [
|
|
1795
|
+
(0, import_vue13.h)("span", { style: styles.muted }, `\xB7 ${s.interval}`),
|
|
1796
|
+
pill(statusTone(s.status), s.status)
|
|
1797
|
+
] : [(0, import_vue13.h)("span", { style: styles.muted }, "no subscription")]
|
|
1798
|
+
]
|
|
1799
|
+
),
|
|
1800
|
+
end ? (0, import_vue13.h)("div", { style: styles.statHint }, `${end.label} ${end.date}`) : null,
|
|
1801
|
+
s?.providerSubscriptionId ? (0, import_vue13.h)(
|
|
1802
|
+
"div",
|
|
1803
|
+
{ style: { ...styles.statHint, fontFamily: "var(--fonderie-mono,monospace)" } },
|
|
1804
|
+
s.providerSubscriptionId
|
|
1805
|
+
) : null
|
|
1806
|
+
]),
|
|
1807
|
+
(0, import_vue13.h)("div", { style: styles.card }, [
|
|
1808
|
+
(0, import_vue13.h)("div", { style: styles.statLabel }, "Credits"),
|
|
1809
|
+
...w ? [
|
|
1810
|
+
(0, import_vue13.h)("div", { style: styles.statValue }, [
|
|
1811
|
+
`${w.balance} `,
|
|
1812
|
+
(0, import_vue13.h)("span", { style: { fontSize: "14px", fontWeight: 500 } }, w.currency)
|
|
1813
|
+
]),
|
|
1814
|
+
(0, import_vue13.h)(
|
|
1815
|
+
"div",
|
|
1816
|
+
{ style: styles.statHint },
|
|
1817
|
+
`${w.purchased ?? "0"} permanent \xB7 ${w.granted ?? "0"} plan allowance${w.grantedExpiresAt ? ` (expires ${new Date(w.grantedExpiresAt).toLocaleDateString()})` : ""}`
|
|
1818
|
+
)
|
|
1819
|
+
] : [
|
|
1820
|
+
(0, import_vue13.h)(
|
|
1821
|
+
"div",
|
|
1822
|
+
{ style: styles.statHint },
|
|
1823
|
+
"No wallet \u2014 billing has no wallet configuration."
|
|
1824
|
+
)
|
|
1825
|
+
]
|
|
1826
|
+
])
|
|
1827
|
+
]),
|
|
1828
|
+
w ? (0, import_vue13.h)(
|
|
1829
|
+
"form",
|
|
1830
|
+
{
|
|
1831
|
+
style: {
|
|
1832
|
+
...styles.card,
|
|
1833
|
+
display: "flex",
|
|
1834
|
+
gap: "8px",
|
|
1835
|
+
alignItems: "center",
|
|
1836
|
+
flexWrap: "wrap",
|
|
1837
|
+
marginBottom: "16px"
|
|
1838
|
+
},
|
|
1839
|
+
onSubmit: (e) => {
|
|
1840
|
+
e.preventDefault();
|
|
1841
|
+
const key = `admin-grant-${props.subscriberType}-${props.subscriberId}-${Date.now()}`;
|
|
1842
|
+
const a = amount.value.trim();
|
|
1843
|
+
void grant({
|
|
1844
|
+
amount: a,
|
|
1845
|
+
...note.value ? { description: note.value } : {},
|
|
1846
|
+
idempotencyKey: key
|
|
1847
|
+
}).then(() => {
|
|
1848
|
+
granted.value = `Granted ${a} ${w.currency}.`;
|
|
1849
|
+
amount.value = "";
|
|
1850
|
+
note.value = "";
|
|
1851
|
+
}).catch(() => {
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
},
|
|
1855
|
+
[
|
|
1856
|
+
(0, import_vue13.h)("strong", { style: { fontSize: "13.5px", marginRight: "4px" } }, "Grant credits"),
|
|
1857
|
+
(0, import_vue13.h)("input", {
|
|
1858
|
+
value: amount.value,
|
|
1859
|
+
onInput: (e) => amount.value = e.target.value,
|
|
1860
|
+
placeholder: `amount (${w.currency})`,
|
|
1861
|
+
inputmode: "numeric",
|
|
1862
|
+
style: { ...styles.input, width: "150px" },
|
|
1863
|
+
"aria-label": "Amount"
|
|
1864
|
+
}),
|
|
1865
|
+
(0, import_vue13.h)("input", {
|
|
1866
|
+
value: note.value,
|
|
1867
|
+
onInput: (e) => note.value = e.target.value,
|
|
1868
|
+
placeholder: "reason (optional)",
|
|
1869
|
+
style: { ...styles.input, flex: 1, minWidth: "180px" },
|
|
1870
|
+
"aria-label": "Reason"
|
|
1871
|
+
}),
|
|
1872
|
+
(0, import_vue13.h)(
|
|
1873
|
+
"button",
|
|
1874
|
+
{
|
|
1875
|
+
type: "submit",
|
|
1876
|
+
style: styles.buttonPrimary,
|
|
1877
|
+
disabled: !/^\d+$/.test(amount.value.trim())
|
|
1878
|
+
},
|
|
1879
|
+
[icon("plus", 14), "Grant"]
|
|
1880
|
+
),
|
|
1881
|
+
granted.value ? (0, import_vue13.h)("span", { style: styles.ok }, granted.value) : null
|
|
1882
|
+
]
|
|
1883
|
+
) : null,
|
|
1884
|
+
w ? ledger.value.length === 0 ? empty("No credit movements yet", void 0, "subscriber") : (0, import_vue13.h)("div", [
|
|
1885
|
+
table(
|
|
1886
|
+
["When", "Kind", "Amount", "Note"],
|
|
1887
|
+
ledger.value.map(
|
|
1888
|
+
(t2) => (0, import_vue13.h)("tr", { key: t2.id }, [
|
|
1889
|
+
td(new Date(t2.createdAt).toLocaleString(), {
|
|
1890
|
+
...styles.muted,
|
|
1891
|
+
whiteSpace: "nowrap"
|
|
1892
|
+
}),
|
|
1893
|
+
td(t2.type),
|
|
1894
|
+
td(t2.amount, styles.mono),
|
|
1895
|
+
td(t2.description ?? "", styles.muted)
|
|
1896
|
+
])
|
|
1897
|
+
)
|
|
1898
|
+
),
|
|
1899
|
+
hasMoreLedger.value ? loadMoreButton(() => void loadMoreLedger()) : null
|
|
1900
|
+
]) : null
|
|
1901
|
+
]);
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
// src/screens/UsersScreen.ts
|
|
1907
|
+
var UsersScreen = (0, import_vue14.defineComponent)({
|
|
1712
1908
|
name: "FonderieUsersScreen",
|
|
1713
1909
|
props: {
|
|
1714
1910
|
client: { type: Object, required: true },
|
|
1715
|
-
pageSize: { type: Number, default: 50 }
|
|
1911
|
+
pageSize: { type: Number, default: 50 },
|
|
1912
|
+
// Given ⇒ a Plan column, and each user's plan, credits and grant form on
|
|
1913
|
+
// their page. Everyone has a wallet; no subscription means the free tier.
|
|
1914
|
+
billingClient: { type: Object, default: void 0 },
|
|
1915
|
+
// Open this user on arrival (e.g. from the Subscriptions list).
|
|
1916
|
+
openUserId: { type: String, default: void 0 }
|
|
1716
1917
|
},
|
|
1717
1918
|
setup(props) {
|
|
1718
|
-
const input = (0,
|
|
1719
|
-
const email = (0,
|
|
1720
|
-
const selectedId = (0,
|
|
1721
|
-
const
|
|
1722
|
-
const
|
|
1919
|
+
const input = (0, import_vue14.ref)("");
|
|
1920
|
+
const email = (0, import_vue14.ref)("");
|
|
1921
|
+
const selectedId = (0, import_vue14.ref)(props.openUserId ?? "");
|
|
1922
|
+
const plans = useSubscriptionIndex(props.billingClient);
|
|
1923
|
+
const list = (0, import_vue_admin11.useAdminUsers)(props.client, { limit: props.pageSize });
|
|
1924
|
+
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin11.useAdminUser)(
|
|
1723
1925
|
props.client,
|
|
1724
1926
|
{ email, id: selectedId }
|
|
1725
1927
|
);
|
|
1726
|
-
const userId = (0,
|
|
1727
|
-
const sessions = (0,
|
|
1728
|
-
const history = (0,
|
|
1729
|
-
const showList = (0,
|
|
1928
|
+
const userId = (0, import_vue14.computed)(() => user.value?.id ?? null);
|
|
1929
|
+
const sessions = (0, import_vue_admin11.useAdminUserSessions)(props.client, userId);
|
|
1930
|
+
const history = (0, import_vue_admin11.useAdminLoginHistory)(props.client, userId, { limit: 20 });
|
|
1931
|
+
const showList = (0, import_vue14.computed)(() => !email.value && !selectedId.value);
|
|
1730
1932
|
const yesNo = (v) => v ? pill("ok", "yes") : pill("neutral", "no");
|
|
1731
|
-
const row = (k, v) => (0,
|
|
1933
|
+
const row = (k, v) => (0, import_vue14.h)("tr", [td(k), td(v)]);
|
|
1934
|
+
const planCell = (id) => {
|
|
1935
|
+
if (!plans.value) return (0, import_vue14.h)("span", { style: styles.muted }, "\u2026");
|
|
1936
|
+
const sub = plans.value.get(`user/${id}`);
|
|
1937
|
+
if (!sub || sub.status === "canceled") return (0, import_vue14.h)("span", { style: styles.muted }, "free");
|
|
1938
|
+
return [
|
|
1939
|
+
(0, import_vue14.h)("strong", sub.plan),
|
|
1940
|
+
" ",
|
|
1941
|
+
sub.status !== "active" ? pill(statusTone(sub.status), sub.status) : null
|
|
1942
|
+
];
|
|
1943
|
+
};
|
|
1732
1944
|
const clear = () => {
|
|
1733
1945
|
email.value = "";
|
|
1734
1946
|
selectedId.value = "";
|
|
@@ -1736,12 +1948,12 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1736
1948
|
};
|
|
1737
1949
|
return () => {
|
|
1738
1950
|
const u = user.value;
|
|
1739
|
-
return (0,
|
|
1951
|
+
return (0, import_vue14.h)("div", { style: styles.container }, [
|
|
1740
1952
|
pageHeader(
|
|
1741
1953
|
"Users",
|
|
1742
1954
|
"Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
1743
1955
|
),
|
|
1744
|
-
(0,
|
|
1956
|
+
(0, import_vue14.h)(
|
|
1745
1957
|
"form",
|
|
1746
1958
|
{
|
|
1747
1959
|
style: styles.toolbar,
|
|
@@ -1752,7 +1964,7 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1752
1964
|
}
|
|
1753
1965
|
},
|
|
1754
1966
|
[
|
|
1755
|
-
(0,
|
|
1967
|
+
(0, import_vue14.h)("input", {
|
|
1756
1968
|
type: "email",
|
|
1757
1969
|
value: input.value,
|
|
1758
1970
|
onInput: (e) => input.value = e.target.value,
|
|
@@ -1760,22 +1972,22 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1760
1972
|
style: { ...styles.input, minWidth: "280px" },
|
|
1761
1973
|
"aria-label": "Email"
|
|
1762
1974
|
}),
|
|
1763
|
-
(0,
|
|
1975
|
+
(0, import_vue14.h)(
|
|
1764
1976
|
"button",
|
|
1765
1977
|
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
1766
1978
|
[icon("search", 14), "Look up"]
|
|
1767
1979
|
),
|
|
1768
|
-
showList.value ?
|
|
1980
|
+
showList.value ? refreshButton2("Refresh", list.isLoading.value, () => void list.refresh()) : actionButton([icon("back", 14), "All users"], false, clear, styles.buttonGhost)
|
|
1769
1981
|
]
|
|
1770
1982
|
),
|
|
1771
|
-
showList.value ? (0,
|
|
1772
|
-
list.error.value ? (0,
|
|
1983
|
+
showList.value ? (0, import_vue14.h)("div", [
|
|
1984
|
+
list.error.value ? (0, import_vue14.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
1773
1985
|
list.users.value.length === 0 && !list.isLoading.value ? empty("No users yet", "Sign-ups appear here as they happen.", "users") : table(
|
|
1774
|
-
["Email", "Name", "Created", "Status"],
|
|
1986
|
+
props.billingClient ? ["Email", "Name", "Plan", "Created", "Status"] : ["Email", "Name", "Created", "Status"],
|
|
1775
1987
|
list.users.value.map(
|
|
1776
|
-
(u2) => (0,
|
|
1988
|
+
(u2) => (0, import_vue14.h)("tr", { key: u2.id }, [
|
|
1777
1989
|
td(
|
|
1778
|
-
(0,
|
|
1990
|
+
(0, import_vue14.h)(
|
|
1779
1991
|
"button",
|
|
1780
1992
|
{
|
|
1781
1993
|
type: "button",
|
|
@@ -1786,8 +1998,9 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1786
1998
|
)
|
|
1787
1999
|
),
|
|
1788
2000
|
td(
|
|
1789
|
-
`${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0,
|
|
2001
|
+
`${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || (0, import_vue14.h)("span", { style: styles.muted }, "\u2014")
|
|
1790
2002
|
),
|
|
2003
|
+
props.billingClient ? td(planCell(u2.id)) : null,
|
|
1791
2004
|
td(new Date(u2.createdAt).toLocaleDateString()),
|
|
1792
2005
|
td(
|
|
1793
2006
|
u2.suspended ? pill("warn", "suspended") : u2.deletedAt ? pill("neutral", "deleted") : pill("ok", "active")
|
|
@@ -1795,24 +2008,24 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1795
2008
|
])
|
|
1796
2009
|
)
|
|
1797
2010
|
),
|
|
1798
|
-
list.isLoading.value ? (0,
|
|
2011
|
+
list.isLoading.value ? (0, import_vue14.h)("p", { style: styles.status }, "Loading\u2026") : null,
|
|
1799
2012
|
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore()) : null
|
|
1800
2013
|
]) : null,
|
|
1801
|
-
error.value ? (0,
|
|
2014
|
+
error.value ? (0, import_vue14.h)(
|
|
1802
2015
|
"p",
|
|
1803
2016
|
{ style: styles.error, role: "alert" },
|
|
1804
2017
|
error.value.status === 404 ? "No user with that email." : error.value.explanation
|
|
1805
2018
|
) : null,
|
|
1806
2019
|
u ? [
|
|
1807
|
-
(0,
|
|
2020
|
+
(0, import_vue14.h)("h2", { style: styles.subtitle }, [
|
|
1808
2021
|
u.firstName || u.lastName ? `${u.firstName} ${u.lastName}`.trim() : u.email,
|
|
1809
2022
|
" ",
|
|
1810
2023
|
u.suspended ? pill("warn", "suspended") : null,
|
|
1811
2024
|
u.deletedAt ? pill("neutral", "deleted") : null
|
|
1812
2025
|
]),
|
|
1813
|
-
(0,
|
|
1814
|
-
(0,
|
|
1815
|
-
row("id", (0,
|
|
2026
|
+
(0, import_vue14.h)("table", { style: styles.table }, [
|
|
2027
|
+
(0, import_vue14.h)("tbody", [
|
|
2028
|
+
row("id", (0, import_vue14.h)("span", { style: styles.mono }, u.id)),
|
|
1816
2029
|
row("email", [u.email, " \xB7 verified ", yesNo(u.isEmailVerified)]),
|
|
1817
2030
|
row("MFA", yesNo(u.mfaEnabled)),
|
|
1818
2031
|
row(
|
|
@@ -1821,12 +2034,12 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1821
2034
|
),
|
|
1822
2035
|
row(
|
|
1823
2036
|
"last login",
|
|
1824
|
-
u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0,
|
|
2037
|
+
u.lastLogin ? new Date(u.lastLogin).toLocaleString() : (0, import_vue14.h)("span", { style: styles.muted }, "never")
|
|
1825
2038
|
),
|
|
1826
2039
|
row("created", new Date(u.createdAt).toLocaleString())
|
|
1827
2040
|
])
|
|
1828
2041
|
]),
|
|
1829
|
-
(0,
|
|
2042
|
+
(0, import_vue14.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
|
|
1830
2043
|
u.suspended ? actionButton("Unsuspend", isLoading.value, () => void unsuspend()) : actionButton(
|
|
1831
2044
|
"Suspend",
|
|
1832
2045
|
isLoading.value,
|
|
@@ -1839,19 +2052,26 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1839
2052
|
() => void revokeSessions().then(() => sessions.refresh())
|
|
1840
2053
|
)
|
|
1841
2054
|
]),
|
|
1842
|
-
(0,
|
|
1843
|
-
|
|
2055
|
+
props.billingClient ? (0, import_vue14.h)("h2", { style: styles.subtitle }, "Plan & credits") : null,
|
|
2056
|
+
props.billingClient ? (0, import_vue14.h)(SubscriberBilling, {
|
|
2057
|
+
key: u.id,
|
|
2058
|
+
client: props.billingClient,
|
|
2059
|
+
subscriberType: "user",
|
|
2060
|
+
subscriberId: u.id
|
|
2061
|
+
}) : null,
|
|
2062
|
+
(0, import_vue14.h)("h2", { style: styles.subtitle }, "Live sessions"),
|
|
2063
|
+
sessions.sessions.value.length === 0 ? (0, import_vue14.h)("p", { style: styles.muted }, "None.") : (0, import_vue14.h)(
|
|
1844
2064
|
"ul",
|
|
1845
2065
|
{ style: styles.list },
|
|
1846
2066
|
sessions.sessions.value.map(
|
|
1847
|
-
(s) => (0,
|
|
1848
|
-
(0,
|
|
2067
|
+
(s) => (0, import_vue14.h)("li", { key: s.id, style: styles.row }, [
|
|
2068
|
+
(0, import_vue14.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
|
|
1849
2069
|
" ",
|
|
1850
|
-
(0,
|
|
2070
|
+
(0, import_vue14.h)("span", { style: styles.muted }, (0, import_client.describeLocation)(s.location) ?? ""),
|
|
1851
2071
|
" ",
|
|
1852
|
-
(0,
|
|
2072
|
+
(0, import_vue14.h)("span", { style: styles.muted }, s.userAgent ?? ""),
|
|
1853
2073
|
" ",
|
|
1854
|
-
(0,
|
|
2074
|
+
(0, import_vue14.h)(
|
|
1855
2075
|
"span",
|
|
1856
2076
|
{ style: styles.muted },
|
|
1857
2077
|
`since ${new Date(s.createdAt).toLocaleString()}`
|
|
@@ -1859,22 +2079,22 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1859
2079
|
])
|
|
1860
2080
|
)
|
|
1861
2081
|
),
|
|
1862
|
-
(0,
|
|
1863
|
-
history.events.value.length === 0 && !history.isLoading.value ? (0,
|
|
2082
|
+
(0, import_vue14.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
|
|
2083
|
+
history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue14.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue14.h)(
|
|
1864
2084
|
"ul",
|
|
1865
2085
|
{ style: styles.list },
|
|
1866
2086
|
history.events.value.map(
|
|
1867
|
-
(e) => (0,
|
|
2087
|
+
(e) => (0, import_vue14.h)("li", { key: e.id, style: styles.row }, [
|
|
1868
2088
|
pill(e.outcome === "success" ? "ok" : "bad", e.outcome),
|
|
1869
2089
|
" ",
|
|
1870
|
-
(0,
|
|
2090
|
+
(0, import_vue14.h)("span", { style: styles.muted }, e.method),
|
|
1871
2091
|
" ",
|
|
1872
|
-
(0,
|
|
2092
|
+
(0, import_vue14.h)("span", { style: styles.mono }, e.ipAddress ?? "\u2014"),
|
|
1873
2093
|
" ",
|
|
1874
|
-
(0,
|
|
2094
|
+
(0, import_vue14.h)("span", { style: styles.muted }, (0, import_client.describeLocation)(e.location) ?? ""),
|
|
1875
2095
|
e.location?.proxy || e.location?.hosting ? [" ", pill("warn", e.location.proxy ? "proxy/VPN" : "hosting")] : "",
|
|
1876
2096
|
" ",
|
|
1877
|
-
(0,
|
|
2097
|
+
(0, import_vue14.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
|
|
1878
2098
|
])
|
|
1879
2099
|
)
|
|
1880
2100
|
),
|
|
@@ -1886,27 +2106,27 @@ var UsersScreen = (0, import_vue12.defineComponent)({
|
|
|
1886
2106
|
});
|
|
1887
2107
|
|
|
1888
2108
|
// src/screens/CatalogScreen.ts
|
|
1889
|
-
var
|
|
1890
|
-
var
|
|
1891
|
-
var CatalogScreen = (0,
|
|
2109
|
+
var import_vue_admin12 = require("@fonderie/vue-admin");
|
|
2110
|
+
var import_vue15 = require("vue");
|
|
2111
|
+
var CatalogScreen = (0, import_vue15.defineComponent)({
|
|
1892
2112
|
name: "FonderieCatalogScreen",
|
|
1893
2113
|
props: { client: { type: Object, required: true } },
|
|
1894
2114
|
setup(props) {
|
|
1895
|
-
const { catalog, isLoading, error, refresh, deletePlan } = (0,
|
|
2115
|
+
const { catalog, isLoading, error, refresh, deletePlan } = (0, import_vue_admin12.useAdminCatalog)(props.client);
|
|
1896
2116
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
1897
2117
|
return () => {
|
|
1898
2118
|
const c = catalog.value;
|
|
1899
|
-
return (0,
|
|
2119
|
+
return (0, import_vue15.h)("div", { style: styles.container }, [
|
|
1900
2120
|
pageHeader(
|
|
1901
2121
|
"Catalog",
|
|
1902
2122
|
"What you sell: plans as configured in code, and as stored in the database, side by side.",
|
|
1903
|
-
[
|
|
2123
|
+
[refreshButton2("Refresh", isLoading.value, () => void refresh())]
|
|
1904
2124
|
),
|
|
1905
|
-
error.value ? (0,
|
|
1906
|
-
isLoading.value && !c ? (0,
|
|
2125
|
+
error.value ? (0, import_vue15.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
2126
|
+
isLoading.value && !c ? (0, import_vue15.h)("p", { style: styles.status }, "Loading\u2026") : null,
|
|
1907
2127
|
c ? [
|
|
1908
|
-
(0,
|
|
1909
|
-
(0,
|
|
2128
|
+
(0, import_vue15.h)("h2", { style: { ...styles.subtitle, marginTop: 0 } }, "Configured (code)"),
|
|
2129
|
+
(0, import_vue15.h)(
|
|
1910
2130
|
"pre",
|
|
1911
2131
|
{
|
|
1912
2132
|
style: {
|
|
@@ -1920,7 +2140,7 @@ var CatalogScreen = (0, import_vue13.defineComponent)({
|
|
|
1920
2140
|
},
|
|
1921
2141
|
JSON.stringify(c.configured, null, 2)
|
|
1922
2142
|
),
|
|
1923
|
-
(0,
|
|
2143
|
+
(0, import_vue15.h)("h2", { style: styles.subtitle }, "Stored (database)"),
|
|
1924
2144
|
c.stored.length === 0 ? empty(
|
|
1925
2145
|
"No stored plans",
|
|
1926
2146
|
"Plans are configured in code; nothing has been written to the database.",
|
|
@@ -1928,10 +2148,10 @@ var CatalogScreen = (0, import_vue13.defineComponent)({
|
|
|
1928
2148
|
) : table(
|
|
1929
2149
|
["Plan", "Tier", "Seats", "Monthly", "Yearly", "Trial", ""],
|
|
1930
2150
|
c.stored.map(
|
|
1931
|
-
(p) => (0,
|
|
2151
|
+
(p) => (0, import_vue15.h)("tr", { key: p.id }, [
|
|
1932
2152
|
td([
|
|
1933
|
-
(0,
|
|
1934
|
-
p.description ? (0,
|
|
2153
|
+
(0, import_vue15.h)("strong", p.name),
|
|
2154
|
+
p.description ? (0, import_vue15.h)("div", { style: styles.muted }, p.description) : ""
|
|
1935
2155
|
]),
|
|
1936
2156
|
td(String(p.tier)),
|
|
1937
2157
|
td(p.seats == null ? "\u221E" : String(p.seats)),
|
|
@@ -1939,7 +2159,7 @@ var CatalogScreen = (0, import_vue13.defineComponent)({
|
|
|
1939
2159
|
td(money(p.pricing?.yearly)),
|
|
1940
2160
|
td(p.trialDays ? `${p.trialDays} d` : "\u2014"),
|
|
1941
2161
|
td(
|
|
1942
|
-
(0,
|
|
2162
|
+
(0, import_vue15.h)(
|
|
1943
2163
|
"button",
|
|
1944
2164
|
{
|
|
1945
2165
|
type: "button",
|
|
@@ -1963,293 +2183,205 @@ var CatalogScreen = (0, import_vue13.defineComponent)({
|
|
|
1963
2183
|
});
|
|
1964
2184
|
|
|
1965
2185
|
// src/screens/SubscriberScreen.ts
|
|
1966
|
-
var
|
|
1967
|
-
var
|
|
1968
|
-
var
|
|
2186
|
+
var import_vue_admin13 = require("@fonderie/vue-admin");
|
|
2187
|
+
var import_vue16 = require("vue");
|
|
2188
|
+
var FILTERS = [
|
|
2189
|
+
{ label: "All", match: () => true },
|
|
2190
|
+
{ label: "Active", match: (s) => s === "active" },
|
|
2191
|
+
{ label: "Trialing", match: (s) => s === "trialing" },
|
|
2192
|
+
{ label: "Past due", match: (s) => s === "past_due" || s === "unpaid" },
|
|
2193
|
+
{ label: "Canceled", match: (s) => s === "canceled" }
|
|
2194
|
+
];
|
|
2195
|
+
var SubscriberScreen = (0, import_vue16.defineComponent)({
|
|
1969
2196
|
name: "FonderieSubscriberScreen",
|
|
1970
2197
|
props: {
|
|
1971
2198
|
client: { type: Object, required: true },
|
|
1972
|
-
pageSize: { type: Number, default: 50 }
|
|
2199
|
+
pageSize: { type: Number, default: 50 },
|
|
2200
|
+
/**
|
|
2201
|
+
* A user subscriber opens on the Users page, where their plan and credits
|
|
2202
|
+
* live beside their account. Omit to open everything here.
|
|
2203
|
+
*/
|
|
2204
|
+
onOpenUser: { type: Function, default: void 0 }
|
|
1973
2205
|
},
|
|
1974
2206
|
setup(props) {
|
|
1975
|
-
const
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
const
|
|
1979
|
-
const
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
idInput.value = id;
|
|
1984
|
-
subscriber.value = { type: t2, id };
|
|
2207
|
+
const list = (0, import_vue_admin13.useAdminSubscribers)(props.client, { limit: props.pageSize });
|
|
2208
|
+
const filter = (0, import_vue16.ref)("All");
|
|
2209
|
+
const open = (0, import_vue16.ref)(null);
|
|
2210
|
+
const type = (0, import_vue16.ref)("workspace");
|
|
2211
|
+
const idInput = (0, import_vue16.ref)("");
|
|
2212
|
+
const openRow = (t2, id) => {
|
|
2213
|
+
if (t2 === "user" && props.onOpenUser) props.onOpenUser(id);
|
|
2214
|
+
else open.value = { type: t2, id };
|
|
1985
2215
|
};
|
|
1986
|
-
const amount = (0, import_vue14.ref)("");
|
|
1987
|
-
const note = (0, import_vue14.ref)("");
|
|
1988
|
-
const granted = (0, import_vue14.ref)(null);
|
|
1989
|
-
const input = (model, placeholder, extra = {}) => (0, import_vue14.h)("input", {
|
|
1990
|
-
value: model.value,
|
|
1991
|
-
onInput: (e) => model.value = e.target.value,
|
|
1992
|
-
placeholder,
|
|
1993
|
-
style: { ...styles.input, ...extra },
|
|
1994
|
-
"aria-label": placeholder
|
|
1995
|
-
});
|
|
1996
2216
|
return () => {
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
"Who is subscribed. Open one for their plan, wallet and ledger, and to grant credit."
|
|
2003
|
-
),
|
|
2004
|
-
(0, import_vue14.h)(
|
|
2005
|
-
"form",
|
|
2006
|
-
{
|
|
2007
|
-
style: styles.toolbar,
|
|
2008
|
-
onSubmit: (e) => {
|
|
2009
|
-
e.preventDefault();
|
|
2010
|
-
granted.value = null;
|
|
2011
|
-
subscriber.value = idInput.value.trim() ? { type: type.value, id: idInput.value.trim() } : null;
|
|
2012
|
-
}
|
|
2013
|
-
},
|
|
2014
|
-
[
|
|
2015
|
-
(0, import_vue14.h)(
|
|
2016
|
-
"select",
|
|
2017
|
-
{
|
|
2018
|
-
value: type.value,
|
|
2019
|
-
onChange: (e) => type.value = e.target.value,
|
|
2020
|
-
style: styles.input,
|
|
2021
|
-
"aria-label": "Type"
|
|
2022
|
-
},
|
|
2023
|
-
[
|
|
2024
|
-
(0, import_vue14.h)("option", { value: "user" }, "user"),
|
|
2025
|
-
(0, import_vue14.h)("option", { value: "workspace" }, "workspace")
|
|
2026
|
-
]
|
|
2027
|
-
),
|
|
2028
|
-
input(idInput, "subscriber id", { minWidth: "280px" }),
|
|
2029
|
-
(0, import_vue14.h)(
|
|
2030
|
-
"button",
|
|
2031
|
-
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
2032
|
-
[icon("search", 14), "Look up"]
|
|
2033
|
-
),
|
|
2034
|
-
subscriber.value ? (0, import_vue14.h)(
|
|
2035
|
-
"button",
|
|
2036
|
-
{
|
|
2037
|
-
type: "button",
|
|
2038
|
-
style: styles.button,
|
|
2039
|
-
onClick: () => {
|
|
2040
|
-
subscriber.value = null;
|
|
2041
|
-
idInput.value = "";
|
|
2042
|
-
granted.value = null;
|
|
2043
|
-
}
|
|
2044
|
-
},
|
|
2045
|
-
[icon("back", 14), "All subscribers"]
|
|
2046
|
-
) : (0, import_vue14.h)(
|
|
2217
|
+
const o = open.value;
|
|
2218
|
+
if (o) {
|
|
2219
|
+
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2220
|
+
pageHeader(`${o.type}/${o.id}`, "Plan, credits and what moved.", [
|
|
2221
|
+
(0, import_vue16.h)(
|
|
2047
2222
|
"button",
|
|
2048
|
-
{
|
|
2049
|
-
|
|
2050
|
-
style: styles.button,
|
|
2051
|
-
disabled: list.isLoading.value,
|
|
2052
|
-
onClick: () => void list.refresh()
|
|
2053
|
-
},
|
|
2054
|
-
"Refresh"
|
|
2223
|
+
{ type: "button", style: styles.buttonGhost, onClick: () => open.value = null },
|
|
2224
|
+
[icon("back", 14), "All subscriptions"]
|
|
2055
2225
|
)
|
|
2056
|
-
]
|
|
2226
|
+
]),
|
|
2227
|
+
(0, import_vue16.h)(SubscriberBilling, {
|
|
2228
|
+
key: `${o.type}/${o.id}`,
|
|
2229
|
+
client: props.client,
|
|
2230
|
+
subscriberType: o.type,
|
|
2231
|
+
subscriberId: o.id
|
|
2232
|
+
})
|
|
2233
|
+
]);
|
|
2234
|
+
}
|
|
2235
|
+
const match = FILTERS.find((f) => f.label === filter.value)?.match ?? (() => true);
|
|
2236
|
+
const rows = list.subscriptions.value.filter((s) => match(s.status));
|
|
2237
|
+
return (0, import_vue16.h)("div", { style: styles.container }, [
|
|
2238
|
+
pageHeader(
|
|
2239
|
+
"Subscriptions",
|
|
2240
|
+
"Who pays, and who is behind. A user's plan and credits are also on their page under Users \u2014 free users appear only there.",
|
|
2241
|
+
[refreshButton(() => void list.refresh(), list.isLoading.value)]
|
|
2057
2242
|
),
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : (0, import_vue14.h)("span", { style: styles.muted }, "\u2014")
|
|
2089
|
-
)
|
|
2090
|
-
])
|
|
2091
|
-
)
|
|
2243
|
+
(0, import_vue16.h)("div", { style: { ...styles.toolbar, justifyContent: "space-between" } }, [
|
|
2244
|
+
(0, import_vue16.h)(
|
|
2245
|
+
"fieldset",
|
|
2246
|
+
{
|
|
2247
|
+
style: {
|
|
2248
|
+
display: "flex",
|
|
2249
|
+
gap: "6px",
|
|
2250
|
+
flexWrap: "wrap",
|
|
2251
|
+
border: "none",
|
|
2252
|
+
margin: 0,
|
|
2253
|
+
padding: 0,
|
|
2254
|
+
minWidth: 0
|
|
2255
|
+
},
|
|
2256
|
+
"aria-label": "Filter by status"
|
|
2257
|
+
},
|
|
2258
|
+
FILTERS.map((f) => {
|
|
2259
|
+
const n = list.subscriptions.value.filter((s) => f.match(s.status)).length;
|
|
2260
|
+
const on = filter.value === f.label;
|
|
2261
|
+
return (0, import_vue16.h)(
|
|
2262
|
+
"button",
|
|
2263
|
+
{
|
|
2264
|
+
key: f.label,
|
|
2265
|
+
type: "button",
|
|
2266
|
+
"aria-pressed": on,
|
|
2267
|
+
onClick: () => filter.value = f.label,
|
|
2268
|
+
style: { ...on ? styles.buttonPrimary : styles.button, height: "28px" }
|
|
2269
|
+
},
|
|
2270
|
+
[f.label, (0, import_vue16.h)("span", { style: { opacity: 0.7 } }, String(n))]
|
|
2271
|
+
);
|
|
2272
|
+
})
|
|
2092
2273
|
),
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
"button",
|
|
2274
|
+
(0, import_vue16.h)(
|
|
2275
|
+
"form",
|
|
2096
2276
|
{
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2277
|
+
style: { display: "flex", gap: "6px" },
|
|
2278
|
+
onSubmit: (e) => {
|
|
2279
|
+
e.preventDefault();
|
|
2280
|
+
if (idInput.value.trim()) openRow(type.value, idInput.value.trim());
|
|
2281
|
+
}
|
|
2100
2282
|
},
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
2143
|
-
}
|
|
2144
|
-
},
|
|
2145
|
-
w.balance
|
|
2146
|
-
),
|
|
2147
|
-
` ${w.currency} `,
|
|
2148
|
-
(0, import_vue14.h)(
|
|
2149
|
-
"span",
|
|
2150
|
-
{ style: styles.muted },
|
|
2151
|
-
`(granted ${w.granted ?? "0"} \xB7 purchased ${w.purchased ?? "0"}) \xB7 minor units, precision ${w.precision}`
|
|
2152
|
-
)
|
|
2153
|
-
]
|
|
2154
|
-
),
|
|
2155
|
-
(0, import_vue14.h)(
|
|
2156
|
-
"form",
|
|
2157
|
-
{
|
|
2158
|
-
style: styles.toolbar,
|
|
2159
|
-
onSubmit: (e) => {
|
|
2160
|
-
e.preventDefault();
|
|
2161
|
-
const sub = subscriber.value;
|
|
2162
|
-
if (!sub) return;
|
|
2163
|
-
const key = `admin-grant-${sub.type}-${sub.id}-${Date.now()}`;
|
|
2164
|
-
const amt = amount.value.trim();
|
|
2165
|
-
void grant({
|
|
2166
|
-
amount: amt,
|
|
2167
|
-
...note.value ? { description: note.value } : {},
|
|
2168
|
-
idempotencyKey: key
|
|
2169
|
-
}).then(() => {
|
|
2170
|
-
granted.value = `Granted ${amt} ${w.currency}.`;
|
|
2171
|
-
amount.value = "";
|
|
2172
|
-
note.value = "";
|
|
2173
|
-
}).catch(() => {
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
},
|
|
2177
|
-
[
|
|
2178
|
-
input(amount, "amount (minor units)"),
|
|
2179
|
-
input(note, "reason (optional)", { minWidth: "220px" }),
|
|
2180
|
-
(0, import_vue14.h)(
|
|
2283
|
+
[
|
|
2284
|
+
(0, import_vue16.h)(
|
|
2285
|
+
"select",
|
|
2286
|
+
{
|
|
2287
|
+
value: type.value,
|
|
2288
|
+
onChange: (e) => type.value = e.target.value,
|
|
2289
|
+
style: styles.input,
|
|
2290
|
+
"aria-label": "Type"
|
|
2291
|
+
},
|
|
2292
|
+
[
|
|
2293
|
+
(0, import_vue16.h)("option", { value: "workspace" }, "workspace"),
|
|
2294
|
+
(0, import_vue16.h)("option", { value: "user" }, "user")
|
|
2295
|
+
]
|
|
2296
|
+
),
|
|
2297
|
+
(0, import_vue16.h)("input", {
|
|
2298
|
+
value: idInput.value,
|
|
2299
|
+
onInput: (e) => idInput.value = e.target.value,
|
|
2300
|
+
placeholder: "subscriber id",
|
|
2301
|
+
style: { ...styles.input, width: "220px" },
|
|
2302
|
+
"aria-label": "Subscriber id"
|
|
2303
|
+
}),
|
|
2304
|
+
(0, import_vue16.h)(
|
|
2305
|
+
"button",
|
|
2306
|
+
{ type: "submit", style: styles.button, disabled: !idInput.value.trim() },
|
|
2307
|
+
[icon("search", 14), "Open"]
|
|
2308
|
+
)
|
|
2309
|
+
]
|
|
2310
|
+
)
|
|
2311
|
+
]),
|
|
2312
|
+
list.error.value ? (0, import_vue16.h)("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
|
|
2313
|
+
rows.length === 0 && !list.isLoading.value ? empty(
|
|
2314
|
+
filter.value === "All" ? "No subscriptions yet" : `No ${filter.value.toLowerCase()} subscriptions`,
|
|
2315
|
+
"Subscriptions appear here once someone checks out.",
|
|
2316
|
+
"subscriber"
|
|
2317
|
+
) : table(
|
|
2318
|
+
["Subscriber", "Plan", "Status", "Renews / ends"],
|
|
2319
|
+
rows.map((s) => {
|
|
2320
|
+
const end = periodEnd(s);
|
|
2321
|
+
return (0, import_vue16.h)("tr", { key: s.id }, [
|
|
2322
|
+
td(
|
|
2323
|
+
(0, import_vue16.h)(
|
|
2181
2324
|
"button",
|
|
2182
2325
|
{
|
|
2183
|
-
type: "
|
|
2184
|
-
style: styles.
|
|
2185
|
-
|
|
2326
|
+
type: "button",
|
|
2327
|
+
style: { ...styles.link, ...styles.mono },
|
|
2328
|
+
onClick: () => openRow(s.subscriberType, s.subscriberId)
|
|
2186
2329
|
},
|
|
2187
|
-
|
|
2188
|
-
)
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
(
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2330
|
+
`${s.subscriberType}/${s.subscriberId}`
|
|
2331
|
+
)
|
|
2332
|
+
),
|
|
2333
|
+
td([
|
|
2334
|
+
(0, import_vue16.h)("strong", s.plan),
|
|
2335
|
+
" ",
|
|
2336
|
+
(0, import_vue16.h)("span", { style: styles.muted }, `\xB7 ${s.interval}`)
|
|
2337
|
+
]),
|
|
2338
|
+
td([
|
|
2339
|
+
pill(statusTone(s.status), s.status),
|
|
2340
|
+
s.cancelAtPeriodEnd && s.status !== "canceled" ? (0, import_vue16.h)("span", [" ", pill("warn", "cancels", false)]) : null
|
|
2341
|
+
]),
|
|
2342
|
+
td(
|
|
2343
|
+
end ? [(0, import_vue16.h)("span", { style: styles.muted }, end.label), ` ${end.date}`] : (0, import_vue16.h)("span", { style: styles.muted }, "\u2014"),
|
|
2344
|
+
{ whiteSpace: "nowrap" }
|
|
2202
2345
|
)
|
|
2203
|
-
)
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
style: { ...styles.button, marginTop: "8px" },
|
|
2209
|
-
onClick: () => void loadMoreLedger()
|
|
2210
|
-
},
|
|
2211
|
-
"Load more"
|
|
2212
|
-
) : null
|
|
2213
|
-
] : (0, import_vue14.h)(
|
|
2214
|
-
"p",
|
|
2215
|
-
{ style: styles.muted },
|
|
2216
|
-
"No wallet \u2014 billing has no wallet configuration."
|
|
2217
|
-
)
|
|
2218
|
-
] : null
|
|
2346
|
+
]);
|
|
2347
|
+
})
|
|
2348
|
+
),
|
|
2349
|
+
list.isLoading.value ? (0, import_vue16.h)("p", { style: styles.status }, "Loading\u2026") : null,
|
|
2350
|
+
list.hasMore.value && !list.isLoading.value ? loadMoreButton(() => void list.loadMore()) : null
|
|
2219
2351
|
]);
|
|
2220
2352
|
};
|
|
2221
2353
|
}
|
|
2222
2354
|
});
|
|
2223
2355
|
|
|
2224
2356
|
// src/screens/AuditScreen.ts
|
|
2225
|
-
var
|
|
2226
|
-
var
|
|
2227
|
-
var AuditScreen = (0,
|
|
2357
|
+
var import_vue_admin14 = require("@fonderie/vue-admin");
|
|
2358
|
+
var import_vue17 = require("vue");
|
|
2359
|
+
var AuditScreen = (0, import_vue17.defineComponent)({
|
|
2228
2360
|
name: "FonderieAuditScreen",
|
|
2229
2361
|
props: {
|
|
2230
2362
|
client: { type: Object, required: true },
|
|
2231
2363
|
pageSize: { type: Number, default: 50 }
|
|
2232
2364
|
},
|
|
2233
2365
|
setup(props) {
|
|
2234
|
-
const draft = (0,
|
|
2235
|
-
const query = (0,
|
|
2236
|
-
const { events, hasMore, isLoading, error, refresh, loadMore } = (0,
|
|
2366
|
+
const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "" });
|
|
2367
|
+
const query = (0, import_vue17.ref)({ limit: props.pageSize });
|
|
2368
|
+
const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin14.useAdminAudit)(
|
|
2237
2369
|
props.client,
|
|
2238
2370
|
query
|
|
2239
2371
|
);
|
|
2240
|
-
const field = (key, placeholder) => (0,
|
|
2372
|
+
const field = (key, placeholder) => (0, import_vue17.h)("input", {
|
|
2241
2373
|
value: draft.value[key],
|
|
2242
2374
|
onInput: (e) => draft.value = { ...draft.value, [key]: e.target.value },
|
|
2243
2375
|
placeholder,
|
|
2244
2376
|
style: styles.input,
|
|
2245
2377
|
"aria-label": placeholder
|
|
2246
2378
|
});
|
|
2247
|
-
return () => (0,
|
|
2379
|
+
return () => (0, import_vue17.h)("div", { style: styles.container }, [
|
|
2248
2380
|
pageHeader(
|
|
2249
2381
|
"Audit",
|
|
2250
2382
|
"What happened, across every workspace unless you name one. The chain's integrity verdict is on the Doctor page."
|
|
2251
2383
|
),
|
|
2252
|
-
(0,
|
|
2384
|
+
(0, import_vue17.h)(
|
|
2253
2385
|
"form",
|
|
2254
2386
|
{
|
|
2255
2387
|
style: styles.toolbar,
|
|
@@ -2268,19 +2400,19 @@ var AuditScreen = (0, import_vue15.defineComponent)({
|
|
|
2268
2400
|
field("workspaceId", "workspace id (all if empty)"),
|
|
2269
2401
|
field("type", "event type"),
|
|
2270
2402
|
field("actorId", "actor id"),
|
|
2271
|
-
(0,
|
|
2403
|
+
(0, import_vue17.h)(
|
|
2272
2404
|
"button",
|
|
2273
2405
|
{ type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
|
|
2274
2406
|
[icon("search", 14), "Filter"]
|
|
2275
2407
|
),
|
|
2276
|
-
|
|
2408
|
+
refreshButton2("Refresh", isLoading.value, () => void refresh())
|
|
2277
2409
|
]
|
|
2278
2410
|
),
|
|
2279
|
-
error.value ? (0,
|
|
2411
|
+
error.value ? (0, import_vue17.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
|
|
2280
2412
|
events.value.length === 0 && !isLoading.value ? empty("Nothing recorded for this filter", void 0, "audit") : table(
|
|
2281
2413
|
["When", "Type", "Workspace", "Actor", "Request"],
|
|
2282
2414
|
events.value.map(
|
|
2283
|
-
(e) => (0,
|
|
2415
|
+
(e) => (0, import_vue17.h)("tr", { key: e.id }, [
|
|
2284
2416
|
td(new Date(e.createdAt).toLocaleString(), styles.muted),
|
|
2285
2417
|
td(e.type, styles.mono),
|
|
2286
2418
|
td(String(e.payload["workspaceId"] ?? "\u2014"), styles.mono),
|
|
@@ -2289,7 +2421,7 @@ var AuditScreen = (0, import_vue15.defineComponent)({
|
|
|
2289
2421
|
])
|
|
2290
2422
|
)
|
|
2291
2423
|
),
|
|
2292
|
-
isLoading.value ? (0,
|
|
2424
|
+
isLoading.value ? (0, import_vue17.h)("p", { style: styles.status }, "Loading\u2026") : null,
|
|
2293
2425
|
hasMore.value && !isLoading.value ? loadMoreButton(() => void loadMore()) : null
|
|
2294
2426
|
]);
|
|
2295
2427
|
}
|
|
@@ -2312,7 +2444,7 @@ var NAV = [
|
|
|
2312
2444
|
group: "Money",
|
|
2313
2445
|
items: [
|
|
2314
2446
|
{ page: "catalog", label: "Catalog", needs: "billing" },
|
|
2315
|
-
{ page: "subscriber", label: "
|
|
2447
|
+
{ page: "subscriber", label: "Subscriptions", needs: "billing" }
|
|
2316
2448
|
]
|
|
2317
2449
|
},
|
|
2318
2450
|
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
@@ -2346,7 +2478,7 @@ var ICON = {
|
|
|
2346
2478
|
migrations: "migrations"
|
|
2347
2479
|
};
|
|
2348
2480
|
var NARROW = "(max-width: 820px)";
|
|
2349
|
-
var AdminShell = (0,
|
|
2481
|
+
var AdminShell = (0, import_vue18.defineComponent)({
|
|
2350
2482
|
name: "FonderieAdminShell",
|
|
2351
2483
|
props: {
|
|
2352
2484
|
client: { type: Object, required: true },
|
|
@@ -2380,60 +2512,74 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2380
2512
|
emits: { navigate: (_page) => true },
|
|
2381
2513
|
setup(props, { emit, slots }) {
|
|
2382
2514
|
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
2383
|
-
const narrow = (0,
|
|
2384
|
-
const drawer = (0,
|
|
2515
|
+
const narrow = (0, import_vue18.ref)(query?.matches ?? false);
|
|
2516
|
+
const drawer = (0, import_vue18.ref)(false);
|
|
2385
2517
|
const onMedia = (e) => narrow.value = e.matches;
|
|
2386
|
-
(0,
|
|
2387
|
-
(0,
|
|
2388
|
-
const own = (0,
|
|
2389
|
-
const current = (0,
|
|
2390
|
-
const editing = (0,
|
|
2518
|
+
(0, import_vue18.onMounted)(() => query?.addEventListener("change", onMedia));
|
|
2519
|
+
(0, import_vue18.onBeforeUnmount)(() => query?.removeEventListener("change", onMedia));
|
|
2520
|
+
const own = (0, import_vue18.ref)("attention");
|
|
2521
|
+
const current = (0, import_vue18.computed)(() => props.page ?? own.value);
|
|
2522
|
+
const editing = (0, import_vue18.ref)(null);
|
|
2523
|
+
const openUser = (0, import_vue18.ref)(void 0);
|
|
2391
2524
|
const go = (p) => {
|
|
2392
2525
|
editing.value = null;
|
|
2393
2526
|
emit("navigate", p);
|
|
2394
2527
|
if (props.page === void 0) own.value = p;
|
|
2395
2528
|
drawer.value = false;
|
|
2396
2529
|
};
|
|
2397
|
-
const back = (label) => (0,
|
|
2398
|
-
(0,
|
|
2530
|
+
const back = (label) => (0, import_vue18.h)("div", { style: { padding: "24px 40px 0" } }, [
|
|
2531
|
+
(0, import_vue18.h)(
|
|
2399
2532
|
"button",
|
|
2400
2533
|
{ type: "button", style: styles.buttonGhost, onClick: () => editing.value = null },
|
|
2401
2534
|
[icon("back", 14), label]
|
|
2402
2535
|
)
|
|
2403
2536
|
]);
|
|
2404
|
-
const missing = (text) => (0,
|
|
2537
|
+
const missing = (text) => (0, import_vue18.h)("p", { style: { ...styles.status, padding: "32px 40px" } }, text);
|
|
2405
2538
|
const body = () => {
|
|
2406
2539
|
const c = props.client;
|
|
2407
2540
|
switch (current.value) {
|
|
2408
2541
|
case "attention":
|
|
2409
|
-
return (0,
|
|
2542
|
+
return (0, import_vue18.h)(AttentionScreen, { client: c });
|
|
2410
2543
|
case "modules":
|
|
2411
|
-
return (0,
|
|
2544
|
+
return (0, import_vue18.h)(ModulesScreen, { client: c });
|
|
2412
2545
|
case "doctor":
|
|
2413
|
-
return (0,
|
|
2546
|
+
return (0, import_vue18.h)(DoctorScreen, { client: c });
|
|
2414
2547
|
case "environment":
|
|
2415
|
-
return (0,
|
|
2548
|
+
return (0, import_vue18.h)(EnvironmentScreen, { client: c });
|
|
2416
2549
|
case "routes":
|
|
2417
|
-
return (0,
|
|
2550
|
+
return (0, import_vue18.h)(RoutesScreen, { client: c });
|
|
2418
2551
|
case "tokens":
|
|
2419
|
-
return (0,
|
|
2552
|
+
return (0, import_vue18.h)(TokensScreen, { client: c });
|
|
2420
2553
|
case "operators":
|
|
2421
|
-
return (0,
|
|
2554
|
+
return (0, import_vue18.h)(OperatorsScreen, {
|
|
2422
2555
|
client: c,
|
|
2423
2556
|
...props.currentOperator ? { me: props.currentOperator } : {}
|
|
2424
2557
|
});
|
|
2425
2558
|
case "migrations":
|
|
2426
|
-
return (0,
|
|
2559
|
+
return (0, import_vue18.h)(MigrationsScreen, { client: c });
|
|
2427
2560
|
case "log":
|
|
2428
|
-
return (0,
|
|
2561
|
+
return (0, import_vue18.h)(AdminLogScreen, { client: c });
|
|
2429
2562
|
case "catalog":
|
|
2430
|
-
return props.billingClient ? (0,
|
|
2563
|
+
return props.billingClient ? (0, import_vue18.h)(CatalogScreen, { client: props.billingClient }) : missing("Pass a BillingAdminClient to see the catalog here.");
|
|
2431
2564
|
case "subscriber":
|
|
2432
|
-
return props.billingClient ? (0,
|
|
2565
|
+
return props.billingClient ? (0, import_vue18.h)(SubscriberScreen, {
|
|
2566
|
+
client: props.billingClient,
|
|
2567
|
+
...props.authClient ? {
|
|
2568
|
+
onOpenUser: (id) => {
|
|
2569
|
+
openUser.value = id;
|
|
2570
|
+
go("users");
|
|
2571
|
+
}
|
|
2572
|
+
} : {}
|
|
2573
|
+
}) : missing("Pass a BillingAdminClient to look up subscribers here.");
|
|
2433
2574
|
case "audit":
|
|
2434
|
-
return props.auditClient ? (0,
|
|
2575
|
+
return props.auditClient ? (0, import_vue18.h)(AuditScreen, { client: props.auditClient }) : missing("Pass an AuditAdminClient to see the audit trail here.");
|
|
2435
2576
|
case "users":
|
|
2436
|
-
return props.authClient ? (0,
|
|
2577
|
+
return props.authClient ? (0, import_vue18.h)(UsersScreen, {
|
|
2578
|
+
key: openUser.value ?? "list",
|
|
2579
|
+
client: props.authClient,
|
|
2580
|
+
...props.billingClient ? { billingClient: props.billingClient } : {},
|
|
2581
|
+
...openUser.value ? { openUserId: openUser.value } : {}
|
|
2582
|
+
}) : missing("Pass an AuthAdminClient to look up users here.");
|
|
2437
2583
|
case "settings": {
|
|
2438
2584
|
const cc = props.configClient;
|
|
2439
2585
|
if (!cc) return missing("Pass a ConfigAdminClient to manage config and secrets here.");
|
|
@@ -2441,7 +2587,7 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2441
2587
|
if (e && e.kind !== "template")
|
|
2442
2588
|
return [
|
|
2443
2589
|
back("Config & secrets"),
|
|
2444
|
-
(0,
|
|
2590
|
+
(0, import_vue18.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
|
|
2445
2591
|
client: cc,
|
|
2446
2592
|
kind: e.kind,
|
|
2447
2593
|
configKey: e.key,
|
|
@@ -2449,7 +2595,7 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2449
2595
|
onSaved: () => editing.value = null
|
|
2450
2596
|
})
|
|
2451
2597
|
];
|
|
2452
|
-
return (0,
|
|
2598
|
+
return (0, import_vue18.h)(import_vue_config_admin_screens.ConfigListScreen, {
|
|
2453
2599
|
client: cc,
|
|
2454
2600
|
...props.environment !== void 0 ? { environment: props.environment } : {},
|
|
2455
2601
|
"onSelect-config": (key) => editing.value = { kind: "config", key },
|
|
@@ -2466,13 +2612,13 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2466
2612
|
if (e?.kind === "template")
|
|
2467
2613
|
return [
|
|
2468
2614
|
back("Templates"),
|
|
2469
|
-
(0,
|
|
2615
|
+
(0, import_vue18.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
|
|
2470
2616
|
client: kc,
|
|
2471
2617
|
type: e.type,
|
|
2472
2618
|
onSaved: () => editing.value = null
|
|
2473
2619
|
})
|
|
2474
2620
|
];
|
|
2475
|
-
return (0,
|
|
2621
|
+
return (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateListScreen, {
|
|
2476
2622
|
client: kc,
|
|
2477
2623
|
"onSelect-template": (t2) => editing.value = { kind: "template", type: t2.type }
|
|
2478
2624
|
});
|
|
@@ -2482,11 +2628,11 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2482
2628
|
const visible = (i) => !i.needs || (i.needs === "operators" ? props.operators : i.needs === "config" ? props.configClient : i.needs === "courier" ? props.courierClient : i.needs === "auth" ? props.authClient : i.needs === "billing" ? props.billingClient : props.auditClient);
|
|
2483
2629
|
const brand = () => {
|
|
2484
2630
|
const production = props.envLabel ? /^prod/i.test(props.envLabel) : false;
|
|
2485
|
-
return (0,
|
|
2486
|
-
(0,
|
|
2487
|
-
(0,
|
|
2488
|
-
(0,
|
|
2489
|
-
props.envLabel ? (0,
|
|
2631
|
+
return (0, import_vue18.h)("div", { style: styles.navBrand }, [
|
|
2632
|
+
(0, import_vue18.h)("div", { style: styles.navMark }, [icon("mark", 15)]),
|
|
2633
|
+
(0, import_vue18.h)("div", { style: { minWidth: 0, flex: 1 } }, [
|
|
2634
|
+
(0, import_vue18.h)("div", { style: styles.navBrandTitle }, props.appName),
|
|
2635
|
+
props.envLabel ? (0, import_vue18.h)(
|
|
2490
2636
|
"span",
|
|
2491
2637
|
{
|
|
2492
2638
|
style: {
|
|
@@ -2503,7 +2649,7 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2503
2649
|
props.envLabel
|
|
2504
2650
|
) : null
|
|
2505
2651
|
]),
|
|
2506
|
-
narrow.value ? (0,
|
|
2652
|
+
narrow.value ? (0, import_vue18.h)(
|
|
2507
2653
|
"button",
|
|
2508
2654
|
{
|
|
2509
2655
|
type: "button",
|
|
@@ -2515,7 +2661,7 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2515
2661
|
) : null
|
|
2516
2662
|
]);
|
|
2517
2663
|
};
|
|
2518
|
-
const nav = () => (0,
|
|
2664
|
+
const nav = () => (0, import_vue18.h)(
|
|
2519
2665
|
"nav",
|
|
2520
2666
|
{
|
|
2521
2667
|
style: {
|
|
@@ -2526,17 +2672,17 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2526
2672
|
},
|
|
2527
2673
|
[
|
|
2528
2674
|
brand(),
|
|
2529
|
-
(0,
|
|
2675
|
+
(0, import_vue18.h)(
|
|
2530
2676
|
"div",
|
|
2531
2677
|
{ style: styles.navScroll },
|
|
2532
2678
|
NAV.map((g) => {
|
|
2533
2679
|
const items = g.items.filter(visible);
|
|
2534
2680
|
if (items.length === 0) return null;
|
|
2535
|
-
return (0,
|
|
2536
|
-
(0,
|
|
2681
|
+
return (0, import_vue18.h)("div", { key: g.group }, [
|
|
2682
|
+
(0, import_vue18.h)("div", { style: styles.navGroup }, g.group),
|
|
2537
2683
|
...items.map((i) => {
|
|
2538
2684
|
const active = current.value === i.page;
|
|
2539
|
-
return (0,
|
|
2685
|
+
return (0, import_vue18.h)(
|
|
2540
2686
|
"button",
|
|
2541
2687
|
{
|
|
2542
2688
|
key: i.page,
|
|
@@ -2544,7 +2690,10 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2544
2690
|
class: "fonderie-admin-nav-item",
|
|
2545
2691
|
style: { ...styles.navItem, ...active ? styles.navItemActive : {} },
|
|
2546
2692
|
"aria-current": active ? "page" : void 0,
|
|
2547
|
-
onClick: () =>
|
|
2693
|
+
onClick: () => {
|
|
2694
|
+
openUser.value = void 0;
|
|
2695
|
+
go(i.page);
|
|
2696
|
+
}
|
|
2548
2697
|
},
|
|
2549
2698
|
[icon(ICON[i.page], 16, active ? styles.navIconActive : void 0), i.label]
|
|
2550
2699
|
);
|
|
@@ -2552,18 +2701,18 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2552
2701
|
]);
|
|
2553
2702
|
})
|
|
2554
2703
|
),
|
|
2555
|
-
slots["footer"] ? (0,
|
|
2704
|
+
slots["footer"] ? (0, import_vue18.h)("div", { style: styles.navFooter }, slots["footer"]()) : null
|
|
2556
2705
|
]
|
|
2557
2706
|
);
|
|
2558
2707
|
return () => {
|
|
2559
2708
|
const title = NAV.flatMap((g) => g.items).find((i) => i.page === current.value)?.label ?? "";
|
|
2560
|
-
return (0,
|
|
2709
|
+
return (0, import_vue18.h)(
|
|
2561
2710
|
"div",
|
|
2562
2711
|
{ style: { ...styles.shell, ...narrow.value ? { flexDirection: "column" } : {} } },
|
|
2563
2712
|
[
|
|
2564
2713
|
...narrow.value ? [
|
|
2565
|
-
(0,
|
|
2566
|
-
(0,
|
|
2714
|
+
(0, import_vue18.h)("div", { style: styles.topbar }, [
|
|
2715
|
+
(0, import_vue18.h)(
|
|
2567
2716
|
"button",
|
|
2568
2717
|
{
|
|
2569
2718
|
type: "button",
|
|
@@ -2574,19 +2723,19 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2574
2723
|
},
|
|
2575
2724
|
[icon("menu")]
|
|
2576
2725
|
),
|
|
2577
|
-
(0,
|
|
2578
|
-
props.envLabel ? (0,
|
|
2726
|
+
(0, import_vue18.h)("span", { style: { fontWeight: 600, fontSize: "14px" } }, title),
|
|
2727
|
+
props.envLabel ? (0, import_vue18.h)("span", { style: { ...styles.badge, marginLeft: "auto" } }, props.envLabel) : null
|
|
2579
2728
|
]),
|
|
2580
|
-
drawer.value ? (0,
|
|
2729
|
+
drawer.value ? (0, import_vue18.h)("div", { style: styles.drawer }, [
|
|
2581
2730
|
nav(),
|
|
2582
|
-
(0,
|
|
2731
|
+
(0, import_vue18.h)("div", {
|
|
2583
2732
|
style: styles.scrim,
|
|
2584
2733
|
"aria-hidden": "true",
|
|
2585
2734
|
onClick: () => drawer.value = false
|
|
2586
2735
|
})
|
|
2587
2736
|
]) : null
|
|
2588
2737
|
] : [nav()],
|
|
2589
|
-
(0,
|
|
2738
|
+
(0, import_vue18.h)("main", { class: "fonderie-admin-main", style: styles.main }, body())
|
|
2590
2739
|
]
|
|
2591
2740
|
);
|
|
2592
2741
|
};
|
|
@@ -2605,6 +2754,7 @@ var AdminShell = (0, import_vue16.defineComponent)({
|
|
|
2605
2754
|
ModulesScreen,
|
|
2606
2755
|
OperatorsScreen,
|
|
2607
2756
|
RoutesScreen,
|
|
2757
|
+
SubscriberBilling,
|
|
2608
2758
|
SubscriberScreen,
|
|
2609
2759
|
TokensScreen,
|
|
2610
2760
|
UsersScreen
|