@fonderie/react-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 +607 -471
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -4
- package/dist/index.d.ts +20 -4
- package/dist/index.js +597 -462
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/screens/AdminShell.tsx
|
|
2
2
|
import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/react-config-admin-screens";
|
|
3
3
|
import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/react-courier-admin-screens";
|
|
4
|
-
import { useEffect, useState as
|
|
4
|
+
import { useEffect as useEffect2, useState as useState9 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/styles.ts
|
|
7
7
|
var t = {
|
|
@@ -1505,11 +1505,218 @@ import {
|
|
|
1505
1505
|
useAdminUserSessions,
|
|
1506
1506
|
useAdminUsers
|
|
1507
1507
|
} from "@fonderie/react-admin";
|
|
1508
|
-
import { useState as
|
|
1508
|
+
import { useState as useState6 } from "react";
|
|
1509
|
+
|
|
1510
|
+
// src/billing.ts
|
|
1511
|
+
import { useEffect, useState as useState4 } from "react";
|
|
1512
|
+
function periodEnd(s) {
|
|
1513
|
+
if (!s.currentPeriodEnd) return null;
|
|
1514
|
+
const date = new Date(s.currentPeriodEnd).toLocaleDateString();
|
|
1515
|
+
if (s.status === "canceled")
|
|
1516
|
+
return { label: new Date(s.currentPeriodEnd) < /* @__PURE__ */ new Date() ? "Ended" : "Ends", date };
|
|
1517
|
+
if (s.cancelAtPeriodEnd) return { label: "Ends", date };
|
|
1518
|
+
return { label: "Renews", date };
|
|
1519
|
+
}
|
|
1520
|
+
var statusTone = (status) => status === "active" ? "ok" : status === "trialing" ? "info" : status === "past_due" || status === "unpaid" ? "warn" : "neutral";
|
|
1521
|
+
function useSubscriptionIndex(client) {
|
|
1522
|
+
const [index, setIndex] = useState4(null);
|
|
1523
|
+
useEffect(() => {
|
|
1524
|
+
if (!client) return;
|
|
1525
|
+
let live = true;
|
|
1526
|
+
(async () => {
|
|
1527
|
+
const map = /* @__PURE__ */ new Map();
|
|
1528
|
+
let cursor;
|
|
1529
|
+
for (let page = 0; page < 20; page++) {
|
|
1530
|
+
const { result } = await client.listSubscriptions({
|
|
1531
|
+
limit: 100,
|
|
1532
|
+
...cursor ? { cursor } : {}
|
|
1533
|
+
});
|
|
1534
|
+
for (const s of result.subscriptions) {
|
|
1535
|
+
const key = `${s.subscriberType}/${s.subscriberId}`;
|
|
1536
|
+
if (!map.has(key)) map.set(key, s);
|
|
1537
|
+
}
|
|
1538
|
+
if (!result.nextCursor) break;
|
|
1539
|
+
cursor = result.nextCursor;
|
|
1540
|
+
}
|
|
1541
|
+
if (live) setIndex(map);
|
|
1542
|
+
})().catch(() => {
|
|
1543
|
+
if (live) setIndex(/* @__PURE__ */ new Map());
|
|
1544
|
+
});
|
|
1545
|
+
return () => {
|
|
1546
|
+
live = false;
|
|
1547
|
+
};
|
|
1548
|
+
}, [client]);
|
|
1549
|
+
return index;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// src/screens/SubscriberBilling.tsx
|
|
1553
|
+
import { useAdminSubscriber } from "@fonderie/react-admin";
|
|
1554
|
+
import { useState as useState5 } from "react";
|
|
1509
1555
|
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1510
|
-
function
|
|
1511
|
-
const
|
|
1512
|
-
const [
|
|
1556
|
+
function SubscriberBilling({ client, subscriber }) {
|
|
1557
|
+
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
|
|
1558
|
+
const [amount, setAmount] = useState5("");
|
|
1559
|
+
const [note, setNote] = useState5("");
|
|
1560
|
+
const [granted, setGranted] = useState5(null);
|
|
1561
|
+
if (isLoading && !wallet && !subscription) return /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading billing\u2026" });
|
|
1562
|
+
const end = subscription ? periodEnd(subscription) : null;
|
|
1563
|
+
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1564
|
+
error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1565
|
+
/* @__PURE__ */ jsxs11("div", { style: { ...styles.grid, marginBottom: 16 }, children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.card, children: [
|
|
1567
|
+
/* @__PURE__ */ jsx11("div", { style: styles.statLabel, children: "Plan" }),
|
|
1568
|
+
/* @__PURE__ */ jsxs11(
|
|
1569
|
+
"div",
|
|
1570
|
+
{
|
|
1571
|
+
style: {
|
|
1572
|
+
display: "flex",
|
|
1573
|
+
alignItems: "center",
|
|
1574
|
+
gap: 8,
|
|
1575
|
+
marginTop: 6,
|
|
1576
|
+
flexWrap: "wrap"
|
|
1577
|
+
},
|
|
1578
|
+
children: [
|
|
1579
|
+
/* @__PURE__ */ jsx11("strong", { style: { fontSize: 18 }, children: subscription ? subscription.plan : "Free" }),
|
|
1580
|
+
subscription ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1581
|
+
/* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
|
|
1582
|
+
"\xB7 ",
|
|
1583
|
+
subscription.interval
|
|
1584
|
+
] }),
|
|
1585
|
+
/* @__PURE__ */ jsx11(Pill, { tone: statusTone(subscription.status), children: subscription.status })
|
|
1586
|
+
] }) : /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "no subscription" })
|
|
1587
|
+
]
|
|
1588
|
+
}
|
|
1589
|
+
),
|
|
1590
|
+
end ? /* @__PURE__ */ jsxs11("div", { style: styles.statHint, children: [
|
|
1591
|
+
end.label,
|
|
1592
|
+
" ",
|
|
1593
|
+
end.date
|
|
1594
|
+
] }) : null,
|
|
1595
|
+
subscription?.providerSubscriptionId ? /* @__PURE__ */ jsx11("div", { style: { ...styles.statHint, fontFamily: "var(--fonderie-mono,monospace)" }, children: subscription.providerSubscriptionId }) : null
|
|
1596
|
+
] }),
|
|
1597
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.card, children: [
|
|
1598
|
+
/* @__PURE__ */ jsx11("div", { style: styles.statLabel, children: "Credits" }),
|
|
1599
|
+
wallet ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1600
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.statValue, children: [
|
|
1601
|
+
wallet.balance,
|
|
1602
|
+
" ",
|
|
1603
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 14, fontWeight: 500 }, children: wallet.currency })
|
|
1604
|
+
] }),
|
|
1605
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.statHint, children: [
|
|
1606
|
+
wallet.purchased ?? "0",
|
|
1607
|
+
" permanent \xB7 ",
|
|
1608
|
+
wallet.granted ?? "0",
|
|
1609
|
+
" plan allowance",
|
|
1610
|
+
wallet.grantedExpiresAt ? ` (expires ${new Date(wallet.grantedExpiresAt).toLocaleDateString()})` : ""
|
|
1611
|
+
] })
|
|
1612
|
+
] }) : /* @__PURE__ */ jsx11("div", { style: styles.statHint, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
1613
|
+
] })
|
|
1614
|
+
] }),
|
|
1615
|
+
wallet ? /* @__PURE__ */ jsxs11(
|
|
1616
|
+
"form",
|
|
1617
|
+
{
|
|
1618
|
+
style: {
|
|
1619
|
+
...styles.card,
|
|
1620
|
+
display: "flex",
|
|
1621
|
+
gap: 8,
|
|
1622
|
+
alignItems: "center",
|
|
1623
|
+
flexWrap: "wrap",
|
|
1624
|
+
marginBottom: 16
|
|
1625
|
+
},
|
|
1626
|
+
onSubmit: (e) => {
|
|
1627
|
+
e.preventDefault();
|
|
1628
|
+
const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
|
|
1629
|
+
void grant({
|
|
1630
|
+
amount: amount.trim(),
|
|
1631
|
+
...note ? { description: note } : {},
|
|
1632
|
+
idempotencyKey: key
|
|
1633
|
+
}).then(() => {
|
|
1634
|
+
setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
|
|
1635
|
+
setAmount("");
|
|
1636
|
+
setNote("");
|
|
1637
|
+
}).catch(() => {
|
|
1638
|
+
});
|
|
1639
|
+
},
|
|
1640
|
+
children: [
|
|
1641
|
+
/* @__PURE__ */ jsx11("strong", { style: { fontSize: 13.5, marginRight: 4 }, children: "Grant credits" }),
|
|
1642
|
+
/* @__PURE__ */ jsx11(
|
|
1643
|
+
"input",
|
|
1644
|
+
{
|
|
1645
|
+
value: amount,
|
|
1646
|
+
onChange: (e) => setAmount(e.target.value),
|
|
1647
|
+
placeholder: `amount (${wallet.currency})`,
|
|
1648
|
+
inputMode: "numeric",
|
|
1649
|
+
style: { ...styles.input, width: 150 },
|
|
1650
|
+
"aria-label": "Amount"
|
|
1651
|
+
}
|
|
1652
|
+
),
|
|
1653
|
+
/* @__PURE__ */ jsx11(
|
|
1654
|
+
"input",
|
|
1655
|
+
{
|
|
1656
|
+
value: note,
|
|
1657
|
+
onChange: (e) => setNote(e.target.value),
|
|
1658
|
+
placeholder: "reason (optional)",
|
|
1659
|
+
style: { ...styles.input, flex: 1, minWidth: 180 },
|
|
1660
|
+
"aria-label": "Reason"
|
|
1661
|
+
}
|
|
1662
|
+
),
|
|
1663
|
+
/* @__PURE__ */ jsxs11(
|
|
1664
|
+
"button",
|
|
1665
|
+
{
|
|
1666
|
+
type: "submit",
|
|
1667
|
+
style: styles.buttonPrimary,
|
|
1668
|
+
disabled: !/^\d+$/.test(amount.trim()),
|
|
1669
|
+
children: [
|
|
1670
|
+
/* @__PURE__ */ jsx11(Icon, { name: "plus", size: 14 }),
|
|
1671
|
+
"Grant"
|
|
1672
|
+
]
|
|
1673
|
+
}
|
|
1674
|
+
),
|
|
1675
|
+
granted ? /* @__PURE__ */ jsx11("span", { style: styles.ok, children: granted }) : null
|
|
1676
|
+
]
|
|
1677
|
+
}
|
|
1678
|
+
) : null,
|
|
1679
|
+
wallet ? ledger.length === 0 ? /* @__PURE__ */ jsx11(Empty, { icon: "subscriber", title: "No credit movements yet" }) : /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1680
|
+
/* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
|
|
1681
|
+
/* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1682
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "When" }),
|
|
1683
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Kind" }),
|
|
1684
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Amount" }),
|
|
1685
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Note" })
|
|
1686
|
+
] }) }),
|
|
1687
|
+
/* @__PURE__ */ jsx11("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1688
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted, whiteSpace: "nowrap" }, children: new Date(t2.createdAt).toLocaleString() }),
|
|
1689
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: t2.type }),
|
|
1690
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
|
|
1691
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
|
|
1692
|
+
] }, t2.id)) })
|
|
1693
|
+
] }),
|
|
1694
|
+
hasMoreLedger ? /* @__PURE__ */ jsx11(
|
|
1695
|
+
"button",
|
|
1696
|
+
{
|
|
1697
|
+
type: "button",
|
|
1698
|
+
style: { ...styles.button, marginTop: 8 },
|
|
1699
|
+
onClick: () => void loadMoreLedger(),
|
|
1700
|
+
children: "Load more"
|
|
1701
|
+
}
|
|
1702
|
+
) : null
|
|
1703
|
+
] }) : null
|
|
1704
|
+
] });
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// src/screens/UsersScreen.tsx
|
|
1708
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1709
|
+
function UsersScreen({
|
|
1710
|
+
client,
|
|
1711
|
+
pageSize = 50,
|
|
1712
|
+
billingClient,
|
|
1713
|
+
openUserId
|
|
1714
|
+
}) {
|
|
1715
|
+
const [input, setInput] = useState6("");
|
|
1716
|
+
const [selected, setSelected] = useState6(
|
|
1717
|
+
openUserId ? { id: openUserId } : null
|
|
1718
|
+
);
|
|
1719
|
+
const plans = useSubscriptionIndex(billingClient);
|
|
1513
1720
|
const list = useAdminUsers(client, { limit: pageSize });
|
|
1514
1721
|
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
|
|
1515
1722
|
client,
|
|
@@ -1518,20 +1725,20 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1518
1725
|
const userId = user?.id ?? null;
|
|
1519
1726
|
const sessions = useAdminUserSessions(client, userId);
|
|
1520
1727
|
const history = useAdminLoginHistory(client, userId, { limit: 20 });
|
|
1521
|
-
const yesNo = (v) => v ? /* @__PURE__ */
|
|
1728
|
+
const yesNo = (v) => v ? /* @__PURE__ */ jsx12(Pill, { tone: "ok", children: "yes" }) : /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "no" });
|
|
1522
1729
|
const clear = () => {
|
|
1523
1730
|
setSelected(null);
|
|
1524
1731
|
setInput("");
|
|
1525
1732
|
};
|
|
1526
|
-
return /* @__PURE__ */
|
|
1527
|
-
/* @__PURE__ */
|
|
1733
|
+
return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
|
|
1734
|
+
/* @__PURE__ */ jsx12(
|
|
1528
1735
|
PageHeader,
|
|
1529
1736
|
{
|
|
1530
1737
|
title: "Users",
|
|
1531
1738
|
lead: "Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
1532
1739
|
}
|
|
1533
1740
|
),
|
|
1534
|
-
/* @__PURE__ */
|
|
1741
|
+
/* @__PURE__ */ jsxs12(
|
|
1535
1742
|
"form",
|
|
1536
1743
|
{
|
|
1537
1744
|
style: styles.toolbar,
|
|
@@ -1541,7 +1748,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1541
1748
|
setSelected(email ? { email } : null);
|
|
1542
1749
|
},
|
|
1543
1750
|
children: [
|
|
1544
|
-
/* @__PURE__ */
|
|
1751
|
+
/* @__PURE__ */ jsx12(
|
|
1545
1752
|
"input",
|
|
1546
1753
|
{
|
|
1547
1754
|
type: "email",
|
|
@@ -1552,14 +1759,14 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1552
1759
|
"aria-label": "Email"
|
|
1553
1760
|
}
|
|
1554
1761
|
),
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
/* @__PURE__ */
|
|
1762
|
+
/* @__PURE__ */ jsxs12("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1763
|
+
/* @__PURE__ */ jsx12(Icon, { name: "search", size: 14 }),
|
|
1557
1764
|
"Look up"
|
|
1558
1765
|
] }),
|
|
1559
|
-
selected ? /* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1766
|
+
selected ? /* @__PURE__ */ jsxs12("button", { type: "button", style: styles.buttonGhost, onClick: clear, children: [
|
|
1767
|
+
/* @__PURE__ */ jsx12(Icon, { name: "back", size: 14 }),
|
|
1561
1768
|
"All users"
|
|
1562
|
-
] }) : /* @__PURE__ */
|
|
1769
|
+
] }) : /* @__PURE__ */ jsx12(
|
|
1563
1770
|
"button",
|
|
1564
1771
|
{
|
|
1565
1772
|
type: "button",
|
|
@@ -1572,18 +1779,19 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1572
1779
|
]
|
|
1573
1780
|
}
|
|
1574
1781
|
),
|
|
1575
|
-
error ? /* @__PURE__ */
|
|
1576
|
-
!selected ? /* @__PURE__ */
|
|
1577
|
-
list.error ? /* @__PURE__ */
|
|
1578
|
-
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */
|
|
1579
|
-
/* @__PURE__ */
|
|
1580
|
-
/* @__PURE__ */
|
|
1581
|
-
/* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
/* @__PURE__ */
|
|
1782
|
+
error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
|
|
1783
|
+
!selected ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1784
|
+
list.error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
1785
|
+
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx12(Empty, { icon: "users", title: "No users yet", children: "Sign-ups appear here as they happen." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
|
|
1786
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1787
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Email" }),
|
|
1788
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Name" }),
|
|
1789
|
+
billingClient ? /* @__PURE__ */ jsx12("th", { style: styles.th, children: "Plan" }) : null,
|
|
1790
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Created" }),
|
|
1791
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Status" })
|
|
1584
1792
|
] }) }),
|
|
1585
|
-
/* @__PURE__ */
|
|
1586
|
-
/* @__PURE__ */
|
|
1793
|
+
/* @__PURE__ */ jsx12("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1794
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsx12(
|
|
1587
1795
|
"button",
|
|
1588
1796
|
{
|
|
1589
1797
|
type: "button",
|
|
@@ -1592,17 +1800,28 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1592
1800
|
children: u.email
|
|
1593
1801
|
}
|
|
1594
1802
|
) }),
|
|
1595
|
-
/* @__PURE__ */
|
|
1596
|
-
/* @__PURE__ */
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1803
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "\u2014" }) }),
|
|
1804
|
+
billingClient ? /* @__PURE__ */ jsx12("td", { style: styles.td, children: (() => {
|
|
1805
|
+
const sub = plans?.get(`user/${u.id}`);
|
|
1806
|
+
if (!plans) return /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "\u2026" });
|
|
1807
|
+
if (!sub || sub.status === "canceled")
|
|
1808
|
+
return /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "free" });
|
|
1809
|
+
return /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1810
|
+
/* @__PURE__ */ jsx12("strong", { children: sub.plan }),
|
|
1811
|
+
" ",
|
|
1812
|
+
sub.status !== "active" ? /* @__PURE__ */ jsx12(Pill, { tone: statusTone(sub.status), children: sub.status }) : null
|
|
1813
|
+
] });
|
|
1814
|
+
})() }) : null,
|
|
1815
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
|
|
1816
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1817
|
+
u.suspended ? /* @__PURE__ */ jsx12(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1818
|
+
u.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "deleted" }) : null,
|
|
1819
|
+
!u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "ok", children: "active" }) : null
|
|
1601
1820
|
] })
|
|
1602
1821
|
] }, u.id)) })
|
|
1603
1822
|
] }),
|
|
1604
|
-
list.isLoading ? /* @__PURE__ */
|
|
1605
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */
|
|
1823
|
+
list.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1824
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx12(
|
|
1606
1825
|
"button",
|
|
1607
1826
|
{
|
|
1608
1827
|
type: "button",
|
|
@@ -1612,48 +1831,48 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1612
1831
|
}
|
|
1613
1832
|
) : null
|
|
1614
1833
|
] }) : null,
|
|
1615
|
-
user ? /* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1834
|
+
user ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1835
|
+
/* @__PURE__ */ jsxs12("h2", { style: styles.subtitle, children: [
|
|
1617
1836
|
user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
|
|
1618
1837
|
" ",
|
|
1619
|
-
user.suspended ? /* @__PURE__ */
|
|
1620
|
-
user.deletedAt ? /* @__PURE__ */
|
|
1838
|
+
user.suspended ? /* @__PURE__ */ jsx12(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1839
|
+
user.deletedAt ? /* @__PURE__ */ jsx12(Pill, { tone: "neutral", children: "deleted" }) : null
|
|
1621
1840
|
] }),
|
|
1622
|
-
/* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
/* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
1841
|
+
/* @__PURE__ */ jsx12("table", { style: styles.table, children: /* @__PURE__ */ jsxs12("tbody", { children: [
|
|
1842
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1843
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "id" }),
|
|
1844
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
|
|
1626
1845
|
] }),
|
|
1627
|
-
/* @__PURE__ */
|
|
1628
|
-
/* @__PURE__ */
|
|
1629
|
-
/* @__PURE__ */
|
|
1846
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1847
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "email" }),
|
|
1848
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1630
1849
|
user.email,
|
|
1631
1850
|
" \xB7 verified ",
|
|
1632
1851
|
yesNo(user.isEmailVerified)
|
|
1633
1852
|
] })
|
|
1634
1853
|
] }),
|
|
1635
|
-
/* @__PURE__ */
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1854
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1855
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "MFA" }),
|
|
1856
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
|
|
1638
1857
|
] }),
|
|
1639
|
-
/* @__PURE__ */
|
|
1640
|
-
/* @__PURE__ */
|
|
1641
|
-
/* @__PURE__ */
|
|
1858
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1859
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "provider" }),
|
|
1860
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1642
1861
|
user.provider || "password",
|
|
1643
1862
|
user.hasPassword ? "" : " \xB7 no password set"
|
|
1644
1863
|
] })
|
|
1645
1864
|
] }),
|
|
1646
|
-
/* @__PURE__ */
|
|
1647
|
-
/* @__PURE__ */
|
|
1648
|
-
/* @__PURE__ */
|
|
1865
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1866
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "last login" }),
|
|
1867
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx12("span", { style: styles.muted, children: "never" }) })
|
|
1649
1868
|
] }),
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
/* @__PURE__ */
|
|
1652
|
-
/* @__PURE__ */
|
|
1869
|
+
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
1870
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: "created" }),
|
|
1871
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
|
|
1653
1872
|
] })
|
|
1654
1873
|
] }) }),
|
|
1655
|
-
/* @__PURE__ */
|
|
1656
|
-
user.suspended ? /* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsxs12("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
|
|
1875
|
+
user.suspended ? /* @__PURE__ */ jsx12(
|
|
1657
1876
|
"button",
|
|
1658
1877
|
{
|
|
1659
1878
|
type: "button",
|
|
@@ -1662,7 +1881,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1662
1881
|
disabled: isLoading,
|
|
1663
1882
|
children: "Unsuspend"
|
|
1664
1883
|
}
|
|
1665
|
-
) : /* @__PURE__ */
|
|
1884
|
+
) : /* @__PURE__ */ jsx12(
|
|
1666
1885
|
"button",
|
|
1667
1886
|
{
|
|
1668
1887
|
type: "button",
|
|
@@ -1672,7 +1891,7 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1672
1891
|
children: "Suspend"
|
|
1673
1892
|
}
|
|
1674
1893
|
),
|
|
1675
|
-
/* @__PURE__ */
|
|
1894
|
+
/* @__PURE__ */ jsx12(
|
|
1676
1895
|
"button",
|
|
1677
1896
|
{
|
|
1678
1897
|
type: "button",
|
|
@@ -1683,38 +1902,48 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1683
1902
|
}
|
|
1684
1903
|
)
|
|
1685
1904
|
] }),
|
|
1686
|
-
/* @__PURE__ */
|
|
1687
|
-
|
|
1688
|
-
/* @__PURE__ */
|
|
1905
|
+
billingClient ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1906
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Plan & credits" }),
|
|
1907
|
+
/* @__PURE__ */ jsx12(
|
|
1908
|
+
SubscriberBilling,
|
|
1909
|
+
{
|
|
1910
|
+
client: billingClient,
|
|
1911
|
+
subscriber: { type: "user", id: user.id }
|
|
1912
|
+
}
|
|
1913
|
+
)
|
|
1914
|
+
] }) : null,
|
|
1915
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Live sessions" }),
|
|
1916
|
+
sessions.sessions.length === 0 ? /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx12("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs12("li", { style: styles.row, children: [
|
|
1917
|
+
/* @__PURE__ */ jsx12("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
|
|
1689
1918
|
" ",
|
|
1690
|
-
describeLocation(s.location) ? /* @__PURE__ */
|
|
1919
|
+
describeLocation(s.location) ? /* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
|
|
1691
1920
|
describeLocation(s.location),
|
|
1692
1921
|
" "
|
|
1693
1922
|
] }) : null,
|
|
1694
|
-
/* @__PURE__ */
|
|
1923
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: s.userAgent ?? "" }),
|
|
1695
1924
|
" ",
|
|
1696
|
-
/* @__PURE__ */
|
|
1925
|
+
/* @__PURE__ */ jsxs12("span", { style: styles.muted, children: [
|
|
1697
1926
|
"since ",
|
|
1698
1927
|
new Date(s.createdAt).toLocaleString()
|
|
1699
1928
|
] })
|
|
1700
1929
|
] }, s.id)) }),
|
|
1701
|
-
/* @__PURE__ */
|
|
1702
|
-
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */
|
|
1703
|
-
/* @__PURE__ */
|
|
1930
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
|
|
1931
|
+
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx12("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs12("li", { style: styles.row, children: [
|
|
1932
|
+
/* @__PURE__ */ jsx12(Pill, { tone: e.outcome === "success" ? "ok" : "bad", children: e.outcome }),
|
|
1704
1933
|
" ",
|
|
1705
|
-
/* @__PURE__ */
|
|
1934
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: e.method }),
|
|
1706
1935
|
" ",
|
|
1707
|
-
/* @__PURE__ */
|
|
1936
|
+
/* @__PURE__ */ jsx12("span", { style: styles.mono, children: e.ipAddress ?? "\u2014" }),
|
|
1708
1937
|
" ",
|
|
1709
|
-
describeLocation(e.location) ? /* @__PURE__ */
|
|
1710
|
-
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */
|
|
1938
|
+
describeLocation(e.location) ? /* @__PURE__ */ jsx12("span", { style: styles.muted, children: describeLocation(e.location) }) : null,
|
|
1939
|
+
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1711
1940
|
" ",
|
|
1712
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsx12(Pill, { tone: "warn", children: e.location.proxy ? "proxy/VPN" : "hosting" })
|
|
1713
1942
|
] }) : null,
|
|
1714
1943
|
" ",
|
|
1715
|
-
/* @__PURE__ */
|
|
1944
|
+
/* @__PURE__ */ jsx12("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
|
|
1716
1945
|
] }, e.id)) }),
|
|
1717
|
-
history.hasMore && !history.isLoading ? /* @__PURE__ */
|
|
1946
|
+
history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx12(
|
|
1718
1947
|
"button",
|
|
1719
1948
|
{
|
|
1720
1949
|
type: "button",
|
|
@@ -1729,24 +1958,24 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
1729
1958
|
|
|
1730
1959
|
// src/screens/CatalogScreen.tsx
|
|
1731
1960
|
import { useAdminCatalog } from "@fonderie/react-admin";
|
|
1732
|
-
import { Fragment as
|
|
1961
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1733
1962
|
function CatalogScreen({ client }) {
|
|
1734
1963
|
const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
|
|
1735
1964
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
1736
|
-
return /* @__PURE__ */
|
|
1737
|
-
/* @__PURE__ */
|
|
1965
|
+
return /* @__PURE__ */ jsxs13("div", { style: styles.container, children: [
|
|
1966
|
+
/* @__PURE__ */ jsx13(
|
|
1738
1967
|
PageHeader,
|
|
1739
1968
|
{
|
|
1740
1969
|
title: "Catalog",
|
|
1741
1970
|
lead: "What you sell: plans as configured in code, and as stored in the database, side by side.",
|
|
1742
|
-
actions: /* @__PURE__ */
|
|
1971
|
+
actions: /* @__PURE__ */ jsx13(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
1743
1972
|
}
|
|
1744
1973
|
),
|
|
1745
|
-
error ? /* @__PURE__ */
|
|
1746
|
-
isLoading && !catalog ? /* @__PURE__ */
|
|
1747
|
-
catalog ? /* @__PURE__ */
|
|
1748
|
-
/* @__PURE__ */
|
|
1749
|
-
/* @__PURE__ */
|
|
1974
|
+
error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1975
|
+
isLoading && !catalog ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1976
|
+
catalog ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1977
|
+
/* @__PURE__ */ jsx13("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Configured (code)" }),
|
|
1978
|
+
/* @__PURE__ */ jsx13(
|
|
1750
1979
|
"pre",
|
|
1751
1980
|
{
|
|
1752
1981
|
style: {
|
|
@@ -1760,32 +1989,32 @@ function CatalogScreen({ client }) {
|
|
|
1760
1989
|
children: JSON.stringify(catalog.configured, null, 2)
|
|
1761
1990
|
}
|
|
1762
1991
|
),
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
catalog.stored.length === 0 ? /* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
1766
|
-
/* @__PURE__ */
|
|
1767
|
-
/* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1769
|
-
/* @__PURE__ */
|
|
1770
|
-
/* @__PURE__ */
|
|
1771
|
-
/* @__PURE__ */
|
|
1772
|
-
/* @__PURE__ */
|
|
1992
|
+
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Stored (database)" }),
|
|
1993
|
+
catalog.stored.length === 0 ? /* @__PURE__ */ jsx13(Empty, { icon: "catalog", title: "No stored plans", children: "Plans are configured in code; nothing has been written to the database." }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
|
|
1994
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
1995
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Plan" }),
|
|
1996
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Tier" }),
|
|
1997
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Seats" }),
|
|
1998
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Monthly" }),
|
|
1999
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Yearly" }),
|
|
2000
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Trial" }),
|
|
2001
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th })
|
|
1773
2002
|
] }) }),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
1777
|
-
p.description ? /* @__PURE__ */
|
|
2003
|
+
/* @__PURE__ */ jsx13("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2004
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
2005
|
+
/* @__PURE__ */ jsx13("strong", { children: p.name }),
|
|
2006
|
+
p.description ? /* @__PURE__ */ jsx13("div", { style: styles.muted, children: p.description }) : null
|
|
1778
2007
|
] }),
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
2008
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.tier }),
|
|
2009
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.seats ?? "\u221E" }),
|
|
2010
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1782
2011
|
money(p.pricing?.monthly),
|
|
1783
2012
|
" ",
|
|
1784
2013
|
p.pricing?.currency ?? ""
|
|
1785
2014
|
] }),
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
2015
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: money(p.pricing?.yearly) }),
|
|
2016
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
|
|
2017
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: /* @__PURE__ */ jsx13(
|
|
1789
2018
|
"button",
|
|
1790
2019
|
{
|
|
1791
2020
|
type: "button",
|
|
@@ -1805,114 +2034,148 @@ function CatalogScreen({ client }) {
|
|
|
1805
2034
|
}
|
|
1806
2035
|
|
|
1807
2036
|
// src/screens/SubscriberScreen.tsx
|
|
1808
|
-
import {
|
|
1809
|
-
import { useState as
|
|
1810
|
-
import { Fragment as
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2037
|
+
import { useAdminSubscribers } from "@fonderie/react-admin";
|
|
2038
|
+
import { useState as useState7 } from "react";
|
|
2039
|
+
import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2040
|
+
var FILTERS = [
|
|
2041
|
+
{ label: "All", match: () => true },
|
|
2042
|
+
{ label: "Active", match: (s) => s === "active" },
|
|
2043
|
+
{ label: "Trialing", match: (s) => s === "trialing" },
|
|
2044
|
+
{ label: "Past due", match: (s) => s === "past_due" || s === "unpaid" },
|
|
2045
|
+
{ label: "Canceled", match: (s) => s === "canceled" }
|
|
2046
|
+
];
|
|
2047
|
+
function SubscriberScreen({ client, pageSize = 50, onOpenUser }) {
|
|
1815
2048
|
const list = useAdminSubscribers(client, { limit: pageSize });
|
|
1816
|
-
const
|
|
1817
|
-
const [
|
|
1818
|
-
const [
|
|
1819
|
-
const [
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2049
|
+
const [filter, setFilter] = useState7("All");
|
|
2050
|
+
const [open, setOpen] = useState7(null);
|
|
2051
|
+
const [type, setType] = useState7("workspace");
|
|
2052
|
+
const [idInput, setIdInput] = useState7("");
|
|
2053
|
+
if (open) {
|
|
2054
|
+
return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
|
|
2055
|
+
/* @__PURE__ */ jsx14(
|
|
2056
|
+
PageHeader,
|
|
2057
|
+
{
|
|
2058
|
+
title: `${open.type}/${open.id}`,
|
|
2059
|
+
lead: "Plan, credits and what moved.",
|
|
2060
|
+
actions: /* @__PURE__ */ jsxs14("button", { type: "button", style: styles.buttonGhost, onClick: () => setOpen(null), children: [
|
|
2061
|
+
/* @__PURE__ */ jsx14(Icon, { name: "back", size: 14 }),
|
|
2062
|
+
"All subscriptions"
|
|
2063
|
+
] })
|
|
2064
|
+
}
|
|
2065
|
+
),
|
|
2066
|
+
/* @__PURE__ */ jsx14(SubscriberBilling, { client, subscriber: open })
|
|
2067
|
+
] });
|
|
2068
|
+
}
|
|
2069
|
+
const match = FILTERS.find((f) => f.label === filter)?.match ?? (() => true);
|
|
2070
|
+
const rows = list.subscriptions.filter((s) => match(s.status));
|
|
2071
|
+
const openRow = (t2, id) => t2 === "user" && onOpenUser ? onOpenUser(id) : setOpen({ type: t2, id });
|
|
2072
|
+
return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
|
|
2073
|
+
/* @__PURE__ */ jsx14(
|
|
1828
2074
|
PageHeader,
|
|
1829
2075
|
{
|
|
1830
|
-
title: "
|
|
1831
|
-
lead: "Who is
|
|
2076
|
+
title: "Subscriptions",
|
|
2077
|
+
lead: "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.",
|
|
2078
|
+
actions: /* @__PURE__ */ jsx14(RefreshButton, { onClick: () => void list.refresh(), busy: list.isLoading })
|
|
1832
2079
|
}
|
|
1833
2080
|
),
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
placeholder: "subscriber id",
|
|
1863
|
-
style: { ...styles.input, minWidth: 280 },
|
|
1864
|
-
"aria-label": "Subscriber id"
|
|
1865
|
-
}
|
|
1866
|
-
),
|
|
1867
|
-
/* @__PURE__ */ jsxs13("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1868
|
-
/* @__PURE__ */ jsx13(Icon, { name: "search", size: 14 }),
|
|
1869
|
-
"Look up"
|
|
1870
|
-
] }),
|
|
1871
|
-
subscriber ? /* @__PURE__ */ jsxs13(
|
|
1872
|
-
"button",
|
|
1873
|
-
{
|
|
1874
|
-
type: "button",
|
|
1875
|
-
style: styles.button,
|
|
1876
|
-
onClick: () => {
|
|
1877
|
-
setSubscriber(null);
|
|
1878
|
-
setIdInput("");
|
|
1879
|
-
setGranted(null);
|
|
2081
|
+
/* @__PURE__ */ jsxs14("div", { style: { ...styles.toolbar, justifyContent: "space-between" }, children: [
|
|
2082
|
+
/* @__PURE__ */ jsx14(
|
|
2083
|
+
"fieldset",
|
|
2084
|
+
{
|
|
2085
|
+
style: {
|
|
2086
|
+
display: "flex",
|
|
2087
|
+
gap: 6,
|
|
2088
|
+
flexWrap: "wrap",
|
|
2089
|
+
border: "none",
|
|
2090
|
+
margin: 0,
|
|
2091
|
+
padding: 0,
|
|
2092
|
+
minWidth: 0
|
|
2093
|
+
},
|
|
2094
|
+
"aria-label": "Filter by status",
|
|
2095
|
+
children: FILTERS.map((f) => {
|
|
2096
|
+
const n = list.subscriptions.filter((s) => f.match(s.status)).length;
|
|
2097
|
+
const on = filter === f.label;
|
|
2098
|
+
return /* @__PURE__ */ jsxs14(
|
|
2099
|
+
"button",
|
|
2100
|
+
{
|
|
2101
|
+
type: "button",
|
|
2102
|
+
"aria-pressed": on,
|
|
2103
|
+
onClick: () => setFilter(f.label),
|
|
2104
|
+
style: { ...on ? styles.buttonPrimary : styles.button, height: 28 },
|
|
2105
|
+
children: [
|
|
2106
|
+
f.label,
|
|
2107
|
+
/* @__PURE__ */ jsx14("span", { style: { opacity: 0.7 }, children: n })
|
|
2108
|
+
]
|
|
1880
2109
|
},
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2110
|
+
f.label
|
|
2111
|
+
);
|
|
2112
|
+
})
|
|
2113
|
+
}
|
|
2114
|
+
),
|
|
2115
|
+
/* @__PURE__ */ jsxs14(
|
|
2116
|
+
"form",
|
|
2117
|
+
{
|
|
2118
|
+
style: { display: "flex", gap: 6 },
|
|
2119
|
+
onSubmit: (e) => {
|
|
2120
|
+
e.preventDefault();
|
|
2121
|
+
if (idInput.trim()) openRow(type, idInput.trim());
|
|
2122
|
+
},
|
|
2123
|
+
children: [
|
|
2124
|
+
/* @__PURE__ */ jsxs14(
|
|
2125
|
+
"select",
|
|
2126
|
+
{
|
|
2127
|
+
value: type,
|
|
2128
|
+
onChange: (e) => setType(e.target.value),
|
|
2129
|
+
style: styles.input,
|
|
2130
|
+
"aria-label": "Type",
|
|
2131
|
+
children: [
|
|
2132
|
+
/* @__PURE__ */ jsx14("option", { value: "workspace", children: "workspace" }),
|
|
2133
|
+
/* @__PURE__ */ jsx14("option", { value: "user", children: "user" })
|
|
2134
|
+
]
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
/* @__PURE__ */ jsx14(
|
|
2138
|
+
"input",
|
|
2139
|
+
{
|
|
2140
|
+
value: idInput,
|
|
2141
|
+
onChange: (e) => setIdInput(e.target.value),
|
|
2142
|
+
placeholder: "subscriber id",
|
|
2143
|
+
style: { ...styles.input, width: 220 },
|
|
2144
|
+
"aria-label": "Subscriber id"
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
/* @__PURE__ */ jsxs14("button", { type: "submit", style: styles.button, disabled: !idInput.trim(), children: [
|
|
2148
|
+
/* @__PURE__ */ jsx14(Icon, { name: "search", size: 14 }),
|
|
2149
|
+
"Open"
|
|
2150
|
+
] })
|
|
2151
|
+
]
|
|
2152
|
+
}
|
|
2153
|
+
)
|
|
2154
|
+
] }),
|
|
2155
|
+
list.error ? /* @__PURE__ */ jsx14("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
2156
|
+
rows.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx14(
|
|
2157
|
+
Empty,
|
|
2158
|
+
{
|
|
2159
|
+
icon: "subscriber",
|
|
2160
|
+
title: filter === "All" ? "No subscriptions yet" : `No ${filter.toLowerCase()} subscriptions`,
|
|
2161
|
+
children: "Subscriptions appear here once someone checks out."
|
|
1897
2162
|
}
|
|
1898
|
-
),
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
/* @__PURE__ */
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
/* @__PURE__ */ jsx13("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
1910
|
-
/* @__PURE__ */ jsx13("td", { style: styles.td, children: /* @__PURE__ */ jsxs13(
|
|
2163
|
+
) : /* @__PURE__ */ jsxs14("table", { style: styles.table, children: [
|
|
2164
|
+
/* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2165
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Subscriber" }),
|
|
2166
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Plan" }),
|
|
2167
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Status" }),
|
|
2168
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Renews / ends" })
|
|
2169
|
+
] }) }),
|
|
2170
|
+
/* @__PURE__ */ jsx14("tbody", { children: rows.map((s) => {
|
|
2171
|
+
const end = periodEnd(s);
|
|
2172
|
+
return /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2173
|
+
/* @__PURE__ */ jsx14("td", { style: styles.td, children: /* @__PURE__ */ jsxs14(
|
|
1911
2174
|
"button",
|
|
1912
2175
|
{
|
|
1913
2176
|
type: "button",
|
|
1914
2177
|
style: { ...styles.link, ...styles.mono },
|
|
1915
|
-
onClick: () =>
|
|
2178
|
+
onClick: () => openRow(s.subscriberType, s.subscriberId),
|
|
1916
2179
|
children: [
|
|
1917
2180
|
s.subscriberType,
|
|
1918
2181
|
"/",
|
|
@@ -1920,206 +2183,56 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
1920
2183
|
]
|
|
1921
2184
|
}
|
|
1922
2185
|
) }),
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
"
|
|
1926
|
-
|
|
2186
|
+
/* @__PURE__ */ jsxs14("td", { style: styles.td, children: [
|
|
2187
|
+
/* @__PURE__ */ jsx14("strong", { children: s.plan }),
|
|
2188
|
+
" ",
|
|
2189
|
+
/* @__PURE__ */ jsxs14("span", { style: styles.muted, children: [
|
|
2190
|
+
"\xB7 ",
|
|
2191
|
+
s.interval
|
|
2192
|
+
] })
|
|
1927
2193
|
] }),
|
|
1928
|
-
/* @__PURE__ */
|
|
1929
|
-
/* @__PURE__ */
|
|
1930
|
-
|
|
1931
|
-
{
|
|
1932
|
-
tone: s.status === "active" ? "ok" : s.status === "trialing" ? "info" : s.status === "past_due" ? "warn" : "neutral",
|
|
1933
|
-
children: s.status
|
|
1934
|
-
}
|
|
1935
|
-
),
|
|
1936
|
-
s.cancelAtPeriodEnd ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
2194
|
+
/* @__PURE__ */ jsxs14("td", { style: styles.td, children: [
|
|
2195
|
+
/* @__PURE__ */ jsx14(Pill, { tone: statusTone(s.status), children: s.status }),
|
|
2196
|
+
s.cancelAtPeriodEnd && s.status !== "canceled" ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1937
2197
|
" ",
|
|
1938
|
-
/* @__PURE__ */
|
|
2198
|
+
/* @__PURE__ */ jsx14(Pill, { tone: "warn", dot: false, children: "cancels" })
|
|
1939
2199
|
] }) : null
|
|
1940
2200
|
] }),
|
|
1941
|
-
/* @__PURE__ */
|
|
1942
|
-
|
|
1943
|
-
] }),
|
|
1944
|
-
list.isLoading ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1945
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx13(
|
|
1946
|
-
"button",
|
|
1947
|
-
{
|
|
1948
|
-
type: "button",
|
|
1949
|
-
style: { ...styles.button, marginTop: 8 },
|
|
1950
|
-
onClick: () => void list.loadMore(),
|
|
1951
|
-
children: "Load more"
|
|
1952
|
-
}
|
|
1953
|
-
) : null
|
|
1954
|
-
] }) : null,
|
|
1955
|
-
subscriber && !isLoading ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1956
|
-
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Subscription" }),
|
|
1957
|
-
subscription ? /* @__PURE__ */ jsx13("table", { style: styles.table, children: /* @__PURE__ */ jsxs13("tbody", { children: [
|
|
1958
|
-
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1959
|
-
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "plan" }),
|
|
1960
|
-
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1961
|
-
/* @__PURE__ */ jsx13("strong", { children: subscription.plan }),
|
|
1962
|
-
" \xB7 ",
|
|
1963
|
-
subscription.interval
|
|
1964
|
-
] })
|
|
1965
|
-
] }),
|
|
1966
|
-
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1967
|
-
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "status" }),
|
|
1968
|
-
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1969
|
-
subscription.status,
|
|
1970
|
-
subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
|
|
1971
|
-
] })
|
|
1972
|
-
] }),
|
|
1973
|
-
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1974
|
-
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "period" }),
|
|
1975
|
-
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1976
|
-
subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
|
|
2201
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, whiteSpace: "nowrap" }, children: end ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
2202
|
+
/* @__PURE__ */ jsx14("span", { style: styles.muted, children: end.label }),
|
|
1977
2203
|
" ",
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
style: {
|
|
1994
|
-
...styles.card,
|
|
1995
|
-
display: "flex",
|
|
1996
|
-
alignItems: "baseline",
|
|
1997
|
-
gap: 8,
|
|
1998
|
-
flexWrap: "wrap"
|
|
1999
|
-
},
|
|
2000
|
-
children: [
|
|
2001
|
-
/* @__PURE__ */ jsx13(
|
|
2002
|
-
"strong",
|
|
2003
|
-
{
|
|
2004
|
-
style: {
|
|
2005
|
-
fontSize: 22,
|
|
2006
|
-
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
2007
|
-
},
|
|
2008
|
-
children: wallet.balance
|
|
2009
|
-
}
|
|
2010
|
-
),
|
|
2011
|
-
" ",
|
|
2012
|
-
wallet.currency,
|
|
2013
|
-
" ",
|
|
2014
|
-
/* @__PURE__ */ jsxs13("span", { style: styles.muted, children: [
|
|
2015
|
-
"(granted ",
|
|
2016
|
-
wallet.granted ?? "0",
|
|
2017
|
-
" \xB7 purchased ",
|
|
2018
|
-
wallet.purchased ?? "0",
|
|
2019
|
-
") \xB7 minor units, precision ",
|
|
2020
|
-
wallet.precision
|
|
2021
|
-
] })
|
|
2022
|
-
]
|
|
2023
|
-
}
|
|
2024
|
-
),
|
|
2025
|
-
/* @__PURE__ */ jsxs13(
|
|
2026
|
-
"form",
|
|
2027
|
-
{
|
|
2028
|
-
style: styles.toolbar,
|
|
2029
|
-
onSubmit: (e) => {
|
|
2030
|
-
e.preventDefault();
|
|
2031
|
-
const key = `admin-grant-${subscriber.type}-${subscriber.id}-${Date.now()}`;
|
|
2032
|
-
void grant({
|
|
2033
|
-
amount: amount.trim(),
|
|
2034
|
-
...note ? { description: note } : {},
|
|
2035
|
-
idempotencyKey: key
|
|
2036
|
-
}).then(() => {
|
|
2037
|
-
setGranted(`Granted ${amount.trim()} ${wallet.currency}.`);
|
|
2038
|
-
setAmount("");
|
|
2039
|
-
setNote("");
|
|
2040
|
-
}).catch(() => {
|
|
2041
|
-
});
|
|
2042
|
-
},
|
|
2043
|
-
children: [
|
|
2044
|
-
/* @__PURE__ */ jsx13(
|
|
2045
|
-
"input",
|
|
2046
|
-
{
|
|
2047
|
-
value: amount,
|
|
2048
|
-
onChange: (e) => setAmount(e.target.value),
|
|
2049
|
-
placeholder: "amount (minor units)",
|
|
2050
|
-
style: styles.input,
|
|
2051
|
-
"aria-label": "Amount"
|
|
2052
|
-
}
|
|
2053
|
-
),
|
|
2054
|
-
/* @__PURE__ */ jsx13(
|
|
2055
|
-
"input",
|
|
2056
|
-
{
|
|
2057
|
-
value: note,
|
|
2058
|
-
onChange: (e) => setNote(e.target.value),
|
|
2059
|
-
placeholder: "reason (optional)",
|
|
2060
|
-
style: { ...styles.input, minWidth: 220 },
|
|
2061
|
-
"aria-label": "Reason"
|
|
2062
|
-
}
|
|
2063
|
-
),
|
|
2064
|
-
/* @__PURE__ */ jsxs13(
|
|
2065
|
-
"button",
|
|
2066
|
-
{
|
|
2067
|
-
type: "submit",
|
|
2068
|
-
style: styles.buttonPrimary,
|
|
2069
|
-
disabled: !/^\d+$/.test(amount.trim()),
|
|
2070
|
-
children: [
|
|
2071
|
-
/* @__PURE__ */ jsx13(Icon, { name: "plus", size: 14 }),
|
|
2072
|
-
"Grant"
|
|
2073
|
-
]
|
|
2074
|
-
}
|
|
2075
|
-
),
|
|
2076
|
-
granted ? /* @__PURE__ */ jsx13("span", { style: styles.ok, children: granted }) : null
|
|
2077
|
-
]
|
|
2078
|
-
}
|
|
2079
|
-
),
|
|
2080
|
-
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Ledger" }),
|
|
2081
|
-
ledger.length === 0 ? /* @__PURE__ */ jsx13(Empty, { icon: "subscriber", title: "Nothing moved yet" }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
|
|
2082
|
-
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2083
|
-
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "When" }),
|
|
2084
|
-
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Kind" }),
|
|
2085
|
-
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Amount" }),
|
|
2086
|
-
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Note" })
|
|
2087
|
-
] }) }),
|
|
2088
|
-
/* @__PURE__ */ jsx13("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2089
|
-
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t2.createdAt).toLocaleString() }),
|
|
2090
|
-
/* @__PURE__ */ jsx13("td", { style: styles.td, children: t2.type }),
|
|
2091
|
-
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
|
|
2092
|
-
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
|
|
2093
|
-
] }, t2.id)) })
|
|
2094
|
-
] }),
|
|
2095
|
-
hasMoreLedger ? /* @__PURE__ */ jsx13(
|
|
2096
|
-
"button",
|
|
2097
|
-
{
|
|
2098
|
-
type: "button",
|
|
2099
|
-
style: { ...styles.button, marginTop: 8 },
|
|
2100
|
-
onClick: () => void loadMoreLedger(),
|
|
2101
|
-
children: "Load more"
|
|
2102
|
-
}
|
|
2103
|
-
) : null
|
|
2104
|
-
] }) : /* @__PURE__ */ jsx13("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
2105
|
-
] }) : null
|
|
2204
|
+
end.date
|
|
2205
|
+
] }) : /* @__PURE__ */ jsx14("span", { style: styles.muted, children: "\u2014" }) })
|
|
2206
|
+
] }, s.id);
|
|
2207
|
+
}) })
|
|
2208
|
+
] }),
|
|
2209
|
+
list.isLoading ? /* @__PURE__ */ jsx14("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
2210
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx14(
|
|
2211
|
+
"button",
|
|
2212
|
+
{
|
|
2213
|
+
type: "button",
|
|
2214
|
+
style: { ...styles.button, marginTop: 8 },
|
|
2215
|
+
onClick: () => void list.loadMore(),
|
|
2216
|
+
children: "Load more"
|
|
2217
|
+
}
|
|
2218
|
+
) : null
|
|
2106
2219
|
] });
|
|
2107
2220
|
}
|
|
2108
2221
|
|
|
2109
2222
|
// src/screens/AuditScreen.tsx
|
|
2110
2223
|
import { useAdminAudit } from "@fonderie/react-admin";
|
|
2111
|
-
import { useState as
|
|
2112
|
-
import { jsx as
|
|
2224
|
+
import { useState as useState8 } from "react";
|
|
2225
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2113
2226
|
function AuditScreen({ client, pageSize = 50 }) {
|
|
2114
|
-
const [draft, setDraft] =
|
|
2115
|
-
const [filter, setFilter] =
|
|
2227
|
+
const [draft, setDraft] = useState8({ workspaceId: "", type: "", actorId: "" });
|
|
2228
|
+
const [filter, setFilter] = useState8(
|
|
2116
2229
|
{}
|
|
2117
2230
|
);
|
|
2118
2231
|
const { events, hasMore, isLoading, error, refresh, loadMore } = useAdminAudit(client, {
|
|
2119
2232
|
...filter,
|
|
2120
2233
|
limit: pageSize
|
|
2121
2234
|
});
|
|
2122
|
-
const field = (key, placeholder) => /* @__PURE__ */
|
|
2235
|
+
const field = (key, placeholder) => /* @__PURE__ */ jsx15(
|
|
2123
2236
|
"input",
|
|
2124
2237
|
{
|
|
2125
2238
|
value: draft[key],
|
|
@@ -2129,15 +2242,15 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
2129
2242
|
"aria-label": placeholder
|
|
2130
2243
|
}
|
|
2131
2244
|
);
|
|
2132
|
-
return /* @__PURE__ */
|
|
2133
|
-
/* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ jsxs15("div", { style: styles.container, children: [
|
|
2246
|
+
/* @__PURE__ */ jsx15(
|
|
2134
2247
|
PageHeader,
|
|
2135
2248
|
{
|
|
2136
2249
|
title: "Audit",
|
|
2137
2250
|
lead: "What happened, across every workspace unless you name one. The chain's integrity verdict is on the Doctor page."
|
|
2138
2251
|
}
|
|
2139
2252
|
),
|
|
2140
|
-
/* @__PURE__ */
|
|
2253
|
+
/* @__PURE__ */ jsxs15(
|
|
2141
2254
|
"form",
|
|
2142
2255
|
{
|
|
2143
2256
|
style: styles.toolbar,
|
|
@@ -2153,11 +2266,11 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
2153
2266
|
field("workspaceId", "workspace id (all if empty)"),
|
|
2154
2267
|
field("type", "event type"),
|
|
2155
2268
|
field("actorId", "actor id"),
|
|
2156
|
-
/* @__PURE__ */
|
|
2157
|
-
/* @__PURE__ */
|
|
2269
|
+
/* @__PURE__ */ jsxs15("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
2270
|
+
/* @__PURE__ */ jsx15(Icon, { name: "search", size: 14 }),
|
|
2158
2271
|
"Filter"
|
|
2159
2272
|
] }),
|
|
2160
|
-
/* @__PURE__ */
|
|
2273
|
+
/* @__PURE__ */ jsx15(
|
|
2161
2274
|
"button",
|
|
2162
2275
|
{
|
|
2163
2276
|
type: "button",
|
|
@@ -2170,25 +2283,25 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
2170
2283
|
]
|
|
2171
2284
|
}
|
|
2172
2285
|
),
|
|
2173
|
-
error ? /* @__PURE__ */
|
|
2174
|
-
events.length === 0 && !isLoading ? /* @__PURE__ */
|
|
2175
|
-
/* @__PURE__ */
|
|
2176
|
-
/* @__PURE__ */
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2180
|
-
/* @__PURE__ */
|
|
2286
|
+
error ? /* @__PURE__ */ jsx15("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
2287
|
+
events.length === 0 && !isLoading ? /* @__PURE__ */ jsx15(Empty, { icon: "audit", title: "Nothing recorded for this filter" }) : /* @__PURE__ */ jsxs15("table", { style: styles.table, children: [
|
|
2288
|
+
/* @__PURE__ */ jsx15("thead", { children: /* @__PURE__ */ jsxs15("tr", { children: [
|
|
2289
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "When" }),
|
|
2290
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Type" }),
|
|
2291
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Workspace" }),
|
|
2292
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Actor" }),
|
|
2293
|
+
/* @__PURE__ */ jsx15("th", { style: styles.th, children: "Request" })
|
|
2181
2294
|
] }) }),
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2295
|
+
/* @__PURE__ */ jsx15("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs15("tr", { children: [
|
|
2296
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
|
|
2297
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
|
|
2298
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
|
|
2299
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
|
|
2300
|
+
/* @__PURE__ */ jsx15("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
|
|
2188
2301
|
] }, e.id)) })
|
|
2189
2302
|
] }),
|
|
2190
|
-
isLoading ? /* @__PURE__ */
|
|
2191
|
-
hasMore && !isLoading ? /* @__PURE__ */
|
|
2303
|
+
isLoading ? /* @__PURE__ */ jsx15("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
2304
|
+
hasMore && !isLoading ? /* @__PURE__ */ jsx15(
|
|
2192
2305
|
"button",
|
|
2193
2306
|
{
|
|
2194
2307
|
type: "button",
|
|
@@ -2201,7 +2314,7 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
2201
2314
|
}
|
|
2202
2315
|
|
|
2203
2316
|
// src/screens/AdminShell.tsx
|
|
2204
|
-
import { Fragment as
|
|
2317
|
+
import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2205
2318
|
var ICON = {
|
|
2206
2319
|
attention: "attention",
|
|
2207
2320
|
modules: "modules",
|
|
@@ -2222,8 +2335,8 @@ var ICON = {
|
|
|
2222
2335
|
var NARROW = "(max-width: 820px)";
|
|
2223
2336
|
function useNarrow() {
|
|
2224
2337
|
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
2225
|
-
const [narrow, setNarrow] =
|
|
2226
|
-
|
|
2338
|
+
const [narrow, setNarrow] = useState9(() => query?.matches ?? false);
|
|
2339
|
+
useEffect2(() => {
|
|
2227
2340
|
if (!query) return;
|
|
2228
2341
|
const on = (e) => setNarrow(e.matches);
|
|
2229
2342
|
query.addEventListener("change", on);
|
|
@@ -2247,7 +2360,7 @@ var NAV = [
|
|
|
2247
2360
|
group: "Money",
|
|
2248
2361
|
items: [
|
|
2249
2362
|
{ page: "catalog", label: "Catalog", needs: "billing" },
|
|
2250
|
-
{ page: "subscriber", label: "
|
|
2363
|
+
{ page: "subscriber", label: "Subscriptions", needs: "billing" }
|
|
2251
2364
|
]
|
|
2252
2365
|
},
|
|
2253
2366
|
{ group: "Settings", items: [{ page: "settings", label: "Config & secrets", needs: "config" }] },
|
|
@@ -2280,15 +2393,16 @@ function AdminShell({
|
|
|
2280
2393
|
currentOperator
|
|
2281
2394
|
}) {
|
|
2282
2395
|
const narrow = useNarrow();
|
|
2283
|
-
const [drawer, setDrawer] =
|
|
2284
|
-
const [own, setOwn] =
|
|
2396
|
+
const [drawer, setDrawer] = useState9(false);
|
|
2397
|
+
const [own, setOwn] = useState9("attention");
|
|
2398
|
+
const [openUser, setOpenUser] = useState9(void 0);
|
|
2285
2399
|
const current = page ?? own;
|
|
2286
2400
|
const go = (p) => {
|
|
2287
2401
|
if (onNavigate) onNavigate(p);
|
|
2288
2402
|
if (page === void 0) setOwn(p);
|
|
2289
2403
|
setDrawer(false);
|
|
2290
2404
|
};
|
|
2291
|
-
const [editing, setEditing] =
|
|
2405
|
+
const [editing, setEditing] = useState9(null);
|
|
2292
2406
|
const has = {
|
|
2293
2407
|
config: Boolean(configClient),
|
|
2294
2408
|
courier: Boolean(courierClient),
|
|
@@ -2300,51 +2414,70 @@ function AdminShell({
|
|
|
2300
2414
|
let body;
|
|
2301
2415
|
switch (current) {
|
|
2302
2416
|
case "attention":
|
|
2303
|
-
body = /* @__PURE__ */
|
|
2417
|
+
body = /* @__PURE__ */ jsx16(AttentionScreen, { client });
|
|
2304
2418
|
break;
|
|
2305
2419
|
case "modules":
|
|
2306
|
-
body = /* @__PURE__ */
|
|
2420
|
+
body = /* @__PURE__ */ jsx16(ModulesScreen, { client });
|
|
2307
2421
|
break;
|
|
2308
2422
|
case "doctor":
|
|
2309
|
-
body = /* @__PURE__ */
|
|
2423
|
+
body = /* @__PURE__ */ jsx16(DoctorScreen, { client });
|
|
2310
2424
|
break;
|
|
2311
2425
|
case "environment":
|
|
2312
|
-
body = /* @__PURE__ */
|
|
2426
|
+
body = /* @__PURE__ */ jsx16(EnvironmentScreen, { client });
|
|
2313
2427
|
break;
|
|
2314
2428
|
case "routes":
|
|
2315
|
-
body = /* @__PURE__ */
|
|
2429
|
+
body = /* @__PURE__ */ jsx16(RoutesScreen, { client });
|
|
2316
2430
|
break;
|
|
2317
2431
|
case "tokens":
|
|
2318
|
-
body = /* @__PURE__ */
|
|
2432
|
+
body = /* @__PURE__ */ jsx16(TokensScreen, { client });
|
|
2319
2433
|
break;
|
|
2320
2434
|
case "operators":
|
|
2321
|
-
body = /* @__PURE__ */
|
|
2435
|
+
body = /* @__PURE__ */ jsx16(OperatorsScreen, { client, me: currentOperator });
|
|
2322
2436
|
break;
|
|
2323
2437
|
case "migrations":
|
|
2324
|
-
body = /* @__PURE__ */
|
|
2438
|
+
body = /* @__PURE__ */ jsx16(MigrationsScreen, { client });
|
|
2325
2439
|
break;
|
|
2326
2440
|
case "log":
|
|
2327
|
-
body = /* @__PURE__ */
|
|
2441
|
+
body = /* @__PURE__ */ jsx16(AdminLogScreen, { client });
|
|
2328
2442
|
break;
|
|
2329
2443
|
case "catalog":
|
|
2330
|
-
body = billingClient ? /* @__PURE__ */
|
|
2444
|
+
body = billingClient ? /* @__PURE__ */ jsx16(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to see the catalog here." });
|
|
2331
2445
|
break;
|
|
2332
2446
|
case "subscriber":
|
|
2333
|
-
body = billingClient ? /* @__PURE__ */
|
|
2447
|
+
body = billingClient ? /* @__PURE__ */ jsx16(
|
|
2448
|
+
SubscriberScreen,
|
|
2449
|
+
{
|
|
2450
|
+
client: billingClient,
|
|
2451
|
+
...authClient ? {
|
|
2452
|
+
onOpenUser: (id) => {
|
|
2453
|
+
setOpenUser(id);
|
|
2454
|
+
go("users");
|
|
2455
|
+
}
|
|
2456
|
+
} : {}
|
|
2457
|
+
}
|
|
2458
|
+
) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to look up subscribers here." });
|
|
2334
2459
|
break;
|
|
2335
2460
|
case "audit":
|
|
2336
|
-
body = auditClient ? /* @__PURE__ */
|
|
2461
|
+
body = auditClient ? /* @__PURE__ */ jsx16(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuditAdminClient to see the audit trail here." });
|
|
2337
2462
|
break;
|
|
2338
2463
|
case "users":
|
|
2339
|
-
body = authClient ? /* @__PURE__ */
|
|
2464
|
+
body = authClient ? /* @__PURE__ */ jsx16(
|
|
2465
|
+
UsersScreen,
|
|
2466
|
+
{
|
|
2467
|
+
client: authClient,
|
|
2468
|
+
billingClient,
|
|
2469
|
+
openUserId: openUser
|
|
2470
|
+
},
|
|
2471
|
+
openUser ?? "list"
|
|
2472
|
+
) : /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuthAdminClient to look up users here." });
|
|
2340
2473
|
break;
|
|
2341
2474
|
case "settings":
|
|
2342
|
-
body = !configClient ? /* @__PURE__ */
|
|
2343
|
-
/* @__PURE__ */
|
|
2344
|
-
/* @__PURE__ */
|
|
2475
|
+
body = !configClient ? /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2476
|
+
/* @__PURE__ */ jsx16("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs16("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2477
|
+
/* @__PURE__ */ jsx16(Icon, { name: "back", size: 14 }),
|
|
2345
2478
|
"Config & secrets"
|
|
2346
2479
|
] }) }),
|
|
2347
|
-
/* @__PURE__ */
|
|
2480
|
+
/* @__PURE__ */ jsx16(
|
|
2348
2481
|
ConfigEditorScreen,
|
|
2349
2482
|
{
|
|
2350
2483
|
client: configClient,
|
|
@@ -2354,7 +2487,7 @@ function AdminShell({
|
|
|
2354
2487
|
onSaved: () => setEditing(null)
|
|
2355
2488
|
}
|
|
2356
2489
|
)
|
|
2357
|
-
] }) : /* @__PURE__ */
|
|
2490
|
+
] }) : /* @__PURE__ */ jsx16(
|
|
2358
2491
|
ConfigListScreen,
|
|
2359
2492
|
{
|
|
2360
2493
|
client: configClient,
|
|
@@ -2367,12 +2500,12 @@ function AdminShell({
|
|
|
2367
2500
|
);
|
|
2368
2501
|
break;
|
|
2369
2502
|
case "templates":
|
|
2370
|
-
body = !courierClient ? /* @__PURE__ */
|
|
2371
|
-
/* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2503
|
+
body = !courierClient ? /* @__PURE__ */ jsx16("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2504
|
+
/* @__PURE__ */ jsx16("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs16("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2505
|
+
/* @__PURE__ */ jsx16(Icon, { name: "back", size: 14 }),
|
|
2373
2506
|
"Templates"
|
|
2374
2507
|
] }) }),
|
|
2375
|
-
/* @__PURE__ */
|
|
2508
|
+
/* @__PURE__ */ jsx16(
|
|
2376
2509
|
TemplateEditorScreen,
|
|
2377
2510
|
{
|
|
2378
2511
|
client: courierClient,
|
|
@@ -2380,7 +2513,7 @@ function AdminShell({
|
|
|
2380
2513
|
onSaved: () => setEditing(null)
|
|
2381
2514
|
}
|
|
2382
2515
|
)
|
|
2383
|
-
] }) : /* @__PURE__ */
|
|
2516
|
+
] }) : /* @__PURE__ */ jsx16(
|
|
2384
2517
|
TemplateListScreen,
|
|
2385
2518
|
{
|
|
2386
2519
|
client: courierClient,
|
|
@@ -2390,11 +2523,11 @@ function AdminShell({
|
|
|
2390
2523
|
break;
|
|
2391
2524
|
}
|
|
2392
2525
|
const production = envLabel ? /^prod/i.test(envLabel) : false;
|
|
2393
|
-
const brand = /* @__PURE__ */
|
|
2394
|
-
/* @__PURE__ */
|
|
2395
|
-
/* @__PURE__ */
|
|
2396
|
-
/* @__PURE__ */
|
|
2397
|
-
envLabel ? /* @__PURE__ */
|
|
2526
|
+
const brand = /* @__PURE__ */ jsxs16("div", { style: styles.navBrand, children: [
|
|
2527
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navMark, children: /* @__PURE__ */ jsx16(Icon, { name: "mark", size: 15 }) }),
|
|
2528
|
+
/* @__PURE__ */ jsxs16("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
2529
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navBrandTitle, children: appName }),
|
|
2530
|
+
envLabel ? /* @__PURE__ */ jsx16(
|
|
2398
2531
|
"span",
|
|
2399
2532
|
{
|
|
2400
2533
|
style: {
|
|
@@ -2411,18 +2544,18 @@ function AdminShell({
|
|
|
2411
2544
|
}
|
|
2412
2545
|
) : null
|
|
2413
2546
|
] }),
|
|
2414
|
-
narrow ? /* @__PURE__ */
|
|
2547
|
+
narrow ? /* @__PURE__ */ jsx16(
|
|
2415
2548
|
"button",
|
|
2416
2549
|
{
|
|
2417
2550
|
type: "button",
|
|
2418
2551
|
style: styles.buttonGhost,
|
|
2419
2552
|
"aria-label": "Close menu",
|
|
2420
2553
|
onClick: () => setDrawer(false),
|
|
2421
|
-
children: /* @__PURE__ */
|
|
2554
|
+
children: /* @__PURE__ */ jsx16(Icon, { name: "close" })
|
|
2422
2555
|
}
|
|
2423
2556
|
) : null
|
|
2424
2557
|
] });
|
|
2425
|
-
const nav = /* @__PURE__ */
|
|
2558
|
+
const nav = /* @__PURE__ */ jsxs16(
|
|
2426
2559
|
"nav",
|
|
2427
2560
|
{
|
|
2428
2561
|
style: {
|
|
@@ -2432,14 +2565,14 @@ function AdminShell({
|
|
|
2432
2565
|
"aria-label": "Admin",
|
|
2433
2566
|
children: [
|
|
2434
2567
|
brand,
|
|
2435
|
-
/* @__PURE__ */
|
|
2568
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navScroll, children: NAV.map((g) => {
|
|
2436
2569
|
const items = g.items.filter((i) => !i.needs || has[i.needs]);
|
|
2437
2570
|
if (items.length === 0) return null;
|
|
2438
|
-
return /* @__PURE__ */
|
|
2439
|
-
/* @__PURE__ */
|
|
2571
|
+
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
2572
|
+
/* @__PURE__ */ jsx16("div", { style: styles.navGroup, children: g.group }),
|
|
2440
2573
|
items.map((i) => {
|
|
2441
2574
|
const active = current === i.page;
|
|
2442
|
-
return /* @__PURE__ */
|
|
2575
|
+
return /* @__PURE__ */ jsxs16(
|
|
2443
2576
|
"button",
|
|
2444
2577
|
{
|
|
2445
2578
|
type: "button",
|
|
@@ -2448,10 +2581,11 @@ function AdminShell({
|
|
|
2448
2581
|
"aria-current": active ? "page" : void 0,
|
|
2449
2582
|
onClick: () => {
|
|
2450
2583
|
setEditing(null);
|
|
2584
|
+
setOpenUser(void 0);
|
|
2451
2585
|
go(i.page);
|
|
2452
2586
|
},
|
|
2453
2587
|
children: [
|
|
2454
|
-
/* @__PURE__ */
|
|
2588
|
+
/* @__PURE__ */ jsx16(Icon, { name: ICON[i.page], style: active ? styles.navIconActive : void 0 }),
|
|
2455
2589
|
i.label
|
|
2456
2590
|
]
|
|
2457
2591
|
},
|
|
@@ -2460,15 +2594,15 @@ function AdminShell({
|
|
|
2460
2594
|
})
|
|
2461
2595
|
] }, g.group);
|
|
2462
2596
|
}) }),
|
|
2463
|
-
footer ? /* @__PURE__ */
|
|
2597
|
+
footer ? /* @__PURE__ */ jsx16("div", { style: styles.navFooter, children: footer }) : null
|
|
2464
2598
|
]
|
|
2465
2599
|
}
|
|
2466
2600
|
);
|
|
2467
2601
|
const title = NAV.flatMap((g) => g.items).find((i) => i.page === current)?.label ?? "";
|
|
2468
|
-
return /* @__PURE__ */
|
|
2469
|
-
narrow ? /* @__PURE__ */
|
|
2470
|
-
/* @__PURE__ */
|
|
2471
|
-
/* @__PURE__ */
|
|
2602
|
+
return /* @__PURE__ */ jsxs16("div", { style: { ...styles.shell, ...narrow ? { flexDirection: "column" } : {} }, children: [
|
|
2603
|
+
narrow ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsxs16("div", { style: styles.topbar, children: [
|
|
2605
|
+
/* @__PURE__ */ jsx16(
|
|
2472
2606
|
"button",
|
|
2473
2607
|
{
|
|
2474
2608
|
type: "button",
|
|
@@ -2476,18 +2610,18 @@ function AdminShell({
|
|
|
2476
2610
|
"aria-label": "Open menu",
|
|
2477
2611
|
"aria-expanded": drawer,
|
|
2478
2612
|
onClick: () => setDrawer(true),
|
|
2479
|
-
children: /* @__PURE__ */
|
|
2613
|
+
children: /* @__PURE__ */ jsx16(Icon, { name: "menu" })
|
|
2480
2614
|
}
|
|
2481
2615
|
),
|
|
2482
|
-
/* @__PURE__ */
|
|
2483
|
-
envLabel ? /* @__PURE__ */
|
|
2616
|
+
/* @__PURE__ */ jsx16("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
|
|
2617
|
+
envLabel ? /* @__PURE__ */ jsx16("span", { style: { ...styles.badge, marginLeft: "auto" }, children: envLabel }) : null
|
|
2484
2618
|
] }),
|
|
2485
|
-
drawer ? /* @__PURE__ */
|
|
2619
|
+
drawer ? /* @__PURE__ */ jsxs16("div", { style: styles.drawer, children: [
|
|
2486
2620
|
nav,
|
|
2487
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsx16("div", { style: styles.scrim, onClick: () => setDrawer(false), "aria-hidden": "true" })
|
|
2488
2622
|
] }) : null
|
|
2489
2623
|
] }) : nav,
|
|
2490
|
-
/* @__PURE__ */
|
|
2624
|
+
/* @__PURE__ */ jsx16("main", { className: "fonderie-admin-main", style: styles.main, children: body })
|
|
2491
2625
|
] });
|
|
2492
2626
|
}
|
|
2493
2627
|
export {
|
|
@@ -2502,6 +2636,7 @@ export {
|
|
|
2502
2636
|
ModulesScreen,
|
|
2503
2637
|
OperatorsScreen,
|
|
2504
2638
|
RoutesScreen,
|
|
2639
|
+
SubscriberBilling,
|
|
2505
2640
|
SubscriberScreen,
|
|
2506
2641
|
TokensScreen,
|
|
2507
2642
|
UsersScreen
|