@withpotter/pages-admin 0.13.0-next.124 → 0.13.0-next.58
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/admin/_money/cash-postings.js +3 -20
- package/dist/admin/_money/cash-postings.js.map +1 -1
- package/dist/admin/_money/cash.module.css +0 -10
- package/dist/admin/_money/finder-format.js +0 -1
- package/dist/admin/_money/finder-format.js.map +1 -1
- package/dist/admin/_money/format.js +0 -1
- package/dist/admin/_money/format.js.map +1 -1
- package/dist/admin/_overview/activation-panel.js +6 -6
- package/dist/admin/_overview/activation-panel.js.map +1 -1
- package/dist/admin/_overview/attention-groups.js +8 -8
- package/dist/admin/_overview/attention-groups.js.map +1 -1
- package/dist/admin/_overview/attention-panel.js +33 -37
- package/dist/admin/_overview/attention-panel.js.map +1 -1
- package/dist/admin/_overview/attention.module.css +3 -0
- package/dist/admin/_overview/control-room.js +13 -41
- package/dist/admin/_overview/control-room.js.map +1 -1
- package/dist/admin/_overview/kpi-row.js +7 -7
- package/dist/admin/_overview/kpi-row.js.map +1 -1
- package/dist/admin/_overview/leaders-panel.js +1 -1
- package/dist/admin/_overview/leaders-panel.js.map +1 -1
- package/dist/admin/_overview/overdue-outstanding.js +1 -2
- package/dist/admin/_overview/overdue-outstanding.js.map +1 -1
- package/dist/admin/_overview/portfolio-panel.js +5 -5
- package/dist/admin/_overview/portfolio-panel.js.map +1 -1
- package/dist/admin/_overview/position-panel.js +12 -8
- package/dist/admin/_overview/position-panel.js.map +1 -1
- package/dist/admin/_pos/pos-figures.js +5 -18
- package/dist/admin/_pos/pos-figures.js.map +1 -1
- package/dist/admin/_pos/pos-rules.js +0 -2
- package/dist/admin/_pos/pos-rules.js.map +1 -1
- package/dist/admin/_pos/pos-words.js +1 -6
- package/dist/admin/_pos/pos-words.js.map +1 -1
- package/dist/admin/_pos/use-pos.js +1 -70
- package/dist/admin/_pos/use-pos.js.map +1 -1
- package/dist/admin/_shell/nav-config.js +3 -3
- package/dist/admin/_shell/nav-config.js.map +1 -1
- package/dist/admin/_shell/nav-filter.js +2 -4
- package/dist/admin/_shell/nav-filter.js.map +1 -1
- package/dist/admin/_shell/nav-row.js +2 -5
- package/dist/admin/_shell/nav-row.js.map +1 -1
- package/dist/admin/_shell/sidebar-footer.js +0 -20
- package/dist/admin/_shell/sidebar-footer.js.map +1 -1
- package/dist/admin/_shell/sidebar-footer.module.css +0 -17
- package/dist/admin/_shell/sidebar.js +15 -25
- package/dist/admin/_shell/sidebar.js.map +1 -1
- package/dist/admin/_shell/sidebar.module.css +0 -69
- package/dist/admin/_shell/use-fluid-hover.js +6 -37
- package/dist/admin/_shell/use-fluid-hover.js.map +1 -1
- package/dist/admin/_ui/admin.css +0 -7
- package/dist/admin/_ui/data-table.js +1 -1
- package/dist/admin/_ui/data-table.js.map +1 -1
- package/dist/admin/_ui/list-card.js +1 -1
- package/dist/admin/_ui/list-card.js.map +1 -1
- package/dist/admin/_ui/list-card.module.css +16 -2
- package/dist/admin/_ui/money-view.js +0 -1
- package/dist/admin/_ui/money-view.js.map +1 -1
- package/dist/admin/_ui/page-header.js +4 -31
- package/dist/admin/_ui/page-header.js.map +1 -1
- package/dist/admin/_ui/panel.module.css +10 -0
- package/dist/admin/_ui/stat-card.js +2 -11
- package/dist/admin/_ui/stat-card.js.map +1 -1
- package/dist/admin/_ui/trend.js +2 -2
- package/dist/admin/_ui/trend.js.map +1 -1
- package/dist/admin/_ui/workspace-tab.js +1 -11
- package/dist/admin/_ui/workspace-tab.js.map +1 -1
- package/dist/admin/accounts/account-panel.js +6 -49
- package/dist/admin/accounts/account-panel.js.map +1 -1
- package/dist/admin/accounts/account-panel.module.css +0 -73
- package/dist/admin/accounts/account-row.js +2 -11
- package/dist/admin/accounts/account-row.js.map +1 -1
- package/dist/admin/accounts/account-vitals.js +13 -15
- package/dist/admin/accounts/account-vitals.js.map +1 -1
- package/dist/admin/accounts/accounts-table.js +8 -39
- package/dist/admin/accounts/accounts-table.js.map +1 -1
- package/dist/admin/accounts/accounts-workspace.js +2 -2
- package/dist/admin/accounts/accounts-workspace.js.map +1 -1
- package/dist/admin/accounts/owner-session.js +6 -0
- package/dist/admin/accounts/owner-session.js.map +1 -1
- package/dist/admin/accounts/rows.module.css +0 -17
- package/dist/admin/audit/status-pill.js +0 -6
- package/dist/admin/audit/status-pill.js.map +1 -1
- package/dist/admin/audit/status-pill.module.css +0 -4
- package/dist/admin/commerce/commerce-overview.js +2 -2
- package/dist/admin/commerce/commerce-overview.js.map +1 -1
- package/dist/admin/commerce/merchant-meta.js +0 -1
- package/dist/admin/commerce/merchant-meta.js.map +1 -1
- package/dist/admin/commerce/merchant-view.js +2 -2
- package/dist/admin/commerce/merchant-view.js.map +1 -1
- package/dist/admin/feedback/prompt-operations.module.css +2 -0
- package/dist/admin/feedback/prompt-preview.module.css +2 -0
- package/dist/admin/hooks.js +1 -57
- package/dist/admin/hooks.js.map +1 -1
- package/dist/admin/layout.js +2 -8
- package/dist/admin/layout.js.map +1 -1
- package/dist/admin/page.js +8 -28
- package/dist/admin/page.js.map +1 -1
- package/dist/admin/payout-tiers/_tier-dialog.js +21 -31
- package/dist/admin/payout-tiers/_tier-dialog.js.map +1 -1
- package/dist/admin/payout-tiers/tier-preview-result.js +9 -10
- package/dist/admin/payout-tiers/tier-preview-result.js.map +1 -1
- package/dist/admin/payout-tiers/tiers-table.js +17 -40
- package/dist/admin/payout-tiers/tiers-table.js.map +1 -1
- package/dist/admin/payout-tiers/tiers-workspace.js +17 -20
- package/dist/admin/payout-tiers/tiers-workspace.js.map +1 -1
- package/dist/admin/pos/pos-workspace.js +2 -6
- package/dist/admin/pos/pos-workspace.js.map +1 -1
- package/dist/admin/pos/shifts-tab.js +9 -16
- package/dist/admin/pos/shifts-tab.js.map +1 -1
- package/dist/admin/revenue/cohort-panel.js +6 -6
- package/dist/admin/revenue/cohort-panel.js.map +1 -1
- package/dist/admin/revenue/health-panel.js +21 -22
- package/dist/admin/revenue/health-panel.js.map +1 -1
- package/dist/admin/revenue/plan-mix-panel.js +3 -3
- package/dist/admin/revenue/plan-mix-panel.js.map +1 -1
- package/dist/admin/revenue/quality-panel.js +19 -19
- package/dist/admin/revenue/quality-panel.js.map +1 -1
- package/dist/admin/revenue/revenue-kpis.js +50 -111
- package/dist/admin/revenue/revenue-kpis.js.map +1 -1
- package/dist/admin/revenue/revenue-overview.js +46 -39
- package/dist/admin/revenue/revenue-overview.js.map +1 -1
- package/dist/admin/revenue/revenue-retention.js +13 -17
- package/dist/admin/revenue/revenue-retention.js.map +1 -1
- package/dist/admin/revenue/revenue-workspace.js +6 -53
- package/dist/admin/revenue/revenue-workspace.js.map +1 -1
- package/dist/admin/revenue/subscriptions-panel.js +14 -14
- package/dist/admin/revenue/subscriptions-panel.js.map +1 -1
- package/dist/admin/revenue/top-tenants-panel.js +4 -4
- package/dist/admin/revenue/top-tenants-panel.js.map +1 -1
- package/dist/admin/settings/account-tab.js +25 -1
- package/dist/admin/settings/account-tab.js.map +1 -1
- package/dist/admin/settings/general-tab.js +2 -2
- package/dist/admin/settings/general-tab.js.map +1 -1
- package/dist/admin/settings/system-status-card.js +30 -0
- package/dist/admin/settings/system-status-card.js.map +1 -0
- package/dist/admin/support/queue-card.module.css +4 -1
- package/dist/admin/tenants/[id]/commerce/format.js +1 -2
- package/dist/admin/tenants/[id]/commerce/format.js.map +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js.map +1 -1
- package/dist/admin/tenants/[id]/page.js +10 -37
- package/dist/admin/tenants/[id]/page.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-layout.module.css +4 -1
- package/dist/admin/tenants/[id]/tenant-overview.js +2 -3
- package/dist/admin/tenants/[id]/tenant-overview.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-overview.module.css +1 -1
- package/dist/admin/tenants/[id]/tenant-pos.js +16 -64
- package/dist/admin/tenants/[id]/tenant-pos.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-wallet-card.js +2 -3
- package/dist/admin/tenants/[id]/tenant-wallet-card.js.map +1 -1
- package/dist/admin/tenants/[id]/verification/document-viewer.js +52 -3
- package/dist/admin/tenants/[id]/verification/document-viewer.js.map +1 -1
- package/dist/admin/tenants/[id]/verification/document-viewer.module.css +0 -51
- package/dist/admin/tenants/[id]/verification/tenant-verification.js +0 -10
- package/dist/admin/tenants/[id]/verification/tenant-verification.js.map +1 -1
- package/dist/admin/tenants/tenant-list-row.js +1 -2
- package/dist/admin/tenants/tenant-list-row.js.map +1 -1
- package/dist/admin/tenants/tenants-table.js +2 -3
- package/dist/admin/tenants/tenants-table.js.map +1 -1
- package/dist/admin/tenants/tenants-workspace.js +2 -2
- package/dist/admin/tenants/tenants-workspace.js.map +1 -1
- package/dist/admin/verification/verification-page.js +3 -19
- package/dist/admin/verification/verification-page.js.map +1 -1
- package/dist/admin/verification/verification.module.css +0 -10
- package/dist/admin/verification/waiting-case-actions.js +68 -0
- package/dist/admin/verification/waiting-case-actions.js.map +1 -0
- package/dist/admin/verification/waiting-cases.js +48 -66
- package/dist/admin/verification/waiting-cases.js.map +1 -1
- package/package.json +5 -5
- package/dist/admin/_pos/olive-balance.js +0 -40
- package/dist/admin/_pos/olive-balance.js.map +0 -1
- package/dist/admin/_pos/olive-state.js +0 -23
- package/dist/admin/_pos/olive-state.js.map +0 -1
- package/dist/admin/_pos/payments-model.js +0 -55
- package/dist/admin/_pos/payments-model.js.map +0 -1
- package/dist/admin/_pos/tenant-operators.js +0 -63
- package/dist/admin/_pos/tenant-operators.js.map +0 -1
- package/dist/admin/_pos/tenant-outlets.js +0 -77
- package/dist/admin/_pos/tenant-outlets.js.map +0 -1
- package/dist/admin/_pos/tenant-pos-figures.js +0 -82
- package/dist/admin/_pos/tenant-pos-figures.js.map +0 -1
- package/dist/admin/_pos/tenant-requests.js +0 -64
- package/dist/admin/_pos/tenant-requests.js.map +0 -1
- package/dist/admin/_pos/tenant-transactions.js +0 -145
- package/dist/admin/_pos/tenant-transactions.js.map +0 -1
- package/dist/admin/_shell/nav-fold.js +0 -14
- package/dist/admin/_shell/nav-fold.js.map +0 -1
- package/dist/admin/_shell/nav-group.js +0 -78
- package/dist/admin/_shell/nav-group.js.map +0 -1
- package/dist/admin/_shell/use-open-groups.js +0 -64
- package/dist/admin/_shell/use-open-groups.js.map +0 -1
- package/dist/admin/_ui/page-header.module.css +0 -45
- package/dist/admin/accounts/account-sessions.js +0 -70
- package/dist/admin/accounts/account-sessions.js.map +0 -1
- package/dist/admin/accounts/account-sessions.module.css +0 -55
- package/dist/admin/accounts/account-stage-filter.js +0 -29
- package/dist/admin/accounts/account-stage-filter.js.map +0 -1
- package/dist/admin/accounts/account-stage.js +0 -81
- package/dist/admin/accounts/account-stage.js.map +0 -1
- package/dist/admin/accounts/hint.js +0 -12
- package/dist/admin/accounts/hint.js.map +0 -1
- package/dist/admin/payout-tiers/requirement-labels.js +0 -70
- package/dist/admin/payout-tiers/requirement-labels.js.map +0 -1
- package/dist/admin/pos/transactions-tab.js +0 -214
- package/dist/admin/pos/transactions-tab.js.map +0 -1
- package/dist/admin/revenue/channel-panel.js +0 -49
- package/dist/admin/revenue/channel-panel.js.map +0 -1
- package/dist/admin/revenue/channel-rows.js +0 -17
- package/dist/admin/revenue/channel-rows.js.map +0 -1
- package/dist/admin/revenue/download-button.js +0 -40
- package/dist/admin/revenue/download-button.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-colors.js +0 -25
- package/dist/admin/revenue/earnings/earnings-colors.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-legend.js +0 -75
- package/dist/admin/revenue/earnings/earnings-legend.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-summary.js +0 -308
- package/dist/admin/revenue/earnings/earnings-summary.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-table.js +0 -143
- package/dist/admin/revenue/earnings/earnings-table.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings.module.css +0 -201
- package/dist/admin/revenue/earnings/use-earnings.js +0 -27
- package/dist/admin/revenue/earnings/use-earnings.js.map +0 -1
- package/dist/admin/revenue/ledger-model.js +0 -104
- package/dist/admin/revenue/ledger-model.js.map +0 -1
- package/dist/admin/revenue/ledger.module.css +0 -144
- package/dist/admin/revenue/merchant-headline.js +0 -37
- package/dist/admin/revenue/merchant-headline.js.map +0 -1
- package/dist/admin/revenue/merchant-payments-view.js +0 -234
- package/dist/admin/revenue/merchant-payments-view.js.map +0 -1
- package/dist/admin/revenue/method-cards.js +0 -40
- package/dist/admin/revenue/method-cards.js.map +0 -1
- package/dist/admin/revenue/payment-drawer.js +0 -53
- package/dist/admin/revenue/payment-drawer.js.map +0 -1
- package/dist/admin/revenue/plan-lines.js +0 -36
- package/dist/admin/revenue/plan-lines.js.map +0 -1
- package/dist/admin/revenue/plan-payment-drawer.js +0 -42
- package/dist/admin/revenue/plan-payment-drawer.js.map +0 -1
- package/dist/admin/revenue/plans-panel.js +0 -48
- package/dist/admin/revenue/plans-panel.js.map +0 -1
- package/dist/admin/revenue/platform-fees.js +0 -9
- package/dist/admin/revenue/platform-fees.js.map +0 -1
- package/dist/admin/revenue/print-summary-button.js +0 -27
- package/dist/admin/revenue/print-summary-button.js.map +0 -1
- package/dist/admin/revenue/subscriptions-view.js +0 -213
- package/dist/admin/revenue/subscriptions-view.js.map +0 -1
- package/dist/admin/revenue/summary-download.js +0 -38
- package/dist/admin/revenue/summary-download.js.map +0 -1
- package/dist/admin/revenue/summary-print.js +0 -82
- package/dist/admin/revenue/summary-print.js.map +0 -1
- package/dist/admin/revenue/summary-rows.js +0 -44
- package/dist/admin/revenue/summary-rows.js.map +0 -1
- package/dist/admin/revenue/url-filters.js +0 -36
- package/dist/admin/revenue/url-filters.js.map +0 -1
- package/dist/admin/revenue/use-url-filters.js +0 -24
- package/dist/admin/revenue/use-url-filters.js.map +0 -1
- package/dist/admin/tenants/[id]/use-tenant-wallet.js +0 -23
- package/dist/admin/tenants/[id]/use-tenant-wallet.js.map +0 -1
- package/dist/admin/tenants/[id]/verification/document-file.js +0 -78
- package/dist/admin/tenants/[id]/verification/document-file.js.map +0 -1
- package/dist/admin/tenants/[id]/verification/sent-files.js +0 -33
- package/dist/admin/tenants/[id]/verification/sent-files.js.map +0 -1
- package/dist/admin/verification/preview-dialog.js +0 -111
- package/dist/admin/verification/preview-dialog.js.map +0 -1
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { ADMIN_NAME } from "../_shell/admin-brand";
|
|
5
|
-
import {
|
|
6
|
-
useMerchantPayments,
|
|
7
|
-
usePlatformCockpit,
|
|
8
|
-
usePlatformRevenue
|
|
9
|
-
} from "../hooks";
|
|
5
|
+
import { usePlatformCockpit, usePlatformRevenue } from "../hooks";
|
|
10
6
|
import { LoadFailed } from "../audit/load-failed";
|
|
11
7
|
import {
|
|
12
8
|
AdmLoading,
|
|
@@ -23,16 +19,10 @@ import {
|
|
|
23
19
|
usePersistedDateRange,
|
|
24
20
|
useTabParam
|
|
25
21
|
} from "../_ui";
|
|
26
|
-
import { MerchantPaymentsView } from "./merchant-payments-view";
|
|
27
|
-
import { usePlanList } from "../pricing/use-plans";
|
|
28
22
|
import { RevenueOverview } from "./revenue-overview";
|
|
29
23
|
import { RevenueRetention } from "./revenue-retention";
|
|
30
|
-
import { SubscriptionsView } from "./subscriptions-view";
|
|
31
|
-
import { SummaryDownload } from "./summary-download";
|
|
32
24
|
const TABS = [
|
|
33
|
-
{ id: "overview", label: "
|
|
34
|
-
{ id: "merchants", label: "Merchant payments" },
|
|
35
|
-
{ id: "subscriptions", label: "Subscriptions" },
|
|
25
|
+
{ id: "overview", label: "Overview" },
|
|
36
26
|
{ id: "retention", label: "Retention" }
|
|
37
27
|
];
|
|
38
28
|
const KEYS = TABS.map((t) => t.id);
|
|
@@ -54,19 +44,13 @@ function RevenueWorkspace() {
|
|
|
54
44
|
startDate: range.startDate,
|
|
55
45
|
endDate: range.endDate
|
|
56
46
|
});
|
|
57
|
-
const plans = usePlanList();
|
|
58
47
|
const cockpit = usePlatformCockpit({
|
|
59
48
|
startDate: range.startDate,
|
|
60
49
|
endDate: range.endDate
|
|
61
50
|
});
|
|
62
|
-
const ledger = useMerchantPayments({
|
|
63
|
-
startDate: range.startDate,
|
|
64
|
-
endDate: range.endDate,
|
|
65
|
-
limit: 1
|
|
66
|
-
});
|
|
67
51
|
const currencies = useMemo(() => data?.currencies ?? [], [data]);
|
|
68
52
|
const focus = focusCurrency(currencies, currency);
|
|
69
|
-
const recurringCurrency = data?.currency || "
|
|
53
|
+
const recurringCurrency = data?.currency || "USD";
|
|
70
54
|
const money = useMemo(() => moneyIn(recurringCurrency), [recurringCurrency]);
|
|
71
55
|
const money2 = useMemo(
|
|
72
56
|
() => moneyIn(recurringCurrency, 2),
|
|
@@ -85,24 +69,6 @@ function RevenueWorkspace() {
|
|
|
85
69
|
);
|
|
86
70
|
} else if (isLoading || !data) {
|
|
87
71
|
body = /* @__PURE__ */ jsx(AdmLoading, { label: "Loading revenue metrics\u2026" });
|
|
88
|
-
} else if (tab === "merchants") {
|
|
89
|
-
body = /* @__PURE__ */ jsx(
|
|
90
|
-
MerchantPaymentsView,
|
|
91
|
-
{
|
|
92
|
-
startDate: range.startDate,
|
|
93
|
-
endDate: range.endDate,
|
|
94
|
-
currency
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
} else if (tab === "subscriptions") {
|
|
98
|
-
body = /* @__PURE__ */ jsx(
|
|
99
|
-
SubscriptionsView,
|
|
100
|
-
{
|
|
101
|
-
startDate: range.startDate,
|
|
102
|
-
endDate: range.endDate,
|
|
103
|
-
revenue: data
|
|
104
|
-
}
|
|
105
|
-
);
|
|
106
72
|
} else if (tab === "retention") {
|
|
107
73
|
body = /* @__PURE__ */ jsx(
|
|
108
74
|
RevenueRetention,
|
|
@@ -117,17 +83,15 @@ function RevenueWorkspace() {
|
|
|
117
83
|
body = /* @__PURE__ */ jsx(
|
|
118
84
|
RevenueOverview,
|
|
119
85
|
{
|
|
120
|
-
startDate: range.startDate,
|
|
121
|
-
endDate: range.endDate,
|
|
122
86
|
data,
|
|
123
87
|
health: cockpit.data?.health,
|
|
124
88
|
money,
|
|
125
89
|
money2,
|
|
126
90
|
currency,
|
|
127
91
|
focus,
|
|
92
|
+
period,
|
|
128
93
|
span,
|
|
129
|
-
recurringCaption: `${period} \xB7 ${recurringCurrency}
|
|
130
|
-
plans: plans.data
|
|
94
|
+
recurringCaption: `${period} \xB7 ${recurringCurrency}`
|
|
131
95
|
}
|
|
132
96
|
);
|
|
133
97
|
}
|
|
@@ -136,19 +100,8 @@ function RevenueWorkspace() {
|
|
|
136
100
|
PageHeader,
|
|
137
101
|
{
|
|
138
102
|
title: "Revenue",
|
|
139
|
-
description:
|
|
103
|
+
description: `${ADMIN_NAME}'s subscription income each month and year, and the merchant sales (GMV) it is based on.`,
|
|
140
104
|
actions: /* @__PURE__ */ jsxs("div", { className: "adm-toolbar", children: [
|
|
141
|
-
tab === "overview" ? /* @__PURE__ */ jsx(
|
|
142
|
-
SummaryDownload,
|
|
143
|
-
{
|
|
144
|
-
ledger: ledger.data,
|
|
145
|
-
revenue: data,
|
|
146
|
-
currency: focus ?? "NGN",
|
|
147
|
-
startDate: range.startDate,
|
|
148
|
-
endDate: range.endDate,
|
|
149
|
-
period
|
|
150
|
-
}
|
|
151
|
-
) : null,
|
|
152
105
|
/* @__PURE__ */ jsx(
|
|
153
106
|
CurrencyFilter,
|
|
154
107
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/revenue-workspace.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { ADMIN_NAME } from '../_shell/admin-brand';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/revenue/revenue-workspace.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { ADMIN_NAME } from '../_shell/admin-brand';\nimport { usePlatformCockpit, usePlatformRevenue } from '../hooks';\nimport { LoadFailed } from '../audit/load-failed';\nimport {\n AdmLoading,\n CurrencyFilter,\n PageHeader,\n PageTabs,\n PeriodSelector,\n customRange,\n focusCurrency,\n moneyIn,\n periodPhrase,\n rangeFromKey,\n useCurrencyParam,\n usePersistedDateRange,\n useTabParam,\n} from '../_ui';\nimport { RevenueOverview } from './revenue-overview';\nimport { RevenueRetention } from './revenue-retention';\n\nconst TABS = [\n { id: 'overview', label: 'Overview' },\n { id: 'retention', label: 'Retention' },\n] as const;\n\nconst KEYS = TABS.map((t) => t.id);\n\n/** Revenue: headline money on Overview, revenue quality and cohorts on Retention. */\nexport function RevenueWorkspace() {\n const [tab, setTab] = useTabParam(KEYS);\n const {\n rangeKey,\n setRangeKey,\n customStart,\n customEnd,\n onCustomChange,\n } = usePersistedDateRange();\n // Merchant GMV comes back once per currency; `?currency=` narrows it.\n const [currency, setCurrency] = useCurrencyParam();\n\n const range = useMemo(\n () =>\n rangeKey === 'custom'\n ? customRange(customStart, customEnd)\n : rangeFromKey(rangeKey),\n [rangeKey, customStart, customEnd],\n );\n\n const { data, isLoading, isError, refetch } = usePlatformRevenue({\n startDate: range.startDate,\n endDate: range.endDate,\n });\n // Revenue health (MRR movement, logo churn, concentration) is computed as\n // part of the cockpit, not the revenue endpoint — fetched here too rather\n // than duplicating that aggregation.\n const cockpit = usePlatformCockpit({\n startDate: range.startDate,\n endDate: range.endDate,\n });\n\n const currencies = useMemo(() => data?.currencies ?? [], [data]);\n const focus = focusCurrency(currencies, currency);\n // Recurring revenue is the platform's own billing, in the currency the API\n // names for it (`currency`), shown as it comes, never converted.\n const recurringCurrency = data?.currency || 'USD';\n const money = useMemo(() => moneyIn(recurringCurrency), [recurringCurrency]);\n const money2 = useMemo(\n () => moneyIn(recurringCurrency, 2),\n [recurringCurrency],\n );\n\n const span = periodPhrase(range);\n const period = `${span[0].toUpperCase()}${span.slice(1)}`;\n\n let body;\n if (isError) {\n body = (\n <LoadFailed\n message=\"Revenue for this period could not be loaded.\"\n onRetry={() => void refetch()}\n />\n );\n } else if (isLoading || !data) {\n body = <AdmLoading label=\"Loading revenue metrics…\" />;\n } else if (tab === 'retention') {\n body = (\n <RevenueRetention\n quality={data.quality}\n money={money}\n currency={currency}\n caption={currency ? `${period} · ${currency}` : period}\n />\n );\n } else {\n body = (\n <RevenueOverview\n data={data}\n health={cockpit.data?.health}\n money={money}\n money2={money2}\n currency={currency}\n focus={focus}\n period={period}\n span={span}\n recurringCaption={`${period} · ${recurringCurrency}`}\n />\n );\n }\n\n return (\n <div>\n <PageHeader\n title=\"Revenue\"\n description={`${ADMIN_NAME}'s subscription income each month and year, and the merchant sales (GMV) it is based on.`}\n actions={\n <div className=\"adm-toolbar\">\n <CurrencyFilter\n value={currency}\n onChange={setCurrency}\n currencies={currencies}\n />\n <PeriodSelector\n value={rangeKey}\n onChange={setRangeKey}\n customStart={customStart}\n customEnd={customEnd}\n onCustomChange={onCustomChange}\n />\n </div>\n }\n />\n <PageTabs tabs={TABS} active={tab} onChange={setTab} />\n {body}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -8,49 +8,49 @@ function SubscriptionsPanel({
|
|
|
8
8
|
}) {
|
|
9
9
|
const rows = [
|
|
10
10
|
{
|
|
11
|
-
label: "
|
|
11
|
+
label: "Past due",
|
|
12
12
|
status: "past_due",
|
|
13
|
-
note: "
|
|
13
|
+
note: "Payment failed, at risk of churning",
|
|
14
14
|
value: integer.format(recurring.pastDueSubscriptions),
|
|
15
15
|
tone: recurring.pastDueSubscriptions > 0 ? "danger" : "default"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
label: "
|
|
18
|
+
label: "Non-renewing",
|
|
19
19
|
status: "non_renewing",
|
|
20
|
-
note: "
|
|
20
|
+
note: "Set to cancel at period end",
|
|
21
21
|
value: integer.format(recurring.nonRenewingSubscriptions),
|
|
22
22
|
tone: recurring.nonRenewingSubscriptions > 0 ? "danger" : "default"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
label: "Active",
|
|
26
26
|
status: "active",
|
|
27
|
-
note: "Paying
|
|
27
|
+
note: "Paying on a live billing cycle",
|
|
28
28
|
value: integer.format(recurring.activeSubscriptions)
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
label: "
|
|
31
|
+
label: "Trialing",
|
|
32
32
|
status: "trialing",
|
|
33
|
-
note: "
|
|
33
|
+
note: "In trial, not yet billed",
|
|
34
34
|
value: integer.format(recurring.trialingSubscriptions)
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
label: "
|
|
38
|
-
note: `Started
|
|
37
|
+
label: "New this period",
|
|
38
|
+
note: `Started in ${span}`,
|
|
39
39
|
value: integer.format(recurring.newInRange)
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
|
-
label: "
|
|
43
|
-
note: `
|
|
42
|
+
label: "Churned this period",
|
|
43
|
+
note: `Cancelled in ${span}`,
|
|
44
44
|
value: integer.format(recurring.churnedInRange),
|
|
45
45
|
tone: recurring.churnedInRange > 0 ? "danger" : "default"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
label: "Cancelled
|
|
49
|
-
note: "
|
|
48
|
+
label: "Cancelled",
|
|
49
|
+
note: "Lifetime total",
|
|
50
50
|
value: integer.format(recurring.cancelledSubscriptions)
|
|
51
51
|
}
|
|
52
52
|
];
|
|
53
|
-
return /* @__PURE__ */ jsx(Panel, { title: "
|
|
53
|
+
return /* @__PURE__ */ jsx(Panel, { title: "Subscriptions", caption: "Today", height: "var(--adm-panel-h)", children: /* @__PURE__ */ jsx(FigureList, { rows }) });
|
|
54
54
|
}
|
|
55
55
|
export {
|
|
56
56
|
SubscriptionsPanel
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/subscriptions-panel.tsx"],"sourcesContent":["import type { PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { Panel } from '../_ui';\nimport { FigureList, type Figure } from './figure-list';\n\nconst integer = new Intl.NumberFormat('en-US');\n\n/** Where the subscription base stands, and what started in the window. */\nexport function SubscriptionsPanel({\n recurring,\n span,\n}: {\n recurring: PlatformRevenue['recurring'];\n span: string;\n}) {\n const rows: Figure[] = [\n {\n label: '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/revenue/subscriptions-panel.tsx"],"sourcesContent":["import type { PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { Panel } from '../_ui';\nimport { FigureList, type Figure } from './figure-list';\n\nconst integer = new Intl.NumberFormat('en-US');\n\n/** Where the subscription base stands, and what started in the window. */\nexport function SubscriptionsPanel({\n recurring,\n span,\n}: {\n recurring: PlatformRevenue['recurring'];\n span: string;\n}) {\n const rows: Figure[] = [\n {\n label: 'Past due',\n status: 'past_due',\n note: 'Payment failed, at risk of churning',\n value: integer.format(recurring.pastDueSubscriptions),\n tone: recurring.pastDueSubscriptions > 0 ? 'danger' : 'default',\n },\n {\n label: 'Non-renewing',\n status: 'non_renewing',\n note: 'Set to cancel at period end',\n value: integer.format(recurring.nonRenewingSubscriptions),\n tone: recurring.nonRenewingSubscriptions > 0 ? 'danger' : 'default',\n },\n {\n label: 'Active',\n status: 'active',\n note: 'Paying on a live billing cycle',\n value: integer.format(recurring.activeSubscriptions),\n },\n {\n label: 'Trialing',\n status: 'trialing',\n note: 'In trial, not yet billed',\n value: integer.format(recurring.trialingSubscriptions),\n },\n {\n label: 'New this period',\n note: `Started in ${span}`,\n value: integer.format(recurring.newInRange),\n },\n {\n label: 'Churned this period',\n note: `Cancelled in ${span}`,\n value: integer.format(recurring.churnedInRange),\n tone: recurring.churnedInRange > 0 ? 'danger' : 'default',\n },\n {\n label: 'Cancelled',\n note: 'Lifetime total',\n value: integer.format(recurring.cancelledSubscriptions),\n },\n ];\n\n return (\n <Panel title=\"Subscriptions\" caption=\"Today\" height=\"var(--adm-panel-h)\">\n <FigureList rows={rows} />\n </Panel>\n );\n}\n"],"mappings":"AA6DM;AA5DN,SAAS,aAAa;AACtB,SAAS,kBAA+B;AAExC,MAAM,UAAU,IAAI,KAAK,aAAa,OAAO;AAGtC,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGG;AACD,QAAM,OAAiB;AAAA,IACrB;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO,QAAQ,OAAO,UAAU,oBAAoB;AAAA,MACpD,MAAM,UAAU,uBAAuB,IAAI,WAAW;AAAA,IACxD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO,QAAQ,OAAO,UAAU,wBAAwB;AAAA,MACxD,MAAM,UAAU,2BAA2B,IAAI,WAAW;AAAA,IAC5D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO,QAAQ,OAAO,UAAU,mBAAmB;AAAA,IACrD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO,QAAQ,OAAO,UAAU,qBAAqB;AAAA,IACvD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,MAAM,cAAc,IAAI;AAAA,MACxB,OAAO,QAAQ,OAAO,UAAU,UAAU;AAAA,IAC5C;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,MAAM,gBAAgB,IAAI;AAAA,MAC1B,OAAO,QAAQ,OAAO,UAAU,cAAc;AAAA,MAC9C,MAAM,UAAU,iBAAiB,IAAI,WAAW;AAAA,IAClD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,MAAM;AAAA,MACN,OAAO,QAAQ,OAAO,UAAU,sBAAsB;AAAA,IACxD;AAAA,EACF;AAEA,SACE,oBAAC,SAAM,OAAM,iBAAgB,SAAQ,SAAQ,QAAO,sBAClD,8BAAC,cAAW,MAAY,GAC1B;AAEJ;","names":[]}
|
|
@@ -17,7 +17,7 @@ function TopTenantsPanel({
|
|
|
17
17
|
return /* @__PURE__ */ jsx(
|
|
18
18
|
Panel,
|
|
19
19
|
{
|
|
20
|
-
title: "
|
|
20
|
+
title: "Top merchants by GMV",
|
|
21
21
|
count: tenants.length || void 0,
|
|
22
22
|
caption: focus ?? void 0,
|
|
23
23
|
action: /* @__PURE__ */ jsx(Link, { href: "/admin/tenants", children: "View all" }),
|
|
@@ -31,9 +31,9 @@ function TopTenantsPanel({
|
|
|
31
31
|
head: /* @__PURE__ */ jsxs("tr", { children: [
|
|
32
32
|
/* @__PURE__ */ jsx(Th, { children: "Merchant" }),
|
|
33
33
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Orders" }),
|
|
34
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "
|
|
35
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "
|
|
36
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "
|
|
34
|
+
/* @__PURE__ */ jsx(Th, { align: "right", children: "AOV" }),
|
|
35
|
+
/* @__PURE__ */ jsx(Th, { align: "right", children: "GMV" }),
|
|
36
|
+
/* @__PURE__ */ jsx(Th, { align: "right", children: "% of GMV" })
|
|
37
37
|
] }),
|
|
38
38
|
children: tenants.map((t) => {
|
|
39
39
|
const aov = t.orders > 0 ? t.gmv / t.orders : 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/top-tenants-panel.tsx"],"sourcesContent":["import Link from 'next/link';\nimport { useRouter } from 'next/navigation';\nimport { EmptyNote, TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport type { PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { PICK_CURRENCY, Panel, moneyIn } from '../_ui';\nimport { logoOf } from '../tenants/tenants-logo';\nimport styles from './revenue.module.css';\n\nconst integer = new Intl.NumberFormat('en-US');\n\n/**\n * Merchants ranked by GMV within one currency, with order count, AOV and\n * share of that currency's lifetime GMV.\n */\nexport function TopTenantsPanel({\n tenants,\n lifetime,\n focus,\n}: {\n tenants: PlatformRevenue['gmv']['topTenants'];\n /** Lifetime GMV in `focus`, the base for each share. */\n lifetime: number;\n /** The one currency ranked, or null when there are several and none is chosen. */\n focus: string | null;\n}) {\n const router = useRouter();\n const money = moneyIn(focus ?? '');\n\n return (\n <Panel\n title=\"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/revenue/top-tenants-panel.tsx"],"sourcesContent":["import Link from 'next/link';\nimport { useRouter } from 'next/navigation';\nimport { EmptyNote, TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport type { PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { PICK_CURRENCY, Panel, moneyIn } from '../_ui';\nimport { logoOf } from '../tenants/tenants-logo';\nimport styles from './revenue.module.css';\n\nconst integer = new Intl.NumberFormat('en-US');\n\n/**\n * Merchants ranked by GMV within one currency, with order count, AOV and\n * share of that currency's lifetime GMV.\n */\nexport function TopTenantsPanel({\n tenants,\n lifetime,\n focus,\n}: {\n tenants: PlatformRevenue['gmv']['topTenants'];\n /** Lifetime GMV in `focus`, the base for each share. */\n lifetime: number;\n /** The one currency ranked, or null when there are several and none is chosen. */\n focus: string | null;\n}) {\n const router = useRouter();\n const money = moneyIn(focus ?? '');\n\n return (\n <Panel\n title=\"Top merchants by GMV\"\n count={tenants.length || undefined}\n caption={focus ?? undefined}\n action={<Link href=\"/admin/tenants\">View all</Link>}\n flush\n >\n {tenants.length === 0 ? (\n <EmptyNote className={styles.empty}>\n {focus ? `No ${focus} merchant revenue recorded yet.` : PICK_CURRENCY}\n </EmptyNote>\n ) : (\n <TableShell\n bare\n minWidth={520}\n density=\"compact\"\n head={\n <tr>\n <Th>Merchant</Th>\n <Th align=\"right\">Orders</Th>\n <Th align=\"right\">AOV</Th>\n <Th align=\"right\">GMV</Th>\n <Th align=\"right\">% of GMV</Th>\n </tr>\n }\n >\n {tenants.map((t) => {\n const aov = t.orders > 0 ? t.gmv / t.orders : 0;\n const share = lifetime > 0 ? (t.gmv / lifetime) * 100 : 0;\n const logo = logoOf(t);\n return (\n <TableRow\n key={t.id}\n onClick={() => router.push(`/admin/tenants/${t.id}`)}\n >\n <td className={styles.name}>\n <span className={styles.tenant}>\n {logo && (\n <EntityAvatar\n name={t.businessName}\n src={logo}\n size={28}\n />\n )}\n <span className={styles.tenantText}>\n <Link\n href={`/admin/tenants/${t.id}`}\n title={t.businessName}\n onClick={(e) => e.stopPropagation()}\n >\n {t.businessName}\n </Link>\n {t.slug && (\n <span className={styles.slug} title={t.slug}>\n {t.slug}\n </span>\n )}\n </span>\n </span>\n </td>\n <td className={styles.num}>{integer.format(t.orders)}</td>\n <td className={styles.num}>{money(aov)}</td>\n <td className={styles.numStrong}>{money(t.gmv)}</td>\n <td className={styles.num}>\n <span className={styles.share}>\n <span className={styles.shareTrack} aria-hidden>\n <span\n className={styles.shareFill}\n style={{ width: `${Math.min(share, 100)}%` }}\n />\n </span>\n {share.toFixed(1)}%\n </span>\n </td>\n </TableRow>\n );\n })}\n </TableShell>\n )}\n </Panel>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import { Button } from "@withpotter/ui-kit";
|
|
5
|
+
import { Button, useConfirm, useToast } from "@withpotter/ui-kit";
|
|
6
6
|
import { SUPPORT_INBOX_EMAIL } from "@withpotter/lib/support-inbox";
|
|
7
7
|
import { useAdminSession } from "../hooks";
|
|
8
|
+
import { settingsStore } from "../_settings";
|
|
8
9
|
import { useModuleOn } from "../_shell/modules-context";
|
|
9
10
|
import { signOutAdmin } from "../_shell/sign-out";
|
|
10
11
|
import { ChangePasswordDialog } from "./change-password-dialog";
|
|
11
12
|
import { ProfileCard } from "./profile-card";
|
|
12
13
|
import { SettingRow, SettingsCard } from "./setting-row";
|
|
14
|
+
import { SystemStatusCard } from "./system-status-card";
|
|
13
15
|
import styles from "./settings.module.css";
|
|
14
16
|
function AccountTab() {
|
|
15
17
|
const router = useRouter();
|
|
18
|
+
const confirm = useConfirm();
|
|
19
|
+
const { toast } = useToast();
|
|
16
20
|
const { data: session } = useAdminSession();
|
|
17
21
|
const [changing, setChanging] = useState(false);
|
|
18
22
|
const [leaving, setLeaving] = useState(false);
|
|
@@ -22,6 +26,17 @@ function AccountTab() {
|
|
|
22
26
|
await signOutAdmin();
|
|
23
27
|
router.replace("/admin/login");
|
|
24
28
|
};
|
|
29
|
+
const reset = async () => {
|
|
30
|
+
const ok = await confirm({
|
|
31
|
+
title: "Reset settings on this device?",
|
|
32
|
+
description: "Density, accessibility, notification and shortcut choices go back to their defaults. Your account is not affected.",
|
|
33
|
+
confirmLabel: "Reset settings",
|
|
34
|
+
tone: "danger"
|
|
35
|
+
});
|
|
36
|
+
if (!ok) return;
|
|
37
|
+
settingsStore.reset();
|
|
38
|
+
toast({ title: "Settings reset on this device" });
|
|
39
|
+
};
|
|
25
40
|
return /* @__PURE__ */ jsxs("div", { className: styles.stack, children: [
|
|
26
41
|
session && /* @__PURE__ */ jsx(ProfileCard, { session }),
|
|
27
42
|
/* @__PURE__ */ jsxs(SettingsCard, { title: "Security", children: [
|
|
@@ -42,6 +57,14 @@ function AccountTab() {
|
|
|
42
57
|
}
|
|
43
58
|
)
|
|
44
59
|
] }),
|
|
60
|
+
/* @__PURE__ */ jsx(SettingsCard, { title: "This device", children: /* @__PURE__ */ jsx(
|
|
61
|
+
SettingRow,
|
|
62
|
+
{
|
|
63
|
+
label: "Reset all settings on this device",
|
|
64
|
+
description: "Every preference on this page returns to its default.",
|
|
65
|
+
children: /* @__PURE__ */ jsx(Button, { variant: "danger", onClick: reset, children: "Reset" })
|
|
66
|
+
}
|
|
67
|
+
) }),
|
|
45
68
|
supportOn && /* @__PURE__ */ jsx(SettingsCard, { title: "Support inbox", children: /* @__PURE__ */ jsx(
|
|
46
69
|
SettingRow,
|
|
47
70
|
{
|
|
@@ -50,6 +73,7 @@ function AccountTab() {
|
|
|
50
73
|
children: /* @__PURE__ */ jsx("span", { className: styles.value, title: SUPPORT_INBOX_EMAIL, children: SUPPORT_INBOX_EMAIL })
|
|
51
74
|
}
|
|
52
75
|
) }),
|
|
76
|
+
/* @__PURE__ */ jsx(SystemStatusCard, {}),
|
|
53
77
|
changing && /* @__PURE__ */ jsx(ChangePasswordDialog, { onClose: () => setChanging(false) })
|
|
54
78
|
] });
|
|
55
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/settings/account-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Button } from '@withpotter/ui-kit';\nimport { SUPPORT_INBOX_EMAIL } from '@withpotter/lib/support-inbox';\nimport { useAdminSession } from '../hooks';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { signOutAdmin } from '../_shell/sign-out';\nimport { ChangePasswordDialog } from './change-password-dialog';\nimport { ProfileCard } from './profile-card';\nimport { SettingRow, SettingsCard } from './setting-row';\nimport styles from './settings.module.css';\n\n/**\n * Who you are
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/settings/account-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Button, useConfirm, useToast } from '@withpotter/ui-kit';\nimport { SUPPORT_INBOX_EMAIL } from '@withpotter/lib/support-inbox';\nimport { useAdminSession } from '../hooks';\nimport { settingsStore } from '../_settings';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { signOutAdmin } from '../_shell/sign-out';\nimport { ChangePasswordDialog } from './change-password-dialog';\nimport { ProfileCard } from './profile-card';\nimport { SettingRow, SettingsCard } from './setting-row';\nimport { SystemStatusCard } from './system-status-card';\nimport styles from './settings.module.css';\n\n/**\n * Who you are, how you sign in, and the box's own health. The support inbox\n * address shows only where this box serves Support.\n */\nexport function AccountTab() {\n const router = useRouter();\n const confirm = useConfirm();\n const { toast } = useToast();\n const { data: session } = useAdminSession();\n const [changing, setChanging] = useState(false);\n const [leaving, setLeaving] = useState(false);\n const supportOn = useModuleOn('support');\n\n const signOut = async () => {\n setLeaving(true);\n await signOutAdmin();\n router.replace('/admin/login');\n };\n\n const reset = async () => {\n const ok = await confirm({\n title: 'Reset settings on this device?',\n description:\n 'Density, accessibility, notification and shortcut choices go back to their defaults. Your account is not affected.',\n confirmLabel: 'Reset settings',\n tone: 'danger',\n });\n if (!ok) return;\n settingsStore.reset();\n toast({ title: 'Settings reset on this device' });\n };\n\n return (\n <div className={styles.stack}>\n {session && <ProfileCard session={session} />}\n <SettingsCard title=\"Security\">\n <SettingRow\n label=\"Password\"\n description=\"At least 12 characters. You stay signed in here.\"\n >\n <Button variant=\"secondary\" onClick={() => setChanging(true)}>\n Change password\n </Button>\n </SettingRow>\n <SettingRow\n label=\"Sign out\"\n description=\"Also ends any View as owner session.\"\n >\n <Button variant=\"secondary\" loading={leaving} onClick={signOut}>\n Sign out\n </Button>\n </SettingRow>\n </SettingsCard>\n <SettingsCard title=\"This device\">\n <SettingRow\n label=\"Reset all settings on this device\"\n description=\"Every preference on this page returns to its default.\"\n >\n <Button variant=\"danger\" onClick={reset}>\n Reset\n </Button>\n </SettingRow>\n </SettingsCard>\n {supportOn && (\n <SettingsCard title=\"Support inbox\">\n <SettingRow\n label=\"Inbound address\"\n description=\"Merchants write here; each email opens a case in Support.\"\n >\n <span className={styles.value} title={SUPPORT_INBOX_EMAIL}>\n {SUPPORT_INBOX_EMAIL}\n </span>\n </SettingRow>\n </SettingsCard>\n )}\n <SystemStatusCard />\n {changing && <ChangePasswordDialog onClose={() => setChanging(false)} />}\n </div>\n );\n}\n"],"mappings":";AAkDkB,cACZ,YADY;AAhDlB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,YAAY,gBAAgB;AAC7C,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,YAAY,oBAAoB;AACzC,SAAS,wBAAwB;AACjC,OAAO,YAAY;AAMZ,SAAS,aAAa;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,UAAU,WAAW;AAC3B,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,EAAE,MAAM,QAAQ,IAAI,gBAAgB;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,YAAY,YAAY,SAAS;AAEvC,QAAM,UAAU,YAAY;AAC1B,eAAW,IAAI;AACf,UAAM,aAAa;AACnB,WAAO,QAAQ,cAAc;AAAA,EAC/B;AAEA,QAAM,QAAQ,YAAY;AACxB,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO;AAAA,MACP,aACE;AAAA,MACF,cAAc;AAAA,MACd,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,GAAI;AACT,kBAAc,MAAM;AACpB,UAAM,EAAE,OAAO,gCAAgC,CAAC;AAAA,EAClD;AAEA,SACE,qBAAC,SAAI,WAAW,OAAO,OACpB;AAAA,eAAW,oBAAC,eAAY,SAAkB;AAAA,IAC3C,qBAAC,gBAAa,OAAM,YAClB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,MAAM,YAAY,IAAI,GAAG,6BAE9D;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,SAAS,SAAS,SAAS,sBAEhE;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACA,oBAAC,gBAAa,OAAM,eAClB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,aAAY;AAAA,QAEZ,8BAAC,UAAO,SAAQ,UAAS,SAAS,OAAO,mBAEzC;AAAA;AAAA,IACF,GACF;AAAA,IACC,aACC,oBAAC,gBAAa,OAAM,iBAClB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,aAAY;AAAA,QAEZ,8BAAC,UAAK,WAAW,OAAO,OAAO,OAAO,qBACnC,+BACH;AAAA;AAAA,IACF,GACF;AAAA,IAEF,oBAAC,oBAAiB;AAAA,IACjB,YAAY,oBAAC,wBAAqB,SAAS,MAAM,YAAY,KAAK,GAAG;AAAA,KACxE;AAEJ;","names":[]}
|
|
@@ -17,8 +17,8 @@ const THEMES = [
|
|
|
17
17
|
const START_OPTIONS = [
|
|
18
18
|
{ value: "overview", label: "Overview" },
|
|
19
19
|
{ value: "support", label: "Support" },
|
|
20
|
-
{ value: "merchants", label: "
|
|
21
|
-
{ value: "people", label: "
|
|
20
|
+
{ value: "merchants", label: "Merchants" },
|
|
21
|
+
{ value: "people", label: "People" }
|
|
22
22
|
];
|
|
23
23
|
function GeneralTab() {
|
|
24
24
|
const settings = useSettings();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/settings/general-tab.tsx"],"sourcesContent":["'use client';\n\nimport { Select } from '@withpotter/ui-kit';\nimport { useTheme } from '@withpotter/lib/theme-context';\nimport { RANGE_OPTIONS, usePersistedDateRange, type RangeKey } from '../_ui';\nimport { useSettings, type AdminSettings } from '../_settings';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useSidebarCollapsed, sidebarStore } from '../_shell/use-shell-prefs';\nimport { Segmented, SettingRow, SettingsCard } from './setting-row';\nimport { useSave } from './use-save';\nimport styles from './settings.module.css';\n\ntype Theme = 'system' | 'light' | 'dark';\n\nconst THEMES = [\n { value: 'system' as Theme, label: 'System' },\n { value: 'light' as Theme, label: 'Light' },\n { value: 'dark' as Theme, label: 'Dark' },\n];\n\nconst START_OPTIONS: Array<{\n value: AdminSettings['startPage'];\n label: string;\n}> = [\n { value: 'overview', label: 'Overview' },\n { value: 'support', label: 'Support' },\n { value: 'merchants', label: '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/settings/general-tab.tsx"],"sourcesContent":["'use client';\n\nimport { Select } from '@withpotter/ui-kit';\nimport { useTheme } from '@withpotter/lib/theme-context';\nimport { RANGE_OPTIONS, usePersistedDateRange, type RangeKey } from '../_ui';\nimport { useSettings, type AdminSettings } from '../_settings';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useSidebarCollapsed, sidebarStore } from '../_shell/use-shell-prefs';\nimport { Segmented, SettingRow, SettingsCard } from './setting-row';\nimport { useSave } from './use-save';\nimport styles from './settings.module.css';\n\ntype Theme = 'system' | 'light' | 'dark';\n\nconst THEMES = [\n { value: 'system' as Theme, label: 'System' },\n { value: 'light' as Theme, label: 'Light' },\n { value: 'dark' as Theme, label: 'Dark' },\n];\n\nconst START_OPTIONS: Array<{\n value: AdminSettings['startPage'];\n label: string;\n}> = [\n { value: 'overview', label: 'Overview' },\n { value: 'support', label: 'Support' },\n { value: 'merchants', label: 'Merchants' },\n { value: 'people', label: 'People' },\n];\n\n/**\n * Look, layout and the views every page opens with. No display currency on a\n * box: money is shown per currency and never converted, so there is nothing\n * to pick. Support is a start page only where this box serves Support.\n */\nexport function GeneralTab() {\n const settings = useSettings();\n const { update, saved } = useSave();\n const { theme, setTheme } = useTheme();\n const { rangeKey, setRangeKey } = usePersistedDateRange();\n const collapsed = useSidebarCollapsed();\n const supportOn = useModuleOn('support');\n const startOptions = START_OPTIONS.filter(\n (o) => supportOn || o.value !== 'support'\n );\n\n return (\n <div className={styles.stack}>\n <SettingsCard title=\"Appearance\">\n <SettingRow\n label=\"Theme\"\n description=\"System follows your computer's light or dark setting.\"\n >\n <Segmented\n label=\"Theme\"\n value={theme}\n options={THEMES}\n onChange={(next) => {\n setTheme(next);\n saved();\n }}\n />\n </SettingRow>\n <SettingRow\n label=\"Density\"\n description=\"Compact fits more rows and cards on screen.\"\n >\n <Segmented\n label=\"Density\"\n value={settings.density}\n options={[\n { value: 'comfortable', label: 'Comfortable' },\n { value: 'compact', label: 'Compact' },\n ]}\n onChange={(density) => update({ density })}\n />\n </SettingRow>\n <SettingRow\n label=\"Sidebar\"\n description=\"Collapsed shows icons only. You can also toggle it from the sidebar.\"\n >\n <Segmented\n label=\"Sidebar\"\n value={collapsed ? 'collapsed' : 'expanded'}\n options={[\n { value: 'expanded', label: 'Expanded' },\n { value: 'collapsed', label: 'Collapsed' },\n ]}\n onChange={(next) => {\n sidebarStore.set(next === 'collapsed');\n saved();\n }}\n />\n </SettingRow>\n </SettingsCard>\n\n <SettingsCard title=\"Defaults\" description=\"What pages open with.\">\n <SettingRow\n label=\"Default period\"\n description=\"The date range reports and charts start on.\"\n >\n <Select\n className={styles.select}\n ariaLabel=\"Default period\"\n value={rangeKey}\n options={RANGE_OPTIONS.map((o) => ({\n value: o.key,\n label: o.label,\n }))}\n onChange={(key) => {\n setRangeKey(key as RangeKey);\n saved();\n }}\n />\n </SettingRow>\n <SettingRow\n label=\"Start page\"\n description=\"Where the console opens when you sign in or reload on Overview.\"\n >\n <Select\n className={styles.select}\n ariaLabel=\"Start page\"\n value={settings.startPage}\n options={startOptions}\n onChange={(value) =>\n update({ startPage: value as AdminSettings['startPage'] })\n }\n />\n </SettingRow>\n </SettingsCard>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { adminReadinessApi } from "@withpotter/lib/admin-readiness-api";
|
|
5
|
+
import { overallOf, SystemStatusPanel } from "../_shell/system-status-panel";
|
|
6
|
+
import { SettingsCard } from "./setting-row";
|
|
7
|
+
function SystemStatusCard() {
|
|
8
|
+
const query = useQuery({
|
|
9
|
+
queryKey: ["platform-admin", "readiness"],
|
|
10
|
+
queryFn: adminReadinessApi.readiness,
|
|
11
|
+
staleTime: 6e4,
|
|
12
|
+
retry: 1
|
|
13
|
+
});
|
|
14
|
+
return /* @__PURE__ */ jsx(SettingsCard, { title: "System status", children: /* @__PURE__ */ jsx(
|
|
15
|
+
SystemStatusPanel,
|
|
16
|
+
{
|
|
17
|
+
data: query.data,
|
|
18
|
+
overall: overallOf(query.data, query.isError),
|
|
19
|
+
isLoading: query.isLoading,
|
|
20
|
+
isError: query.isError,
|
|
21
|
+
isFetching: query.isFetching,
|
|
22
|
+
checkedAt: query.dataUpdatedAt,
|
|
23
|
+
onRefresh: () => void query.refetch()
|
|
24
|
+
}
|
|
25
|
+
) });
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
SystemStatusCard
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=system-status-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/settings/system-status-card.tsx"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { adminReadinessApi } from '@withpotter/lib/admin-readiness-api';\nimport { overallOf, SystemStatusPanel } from '../_shell/system-status-panel';\nimport { SettingsCard } from './setting-row';\n\n/** The deployment's integration checks, each with its state. */\nexport function SystemStatusCard() {\n const query = useQuery({\n queryKey: ['platform-admin', 'readiness'],\n queryFn: adminReadinessApi.readiness,\n staleTime: 60_000,\n retry: 1,\n });\n\n return (\n <SettingsCard title=\"System status\">\n <SystemStatusPanel\n data={query.data}\n overall={overallOf(query.data, query.isError)}\n isLoading={query.isLoading}\n isError={query.isError}\n isFetching={query.isFetching}\n checkedAt={query.dataUpdatedAt}\n onRefresh={() => void query.refetch()}\n />\n </SettingsCard>\n );\n}\n"],"mappings":";AAkBM;AAhBN,SAAS,gBAAgB;AACzB,SAAS,yBAAyB;AAClC,SAAS,WAAW,yBAAyB;AAC7C,SAAS,oBAAoB;AAGtB,SAAS,mBAAmB;AACjC,QAAM,QAAQ,SAAS;AAAA,IACrB,UAAU,CAAC,kBAAkB,WAAW;AAAA,IACxC,SAAS,kBAAkB;AAAA,IAC3B,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,SACE,oBAAC,gBAAa,OAAM,iBAClB;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,MAAM;AAAA,MACZ,SAAS,UAAU,MAAM,MAAM,MAAM,OAAO;AAAA,MAC5C,WAAW,MAAM;AAAA,MACjB,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM,KAAK,MAAM,QAAQ;AAAA;AAAA,EACtC,GACF;AAEJ;","names":[]}
|
|
@@ -75,8 +75,11 @@
|
|
|
75
75
|
outline-offset: -2px;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/*
|
|
78
|
+
/* Sticky column header with a hairline, not a shadow. */
|
|
79
79
|
.scroll thead th {
|
|
80
|
+
position: sticky;
|
|
81
|
+
top: 0;
|
|
82
|
+
z-index: 1;
|
|
80
83
|
background: var(--bg-base);
|
|
81
84
|
box-shadow: inset 0 -1px 0 var(--border-subtle);
|
|
82
85
|
white-space: nowrap;
|
|
@@ -3,8 +3,7 @@ function moneyIn(currency, decimals = 0) {
|
|
|
3
3
|
try {
|
|
4
4
|
return new Intl.NumberFormat("en-US", {
|
|
5
5
|
style: "currency",
|
|
6
|
-
|
|
7
|
-
currency: currency || "NGN",
|
|
6
|
+
currency: currency || "USD",
|
|
8
7
|
maximumFractionDigits: decimals
|
|
9
8
|
}).format(n);
|
|
10
9
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/format.ts"],"sourcesContent":["import type { TenantProduct } from '@withpotter/lib/admin-api';\n\n/** Amounts in the merchant's own currency, whole units for headlines. */\nexport function moneyIn(currency: string, decimals = 0) {\n return (n: number) => {\n try {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n
|
|
1
|
+
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/format.ts"],"sourcesContent":["import type { TenantProduct } from '@withpotter/lib/admin-api';\n\n/** Amounts in the merchant's own currency, whole units for headlines. */\nexport function moneyIn(currency: string, decimals = 0) {\n return (n: number) => {\n try {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n maximumFractionDigits: decimals,\n }).format(n);\n } catch {\n return `${currency} ${Math.round(n).toLocaleString()}`;\n }\n };\n}\n\nexport type Money = (n: number) => string;\n\n/** 1.2k / 3.4m, for figures in tight tiles. */\nexport const compact = (n: number) =>\n new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(n);\n\nexport const shortDate = (iso: string) =>\n new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric' }).format(\n new Date(iso),\n );\n\n/** Sentence-case a machine word: \"checked_in\" → \"Checked in\". */\nexport const humanize = (s: string) => {\n const t = s.replace(/[_-]+/g, ' ').trim();\n return t ? t[0].toUpperCase() + t.slice(1) : '–';\n};\n\nexport type PillTone =\n 'success' | 'pending' | 'failed' | 'processing' | 'neutral';\n\nconst TONE_OF: Record<string, PillTone> = {\n completed: 'success',\n paid: 'success',\n success: 'success',\n settled: 'success',\n fulfilled: 'success',\n delivered: 'success',\n collected: 'success',\n pending: 'pending',\n unpaid: 'pending',\n awaiting_confirmation: 'pending',\n authorized: 'pending',\n unfulfilled: 'pending',\n partial: 'pending',\n confirmed: 'processing',\n processing: 'processing',\n checked_in: 'processing',\n ready: 'processing',\n failed: 'failed',\n reversed: 'failed',\n expired: 'failed',\n cancelled: 'neutral',\n refunded: 'neutral',\n partially_refunded: 'neutral',\n};\n\nexport const toneOf = (status: string): PillTone =>\n TONE_OF[status?.toLowerCase()] ?? 'neutral';\n\nexport type StockState = {\n label: string;\n tone: PillTone;\n available: number | null;\n};\n\n/** What a shelf looks like for one item, from the counts the API sends. */\nexport function stockOf(p: TenantProduct): StockState {\n if (!p.trackInventory || p.quantity == null)\n return { label: 'Not tracked', tone: 'neutral', available: null };\n\n const available = p.quantity - (p.reserved ?? 0);\n\n if (available <= 0)\n return { label: 'Out of stock', tone: 'failed', available };\n\n if (p.lowStockThreshold != null && available <= p.lowStockThreshold)\n return { label: `Low · ${available}`, tone: 'pending', available };\n\n return {\n label: `In stock · ${compact(available)}`,\n tone: 'success',\n available,\n };\n}\n"],"mappings":"AAGO,SAAS,QAAQ,UAAkB,WAAW,GAAG;AACtD,SAAO,CAAC,MAAc;AACpB,QAAI;AACF,aAAO,IAAI,KAAK,aAAa,SAAS;AAAA,QACpC,OAAO;AAAA,QACP,UAAU,YAAY;AAAA,QACtB,uBAAuB;AAAA,MACzB,CAAC,EAAE,OAAO,CAAC;AAAA,IACb,QAAQ;AACN,aAAO,GAAG,QAAQ,IAAI,KAAK,MAAM,CAAC,EAAE,eAAe,CAAC;AAAA,IACtD;AAAA,EACF;AACF;AAKO,MAAM,UAAU,CAAC,MACtB,IAAI,KAAK,aAAa,SAAS;AAAA,EAC7B,UAAU;AAAA,EACV,uBAAuB;AACzB,CAAC,EAAE,OAAO,CAAC;AAEN,MAAM,YAAY,CAAC,QACxB,IAAI,KAAK,eAAe,MAAM,EAAE,OAAO,SAAS,KAAK,UAAU,CAAC,EAAE;AAAA,EAChE,IAAI,KAAK,GAAG;AACd;AAGK,MAAM,WAAW,CAAC,MAAc;AACrC,QAAM,IAAI,EAAE,QAAQ,UAAU,GAAG,EAAE,KAAK;AACxC,SAAO,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,IAAI;AAC/C;AAKA,MAAM,UAAoC;AAAA,EACxC,WAAW;AAAA,EACX,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,uBAAuB;AAAA,EACvB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,oBAAoB;AACtB;AAEO,MAAM,SAAS,CAAC,WACrB,QAAQ,QAAQ,YAAY,CAAC,KAAK;AAS7B,SAAS,QAAQ,GAA8B;AACpD,MAAI,CAAC,EAAE,kBAAkB,EAAE,YAAY;AACrC,WAAO,EAAE,OAAO,eAAe,MAAM,WAAW,WAAW,KAAK;AAElE,QAAM,YAAY,EAAE,YAAY,EAAE,YAAY;AAE9C,MAAI,aAAa;AACf,WAAO,EAAE,OAAO,gBAAgB,MAAM,UAAU,UAAU;AAE5D,MAAI,EAAE,qBAAqB,QAAQ,aAAa,EAAE;AAChD,WAAO,EAAE,OAAO,YAAS,SAAS,IAAI,MAAM,WAAW,UAAU;AAEnE,SAAO;AAAA,IACL,OAAO,iBAAc,QAAQ,SAAS,CAAC;AAAA,IACvC,MAAM;AAAA,IACN;AAAA,EACF;AACF;","names":[]}
|
|
@@ -39,7 +39,7 @@ function RecentOrders({
|
|
|
39
39
|
/* @__PURE__ */ jsx(Th, { children: "Customer" }),
|
|
40
40
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Total" }),
|
|
41
41
|
/* @__PURE__ */ jsx(Th, { children: "Payment" }),
|
|
42
|
-
/* @__PURE__ */ jsx(Th, { children: "
|
|
42
|
+
/* @__PURE__ */ jsx(Th, { children: "Status" }),
|
|
43
43
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Placed" })
|
|
44
44
|
] }),
|
|
45
45
|
children: loading ? Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx("tr", { "aria-hidden": true, children: /* @__PURE__ */ jsx("td", { colSpan: 6, children: /* @__PURE__ */ jsx(Skeleton, { height: 20 }) }) }, i)) : orders.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: styles.emptyCell, children: "No orders yet. They show here as soon as a customer checks out." }) }) : orders.map((o) => /* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/recent-orders.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport type {\n DashboardStats,\n TenantActivityOrder,\n Transaction,\n} from '@withpotter/lib/admin-api';\nimport { TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { Panel, Skeleton } from '../../../_ui';\nimport { moneyIn, shortDate } from './format';\nimport { OrderDrawer } from './order-drawer';\nimport { OrderPipeline } from './order-pipeline';\nimport { StatusPill } from './status-pill';\nimport styles from './orders.module.css';\n\n/** The order pipeline over the latest orders; a row opens the order. */\nexport function RecentOrders({\n orders,\n stats,\n transactions,\n loading,\n statsLoading,\n currency,\n}: {\n orders: TenantActivityOrder[];\n stats: DashboardStats['orderStats'] | undefined;\n transactions: Transaction[];\n loading: boolean;\n statsLoading: boolean;\n currency: string;\n}) {\n const [open, setOpen] = useState<TenantActivityOrder | null>(null);\n\n return (\n <>\n <Panel\n title=\"Orders\"\n count={stats?.total}\n caption={stats ? `${stats.today.toLocaleString()} today` : undefined}\n height=\"calc(var(--adm-panel-h) + 120px)\"\n flush\n >\n <OrderPipeline stats={stats} loading={statsLoading} />\n <TableShell\n bare\n minWidth={640}\n density=\"compact\"\n head={\n <tr>\n <Th>Order</Th>\n <Th>Customer</Th>\n <Th align=\"right\">Total</Th>\n <Th>Payment</Th>\n <Th>
|
|
1
|
+
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/recent-orders.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport type {\n DashboardStats,\n TenantActivityOrder,\n Transaction,\n} from '@withpotter/lib/admin-api';\nimport { TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { Panel, Skeleton } from '../../../_ui';\nimport { moneyIn, shortDate } from './format';\nimport { OrderDrawer } from './order-drawer';\nimport { OrderPipeline } from './order-pipeline';\nimport { StatusPill } from './status-pill';\nimport styles from './orders.module.css';\n\n/** The order pipeline over the latest orders; a row opens the order. */\nexport function RecentOrders({\n orders,\n stats,\n transactions,\n loading,\n statsLoading,\n currency,\n}: {\n orders: TenantActivityOrder[];\n stats: DashboardStats['orderStats'] | undefined;\n transactions: Transaction[];\n loading: boolean;\n statsLoading: boolean;\n currency: string;\n}) {\n const [open, setOpen] = useState<TenantActivityOrder | null>(null);\n\n return (\n <>\n <Panel\n title=\"Orders\"\n count={stats?.total}\n caption={stats ? `${stats.today.toLocaleString()} today` : undefined}\n height=\"calc(var(--adm-panel-h) + 120px)\"\n flush\n >\n <OrderPipeline stats={stats} loading={statsLoading} />\n <TableShell\n bare\n minWidth={640}\n density=\"compact\"\n head={\n <tr>\n <Th>Order</Th>\n <Th>Customer</Th>\n <Th align=\"right\">Total</Th>\n <Th>Payment</Th>\n <Th>Status</Th>\n <Th align=\"right\">Placed</Th>\n </tr>\n }\n >\n {loading ? (\n Array.from({ length: 5 }, (_, i) => (\n <tr key={i} aria-hidden>\n <td colSpan={6}>\n <Skeleton height={20} />\n </td>\n </tr>\n ))\n ) : orders.length === 0 ? (\n <tr>\n <td colSpan={6} className={styles.emptyCell}>\n No orders yet. They show here as soon as a customer checks out.\n </td>\n </tr>\n ) : (\n orders.map((o) => (\n <TableRow\n key={o.id}\n active={open?.id === o.id}\n onClick={() => setOpen(o)}\n >\n <td className={styles.mono} title={o.orderNumber}>\n {/* The row opens on click; this is its keyboard way in. */}\n <button\n type=\"button\"\n className={styles.orderLink}\n onClick={(e) => {\n e.stopPropagation();\n setOpen(o);\n }}\n >\n {o.orderNumber}\n </button>\n </td>\n <td>\n <span\n className={styles.truncate}\n title={o.customerName || o.customerEmail}\n >\n {o.customerName || o.customerEmail}\n </span>\n </td>\n <td className={styles.num}>\n {moneyIn(o.currency || currency, 2)(o.total)}\n </td>\n <td>\n <StatusPill status={o.paymentStatus} />\n </td>\n <td>\n <StatusPill status={o.status} />\n </td>\n <td className={styles.num}>{shortDate(o.createdAt)}</td>\n </TableRow>\n ))\n )}\n </TableShell>\n </Panel>\n <OrderDrawer\n order={open}\n transactions={transactions}\n currency={currency}\n onClose={() => setOpen(null)}\n />\n </>\n );\n}\n"],"mappings":";AAmCI,mBAQI,KAMI,YAdR;AAjCJ,SAAS,gBAAgB;AAMzB,SAAS,UAAU,YAAY,UAAU;AACzC,SAAS,OAAO,gBAAgB;AAChC,SAAS,SAAS,iBAAiB;AACnC,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AAGZ,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAqC,IAAI;AAEjE,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,OAAO;AAAA,QACd,SAAS,QAAQ,GAAG,MAAM,MAAM,eAAe,CAAC,WAAW;AAAA,QAC3D,QAAO;AAAA,QACP,OAAK;AAAA,QAEL;AAAA,8BAAC,iBAAc,OAAc,SAAS,cAAc;AAAA,UACpD;AAAA,YAAC;AAAA;AAAA,cACC,MAAI;AAAA,cACJ,UAAU;AAAA,cACV,SAAQ;AAAA,cACR,MACE,qBAAC,QACC;AAAA,oCAAC,MAAG,mBAAK;AAAA,gBACT,oBAAC,MAAG,sBAAQ;AAAA,gBACZ,oBAAC,MAAG,OAAM,SAAQ,mBAAK;AAAA,gBACvB,oBAAC,MAAG,qBAAO;AAAA,gBACX,oBAAC,MAAG,oBAAM;AAAA,gBACV,oBAAC,MAAG,OAAM,SAAQ,oBAAM;AAAA,iBAC1B;AAAA,cAGD,oBACC,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAC5B,oBAAC,QAAW,eAAW,MACrB,8BAAC,QAAG,SAAS,GACX,8BAAC,YAAS,QAAQ,IAAI,GACxB,KAHO,CAIT,CACD,IACC,OAAO,WAAW,IACpB,oBAAC,QACC,8BAAC,QAAG,SAAS,GAAG,WAAW,OAAO,WAAW,6EAE7C,GACF,IAEA,OAAO,IAAI,CAAC,MACV;AAAA,gBAAC;AAAA;AAAA,kBAEC,QAAQ,MAAM,OAAO,EAAE;AAAA,kBACvB,SAAS,MAAM,QAAQ,CAAC;AAAA,kBAExB;AAAA,wCAAC,QAAG,WAAW,OAAO,MAAM,OAAO,EAAE,aAEnC;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAK;AAAA,wBACL,WAAW,OAAO;AAAA,wBAClB,SAAS,CAAC,MAAM;AACd,4BAAE,gBAAgB;AAClB,kCAAQ,CAAC;AAAA,wBACX;AAAA,wBAEC,YAAE;AAAA;AAAA,oBACL,GACF;AAAA,oBACA,oBAAC,QACC;AAAA,sBAAC;AAAA;AAAA,wBACC,WAAW,OAAO;AAAA,wBAClB,OAAO,EAAE,gBAAgB,EAAE;AAAA,wBAE1B,YAAE,gBAAgB,EAAE;AAAA;AAAA,oBACvB,GACF;AAAA,oBACA,oBAAC,QAAG,WAAW,OAAO,KACnB,kBAAQ,EAAE,YAAY,UAAU,CAAC,EAAE,EAAE,KAAK,GAC7C;AAAA,oBACA,oBAAC,QACC,8BAAC,cAAW,QAAQ,EAAE,eAAe,GACvC;AAAA,oBACA,oBAAC,QACC,8BAAC,cAAW,QAAQ,EAAE,QAAQ,GAChC;AAAA,oBACA,oBAAC,QAAG,WAAW,OAAO,KAAM,oBAAU,EAAE,SAAS,GAAE;AAAA;AAAA;AAAA,gBAlC9C,EAAE;AAAA,cAmCT,CACD;AAAA;AAAA,UAEL;AAAA;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA,SAAS,MAAM,QAAQ,IAAI;AAAA;AAAA,IAC7B;AAAA,KACF;AAEJ;","names":[]}
|