@withpotter/pages-admin 0.13.0-next.123 → 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 -296
- 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,7 +2,6 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { Button } from "@withpotter/ui-kit/button";
|
|
5
|
-
import { FilterSelect, Filters } from "../_ui";
|
|
6
5
|
import { useMoneyPermissions } from "./permissions";
|
|
7
6
|
import { ProposeCashDialog } from "./propose-dialog";
|
|
8
7
|
import { ReviewProposalDialog } from "./review-dialog";
|
|
@@ -25,13 +24,8 @@ function ActionCard({
|
|
|
25
24
|
] })
|
|
26
25
|
] });
|
|
27
26
|
}
|
|
28
|
-
const SHOW = [
|
|
29
|
-
{ value: "post", label: "Cash postings" },
|
|
30
|
-
{ value: "waiting", label: "Waiting for approval" }
|
|
31
|
-
];
|
|
32
27
|
function CashPostings() {
|
|
33
28
|
const perms = useMoneyPermissions();
|
|
34
|
-
const [show, setShow] = useState(SHOW[0].value);
|
|
35
29
|
const [proposing, setProposing] = useState(false);
|
|
36
30
|
const [reviewing, setReviewing] = useState(false);
|
|
37
31
|
const [review, setReview] = useState({ id: "", nonce: 0 });
|
|
@@ -40,18 +34,7 @@ function CashPostings() {
|
|
|
40
34
|
setReviewing(true);
|
|
41
35
|
};
|
|
42
36
|
return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 16 }, children: [
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
-
FilterSelect,
|
|
45
|
-
{
|
|
46
|
-
label: "Show",
|
|
47
|
-
value: show,
|
|
48
|
-
allValue: SHOW[0].value,
|
|
49
|
-
onChange: setShow,
|
|
50
|
-
options: SHOW,
|
|
51
|
-
className: styles.showSelect
|
|
52
|
-
}
|
|
53
|
-
) }) }),
|
|
54
|
-
show === "post" && /* @__PURE__ */ jsxs("div", { className: styles.cashGrid, children: [
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: styles.cashGrid, children: [
|
|
55
38
|
/* @__PURE__ */ jsx(
|
|
56
39
|
ActionCard,
|
|
57
40
|
{
|
|
@@ -67,14 +50,14 @@ function CashPostings() {
|
|
|
67
50
|
ActionCard,
|
|
68
51
|
{
|
|
69
52
|
title: "Review by ID",
|
|
70
|
-
body: "Open a posting by its ID when it is not in
|
|
53
|
+
body: "Open a posting by its ID when it is not in the list below.",
|
|
71
54
|
cta: "Review by ID",
|
|
72
55
|
hint: perms.approveHint,
|
|
73
56
|
onClick: () => openReview("")
|
|
74
57
|
}
|
|
75
58
|
)
|
|
76
59
|
] }),
|
|
77
|
-
|
|
60
|
+
/* @__PURE__ */ jsx(WaitingProposals, { onReview: openReview }),
|
|
78
61
|
/* @__PURE__ */ jsx(ProposeCashDialog, { open: proposing, onClose: () => setProposing(false) }),
|
|
79
62
|
/* @__PURE__ */ jsx(
|
|
80
63
|
ReviewProposalDialog,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_money/cash-postings.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button } from '@withpotter/ui-kit/button';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_money/cash-postings.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { useMoneyPermissions } from './permissions';\nimport { ProposeCashDialog } from './propose-dialog';\nimport { ReviewProposalDialog } from './review-dialog';\nimport { WaitingProposals } from './waiting-proposals';\nimport styles from './cash.module.css';\n\nfunction ActionCard({\n title,\n body,\n cta,\n hint,\n primary,\n onClick,\n}: {\n title: string;\n body: string;\n cta: string;\n /** Set when this admin may lack the permission; the API decides. */\n hint?: string;\n primary?: boolean;\n onClick: () => void;\n}) {\n return (\n <div className={styles.actionCard}>\n <h3 className={styles.actionTitle}>{title}</h3>\n <p className={styles.actionBody}>{body}</p>\n <div className={styles.actionFoot}>\n <Button variant={primary ? 'primary' : 'secondary'} onClick={onClick}>\n {cta}\n </Button>\n {hint && <span className=\"adm-caption\">{hint}</span>}\n </div>\n </div>\n );\n}\n\n/**\n * Maker-checker cash postings: one admin proposes, a different admin\n * approves or rejects it from the waiting list (or by its ID).\n */\nexport function CashPostings() {\n const perms = useMoneyPermissions();\n const [proposing, setProposing] = useState(false);\n const [reviewing, setReviewing] = useState(false);\n // A fresh dialog per opening, so a pasted ID never leaks into the next one.\n const [review, setReview] = useState({ id: '', nonce: 0 });\n const openReview = (id: string) => {\n setReview((r) => ({ id, nonce: r.nonce + 1 }));\n setReviewing(true);\n };\n\n return (\n <div style={{ display: 'grid', gap: 16 }}>\n <div className={styles.cashGrid}>\n <ActionCard\n primary\n title=\"Propose a cash posting\"\n body=\"Book opening cash, or cash a merchant handed over off-platform. Nothing moves until a second admin approves.\"\n cta=\"Propose cash posting\"\n hint={perms.proposeHint}\n onClick={() => setProposing(true)}\n />\n <ActionCard\n title=\"Review by ID\"\n body=\"Open a posting by its ID when it is not in the list below.\"\n cta=\"Review by ID\"\n hint={perms.approveHint}\n onClick={() => openReview('')}\n />\n </div>\n <WaitingProposals onReview={openReview} />\n <ProposeCashDialog open={proposing} onClose={() => setProposing(false)} />\n <ReviewProposalDialog\n key={review.nonce}\n open={reviewing}\n initialId={review.id}\n onClose={() => setReviewing(false)}\n />\n </div>\n );\n}\n"],"mappings":";AA4BM,cAEA,YAFA;AA1BN,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,2BAA2B;AACpC,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,wBAAwB;AACjC,OAAO,YAAY;AAEnB,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,SACE,qBAAC,SAAI,WAAW,OAAO,YACrB;AAAA,wBAAC,QAAG,WAAW,OAAO,aAAc,iBAAM;AAAA,IAC1C,oBAAC,OAAE,WAAW,OAAO,YAAa,gBAAK;AAAA,IACvC,qBAAC,SAAI,WAAW,OAAO,YACrB;AAAA,0BAAC,UAAO,SAAS,UAAU,YAAY,aAAa,SACjD,eACH;AAAA,MACC,QAAQ,oBAAC,UAAK,WAAU,eAAe,gBAAK;AAAA,OAC/C;AAAA,KACF;AAEJ;AAMO,SAAS,eAAe;AAC7B,QAAM,QAAQ,oBAAoB;AAClC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE,IAAI,IAAI,OAAO,EAAE,CAAC;AACzD,QAAM,aAAa,CAAC,OAAe;AACjC,cAAU,CAAC,OAAO,EAAE,IAAI,OAAO,EAAE,QAAQ,EAAE,EAAE;AAC7C,iBAAa,IAAI;AAAA,EACnB;AAEA,SACE,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,GACrC;AAAA,yBAAC,SAAI,WAAW,OAAO,UACrB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAO;AAAA,UACP,OAAM;AAAA,UACN,MAAK;AAAA,UACL,KAAI;AAAA,UACJ,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM,aAAa,IAAI;AAAA;AAAA,MAClC;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,MAAK;AAAA,UACL,KAAI;AAAA,UACJ,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM,WAAW,EAAE;AAAA;AAAA,MAC9B;AAAA,OACF;AAAA,IACA,oBAAC,oBAAiB,UAAU,YAAY;AAAA,IACxC,oBAAC,qBAAkB,MAAM,WAAW,SAAS,MAAM,aAAa,KAAK,GAAG;AAAA,IACxE;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM;AAAA,QACN,WAAW,OAAO;AAAA,QAClB,SAAS,MAAM,aAAa,KAAK;AAAA;AAAA,MAH5B,OAAO;AAAA,IAId;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_money/finder-format.ts"],"sourcesContent":["/** Formatting for the merchant finder and its quick view. */\n\n/**\n * The merchant's currency when the row carries one, flat or on the profile.\n * The tenants list does not send it today, so callers fall back to plain.\n */\nexport function currencyOf(row: object): string | null {\n const r = row as {\n currency?: string | null;\n profile?: { currency?: string | null } | null;\n };\n\n return r.currency ?? r.profile?.currency ?? null;\n}\n\n/** A short figure: \"₦1.2M\" in a currency, \"1.2M\" without one. */\nexport function compactAmount(amount: number, currency: string | null): string {\n const base = { notation: 'compact', maximumFractionDigits: 1 } as const;\n\n if (currency) {\n try {\n return new Intl.NumberFormat('en-US', {\n ...base,\n style: 'currency',\n
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_money/finder-format.ts"],"sourcesContent":["/** Formatting for the merchant finder and its quick view. */\n\n/**\n * The merchant's currency when the row carries one, flat or on the profile.\n * The tenants list does not send it today, so callers fall back to plain.\n */\nexport function currencyOf(row: object): string | null {\n const r = row as {\n currency?: string | null;\n profile?: { currency?: string | null } | null;\n };\n\n return r.currency ?? r.profile?.currency ?? null;\n}\n\n/** A short figure: \"₦1.2M\" in a currency, \"1.2M\" without one. */\nexport function compactAmount(amount: number, currency: string | null): string {\n const base = { notation: 'compact', maximumFractionDigits: 1 } as const;\n\n if (currency) {\n try {\n return new Intl.NumberFormat('en-US', {\n ...base,\n style: 'currency',\n currency,\n }).format(amount);\n } catch {\n // An unknown code falls through to the plain number.\n }\n }\n\n return new Intl.NumberFormat('en-US', base).format(amount ?? 0);\n}\n\n/** An account number cut to its last four: \"•••• 4821\". */\nexport function maskAccount(accountNumber: string): string {\n const last = accountNumber.replace(/\\s/g, '').slice(-4);\n\n return last ? `•••• ${last}` : '–';\n}\n\n/** True when a minor-unit string holds something other than zero. */\nexport function nonZero(minor: string): boolean {\n try {\n return BigInt(minor) !== BigInt(0);\n } catch {\n return false;\n }\n}\n\n/** The wallet ID when the tenant detail sends one (it is not typed yet). */\nexport function walletIdOf(wallet: object | null | undefined): string | null {\n const id = (wallet as { id?: unknown } | null | undefined)?.id;\n\n return typeof id === 'string' && id ? id : null;\n}\n"],"mappings":"AAMO,SAAS,WAAW,KAA4B;AACrD,QAAM,IAAI;AAKV,SAAO,EAAE,YAAY,EAAE,SAAS,YAAY;AAC9C;AAGO,SAAS,cAAc,QAAgB,UAAiC;AAC7E,QAAM,OAAO,EAAE,UAAU,WAAW,uBAAuB,EAAE;AAE7D,MAAI,UAAU;AACZ,QAAI;AACF,aAAO,IAAI,KAAK,aAAa,SAAS;AAAA,QACpC,GAAG;AAAA,QACH,OAAO;AAAA,QACP;AAAA,MACF,CAAC,EAAE,OAAO,MAAM;AAAA,IAClB,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,IAAI,KAAK,aAAa,SAAS,IAAI,EAAE,OAAO,UAAU,CAAC;AAChE;AAGO,SAAS,YAAY,eAA+B;AACzD,QAAM,OAAO,cAAc,QAAQ,OAAO,EAAE,EAAE,MAAM,EAAE;AAEtD,SAAO,OAAO,4BAAQ,IAAI,KAAK;AACjC;AAGO,SAAS,QAAQ,OAAwB;AAC9C,MAAI;AACF,WAAO,OAAO,KAAK,MAAM,OAAO,CAAC;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,WAAW,QAAkD;AAC3E,QAAM,KAAM,QAAgD;AAE5D,SAAO,OAAO,OAAO,YAAY,KAAK,KAAK;AAC7C;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_money/format.ts"],"sourcesContent":["import { formatMinor } from '@withpotter/lib/money-minor';\n\n/** Money and time formatting for the money screens. Amounts are major units. */\n\nexport function money(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_money/format.ts"],"sourcesContent":["import { formatMinor } from '@withpotter/lib/money-minor';\n\n/** Money and time formatting for the money screens. Amounts are major units. */\n\nexport function money(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'NGN',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount ?? 0);\n } catch {\n return `${currency} ${(amount ?? 0).toFixed(2)}`;\n }\n}\n\n/**\n * Minor units held as a string, shown in the currency (\"150000\" NGN → ₦1,500.00,\n * \"1500\" JPY → ¥1,500). Split at the currency's own exponent, never ÷ 100.\n */\nexport function moneyMinor(minor: string, currency: string): string {\n return /^-?\\d+$/.test(minor)\n ? formatMinor(minor, currency || 'NGN')\n : `${minor} ${currency}`;\n}\n\nexport function when(iso: string | null | undefined): string {\n if (!iso) return '–';\n\n const d = new Date(iso);\n\n if (Number.isNaN(d.getTime())) return '–';\n\n return d.toLocaleString(undefined, {\n day: 'numeric',\n month: 'short',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function day(iso: string | null | undefined): string {\n if (!iso) return '–';\n\n const d = new Date(iso);\n\n if (Number.isNaN(d.getTime())) return '–';\n\n // \"15 Oct 2026\" everywhere, whatever the browser's locale.\n return d.toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n });\n}\n\n/** \"3h\", \"2d\": how long a record has been open. */\nexport function age(iso: string, now = Date.now()): string {\n const ms = Math.max(0, now - new Date(iso).getTime());\n const h = Math.floor(ms / 3_600_000);\n\n if (h < 1) return `${Math.max(1, Math.floor(ms / 60_000))}m`;\n\n if (h < 48) return `${h}h`;\n\n return `${Math.floor(h / 24)}d`;\n}\n\n/** Keep the start and end of a long ID: \"8f2c1a…9b0d\". */\nexport function middle(id: string, keep = 6): string {\n return id.length <= keep * 2 + 1\n ? id\n : `${id.slice(0, keep)}…${id.slice(-4)}`;\n}\n\n/** An API error's own words when it has some, a plain line when not. */\nexport function errorText(error: unknown, fallback: string): string {\n if (error instanceof Error && error.message) return error.message;\n\n return fallback;\n}\n"],"mappings":"AAAA,SAAS,mBAAmB;AAIrB,SAAS,MAAM,QAAgB,UAA0B;AAC9D,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,SAAS;AAAA,MACpC,OAAO;AAAA,MACP,UAAU,YAAY;AAAA,MACtB,uBAAuB;AAAA,MACvB,uBAAuB;AAAA,IACzB,CAAC,EAAE,OAAO,UAAU,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO,GAAG,QAAQ,KAAK,UAAU,GAAG,QAAQ,CAAC,CAAC;AAAA,EAChD;AACF;AAMO,SAAS,WAAW,OAAe,UAA0B;AAClE,SAAO,UAAU,KAAK,KAAK,IACvB,YAAY,OAAO,YAAY,KAAK,IACpC,GAAG,KAAK,IAAI,QAAQ;AAC1B;AAEO,SAAS,KAAK,KAAwC;AAC3D,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,IAAI,IAAI,KAAK,GAAG;AAEtB,MAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AAEtC,SAAO,EAAE,eAAe,QAAW;AAAA,IACjC,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACH;AAEO,SAAS,IAAI,KAAwC;AAC1D,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,IAAI,IAAI,KAAK,GAAG;AAEtB,MAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AAGtC,SAAO,EAAE,mBAAmB,SAAS;AAAA,IACnC,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,EACR,CAAC;AACH;AAGO,SAAS,IAAI,KAAa,MAAM,KAAK,IAAI,GAAW;AACzD,QAAM,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,KAAK,GAAG,EAAE,QAAQ,CAAC;AACpD,QAAM,IAAI,KAAK,MAAM,KAAK,IAAS;AAEnC,MAAI,IAAI,EAAG,QAAO,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAM,CAAC,CAAC;AAEzD,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AAEvB,SAAO,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;AAC9B;AAGO,SAAS,OAAO,IAAY,OAAO,GAAW;AACnD,SAAO,GAAG,UAAU,OAAO,IAAI,IAC3B,KACA,GAAG,GAAG,MAAM,GAAG,IAAI,CAAC,SAAI,GAAG,MAAM,EAAE,CAAC;AAC1C;AAGO,SAAS,UAAU,OAAgB,UAA0B;AAClE,MAAI,iBAAiB,SAAS,MAAM,QAAS,QAAO,MAAM;AAE1D,SAAO;AACT;","names":[]}
|
|
@@ -6,11 +6,11 @@ function ActivationPanel({
|
|
|
6
6
|
activation
|
|
7
7
|
}) {
|
|
8
8
|
if (!activation) {
|
|
9
|
-
return /* @__PURE__ */ jsx(Panel, { title: "
|
|
9
|
+
return /* @__PURE__ */ jsx(Panel, { title: "Activation funnel", caption: "All time", height: "auto", children: /* @__PURE__ */ jsx(AdmLoading, { label: "Loading activation funnel\u2026", size: "sm" }) });
|
|
10
10
|
}
|
|
11
11
|
const stages = activation.funnel;
|
|
12
12
|
const top = stages[0]?.count || 1;
|
|
13
|
-
return /* @__PURE__ */ jsxs(Panel, { title: "
|
|
13
|
+
return /* @__PURE__ */ jsxs(Panel, { title: "Activation funnel", caption: "All time", height: "auto", children: [
|
|
14
14
|
/* @__PURE__ */ jsx("ul", { className: styles.bars, children: stages.map((s) => /* @__PURE__ */ jsxs("li", { children: [
|
|
15
15
|
/* @__PURE__ */ jsxs("div", { className: styles.barLabel, children: [
|
|
16
16
|
/* @__PURE__ */ jsx("span", { children: s.stage }),
|
|
@@ -26,24 +26,24 @@ function ActivationPanel({
|
|
|
26
26
|
}
|
|
27
27
|
) })
|
|
28
28
|
] }, s.stage)) }),
|
|
29
|
-
/* @__PURE__ */ jsx("div", { className: styles.subhead, children: "
|
|
29
|
+
/* @__PURE__ */ jsx("div", { className: styles.subhead, children: "Conversion" }),
|
|
30
30
|
/* @__PURE__ */ jsxs("ul", { className: styles.bars, children: [
|
|
31
31
|
/* @__PURE__ */ jsxs("li", { className: styles.barLabel, children: [
|
|
32
|
-
/* @__PURE__ */ jsx("span", { children: "Signed up
|
|
32
|
+
/* @__PURE__ */ jsx("span", { children: "Signed up \u2192 activated" }),
|
|
33
33
|
/* @__PURE__ */ jsxs("strong", { children: [
|
|
34
34
|
activation.activationRatePct,
|
|
35
35
|
"%"
|
|
36
36
|
] })
|
|
37
37
|
] }),
|
|
38
38
|
/* @__PURE__ */ jsxs("li", { className: styles.barLabel, children: [
|
|
39
|
-
/* @__PURE__ */ jsx("span", { children: "Signed up
|
|
39
|
+
/* @__PURE__ */ jsx("span", { children: "Signed up \u2192 first order" }),
|
|
40
40
|
/* @__PURE__ */ jsxs("strong", { children: [
|
|
41
41
|
activation.orderConversionPct,
|
|
42
42
|
"%"
|
|
43
43
|
] })
|
|
44
44
|
] }),
|
|
45
45
|
/* @__PURE__ */ jsxs("li", { className: styles.barLabel, children: [
|
|
46
|
-
/* @__PURE__ */ jsx("span", { children: "
|
|
46
|
+
/* @__PURE__ */ jsx("span", { children: "Median days to first order" }),
|
|
47
47
|
/* @__PURE__ */ jsx("strong", { children: activation.medianDaysToFirstOrder == null ? "\u2013" : activation.medianDaysToFirstOrder })
|
|
48
48
|
] })
|
|
49
49
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_overview/activation-panel.tsx"],"sourcesContent":["import type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { AdmLoading, Panel } from '../_ui';\nimport styles from './figures.module.css';\n\nconst integer = new Intl.NumberFormat();\n\n/**\n * Signed up → Activated → First order → First payout, lifetime (not\n * range-scoped — a funnel this shallow needs the whole base to read).\n */\nexport function ActivationPanel({\n activation,\n}: {\n activation: PlatformCockpit['activation'];\n}) {\n if (!activation) {\n return (\n <Panel title=\"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_overview/activation-panel.tsx"],"sourcesContent":["import type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { AdmLoading, Panel } from '../_ui';\nimport styles from './figures.module.css';\n\nconst integer = new Intl.NumberFormat();\n\n/**\n * Signed up → Activated → First order → First payout, lifetime (not\n * range-scoped — a funnel this shallow needs the whole base to read).\n */\nexport function ActivationPanel({\n activation,\n}: {\n activation: PlatformCockpit['activation'];\n}) {\n if (!activation) {\n return (\n <Panel title=\"Activation funnel\" caption=\"All time\" height=\"auto\">\n <AdmLoading label=\"Loading activation funnel…\" size=\"sm\" />\n </Panel>\n );\n }\n\n const stages = activation.funnel;\n const top = stages[0]?.count || 1;\n\n return (\n <Panel title=\"Activation funnel\" caption=\"All time\" height=\"auto\">\n <ul className={styles.bars}>\n {stages.map((s) => (\n <li key={s.stage}>\n <div className={styles.barLabel}>\n <span>{s.stage}</span>\n <strong>{integer.format(s.count)}</strong>\n </div>\n <div className={styles.track}>\n <span\n style={{\n width: `${Math.max((s.count / top) * 100, 2)}%`,\n background: 'var(--accent-primary)',\n }}\n />\n </div>\n </li>\n ))}\n </ul>\n\n <div className={styles.subhead}>Conversion</div>\n <ul className={styles.bars}>\n <li className={styles.barLabel}>\n <span>Signed up → activated</span>\n <strong>{activation.activationRatePct}%</strong>\n </li>\n <li className={styles.barLabel}>\n <span>Signed up → first order</span>\n <strong>{activation.orderConversionPct}%</strong>\n </li>\n <li className={styles.barLabel}>\n <span>Median days to first order</span>\n <strong>\n {activation.medianDaysToFirstOrder == null\n ? '–'\n : activation.medianDaysToFirstOrder}\n </strong>\n </li>\n </ul>\n </Panel>\n );\n}\n"],"mappings":"AAkBQ,cAaI,YAbJ;AAjBR,SAAS,YAAY,aAAa;AAClC,OAAO,YAAY;AAEnB,MAAM,UAAU,IAAI,KAAK,aAAa;AAM/B,SAAS,gBAAgB;AAAA,EAC9B;AACF,GAEG;AACD,MAAI,CAAC,YAAY;AACf,WACE,oBAAC,SAAM,OAAM,qBAAoB,SAAQ,YAAW,QAAO,QACzD,8BAAC,cAAW,OAAM,mCAA6B,MAAK,MAAK,GAC3D;AAAA,EAEJ;AAEA,QAAM,SAAS,WAAW;AAC1B,QAAM,MAAM,OAAO,CAAC,GAAG,SAAS;AAEhC,SACE,qBAAC,SAAM,OAAM,qBAAoB,SAAQ,YAAW,QAAO,QACzD;AAAA,wBAAC,QAAG,WAAW,OAAO,MACnB,iBAAO,IAAI,CAAC,MACX,qBAAC,QACC;AAAA,2BAAC,SAAI,WAAW,OAAO,UACrB;AAAA,4BAAC,UAAM,YAAE,OAAM;AAAA,QACf,oBAAC,YAAQ,kBAAQ,OAAO,EAAE,KAAK,GAAE;AAAA,SACnC;AAAA,MACA,oBAAC,SAAI,WAAW,OAAO,OACrB;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,OAAO,GAAG,KAAK,IAAK,EAAE,QAAQ,MAAO,KAAK,CAAC,CAAC;AAAA,YAC5C,YAAY;AAAA,UACd;AAAA;AAAA,MACF,GACF;AAAA,SAZO,EAAE,KAaX,CACD,GACH;AAAA,IAEA,oBAAC,SAAI,WAAW,OAAO,SAAS,wBAAU;AAAA,IAC1C,qBAAC,QAAG,WAAW,OAAO,MACpB;AAAA,2BAAC,QAAG,WAAW,OAAO,UACpB;AAAA,4BAAC,UAAK,wCAAqB;AAAA,QAC3B,qBAAC,YAAQ;AAAA,qBAAW;AAAA,UAAkB;AAAA,WAAC;AAAA,SACzC;AAAA,MACA,qBAAC,QAAG,WAAW,OAAO,UACpB;AAAA,4BAAC,UAAK,0CAAuB;AAAA,QAC7B,qBAAC,YAAQ;AAAA,qBAAW;AAAA,UAAmB;AAAA,WAAC;AAAA,SAC1C;AAAA,MACA,qBAAC,QAAG,WAAW,OAAO,UACpB;AAAA,4BAAC,UAAK,wCAA0B;AAAA,QAChC,oBAAC,YACE,qBAAW,0BAA0B,OAClC,WACA,WAAW,wBACjB;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -13,13 +13,13 @@ function attentionGroups(a) {
|
|
|
13
13
|
tenantId: s.tenantId,
|
|
14
14
|
href: tenant(s.tenantId),
|
|
15
15
|
title: s.tenantName,
|
|
16
|
-
detail: "
|
|
16
|
+
detail: "Settlement failed",
|
|
17
17
|
value: inCurrency(s.netAmount, s.currency)
|
|
18
18
|
}))
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
key: "invoices",
|
|
22
|
-
title: "Overdue
|
|
22
|
+
title: "Overdue",
|
|
23
23
|
count: a.overdueInvoices.count,
|
|
24
24
|
note: overdueOutstanding(a.overdueInvoices) ?? void 0,
|
|
25
25
|
items: a.overdueInvoices.sample.map((i) => ({
|
|
@@ -28,13 +28,13 @@ function attentionGroups(a) {
|
|
|
28
28
|
tenantId: i.tenantId,
|
|
29
29
|
href: tenant(i.tenantId),
|
|
30
30
|
title: i.tenantName,
|
|
31
|
-
detail: `
|
|
31
|
+
detail: `Invoice ${i.invoiceNumber}`,
|
|
32
32
|
value: inCurrency(i.amountDue, i.currency)
|
|
33
33
|
}))
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
key: "review",
|
|
37
|
-
title: "
|
|
37
|
+
title: "Review",
|
|
38
38
|
count: a.needsReview.length,
|
|
39
39
|
items: a.needsReview.map((t) => ({
|
|
40
40
|
id: t.id,
|
|
@@ -43,12 +43,12 @@ function attentionGroups(a) {
|
|
|
43
43
|
href: tenant(t.id),
|
|
44
44
|
title: t.businessName,
|
|
45
45
|
detail: t.slug,
|
|
46
|
-
value: t.status
|
|
46
|
+
value: t.status
|
|
47
47
|
}))
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
key: "trials",
|
|
51
|
-
title: "
|
|
51
|
+
title: "Trials ending",
|
|
52
52
|
count: a.trialsEndingSoon.length,
|
|
53
53
|
items: a.trialsEndingSoon.map((t) => ({
|
|
54
54
|
id: t.id,
|
|
@@ -57,12 +57,12 @@ function attentionGroups(a) {
|
|
|
57
57
|
href: tenant(t.id),
|
|
58
58
|
title: t.businessName,
|
|
59
59
|
detail: t.slug,
|
|
60
|
-
value: t.daysLeft === 0 ? "Today" : `${t.daysLeft}
|
|
60
|
+
value: t.daysLeft === 0 ? "Today" : `${t.daysLeft}d left`
|
|
61
61
|
}))
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
key: "dormant",
|
|
65
|
-
title: "
|
|
65
|
+
title: "Dormant",
|
|
66
66
|
count: a.dormantTenants.length,
|
|
67
67
|
note: "No orders or sign-ins for 60+ days.",
|
|
68
68
|
items: a.dormantTenants.map((t) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_overview/attention-groups.ts"],"sourcesContent":["import type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { logoOf } from '../commerce/merchant-meta';\nimport { inCurrency, overdueOutstanding } from './overdue-outstanding';\n\nexport interface AttentionItem {\n id: string;\n /** The merchant the row is about, for views that open it in place. */\n tenantId: string;\n href: string;\n title: string;\n detail: string;\n value: string;\n /** The merchant's logo, once the cockpit sends one; initials until then. */\n logo?: string | null;\n}\n\nexport interface AttentionGroup {\n key: string;\n title: string;\n count: number;\n note?: string;\n items: AttentionItem[];\n}\n\nconst tenant = (id: string) => `/admin/tenants/${id}`;\n\n/** The cockpit's exceptions as uniform groups, most urgent kinds first. */\nexport function attentionGroups(\n a: PlatformCockpit['attention'],\n): AttentionGroup[] {\n return [\n {\n key: 'settlements',\n title: 'Failed payouts',\n count: a.failedSettlements.count,\n items: a.failedSettlements.sample.map((s) => ({\n id: s.id,\n logo: logoOf(s),\n tenantId: s.tenantId,\n href: tenant(s.tenantId),\n title: s.tenantName,\n detail: '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_overview/attention-groups.ts"],"sourcesContent":["import type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { logoOf } from '../commerce/merchant-meta';\nimport { inCurrency, overdueOutstanding } from './overdue-outstanding';\n\nexport interface AttentionItem {\n id: string;\n /** The merchant the row is about, for views that open it in place. */\n tenantId: string;\n href: string;\n title: string;\n detail: string;\n value: string;\n /** The merchant's logo, once the cockpit sends one; initials until then. */\n logo?: string | null;\n}\n\nexport interface AttentionGroup {\n key: string;\n title: string;\n count: number;\n note?: string;\n items: AttentionItem[];\n}\n\nconst tenant = (id: string) => `/admin/tenants/${id}`;\n\n/** The cockpit's exceptions as uniform groups, most urgent kinds first. */\nexport function attentionGroups(\n a: PlatformCockpit['attention'],\n): AttentionGroup[] {\n return [\n {\n key: 'settlements',\n title: 'Failed payouts',\n count: a.failedSettlements.count,\n items: a.failedSettlements.sample.map((s) => ({\n id: s.id,\n logo: logoOf(s),\n tenantId: s.tenantId,\n href: tenant(s.tenantId),\n title: s.tenantName,\n detail: 'Settlement failed',\n value: inCurrency(s.netAmount, s.currency),\n })),\n },\n {\n key: 'invoices',\n title: 'Overdue',\n count: a.overdueInvoices.count,\n note: overdueOutstanding(a.overdueInvoices) ?? undefined,\n items: a.overdueInvoices.sample.map((i) => ({\n id: i.id,\n logo: logoOf(i),\n tenantId: i.tenantId,\n href: tenant(i.tenantId),\n title: i.tenantName,\n detail: `Invoice ${i.invoiceNumber}`,\n value: inCurrency(i.amountDue, i.currency),\n })),\n },\n {\n key: 'review',\n title: 'Review',\n count: a.needsReview.length,\n items: a.needsReview.map((t) => ({\n id: t.id,\n logo: logoOf(t),\n tenantId: t.id,\n href: tenant(t.id),\n title: t.businessName,\n detail: t.slug,\n value: t.status,\n })),\n },\n {\n key: 'trials',\n title: 'Trials ending',\n count: a.trialsEndingSoon.length,\n items: a.trialsEndingSoon.map((t) => ({\n id: t.id,\n logo: logoOf(t),\n tenantId: t.id,\n href: tenant(t.id),\n title: t.businessName,\n detail: t.slug,\n value: t.daysLeft === 0 ? 'Today' : `${t.daysLeft}d left`,\n })),\n },\n {\n key: 'dormant',\n title: 'Dormant',\n count: a.dormantTenants.length,\n note: 'No orders or sign-ins for 60+ days.',\n items: a.dormantTenants.map((t) => ({\n id: t.id,\n logo: logoOf(t),\n tenantId: t.id,\n href: tenant(t.id),\n title: t.businessName,\n detail: t.slug,\n value: '60d+',\n })),\n },\n ];\n}\n"],"mappings":"AACA,SAAS,cAAc;AACvB,SAAS,YAAY,0BAA0B;AAsB/C,MAAM,SAAS,CAAC,OAAe,kBAAkB,EAAE;AAG5C,SAAS,gBACd,GACkB;AAClB,SAAO;AAAA,IACL;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO,EAAE,kBAAkB;AAAA,MAC3B,OAAO,EAAE,kBAAkB,OAAO,IAAI,CAAC,OAAO;AAAA,QAC5C,IAAI,EAAE;AAAA,QACN,MAAM,OAAO,CAAC;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,MAAM,OAAO,EAAE,QAAQ;AAAA,QACvB,OAAO,EAAE;AAAA,QACT,QAAQ;AAAA,QACR,OAAO,WAAW,EAAE,WAAW,EAAE,QAAQ;AAAA,MAC3C,EAAE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO,EAAE,gBAAgB;AAAA,MACzB,MAAM,mBAAmB,EAAE,eAAe,KAAK;AAAA,MAC/C,OAAO,EAAE,gBAAgB,OAAO,IAAI,CAAC,OAAO;AAAA,QAC1C,IAAI,EAAE;AAAA,QACN,MAAM,OAAO,CAAC;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,MAAM,OAAO,EAAE,QAAQ;AAAA,QACvB,OAAO,EAAE;AAAA,QACT,QAAQ,WAAW,EAAE,aAAa;AAAA,QAClC,OAAO,WAAW,EAAE,WAAW,EAAE,QAAQ;AAAA,MAC3C,EAAE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO,EAAE,YAAY;AAAA,MACrB,OAAO,EAAE,YAAY,IAAI,CAAC,OAAO;AAAA,QAC/B,IAAI,EAAE;AAAA,QACN,MAAM,OAAO,CAAC;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,MAAM,OAAO,EAAE,EAAE;AAAA,QACjB,OAAO,EAAE;AAAA,QACT,QAAQ,EAAE;AAAA,QACV,OAAO,EAAE;AAAA,MACX,EAAE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO,EAAE,iBAAiB;AAAA,MAC1B,OAAO,EAAE,iBAAiB,IAAI,CAAC,OAAO;AAAA,QACpC,IAAI,EAAE;AAAA,QACN,MAAM,OAAO,CAAC;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,MAAM,OAAO,EAAE,EAAE;AAAA,QACjB,OAAO,EAAE;AAAA,QACT,QAAQ,EAAE;AAAA,QACV,OAAO,EAAE,aAAa,IAAI,UAAU,GAAG,EAAE,QAAQ;AAAA,MACnD,EAAE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO,EAAE,eAAe;AAAA,MACxB,MAAM;AAAA,MACN,OAAO,EAAE,eAAe,IAAI,CAAC,OAAO;AAAA,QAClC,IAAI,EAAE;AAAA,QACN,MAAM,OAAO,CAAC;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,MAAM,OAAO,EAAE,EAAE;AAAA,QACjB,OAAO,EAAE;AAAA,QACT,QAAQ,EAAE;AAAA,QACV,OAAO;AAAA,MACT,EAAE;AAAA,IACJ;AAAA,EACF;AACF;","names":[]}
|
|
@@ -14,43 +14,39 @@ function AttentionPanel({
|
|
|
14
14
|
const total = groups.reduce((sum, g) => sum + g.count, 0);
|
|
15
15
|
const [picked, setPicked] = useState(null);
|
|
16
16
|
const active = groups.find((g) => g.key === picked) ?? [...groups].sort((a, b) => b.count - a.count)[0];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
),
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/* @__PURE__ */ jsx("span", { className: styles.rowValue, children: item.value })
|
|
51
|
-
] }) }, item.id)) })
|
|
52
|
-
] })
|
|
53
|
-
] });
|
|
17
|
+
return /* @__PURE__ */ jsx(Panel, { title: "Needs attention", count: total, flush: true, children: !active ? /* @__PURE__ */ jsx("div", { className: styles.clear, children: /* @__PURE__ */ jsx(EmptyNote, { children: "No exceptions in this period." }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
18
|
+
/* @__PURE__ */ jsx(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
className: styles.filters,
|
|
22
|
+
role: "group",
|
|
23
|
+
"aria-label": "Show exceptions of one kind",
|
|
24
|
+
children: groups.map((g) => /* @__PURE__ */ jsxs(
|
|
25
|
+
"button",
|
|
26
|
+
{
|
|
27
|
+
type: "button",
|
|
28
|
+
"aria-pressed": g.key === active.key,
|
|
29
|
+
className: styles.filter,
|
|
30
|
+
onClick: () => setPicked(g.key),
|
|
31
|
+
children: [
|
|
32
|
+
g.title,
|
|
33
|
+
/* @__PURE__ */ jsx("span", { children: g.count })
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
g.key
|
|
37
|
+
))
|
|
38
|
+
}
|
|
39
|
+
),
|
|
40
|
+
active.note && /* @__PURE__ */ jsx("p", { className: styles.note, children: active.note }),
|
|
41
|
+
/* @__PURE__ */ jsx("ul", { className: styles.rows, children: active.items.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, { href: item.href, className: styles.row, children: [
|
|
42
|
+
item.logo && /* @__PURE__ */ jsx(EntityAvatar, { name: item.title, src: item.logo, size: 32 }),
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { className: styles.rowMain, children: [
|
|
44
|
+
/* @__PURE__ */ jsx("strong", { children: item.title }),
|
|
45
|
+
/* @__PURE__ */ jsx("small", { children: item.detail })
|
|
46
|
+
] }),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: styles.rowValue, children: item.value })
|
|
48
|
+
] }) }, item.id)) })
|
|
49
|
+
] }) });
|
|
54
50
|
}
|
|
55
51
|
export {
|
|
56
52
|
AttentionPanel
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_overview/attention-panel.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { Panel } from '../_ui';\nimport { attentionGroups } from './attention-groups';\nimport styles from './attention.module.css';\n\n/**\n * Every exception in one card: a filter per kind, the rows scrolling inside.\n * The busiest kind is open first; empty kinds are not offered at all.\n */\nexport function AttentionPanel({\n attention,\n}: {\n attention: PlatformCockpit['attention'];\n}) {\n const groups = attentionGroups(attention).filter((g) => g.count);\n const total = groups.reduce((sum, g) => sum + g.count, 0);\n const [picked, setPicked] = useState<string | null>(null);\n const active =\n groups.find((g) => g.key === picked) ??\n [...groups].sort((a, b) => b.count - a.count)[0];\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_overview/attention-panel.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { Panel } from '../_ui';\nimport { attentionGroups } from './attention-groups';\nimport styles from './attention.module.css';\n\n/**\n * Every exception in one card: a filter per kind, the rows scrolling inside.\n * The busiest kind is open first; empty kinds are not offered at all.\n */\nexport function AttentionPanel({\n attention,\n}: {\n attention: PlatformCockpit['attention'];\n}) {\n const groups = attentionGroups(attention).filter((g) => g.count);\n const total = groups.reduce((sum, g) => sum + g.count, 0);\n const [picked, setPicked] = useState<string | null>(null);\n const active =\n groups.find((g) => g.key === picked) ??\n [...groups].sort((a, b) => b.count - a.count)[0];\n\n return (\n <Panel title=\"Needs attention\" count={total} flush>\n {!active ? (\n <div className={styles.clear}>\n <EmptyNote>No exceptions in this period.</EmptyNote>\n </div>\n ) : (\n <>\n <div\n className={styles.filters}\n role=\"group\"\n aria-label=\"Show exceptions of one kind\"\n >\n {groups.map((g) => (\n <button\n key={g.key}\n type=\"button\"\n aria-pressed={g.key === active.key}\n className={styles.filter}\n onClick={() => setPicked(g.key)}\n >\n {g.title}\n <span>{g.count}</span>\n </button>\n ))}\n </div>\n {active.note && <p className={styles.note}>{active.note}</p>}\n <ul className={styles.rows}>\n {active.items.map((item) => (\n <li key={item.id}>\n <Link href={item.href} className={styles.row}>\n {item.logo && (\n <EntityAvatar name={item.title} src={item.logo} size={32} />\n )}\n <span className={styles.rowMain}>\n <strong>{item.title}</strong>\n <small>{item.detail}</small>\n </span>\n <span className={styles.rowValue}>{item.value}</span>\n </Link>\n </li>\n ))}\n </ul>\n </>\n )}\n </Panel>\n );\n}\n"],"mappings":";AA+BU,SAGF,UAHE,KAUI,YAVJ;AA7BV,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAE1B,SAAS,oBAAoB;AAC7B,SAAS,aAAa;AACtB,SAAS,uBAAuB;AAChC,OAAO,YAAY;AAMZ,SAAS,eAAe;AAAA,EAC7B;AACF,GAEG;AACD,QAAM,SAAS,gBAAgB,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK;AAC/D,QAAM,QAAQ,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACxD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,QAAM,SACJ,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,MAAM,KACnC,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAEjD,SACE,oBAAC,SAAM,OAAM,mBAAkB,OAAO,OAAO,OAAK,MAC/C,WAAC,SACA,oBAAC,SAAI,WAAW,OAAO,OACrB,8BAAC,aAAU,2CAA6B,GAC1C,IAEA,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,OAAO;AAAA,QAClB,MAAK;AAAA,QACL,cAAW;AAAA,QAEV,iBAAO,IAAI,CAAC,MACX;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,gBAAc,EAAE,QAAQ,OAAO;AAAA,YAC/B,WAAW,OAAO;AAAA,YAClB,SAAS,MAAM,UAAU,EAAE,GAAG;AAAA,YAE7B;AAAA,gBAAE;AAAA,cACH,oBAAC,UAAM,YAAE,OAAM;AAAA;AAAA;AAAA,UAPV,EAAE;AAAA,QAQT,CACD;AAAA;AAAA,IACH;AAAA,IACC,OAAO,QAAQ,oBAAC,OAAE,WAAW,OAAO,MAAO,iBAAO,MAAK;AAAA,IACxD,oBAAC,QAAG,WAAW,OAAO,MACnB,iBAAO,MAAM,IAAI,CAAC,SACjB,oBAAC,QACC,+BAAC,QAAK,MAAM,KAAK,MAAM,WAAW,OAAO,KACtC;AAAA,WAAK,QACJ,oBAAC,gBAAa,MAAM,KAAK,OAAO,KAAK,KAAK,MAAM,MAAM,IAAI;AAAA,MAE5D,qBAAC,UAAK,WAAW,OAAO,SACtB;AAAA,4BAAC,YAAQ,eAAK,OAAM;AAAA,QACpB,oBAAC,WAAO,eAAK,QAAO;AAAA,SACtB;AAAA,MACA,oBAAC,UAAK,WAAW,OAAO,UAAW,eAAK,OAAM;AAAA,OAChD,KAVO,KAAK,EAWd,CACD,GACH;AAAA,KACF,GAEJ;AAEJ;","names":[]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { CountUp } from "../_fx";
|
|
4
|
-
import { StatCard
|
|
5
|
-
import { usePlatformRevenue } from "../hooks";
|
|
4
|
+
import { StatCard } from "../_ui";
|
|
6
5
|
import { useModuleOn } from "../_shell/modules-context";
|
|
7
6
|
import styles from "./control-room.module.css";
|
|
8
7
|
import { useWaitingCases } from "./use-waiting-cases";
|
|
@@ -13,73 +12,46 @@ function VerificationCard() {
|
|
|
13
12
|
StatCard,
|
|
14
13
|
{
|
|
15
14
|
compact: true,
|
|
16
|
-
label: "
|
|
15
|
+
label: "KYC / KYB decisions",
|
|
17
16
|
value: waiting === void 0 ? "\u2013" : /* @__PURE__ */ jsx(CountUp, { value: waiting }),
|
|
18
|
-
sub: "
|
|
17
|
+
sub: "Businesses waiting for review",
|
|
19
18
|
href: "/admin/verification",
|
|
20
19
|
alert: !!waiting
|
|
21
20
|
}
|
|
22
21
|
);
|
|
23
22
|
}
|
|
24
|
-
function SubscriptionsCard() {
|
|
25
|
-
const revenue = usePlatformRevenue().data;
|
|
26
|
-
if (!revenue?.recurring.tracked) return null;
|
|
27
|
-
return /* @__PURE__ */ jsx(
|
|
28
|
-
StatCard,
|
|
29
|
-
{
|
|
30
|
-
compact: true,
|
|
31
|
-
label: "Plan income",
|
|
32
|
-
value: formatMoney(revenue.recurring.mrr, revenue.currency),
|
|
33
|
-
sub: "What merchants pay for their plans each month",
|
|
34
|
-
href: "/admin/revenue"
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
23
|
function ControlRoom({
|
|
39
24
|
attention,
|
|
40
|
-
currency = ""
|
|
41
|
-
totals
|
|
25
|
+
currency = ""
|
|
42
26
|
}) {
|
|
43
27
|
const failed = attention.failedSettlements.count;
|
|
44
28
|
const overdue = attention.overdueInvoices.count;
|
|
45
|
-
const failedUnknown = !!attention.unavailable?.includes("failedSettlements");
|
|
46
|
-
const overdueUnknown = !!attention.unavailable?.includes("overdueInvoices");
|
|
47
29
|
const withdrawals = useModuleOn("withdrawals");
|
|
48
30
|
const verification = useModuleOn("verification");
|
|
49
|
-
return /* @__PURE__ */ jsxs("section", { className: styles.room, "aria-label": "
|
|
31
|
+
return /* @__PURE__ */ jsxs("section", { className: styles.room, "aria-label": "Control room", children: [
|
|
50
32
|
/* @__PURE__ */ jsx(
|
|
51
33
|
StatCard,
|
|
52
34
|
{
|
|
53
35
|
compact: true,
|
|
54
36
|
label: "Failed payouts",
|
|
55
|
-
value:
|
|
56
|
-
sub:
|
|
37
|
+
value: /* @__PURE__ */ jsx(CountUp, { value: failed }),
|
|
38
|
+
sub: "Settlements that did not land",
|
|
57
39
|
href: withdrawals ? "/admin/withdrawals?tab=failed" : void 0,
|
|
58
|
-
alert:
|
|
40
|
+
alert: failed > 0
|
|
59
41
|
}
|
|
60
42
|
),
|
|
61
43
|
/* @__PURE__ */ jsx(
|
|
62
44
|
StatCard,
|
|
63
45
|
{
|
|
64
46
|
compact: true,
|
|
65
|
-
label: "Overdue
|
|
66
|
-
value:
|
|
67
|
-
sub:
|
|
47
|
+
label: "Overdue invoices",
|
|
48
|
+
value: /* @__PURE__ */ jsx(CountUp, { value: overdue }),
|
|
49
|
+
sub: overdueOutstanding(attention.overdueInvoices, currency) ?? "Invoices past their due date",
|
|
68
50
|
href: "/admin/revenue",
|
|
69
|
-
alert:
|
|
70
|
-
}
|
|
71
|
-
),
|
|
72
|
-
verification && /* @__PURE__ */ jsx(VerificationCard, {}),
|
|
73
|
-
totals?.verifiedWallets !== void 0 && /* @__PURE__ */ jsx(
|
|
74
|
-
StatCard,
|
|
75
|
-
{
|
|
76
|
-
compact: true,
|
|
77
|
-
label: "Verified wallets",
|
|
78
|
-
value: /* @__PURE__ */ jsx(CountUp, { value: totals.verifiedWallets }),
|
|
79
|
-
sub: "Merchants who have passed their identity checks"
|
|
51
|
+
alert: overdue > 0
|
|
80
52
|
}
|
|
81
53
|
),
|
|
82
|
-
/* @__PURE__ */ jsx(
|
|
54
|
+
verification && /* @__PURE__ */ jsx(VerificationCard, {})
|
|
83
55
|
] });
|
|
84
56
|
}
|
|
85
57
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_overview/control-room.tsx"],"sourcesContent":["'use client';\n\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { CountUp } from '../_fx';\nimport { StatCard
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_overview/control-room.tsx"],"sourcesContent":["'use client';\n\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { CountUp } from '../_fx';\nimport { StatCard } from '../_ui';\nimport { useModuleOn } from '../_shell/modules-context';\nimport styles from './control-room.module.css';\nimport { useWaitingCases } from './use-waiting-cases';\nimport { overdueOutstanding } from './overdue-outstanding';\n\n/** KYC / KYB waiting count; mounted only where Verification is served. */\nfunction VerificationCard() {\n const waiting = useWaitingCases().data;\n\n return (\n <StatCard\n compact\n label=\"KYC / KYB decisions\"\n value={waiting === undefined ? '–' : <CountUp value={waiting} />}\n sub=\"Businesses waiting for review\"\n href=\"/admin/verification\"\n alert={!!waiting}\n />\n );\n}\n\n/**\n * The control room: every queue that needs a decision, as one row of\n * identical metric cards that wraps. A card opens its screen when this Box\n * serves that screen; a queue whose module is off is not shown.\n */\nexport function ControlRoom({\n attention,\n currency = '',\n}: {\n attention: PlatformCockpit['attention'];\n /** The currency filter's choice; narrows what overdue invoices owe. */\n currency?: string;\n}) {\n const failed = attention.failedSettlements.count;\n const overdue = attention.overdueInvoices.count;\n const withdrawals = useModuleOn('withdrawals');\n const verification = useModuleOn('verification');\n\n return (\n <section className={styles.room} aria-label=\"Control room\">\n <StatCard\n compact\n label=\"Failed payouts\"\n value={<CountUp value={failed} />}\n sub=\"Settlements that did not land\"\n href={withdrawals ? '/admin/withdrawals?tab=failed' : undefined}\n alert={failed > 0}\n />\n <StatCard\n compact\n label=\"Overdue invoices\"\n value={<CountUp value={overdue} />}\n sub={\n overdueOutstanding(attention.overdueInvoices, currency) ??\n 'Invoices past their due date'\n }\n href=\"/admin/revenue\"\n alert={overdue > 0}\n />\n {verification && <VerificationCard />}\n </section>\n );\n}\n"],"mappings":";AAkB2C,cA2BvC,YA3BuC;AAf3C,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,OAAO,YAAY;AACnB,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AAGnC,SAAS,mBAAmB;AAC1B,QAAM,UAAU,gBAAgB,EAAE;AAElC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAO;AAAA,MACP,OAAM;AAAA,MACN,OAAO,YAAY,SAAY,WAAM,oBAAC,WAAQ,OAAO,SAAS;AAAA,MAC9D,KAAI;AAAA,MACJ,MAAK;AAAA,MACL,OAAO,CAAC,CAAC;AAAA;AAAA,EACX;AAEJ;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,WAAW;AACb,GAIG;AACD,QAAM,SAAS,UAAU,kBAAkB;AAC3C,QAAM,UAAU,UAAU,gBAAgB;AAC1C,QAAM,cAAc,YAAY,aAAa;AAC7C,QAAM,eAAe,YAAY,cAAc;AAE/C,SACE,qBAAC,aAAQ,WAAW,OAAO,MAAM,cAAW,gBAC1C;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,OAAO,oBAAC,WAAQ,OAAO,QAAQ;AAAA,QAC/B,KAAI;AAAA,QACJ,MAAM,cAAc,kCAAkC;AAAA,QACtD,OAAO,SAAS;AAAA;AAAA,IAClB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,OAAO,oBAAC,WAAQ,OAAO,SAAS;AAAA,QAChC,KACE,mBAAmB,UAAU,iBAAiB,QAAQ,KACtD;AAAA,QAEF,MAAK;AAAA,QACL,OAAO,UAAU;AAAA;AAAA,IACnB;AAAA,IACC,gBAAgB,oBAAC,oBAAiB;AAAA,KACrC;AAEJ;","names":[]}
|
|
@@ -8,17 +8,17 @@ function KpiRow({
|
|
|
8
8
|
compare,
|
|
9
9
|
caption
|
|
10
10
|
}) {
|
|
11
|
-
const delta = (kpi) => compare ? { pct: kpi.pct, caption: "vs
|
|
11
|
+
const delta = (kpi) => compare ? { pct: kpi.pct, caption: "vs prior period" } : void 0;
|
|
12
12
|
const gmvKpi = rowFor(data.kpis.gmvByCurrency, focus);
|
|
13
13
|
const gmvSpark = rowFor(
|
|
14
14
|
data.trend.performanceSeriesByCurrency,
|
|
15
15
|
focus
|
|
16
16
|
)?.series.map((p) => p.gmv);
|
|
17
|
-
return /* @__PURE__ */ jsxs("section", { "aria-label": "
|
|
17
|
+
return /* @__PURE__ */ jsxs("section", { "aria-label": "Performance indicators", className: "adm-grid", children: [
|
|
18
18
|
/* @__PURE__ */ jsx("div", { className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
19
19
|
StatCard,
|
|
20
20
|
{
|
|
21
|
-
label: "
|
|
21
|
+
label: "Merchant GMV",
|
|
22
22
|
value: /* @__PURE__ */ jsx(
|
|
23
23
|
MoneyLines,
|
|
24
24
|
{
|
|
@@ -32,7 +32,7 @@ function KpiRow({
|
|
|
32
32
|
delta: gmvKpi ? delta(gmvKpi) : void 0,
|
|
33
33
|
sub: compare && gmvKpi ? void 0 : caption,
|
|
34
34
|
spark: gmvSpark,
|
|
35
|
-
hint: "
|
|
35
|
+
hint: "Gross value of paid orders in the selected period, one line per currency. Currencies are never added together."
|
|
36
36
|
}
|
|
37
37
|
) }),
|
|
38
38
|
/* @__PURE__ */ jsx("div", { className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
@@ -44,13 +44,13 @@ function KpiRow({
|
|
|
44
44
|
delta: delta(data.kpis.orders),
|
|
45
45
|
sub: compare ? void 0 : "Paid orders, all time",
|
|
46
46
|
spark: data.sparklines.orders,
|
|
47
|
-
hint: "Paid orders across all
|
|
47
|
+
hint: "Paid orders across all merchant workspaces, in every currency."
|
|
48
48
|
}
|
|
49
49
|
) }),
|
|
50
50
|
/* @__PURE__ */ jsx("div", { className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
51
51
|
StatCard,
|
|
52
52
|
{
|
|
53
|
-
label: "New
|
|
53
|
+
label: "New workspaces",
|
|
54
54
|
value: integer.format(data.kpis.newTenants.current),
|
|
55
55
|
href: "/admin/tenants",
|
|
56
56
|
delta: delta(data.kpis.newTenants),
|
|
@@ -61,7 +61,7 @@ function KpiRow({
|
|
|
61
61
|
/* @__PURE__ */ jsx("div", { className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
62
62
|
StatCard,
|
|
63
63
|
{
|
|
64
|
-
label: "New
|
|
64
|
+
label: "New accounts",
|
|
65
65
|
value: integer.format(data.kpis.newAccounts.current),
|
|
66
66
|
href: "/admin/accounts",
|
|
67
67
|
delta: delta(data.kpis.newAccounts),
|