@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
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/* The Merchant payments and Subscriptions views. Cards and tables come from
|
|
2
|
-
the console's own kit; this is only the spacing between them. */
|
|
3
|
-
|
|
4
|
-
.lede {
|
|
5
|
-
max-width: 68ch;
|
|
6
|
-
margin: 0 0 16px;
|
|
7
|
-
font-size: 13px;
|
|
8
|
-
line-height: 1.5;
|
|
9
|
-
color: var(--text-secondary);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.sectionTitle {
|
|
13
|
-
margin: 28px 0 4px;
|
|
14
|
-
font-size: 15px;
|
|
15
|
-
font-weight: 600;
|
|
16
|
-
color: var(--text-primary);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.sectionNote {
|
|
20
|
-
margin: 0 0 12px;
|
|
21
|
-
font-size: 13px;
|
|
22
|
-
color: var(--text-secondary);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.toolbar {
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-wrap: wrap;
|
|
28
|
-
align-items: center;
|
|
29
|
-
justify-content: space-between;
|
|
30
|
-
gap: 8px 12px;
|
|
31
|
-
margin: 20px 0 8px;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.strip {
|
|
35
|
-
display: flex;
|
|
36
|
-
flex-wrap: wrap;
|
|
37
|
-
gap: 4px 20px;
|
|
38
|
-
margin: 8px 0 12px;
|
|
39
|
-
font-size: 13px;
|
|
40
|
-
color: var(--text-secondary);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.strip strong {
|
|
44
|
-
font-weight: 500;
|
|
45
|
-
color: var(--text-primary);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.who {
|
|
49
|
-
display: grid;
|
|
50
|
-
gap: 1px;
|
|
51
|
-
min-width: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.whoName {
|
|
55
|
-
font-weight: 500;
|
|
56
|
-
color: var(--text-primary);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.whoSub {
|
|
60
|
-
font-size: 12px;
|
|
61
|
-
color: var(--text-tertiary);
|
|
62
|
-
overflow-wrap: anywhere;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.status {
|
|
66
|
-
display: inline-block;
|
|
67
|
-
padding: 2px 8px;
|
|
68
|
-
border-radius: 999px;
|
|
69
|
-
font-size: 12px;
|
|
70
|
-
font-weight: 500;
|
|
71
|
-
white-space: nowrap;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.statusGood {
|
|
75
|
-
background: var(--success-bg, rgba(34, 139, 34, 0.12));
|
|
76
|
-
color: var(--success, #1f7a1f);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.statusBad {
|
|
80
|
-
background: var(--error-bg, rgba(200, 40, 40, 0.12));
|
|
81
|
-
color: var(--error, #b42318);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.statusWait {
|
|
85
|
-
background: var(--warning-bg, rgba(200, 140, 0, 0.14));
|
|
86
|
-
color: var(--warning, #8a5a00);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.statusMuted {
|
|
90
|
-
background: var(--bg-subtle, rgba(0, 0, 0, 0.06));
|
|
91
|
-
color: var(--text-secondary);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.notice {
|
|
95
|
-
padding: 20px;
|
|
96
|
-
border: 1px dashed var(--border-subtle);
|
|
97
|
-
border-radius: var(--adm-card-radius);
|
|
98
|
-
font-size: 13px;
|
|
99
|
-
line-height: 1.5;
|
|
100
|
-
color: var(--text-secondary);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.drawerBody {
|
|
104
|
-
display: grid;
|
|
105
|
-
gap: 16px;
|
|
106
|
-
padding: 4px 0 24px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.drawerHeading {
|
|
110
|
-
margin: 8px 0 0;
|
|
111
|
-
font-size: 13px;
|
|
112
|
-
font-weight: 600;
|
|
113
|
-
color: var(--text-primary);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.methodBar {
|
|
117
|
-
display: flex;
|
|
118
|
-
height: 6px;
|
|
119
|
-
min-width: 120px;
|
|
120
|
-
overflow: hidden;
|
|
121
|
-
border-radius: 999px;
|
|
122
|
-
background: var(--bg-subtle, rgba(0, 0, 0, 0.06));
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.methodSeg {
|
|
126
|
-
height: 100%;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.methodText {
|
|
130
|
-
margin-top: 4px;
|
|
131
|
-
font-size: 12px;
|
|
132
|
-
color: var(--text-tertiary);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.clickRow {
|
|
136
|
-
cursor: pointer;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.subTitle {
|
|
140
|
-
margin: 20px 0 8px;
|
|
141
|
-
font-size: 14px;
|
|
142
|
-
font-weight: 600;
|
|
143
|
-
color: var(--text-primary);
|
|
144
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { CountUp, Reveal } from "../_fx";
|
|
3
|
-
import { StatCard } from "../_ui";
|
|
4
|
-
import { minorToMoney, pctChange } from "./ledger-model";
|
|
5
|
-
function MerchantHeadline({ ledger, focus }) {
|
|
6
|
-
const now = ledger.cards.current.find((card) => card.currency === focus);
|
|
7
|
-
const before = ledger.cards.previous?.find((card) => card.currency === focus);
|
|
8
|
-
if (!now) return null;
|
|
9
|
-
const money = (minor) => minorToMoney(minor, focus);
|
|
10
|
-
const delta = (a, b) => before ? { pct: pctChange(a, b), caption: "vs the period before" } : void 0;
|
|
11
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12
|
-
/* @__PURE__ */ jsx(Reveal, { index: 1, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
13
|
-
StatCard,
|
|
14
|
-
{
|
|
15
|
-
label: "Money received",
|
|
16
|
-
value: /* @__PURE__ */ jsx(CountUp, { value: Number(now.grossMinor) / 100, format: (v) => money(String(Math.round(v * 100))) }),
|
|
17
|
-
sub: `${now.payments} payments that went through`,
|
|
18
|
-
hint: "Everything customers paid to merchants that went through, before any fees are taken.",
|
|
19
|
-
delta: delta(now.grossMinor, before?.grossMinor)
|
|
20
|
-
}
|
|
21
|
-
) }),
|
|
22
|
-
/* @__PURE__ */ jsx(Reveal, { index: 2, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
23
|
-
StatCard,
|
|
24
|
-
{
|
|
25
|
-
label: "Merchants keep",
|
|
26
|
-
value: money(now.netMinor),
|
|
27
|
-
sub: `After ${money(now.feeMinor)} in fees`,
|
|
28
|
-
hint: "What is left for merchants once the fees are taken: money received minus fees.",
|
|
29
|
-
delta: delta(now.netMinor, before?.netMinor)
|
|
30
|
-
}
|
|
31
|
-
) })
|
|
32
|
-
] });
|
|
33
|
-
}
|
|
34
|
-
export {
|
|
35
|
-
MerchantHeadline
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=merchant-headline.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/merchant-headline.tsx"],"sourcesContent":["import type { MerchantPaymentsPage } from '@withpotter/lib/admin-api';\nimport { CountUp, Reveal } from '../_fx';\nimport { StatCard } from '../_ui';\nimport { minorToMoney, pctChange } from './ledger-model';\n\n/**\n * Two more cards for the merchant group on the Overview: the money customers paid that went through, and what\n * merchants are left with after fees, each against the period before. Nothing shows when the box has no payments view.\n */\nexport function MerchantHeadline({ ledger, focus }: { ledger: MerchantPaymentsPage; focus: string }) {\n const now = ledger.cards.current.find((card) => card.currency === focus);\n const before = ledger.cards.previous?.find((card) => card.currency === focus);\n\n if (!now) return null;\n\n const money = (minor: string) => minorToMoney(minor, focus);\n const delta = (a: string, b?: string) =>\n before ? { pct: pctChange(a, b), caption: 'vs the period before' } : undefined;\n\n return (\n <>\n <Reveal index={1} className=\"adm-span-3\">\n <StatCard\n label=\"Money received\"\n value={<CountUp value={Number(now.grossMinor) / 100} format={(v) => money(String(Math.round(v * 100)))} />}\n sub={`${now.payments} payments that went through`}\n hint=\"Everything customers paid to merchants that went through, before any fees are taken.\"\n delta={delta(now.grossMinor, before?.grossMinor)}\n />\n </Reveal>\n <Reveal index={2} className=\"adm-span-3\">\n <StatCard\n label=\"Merchants keep\"\n value={money(now.netMinor)}\n sub={`After ${money(now.feeMinor)} in fees`}\n hint=\"What is left for merchants once the fees are taken: money received minus fees.\"\n delta={delta(now.netMinor, before?.netMinor)}\n />\n </Reveal>\n </>\n );\n}\n"],"mappings":"AAoBI,mBAIa,KAJb;AAnBJ,SAAS,SAAS,cAAc;AAChC,SAAS,gBAAgB;AACzB,SAAS,cAAc,iBAAiB;AAMjC,SAAS,iBAAiB,EAAE,QAAQ,MAAM,GAAoD;AACnG,QAAM,MAAM,OAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,KAAK,aAAa,KAAK;AACvE,QAAM,SAAS,OAAO,MAAM,UAAU,KAAK,CAAC,SAAS,KAAK,aAAa,KAAK;AAE5E,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,QAAQ,CAAC,UAAkB,aAAa,OAAO,KAAK;AAC1D,QAAM,QAAQ,CAAC,GAAW,MACxB,SAAS,EAAE,KAAK,UAAU,GAAG,CAAC,GAAG,SAAS,uBAAuB,IAAI;AAEvE,SACE,iCACE;AAAA,wBAAC,UAAO,OAAO,GAAG,WAAU,cAC1B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,oBAAC,WAAQ,OAAO,OAAO,IAAI,UAAU,IAAI,KAAK,QAAQ,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG;AAAA,QACxG,KAAK,GAAG,IAAI,QAAQ;AAAA,QACpB,MAAK;AAAA,QACL,OAAO,MAAM,IAAI,YAAY,QAAQ,UAAU;AAAA;AAAA,IACjD,GACF;AAAA,IACA,oBAAC,UAAO,OAAO,GAAG,WAAU,cAC1B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,MAAM,IAAI,QAAQ;AAAA,QACzB,KAAK,SAAS,MAAM,IAAI,QAAQ,CAAC;AAAA,QACjC,MAAK;AAAA,QACL,OAAO,MAAM,IAAI,UAAU,QAAQ,QAAQ;AAAA;AAAA,IAC7C,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useMemo, useState } from "react";
|
|
4
|
-
import Link from "next/link";
|
|
5
|
-
import { EmptyNote, TableRow, TableShell, Th } from "@withpotter/ui-kit";
|
|
6
|
-
import { platformAdminApi } from "@withpotter/lib/admin-api";
|
|
7
|
-
import { useMerchantOptions, useMerchantPayments } from "../hooks";
|
|
8
|
-
import { LoadFailed } from "../audit/load-failed";
|
|
9
|
-
import { CountUp, Reveal } from "../_fx";
|
|
10
|
-
import {
|
|
11
|
-
AdmLoading,
|
|
12
|
-
FilterSearch,
|
|
13
|
-
FilterSelect,
|
|
14
|
-
Filters,
|
|
15
|
-
Pagination,
|
|
16
|
-
StatCard,
|
|
17
|
-
focusCurrency
|
|
18
|
-
} from "../_ui";
|
|
19
|
-
import { DownloadButton } from "./download-button";
|
|
20
|
-
import {
|
|
21
|
-
STATUS_WORDS,
|
|
22
|
-
TYPE_WORDS,
|
|
23
|
-
downloadName,
|
|
24
|
-
minorToMoney,
|
|
25
|
-
pctChange,
|
|
26
|
-
word
|
|
27
|
-
} from "./ledger-model";
|
|
28
|
-
import { MethodCards } from "./method-cards";
|
|
29
|
-
import { PaymentDrawer } from "./payment-drawer";
|
|
30
|
-
import { useUrlFilters } from "./use-url-filters";
|
|
31
|
-
import styles from "./ledger.module.css";
|
|
32
|
-
import table from "./revenue.module.css";
|
|
33
|
-
const PAGE_SIZE = 25;
|
|
34
|
-
const FILTERS = { q: "", method: "", status: "", type: "", merchant: "" };
|
|
35
|
-
const integer = new Intl.NumberFormat("en-US");
|
|
36
|
-
const METHODS = [
|
|
37
|
-
{ value: "", label: "Every way of paying" },
|
|
38
|
-
{ value: "card", label: "Card" },
|
|
39
|
-
{ value: "ussd", label: "USSD" },
|
|
40
|
-
{ value: "transfer", label: "Bank transfer" },
|
|
41
|
-
{ value: "opay", label: "Opay" }
|
|
42
|
-
];
|
|
43
|
-
const STATUSES = [
|
|
44
|
-
{ value: "", label: "Paid" },
|
|
45
|
-
{ value: "pending", label: "Waiting" },
|
|
46
|
-
{ value: "failed", label: "Failed" },
|
|
47
|
-
{ value: "refunded", label: "Refunded" }
|
|
48
|
-
];
|
|
49
|
-
const TYPES = [
|
|
50
|
-
{ value: "", label: "Every kind" },
|
|
51
|
-
...["order", "invoice", "booking", "membership", "subscription", "deposit", "tip", "refund"].map((value) => ({
|
|
52
|
-
value,
|
|
53
|
-
label: word(TYPE_WORDS, value)
|
|
54
|
-
}))
|
|
55
|
-
];
|
|
56
|
-
const tone = (status) => status === "success" ? styles.statusGood : status === "failed" ? styles.statusBad : status === "pending" ? styles.statusWait : styles.statusMuted;
|
|
57
|
-
function MerchantPaymentsView({
|
|
58
|
-
startDate,
|
|
59
|
-
endDate,
|
|
60
|
-
currency
|
|
61
|
-
}) {
|
|
62
|
-
const { values, page, set } = useUrlFilters("mp", FILTERS);
|
|
63
|
-
const [search, setSearch] = useState(values.q);
|
|
64
|
-
const [open, setOpen] = useState(null);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
const timer = setTimeout(() => {
|
|
67
|
-
if (search.trim() !== values.q) set({ q: search.trim() });
|
|
68
|
-
}, 300);
|
|
69
|
-
return () => clearTimeout(timer);
|
|
70
|
-
}, [search]);
|
|
71
|
-
const filters = useMemo(
|
|
72
|
-
() => ({
|
|
73
|
-
startDate,
|
|
74
|
-
endDate,
|
|
75
|
-
method: values.method,
|
|
76
|
-
status: values.status,
|
|
77
|
-
type: values.type,
|
|
78
|
-
q: values.q,
|
|
79
|
-
merchantId: values.merchant,
|
|
80
|
-
page,
|
|
81
|
-
limit: PAGE_SIZE
|
|
82
|
-
}),
|
|
83
|
-
[startDate, endDate, values, page]
|
|
84
|
-
);
|
|
85
|
-
const merchants = useMerchantOptions({ startDate, endDate });
|
|
86
|
-
const merchantChoices = [
|
|
87
|
-
{ value: "", label: "Every merchant" },
|
|
88
|
-
...(merchants.data?.merchants ?? []).map((one) => ({ value: one.id, label: one.name }))
|
|
89
|
-
];
|
|
90
|
-
const { data, isLoading, isError, refetch } = useMerchantPayments(filters);
|
|
91
|
-
if (isError) {
|
|
92
|
-
return /* @__PURE__ */ jsx(
|
|
93
|
-
LoadFailed,
|
|
94
|
-
{
|
|
95
|
-
message: "Merchant payments could not be loaded. This box may not have them yet.",
|
|
96
|
-
onRetry: () => void refetch()
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (isLoading || !data) return /* @__PURE__ */ jsx(AdmLoading, { label: "Loading merchant payments\u2026" });
|
|
101
|
-
const currencies = [...new Set(data.cards.current.map((c) => c.currency))];
|
|
102
|
-
const focus = focusCurrency(currencies, currency) ?? "NGN";
|
|
103
|
-
const now = data.cards.current.find((c) => c.currency === focus);
|
|
104
|
-
const before = data.cards.previous?.find((c) => c.currency === focus) ?? null;
|
|
105
|
-
const money = (minor) => minorToMoney(minor, focus);
|
|
106
|
-
const delta = (a, b, goodWhenUp = true) => before ? { pct: pctChange(a, b), positiveIsGood: goodWhenUp, caption: "vs the period before" } : void 0;
|
|
107
|
-
const totalPages = Math.max(Math.ceil(data.total / PAGE_SIZE), 1);
|
|
108
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
109
|
-
/* @__PURE__ */ jsx("p", { className: styles.lede, children: "This is money that customers paid to merchants. Use the filters to narrow it down, and download exactly what you see." }),
|
|
110
|
-
/* @__PURE__ */ jsxs("section", { "aria-label": "Merchant payments at a glance", className: "adm-grid", children: [
|
|
111
|
-
/* @__PURE__ */ jsx(Reveal, { index: 0, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
112
|
-
StatCard,
|
|
113
|
-
{
|
|
114
|
-
label: "Money received",
|
|
115
|
-
value: /* @__PURE__ */ jsx(CountUp, { value: Number(now?.grossMinor ?? 0) / 100, format: (v) => money(Math.round(v * 100)) }),
|
|
116
|
-
sub: now ? `${integer.format(now.payments)} payments that went through` : "No payments in this period",
|
|
117
|
-
hint: "Everything customers paid to merchants that went through, before any fees are taken.",
|
|
118
|
-
delta: delta(now?.grossMinor, before?.grossMinor)
|
|
119
|
-
}
|
|
120
|
-
) }),
|
|
121
|
-
/* @__PURE__ */ jsx(Reveal, { index: 1, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
122
|
-
StatCard,
|
|
123
|
-
{
|
|
124
|
-
label: "Fees taken",
|
|
125
|
-
value: money(now?.feeMinor ?? 0),
|
|
126
|
-
hint: "What the payment gateway and Olive kept from those payments.",
|
|
127
|
-
delta: delta(now?.feeMinor, before?.feeMinor, false)
|
|
128
|
-
}
|
|
129
|
-
) }),
|
|
130
|
-
/* @__PURE__ */ jsx(Reveal, { index: 2, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
131
|
-
StatCard,
|
|
132
|
-
{
|
|
133
|
-
label: "Merchants keep",
|
|
134
|
-
value: money(now?.netMinor ?? 0),
|
|
135
|
-
hint: "What is left for merchants after the fees: money received minus fees taken.",
|
|
136
|
-
delta: delta(now?.netMinor, before?.netMinor)
|
|
137
|
-
}
|
|
138
|
-
) }),
|
|
139
|
-
/* @__PURE__ */ jsx(Reveal, { index: 3, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
140
|
-
StatCard,
|
|
141
|
-
{
|
|
142
|
-
label: "Payments that worked",
|
|
143
|
-
value: now?.successRate == null ? "\u2013" : `${now.successRate}%`,
|
|
144
|
-
sub: now?.successRate == null ? "Nothing was tried in this period" : `Refunded: ${money(now.refundsMinor)}`,
|
|
145
|
-
hint: "Out of every payment that was tried, the share that went through. A low number can mean a way of paying is broken."
|
|
146
|
-
}
|
|
147
|
-
) })
|
|
148
|
-
] }),
|
|
149
|
-
/* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "How customers paid" }),
|
|
150
|
-
/* @__PURE__ */ jsx("p", { className: styles.sectionNote, children: "The ways of paying on the checkout, and how much came in through each." }),
|
|
151
|
-
/* @__PURE__ */ jsx(MethodCards, { now: data.byChannel.current, before: data.byChannel.previous, currency: focus }),
|
|
152
|
-
/* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "Merchant revenue" }),
|
|
153
|
-
/* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
|
|
154
|
-
/* @__PURE__ */ jsxs(Filters, { label: "Filter payments", children: [
|
|
155
|
-
/* @__PURE__ */ jsx(
|
|
156
|
-
FilterSearch,
|
|
157
|
-
{
|
|
158
|
-
value: search,
|
|
159
|
-
onChange: setSearch,
|
|
160
|
-
placeholder: "Search a name, email, phone, reference or merchant",
|
|
161
|
-
label: "Search payments"
|
|
162
|
-
}
|
|
163
|
-
),
|
|
164
|
-
/* @__PURE__ */ jsx(
|
|
165
|
-
FilterSelect,
|
|
166
|
-
{
|
|
167
|
-
value: values.merchant,
|
|
168
|
-
onChange: (merchant) => set({ merchant }),
|
|
169
|
-
options: merchantChoices,
|
|
170
|
-
label: "Merchant",
|
|
171
|
-
searchable: true
|
|
172
|
-
}
|
|
173
|
-
),
|
|
174
|
-
/* @__PURE__ */ jsx(FilterSelect, { value: values.method, onChange: (method) => set({ method }), options: METHODS, label: "Way of paying" }),
|
|
175
|
-
/* @__PURE__ */ jsx(FilterSelect, { value: values.status, onChange: (status) => set({ status }), options: STATUSES, label: "Payment result" }),
|
|
176
|
-
/* @__PURE__ */ jsx(FilterSelect, { value: values.type, onChange: (type) => set({ type }), options: TYPES, label: "What it was for" })
|
|
177
|
-
] }),
|
|
178
|
-
/* @__PURE__ */ jsx(
|
|
179
|
-
DownloadButton,
|
|
180
|
-
{
|
|
181
|
-
filename: downloadName("merchant-payments", startDate, endDate),
|
|
182
|
-
load: () => platformAdminApi.merchantPaymentsCsv({ ...filters, page: void 0, limit: void 0 })
|
|
183
|
-
}
|
|
184
|
-
)
|
|
185
|
-
] }),
|
|
186
|
-
data.rows.length === 0 ? /* @__PURE__ */ jsx(EmptyNote, { className: table.empty, children: "No payments match. Try a wider date range or clear a filter." }) : /* @__PURE__ */ jsx(
|
|
187
|
-
TableShell,
|
|
188
|
-
{
|
|
189
|
-
minWidth: 980,
|
|
190
|
-
density: "compact",
|
|
191
|
-
head: /* @__PURE__ */ jsxs("tr", { children: [
|
|
192
|
-
/* @__PURE__ */ jsx(Th, { children: "When" }),
|
|
193
|
-
/* @__PURE__ */ jsx(Th, { children: "Paid by" }),
|
|
194
|
-
/* @__PURE__ */ jsx(Th, { children: "How" }),
|
|
195
|
-
/* @__PURE__ */ jsx(Th, { children: "Merchant" }),
|
|
196
|
-
/* @__PURE__ */ jsx(Th, { children: "For" }),
|
|
197
|
-
/* @__PURE__ */ jsx(Th, { children: "Result" }),
|
|
198
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "Amount" }),
|
|
199
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "Fee" }),
|
|
200
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "Merchant gets" })
|
|
201
|
-
] }),
|
|
202
|
-
children: data.rows.map((row) => /* @__PURE__ */ jsx(PaymentRow, { row, onOpen: setOpen }, row.id))
|
|
203
|
-
}
|
|
204
|
-
),
|
|
205
|
-
/* @__PURE__ */ jsx(Pagination, { page, totalPages, total: data.total, noun: "payments", onPage: (next) => set({ page: next }) }),
|
|
206
|
-
/* @__PURE__ */ jsx(PaymentDrawer, { row: open, onClose: () => setOpen(null) })
|
|
207
|
-
] });
|
|
208
|
-
}
|
|
209
|
-
function PaymentRow({ row, onOpen }) {
|
|
210
|
-
const { payer } = row;
|
|
211
|
-
const contact = [payer.email, payer.phone].filter(Boolean).join(" \xB7 ");
|
|
212
|
-
const money = (minor) => minorToMoney(minor, row.currency);
|
|
213
|
-
return /* @__PURE__ */ jsxs(TableRow, { onClick: () => onOpen(row), children: [
|
|
214
|
-
/* @__PURE__ */ jsx("td", { className: table.num, children: new Date(row.paidAt).toLocaleString(void 0, { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }) }),
|
|
215
|
-
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
|
|
216
|
-
/* @__PURE__ */ jsx("span", { className: styles.whoName, children: payer.name || "Name not recorded" }),
|
|
217
|
-
contact ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: contact }) : null
|
|
218
|
-
] }) }),
|
|
219
|
-
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
|
|
220
|
-
/* @__PURE__ */ jsx("span", { className: styles.whoName, children: row.channel.label }),
|
|
221
|
-
row.methodDetail ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: row.methodDetail }) : null
|
|
222
|
-
] }) }),
|
|
223
|
-
/* @__PURE__ */ jsx("td", { className: table.name, children: row.merchant.name ? /* @__PURE__ */ jsx(Link, { href: `/admin/tenants/${row.merchant.id}`, children: row.merchant.name }) : "Unknown merchant" }),
|
|
224
|
-
/* @__PURE__ */ jsx("td", { children: word(TYPE_WORDS, row.type) }),
|
|
225
|
-
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: `${styles.status} ${tone(row.status)}`, children: word(STATUS_WORDS, row.status) }) }),
|
|
226
|
-
/* @__PURE__ */ jsx("td", { className: table.numStrong, children: money(row.amountMinor) }),
|
|
227
|
-
/* @__PURE__ */ jsx("td", { className: table.num, children: money(row.feeMinor) }),
|
|
228
|
-
/* @__PURE__ */ jsx("td", { className: table.num, children: money(row.netMinor) })
|
|
229
|
-
] });
|
|
230
|
-
}
|
|
231
|
-
export {
|
|
232
|
-
MerchantPaymentsView
|
|
233
|
-
};
|
|
234
|
-
//# sourceMappingURL=merchant-payments-view.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/merchant-payments-view.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport Link from 'next/link';\nimport { EmptyNote, TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { platformAdminApi, type MerchantPaymentRow } from '@withpotter/lib/admin-api';\nimport { useMerchantOptions, useMerchantPayments } from '../hooks';\nimport { LoadFailed } from '../audit/load-failed';\nimport { CountUp, Reveal } from '../_fx';\nimport {\n AdmLoading,\n FilterSearch,\n FilterSelect,\n Filters,\n Pagination,\n StatCard,\n focusCurrency,\n} from '../_ui';\nimport { DownloadButton } from './download-button';\nimport {\n STATUS_WORDS,\n TYPE_WORDS,\n downloadName,\n minorToMoney,\n pctChange,\n word,\n} from './ledger-model';\nimport { MethodCards } from './method-cards';\nimport { PaymentDrawer } from './payment-drawer';\nimport { useUrlFilters } from './use-url-filters';\nimport styles from './ledger.module.css';\nimport table from './revenue.module.css';\n\nconst PAGE_SIZE = 25;\nconst FILTERS = { q: '', method: '', status: '', type: '', merchant: '' };\nconst integer = new Intl.NumberFormat('en-US');\n\nconst METHODS = [\n { value: '', label: 'Every way of paying' },\n { value: 'card', label: 'Card' },\n { value: 'ussd', label: 'USSD' },\n { value: 'transfer', label: 'Bank transfer' },\n { value: 'opay', label: 'Opay' },\n];\nconst STATUSES = [\n { value: '', label: 'Paid' },\n { value: 'pending', label: 'Waiting' },\n { value: 'failed', label: 'Failed' },\n { value: 'refunded', label: 'Refunded' },\n];\nconst TYPES = [\n { value: '', label: 'Every kind' },\n ...['order', 'invoice', 'booking', 'membership', 'subscription', 'deposit', 'tip', 'refund'].map((value) => ({\n value,\n label: word(TYPE_WORDS, value),\n })),\n];\n\nconst tone = (status: string) =>\n status === 'success' ? styles.statusGood : status === 'failed' ? styles.statusBad : status === 'pending' ? styles.statusWait : styles.statusMuted;\n\n/** The merchant payments view: who paid, how, and which merchant received it. */\nexport function MerchantPaymentsView({\n startDate,\n endDate,\n currency,\n}: {\n startDate?: string;\n endDate?: string;\n /** The currency filter's choice; empty follows the only one there is. */\n currency: string;\n}) {\n // The filters live in the page address, so this view can be bookmarked or sent to someone.\n const { values, page, set } = useUrlFilters('mp', FILTERS);\n const [search, setSearch] = useState(values.q);\n const [open, setOpen] = useState<MerchantPaymentRow | null>(null);\n\n // Typing waits a moment, so a search is one request and not one per key.\n useEffect(() => {\n const timer = setTimeout(() => {\n if (search.trim() !== values.q) set({ q: search.trim() });\n }, 300);\n\n return () => clearTimeout(timer);\n // Only a change the person typed should write to the address.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [search]);\n\n const filters = useMemo(\n () => ({\n startDate,\n endDate,\n method: values.method,\n status: values.status,\n type: values.type,\n q: values.q,\n merchantId: values.merchant,\n page,\n limit: PAGE_SIZE,\n }),\n [startDate, endDate, values, page],\n );\n const merchants = useMerchantOptions({ startDate, endDate });\n const merchantChoices = [\n { value: '', label: 'Every merchant' },\n ...(merchants.data?.merchants ?? []).map((one) => ({ value: one.id, label: one.name })),\n ];\n\n const { data, isLoading, isError, refetch } = useMerchantPayments(filters);\n\n if (isError) {\n return (\n <LoadFailed\n message=\"Merchant payments could not be loaded. This box may not have them yet.\"\n onRetry={() => void refetch()}\n />\n );\n }\n if (isLoading || !data) return <AdmLoading label=\"Loading merchant payments…\" />;\n\n const currencies = [...new Set(data.cards.current.map((c) => c.currency))];\n const focus = focusCurrency(currencies, currency) ?? 'NGN';\n const now = data.cards.current.find((c) => c.currency === focus);\n const before = data.cards.previous?.find((c) => c.currency === focus) ?? null;\n const money = (minor: string | number) => minorToMoney(minor, focus);\n const delta = (a?: string, b?: string, goodWhenUp = true) =>\n before ? { pct: pctChange(a, b), positiveIsGood: goodWhenUp, caption: 'vs the period before' } : undefined;\n const totalPages = Math.max(Math.ceil(data.total / PAGE_SIZE), 1);\n\n return (\n <div>\n <p className={styles.lede}>\n This is money that customers paid to merchants. Use the filters to narrow it down, and download exactly what you\n see.\n </p>\n\n <section aria-label=\"Merchant payments at a glance\" className=\"adm-grid\">\n <Reveal index={0} className=\"adm-span-3\">\n <StatCard\n label=\"Money received\"\n value={<CountUp value={Number(now?.grossMinor ?? 0) / 100} format={(v) => money(Math.round(v * 100))} />}\n sub={now ? `${integer.format(now.payments)} payments that went through` : 'No payments in this period'}\n hint=\"Everything customers paid to merchants that went through, before any fees are taken.\"\n delta={delta(now?.grossMinor, before?.grossMinor)}\n />\n </Reveal>\n <Reveal index={1} className=\"adm-span-3\">\n <StatCard\n label=\"Fees taken\"\n value={money(now?.feeMinor ?? 0)}\n hint=\"What the payment gateway and Olive kept from those payments.\"\n delta={delta(now?.feeMinor, before?.feeMinor, false)}\n />\n </Reveal>\n <Reveal index={2} className=\"adm-span-3\">\n <StatCard\n label=\"Merchants keep\"\n value={money(now?.netMinor ?? 0)}\n hint=\"What is left for merchants after the fees: money received minus fees taken.\"\n delta={delta(now?.netMinor, before?.netMinor)}\n />\n </Reveal>\n <Reveal index={3} className=\"adm-span-3\">\n <StatCard\n label=\"Payments that worked\"\n value={now?.successRate == null ? '–' : `${now.successRate}%`}\n sub={now?.successRate == null ? 'Nothing was tried in this period' : `Refunded: ${money(now.refundsMinor)}`}\n hint=\"Out of every payment that was tried, the share that went through. A low number can mean a way of paying is broken.\"\n />\n </Reveal>\n </section>\n\n <h2 className={styles.sectionTitle}>How customers paid</h2>\n <p className={styles.sectionNote}>The ways of paying on the checkout, and how much came in through each.</p>\n <MethodCards now={data.byChannel.current} before={data.byChannel.previous} currency={focus} />\n\n <h2 className={styles.sectionTitle}>Merchant revenue</h2>\n <div className={styles.toolbar}>\n <Filters label=\"Filter payments\">\n <FilterSearch\n value={search}\n onChange={setSearch}\n placeholder=\"Search a name, email, phone, reference or merchant\"\n label=\"Search payments\"\n />\n <FilterSelect\n value={values.merchant}\n onChange={(merchant) => set({ merchant })}\n options={merchantChoices}\n label=\"Merchant\"\n searchable\n />\n <FilterSelect value={values.method} onChange={(method) => set({ method })} options={METHODS} label=\"Way of paying\" />\n <FilterSelect value={values.status} onChange={(status) => set({ status })} options={STATUSES} label=\"Payment result\" />\n <FilterSelect value={values.type} onChange={(type) => set({ type })} options={TYPES} label=\"What it was for\" />\n </Filters>\n <DownloadButton\n filename={downloadName('merchant-payments', startDate, endDate)}\n load={() => platformAdminApi.merchantPaymentsCsv({ ...filters, page: undefined, limit: undefined })}\n />\n </div>\n\n {data.rows.length === 0 ? (\n <EmptyNote className={table.empty}>No payments match. Try a wider date range or clear a filter.</EmptyNote>\n ) : (\n <TableShell\n minWidth={980}\n density=\"compact\"\n head={\n <tr>\n <Th>When</Th>\n <Th>Paid by</Th>\n <Th>How</Th>\n <Th>Merchant</Th>\n <Th>For</Th>\n <Th>Result</Th>\n <Th align=\"right\">Amount</Th>\n <Th align=\"right\">Fee</Th>\n <Th align=\"right\">Merchant gets</Th>\n </tr>\n }\n >\n {data.rows.map((row) => (\n <PaymentRow key={row.id} row={row} onOpen={setOpen} />\n ))}\n </TableShell>\n )}\n\n <Pagination page={page} totalPages={totalPages} total={data.total} noun=\"payments\" onPage={(next) => set({ page: next })} />\n <PaymentDrawer row={open} onClose={() => setOpen(null)} />\n </div>\n );\n}\n\nfunction PaymentRow({ row, onOpen }: { row: MerchantPaymentRow; onOpen: (row: MerchantPaymentRow) => void }) {\n const { payer } = row;\n const contact = [payer.email, payer.phone].filter(Boolean).join(' · ');\n const money = (minor: string) => minorToMoney(minor, row.currency);\n\n return (\n <TableRow onClick={() => onOpen(row)}>\n <td className={table.num}>\n {new Date(row.paidAt).toLocaleString(undefined, { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' })}\n </td>\n <td>\n <span className={styles.who}>\n <span className={styles.whoName}>{payer.name || 'Name not recorded'}</span>\n {contact ? <span className={styles.whoSub}>{contact}</span> : null}\n </span>\n </td>\n <td>\n <span className={styles.who}>\n <span className={styles.whoName}>{row.channel.label}</span>\n {row.methodDetail ? <span className={styles.whoSub}>{row.methodDetail}</span> : null}\n </span>\n </td>\n <td className={table.name}>\n {row.merchant.name ? <Link href={`/admin/tenants/${row.merchant.id}`}>{row.merchant.name}</Link> : 'Unknown merchant'}\n </td>\n <td>{word(TYPE_WORDS, row.type)}</td>\n <td>\n <span className={`${styles.status} ${tone(row.status)}`}>{word(STATUS_WORDS, row.status)}</span>\n </td>\n <td className={table.numStrong}>{money(row.amountMinor)}</td>\n <td className={table.num}>{money(row.feeMinor)}</td>\n <td className={table.num}>{money(row.netMinor)}</td>\n </TableRow>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { CountUp, Reveal } from "../_fx";
|
|
3
|
-
import { StatCard } from "../_ui";
|
|
4
|
-
import { methodCards, minorToMoney } from "./ledger-model";
|
|
5
|
-
const integer = new Intl.NumberFormat("en-US");
|
|
6
|
-
function MethodCards({
|
|
7
|
-
now,
|
|
8
|
-
before,
|
|
9
|
-
currency,
|
|
10
|
-
order,
|
|
11
|
-
labels,
|
|
12
|
-
title = "How customers paid",
|
|
13
|
-
received = "of the money received",
|
|
14
|
-
describe = (label) => `Money customers paid by ${label.toLowerCase()}, before fees.`,
|
|
15
|
-
hide
|
|
16
|
-
}) {
|
|
17
|
-
const cards = methodCards(now, before, currency, { order, labels }).filter(
|
|
18
|
-
(card) => !(hide?.includes(card.key) && card.payments === 0)
|
|
19
|
-
);
|
|
20
|
-
return /* @__PURE__ */ jsx("section", { "aria-label": title, className: "adm-grid", children: cards.map((card, index) => /* @__PURE__ */ jsx(Reveal, { index, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
21
|
-
StatCard,
|
|
22
|
-
{
|
|
23
|
-
label: card.label,
|
|
24
|
-
value: /* @__PURE__ */ jsx(
|
|
25
|
-
CountUp,
|
|
26
|
-
{
|
|
27
|
-
value: Number(card.amountMinor) / 100,
|
|
28
|
-
format: (v) => minorToMoney(Math.round(v * 100), currency)
|
|
29
|
-
}
|
|
30
|
-
),
|
|
31
|
-
sub: card.payments === 0 ? "No payments this way yet" : `${integer.format(card.payments)} ${card.payments === 1 ? "payment" : "payments"} \xB7 ${card.share}% ${received}`,
|
|
32
|
-
hint: describe(card.label, card.key),
|
|
33
|
-
delta: card.change === null ? void 0 : { pct: card.change, caption: "vs the period before" }
|
|
34
|
-
}
|
|
35
|
-
) }, card.key)) });
|
|
36
|
-
}
|
|
37
|
-
export {
|
|
38
|
-
MethodCards
|
|
39
|
-
};
|
|
40
|
-
//# sourceMappingURL=method-cards.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/method-cards.tsx"],"sourcesContent":["import type { GatewayChannelTotal } from '@withpotter/lib/admin-api';\nimport { CountUp, Reveal } from '../_fx';\nimport { StatCard } from '../_ui';\nimport { methodCards, minorToMoney } from './ledger-model';\n\nconst integer = new Intl.NumberFormat('en-US');\n\n/**\n * One card for each way customers pay (Card, USSD, Bank transfer, Opay): how\n * much came in that way, how many payments, its share of all the money\n * received, and how it moved since the period before. A quiet method shows\n * zero, so it is seen rather than missing.\n */\nexport function MethodCards({\n now,\n before,\n currency,\n order,\n labels,\n title = 'How customers paid',\n received = 'of the money received',\n describe = (label: string) => `Money customers paid by ${label.toLowerCase()}, before fees.`,\n hide,\n}: {\n now: readonly GatewayChannelTotal[];\n before: readonly GatewayChannelTotal[] | null;\n currency: string;\n /** The ways to always show, in this order. Customers' checkout methods when left out. */\n order?: readonly string[];\n /** Names for those ways. */\n labels?: Record<string, string>;\n title?: string;\n /** What the share is a share of (\"of the money received\"). */\n received?: string;\n /** The help text for one card. */\n describe?: (label: string, key: string) => string;\n /** Ways to leave out of the row when nothing came in that way. */\n hide?: readonly string[];\n}) {\n const cards = methodCards(now, before, currency, { order, labels }).filter(\n (card) => !(hide?.includes(card.key) && card.payments === 0),\n );\n\n return (\n <section aria-label={title} className=\"adm-grid\">\n {cards.map((card, index) => (\n <Reveal key={card.key} index={index} className=\"adm-span-3\">\n <StatCard\n label={card.label}\n value={\n <CountUp\n value={Number(card.amountMinor) / 100}\n format={(v) => minorToMoney(Math.round(v * 100), currency)}\n />\n }\n sub={\n card.payments === 0\n ? 'No payments this way yet'\n : `${integer.format(card.payments)} ${card.payments === 1 ? 'payment' : 'payments'} · ${card.share}% ${received}`\n }\n hint={describe(card.label, card.key)}\n delta={\n card.change === null\n ? undefined\n : { pct: card.change, caption: 'vs the period before' }\n }\n />\n </Reveal>\n ))}\n </section>\n );\n}\n"],"mappings":"AAkDc;AAjDd,SAAS,SAAS,cAAc;AAChC,SAAS,gBAAgB;AACzB,SAAS,aAAa,oBAAoB;AAE1C,MAAM,UAAU,IAAI,KAAK,aAAa,OAAO;AAQtC,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,WAAW,CAAC,UAAkB,2BAA2B,MAAM,YAAY,CAAC;AAAA,EAC5E;AACF,GAeG;AACD,QAAM,QAAQ,YAAY,KAAK,QAAQ,UAAU,EAAE,OAAO,OAAO,CAAC,EAAE;AAAA,IAClE,CAAC,SAAS,EAAE,MAAM,SAAS,KAAK,GAAG,KAAK,KAAK,aAAa;AAAA,EAC5D;AAEA,SACE,oBAAC,aAAQ,cAAY,OAAO,WAAU,YACnC,gBAAM,IAAI,CAAC,MAAM,UAChB,oBAAC,UAAsB,OAAc,WAAU,cAC7C;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,KAAK;AAAA,MACZ,OACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,OAAO,KAAK,WAAW,IAAI;AAAA,UAClC,QAAQ,CAAC,MAAM,aAAa,KAAK,MAAM,IAAI,GAAG,GAAG,QAAQ;AAAA;AAAA,MAC3D;AAAA,MAEF,KACE,KAAK,aAAa,IACd,6BACA,GAAG,QAAQ,OAAO,KAAK,QAAQ,CAAC,IAAI,KAAK,aAAa,IAAI,YAAY,UAAU,SAAM,KAAK,KAAK,KAAK,QAAQ;AAAA,MAEnH,MAAM,SAAS,KAAK,OAAO,KAAK,GAAG;AAAA,MACnC,OACE,KAAK,WAAW,OACZ,SACA,EAAE,KAAK,KAAK,QAAQ,SAAS,uBAAuB;AAAA;AAAA,EAE5D,KApBW,KAAK,GAqBlB,CACD,GACH;AAEJ;","names":[]}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { Fact, FactList } from "@withpotter/ui-kit";
|
|
5
|
-
import { StatusPill } from "../audit/status-pill";
|
|
6
|
-
import { Drawer } from "../_ui";
|
|
7
|
-
import { STATUS_WORDS, TYPE_WORDS, minorToMoney, word } from "./ledger-model";
|
|
8
|
-
import styles from "./ledger.module.css";
|
|
9
|
-
const when = (iso) => new Date(iso).toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" });
|
|
10
|
-
const TONE = { success: "success", pending: "pending", failed: "failed" };
|
|
11
|
-
function PaymentDrawer({ row, onClose }) {
|
|
12
|
-
const money = (minor) => row ? minorToMoney(minor, row.currency) : "";
|
|
13
|
-
return /* @__PURE__ */ jsx(
|
|
14
|
-
Drawer,
|
|
15
|
-
{
|
|
16
|
-
open: Boolean(row),
|
|
17
|
-
onClose,
|
|
18
|
-
title: row ? money(row.amountMinor) : "Payment",
|
|
19
|
-
subtitle: row ? row.payer.name ?? row.payer.email ?? "Customer name not recorded" : void 0,
|
|
20
|
-
meta: row ? /* @__PURE__ */ jsx(StatusPill, { status: TONE[row.status] ?? "neutral", label: word(STATUS_WORDS, row.status) }) : void 0,
|
|
21
|
-
children: row ? /* @__PURE__ */ jsxs("div", { className: styles.drawerBody, children: [
|
|
22
|
-
/* @__PURE__ */ jsxs(FactList, { columns: 2, children: [
|
|
23
|
-
/* @__PURE__ */ jsx(Fact, { label: "Paid on", value: when(row.paidAt) }),
|
|
24
|
-
/* @__PURE__ */ jsx(Fact, { label: "What it was for", value: word(TYPE_WORDS, row.type) }),
|
|
25
|
-
/* @__PURE__ */ jsx(Fact, { label: "Paid by", value: row.payer.name ?? "Name not recorded" }),
|
|
26
|
-
/* @__PURE__ */ jsx(Fact, { label: "Email", value: row.payer.email ?? "\u2013" }),
|
|
27
|
-
/* @__PURE__ */ jsx(Fact, { label: "Phone", value: row.payer.phone ?? "\u2013" }),
|
|
28
|
-
/* @__PURE__ */ jsx(Fact, { label: "How they paid", value: row.channel.label }),
|
|
29
|
-
/* @__PURE__ */ jsx(Fact, { label: "Card or bank", value: row.methodDetail ?? "\u2013" }),
|
|
30
|
-
/* @__PURE__ */ jsx(
|
|
31
|
-
Fact,
|
|
32
|
-
{
|
|
33
|
-
label: "Merchant",
|
|
34
|
-
value: row.merchant.name ? /* @__PURE__ */ jsx(Link, { href: `/admin/tenants/${row.merchant.id}`, children: row.merchant.name }) : "Unknown merchant"
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
-
] }),
|
|
38
|
-
/* @__PURE__ */ jsx("h3", { className: styles.drawerHeading, children: "Where the money went" }),
|
|
39
|
-
/* @__PURE__ */ jsxs(FactList, { columns: 2, children: [
|
|
40
|
-
/* @__PURE__ */ jsx(Fact, { label: "Customer paid", value: money(row.amountMinor) }),
|
|
41
|
-
/* @__PURE__ */ jsx(Fact, { label: "Fee taken", value: money(row.feeMinor) }),
|
|
42
|
-
/* @__PURE__ */ jsx(Fact, { label: "Merchant gets", value: money(row.netMinor) }),
|
|
43
|
-
/* @__PURE__ */ jsx(Fact, { label: "Reference", value: row.reference })
|
|
44
|
-
] }),
|
|
45
|
-
row.orderId ? /* @__PURE__ */ jsx("p", { className: styles.sectionNote, children: "This payment is for an order. Find it on the merchant's page, under Orders." }) : null
|
|
46
|
-
] }) : null
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
export {
|
|
51
|
-
PaymentDrawer
|
|
52
|
-
};
|
|
53
|
-
//# sourceMappingURL=payment-drawer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/payment-drawer.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { Fact, FactList } from '@withpotter/ui-kit';\nimport type { MerchantPaymentRow } from '@withpotter/lib/admin-api';\nimport { StatusPill } from '../audit/status-pill';\nimport { Drawer } from '../_ui';\nimport { STATUS_WORDS, TYPE_WORDS, minorToMoney, word } from './ledger-model';\nimport styles from './ledger.module.css';\n\nconst when = (iso: string): string =>\n new Date(iso).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' });\n\nconst TONE: Record<string, string> = { success: 'success', pending: 'pending', failed: 'failed' };\n\n/** One payment in full: who paid, how, to which merchant, and where the money went. */\nexport function PaymentDrawer({ row, onClose }: { row: MerchantPaymentRow | null; onClose: () => void }) {\n const money = (minor: string) => (row ? minorToMoney(minor, row.currency) : '');\n\n return (\n <Drawer\n open={Boolean(row)}\n onClose={onClose}\n title={row ? money(row.amountMinor) : 'Payment'}\n subtitle={row ? (row.payer.name ?? row.payer.email ?? 'Customer name not recorded') : undefined}\n meta={\n row ? (\n <StatusPill status={TONE[row.status] ?? 'neutral'} label={word(STATUS_WORDS, row.status)} />\n ) : undefined\n }\n >\n {row ? (\n <div className={styles.drawerBody}>\n <FactList columns={2}>\n <Fact label=\"Paid on\" value={when(row.paidAt)} />\n <Fact label=\"What it was for\" value={word(TYPE_WORDS, row.type)} />\n <Fact label=\"Paid by\" value={row.payer.name ?? 'Name not recorded'} />\n <Fact label=\"Email\" value={row.payer.email ?? '–'} />\n <Fact label=\"Phone\" value={row.payer.phone ?? '–'} />\n <Fact label=\"How they paid\" value={row.channel.label} />\n <Fact label=\"Card or bank\" value={row.methodDetail ?? '–'} />\n <Fact\n label=\"Merchant\"\n value={\n row.merchant.name ? (\n <Link href={`/admin/tenants/${row.merchant.id}`}>{row.merchant.name}</Link>\n ) : (\n 'Unknown merchant'\n )\n }\n />\n </FactList>\n <h3 className={styles.drawerHeading}>Where the money went</h3>\n <FactList columns={2}>\n <Fact label=\"Customer paid\" value={money(row.amountMinor)} />\n <Fact label=\"Fee taken\" value={money(row.feeMinor)} />\n <Fact label=\"Merchant gets\" value={money(row.netMinor)} />\n <Fact label=\"Reference\" value={row.reference} />\n </FactList>\n {row.orderId ? (\n <p className={styles.sectionNote}>\n This payment is for an order. Find it on the merchant's page, under Orders.\n </p>\n ) : null}\n </div>\n ) : null}\n </Drawer>\n );\n}\n"],"mappings":";AA2BU,cAMA,YANA;AAzBV,OAAO,UAAU;AACjB,SAAS,MAAM,gBAAgB;AAE/B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,cAAc,YAAY,cAAc,YAAY;AAC7D,OAAO,YAAY;AAEnB,MAAM,OAAO,CAAC,QACZ,IAAI,KAAK,GAAG,EAAE,eAAe,SAAS,EAAE,WAAW,UAAU,WAAW,QAAQ,CAAC;AAEnF,MAAM,OAA+B,EAAE,SAAS,WAAW,SAAS,WAAW,QAAQ,SAAS;AAGzF,SAAS,cAAc,EAAE,KAAK,QAAQ,GAA4D;AACvG,QAAM,QAAQ,CAAC,UAAmB,MAAM,aAAa,OAAO,IAAI,QAAQ,IAAI;AAE5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,QAAQ,GAAG;AAAA,MACjB;AAAA,MACA,OAAO,MAAM,MAAM,IAAI,WAAW,IAAI;AAAA,MACtC,UAAU,MAAO,IAAI,MAAM,QAAQ,IAAI,MAAM,SAAS,+BAAgC;AAAA,MACtF,MACE,MACE,oBAAC,cAAW,QAAQ,KAAK,IAAI,MAAM,KAAK,WAAW,OAAO,KAAK,cAAc,IAAI,MAAM,GAAG,IACxF;AAAA,MAGL,gBACC,qBAAC,SAAI,WAAW,OAAO,YACrB;AAAA,6BAAC,YAAS,SAAS,GACjB;AAAA,8BAAC,QAAK,OAAM,WAAU,OAAO,KAAK,IAAI,MAAM,GAAG;AAAA,UAC/C,oBAAC,QAAK,OAAM,mBAAkB,OAAO,KAAK,YAAY,IAAI,IAAI,GAAG;AAAA,UACjE,oBAAC,QAAK,OAAM,WAAU,OAAO,IAAI,MAAM,QAAQ,qBAAqB;AAAA,UACpE,oBAAC,QAAK,OAAM,SAAQ,OAAO,IAAI,MAAM,SAAS,UAAK;AAAA,UACnD,oBAAC,QAAK,OAAM,SAAQ,OAAO,IAAI,MAAM,SAAS,UAAK;AAAA,UACnD,oBAAC,QAAK,OAAM,iBAAgB,OAAO,IAAI,QAAQ,OAAO;AAAA,UACtD,oBAAC,QAAK,OAAM,gBAAe,OAAO,IAAI,gBAAgB,UAAK;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OACE,IAAI,SAAS,OACX,oBAAC,QAAK,MAAM,kBAAkB,IAAI,SAAS,EAAE,IAAK,cAAI,SAAS,MAAK,IAEpE;AAAA;AAAA,UAGN;AAAA,WACF;AAAA,QACA,oBAAC,QAAG,WAAW,OAAO,eAAe,kCAAoB;AAAA,QACzD,qBAAC,YAAS,SAAS,GACjB;AAAA,8BAAC,QAAK,OAAM,iBAAgB,OAAO,MAAM,IAAI,WAAW,GAAG;AAAA,UAC3D,oBAAC,QAAK,OAAM,aAAY,OAAO,MAAM,IAAI,QAAQ,GAAG;AAAA,UACpD,oBAAC,QAAK,OAAM,iBAAgB,OAAO,MAAM,IAAI,QAAQ,GAAG;AAAA,UACxD,oBAAC,QAAK,OAAM,aAAY,OAAO,IAAI,WAAW;AAAA,WAChD;AAAA,QACC,IAAI,UACH,oBAAC,OAAE,WAAW,OAAO,aAAa,yFAElC,IACE;AAAA,SACN,IACE;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { byCurrency, formatPlanPrice } from "@withpotter/lib/plan-price";
|
|
2
|
-
function priceList(prices, currencies) {
|
|
3
|
-
return Object.keys(prices).sort(byCurrency).map(
|
|
4
|
-
(code) => formatPlanPrice(prices[code], code, currencies.find((c) => c.code === code)?.exponent)
|
|
5
|
-
);
|
|
6
|
-
}
|
|
7
|
-
function planLines(plans, currencies, breakdown) {
|
|
8
|
-
const earned = /* @__PURE__ */ new Map();
|
|
9
|
-
for (const row of breakdown) {
|
|
10
|
-
const key = row.planId.toLowerCase();
|
|
11
|
-
const seen = earned.get(key);
|
|
12
|
-
earned.set(key, {
|
|
13
|
-
subscribers: (seen?.subscribers ?? 0) + row.subscribers,
|
|
14
|
-
mrr: (seen?.mrr ?? 0) + row.mrr
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return plans.map((plan) => {
|
|
18
|
-
const version = plan.live ?? plan.open;
|
|
19
|
-
const prices = version?.prices ?? {};
|
|
20
|
-
const hit = earned.get(plan.id.toLowerCase()) ?? earned.get(plan.slug.toLowerCase()) ?? (version ? earned.get(version.name.toLowerCase()) : void 0);
|
|
21
|
-
return {
|
|
22
|
-
id: plan.id,
|
|
23
|
-
name: version?.name ?? plan.slug,
|
|
24
|
-
slug: plan.slug,
|
|
25
|
-
prices: version ? priceList(prices, currencies) : [],
|
|
26
|
-
free: Object.keys(prices).length > 0 && Object.values(prices).every((minor) => /^0*$/.test(minor)),
|
|
27
|
-
draft: plan.live === null,
|
|
28
|
-
subscribers: hit?.subscribers ?? plan.subscribers,
|
|
29
|
-
mrr: hit?.mrr ?? 0
|
|
30
|
-
};
|
|
31
|
-
}).sort((a, b) => b.mrr - a.mrr || b.subscribers - a.subscribers || a.name.localeCompare(b.name));
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
planLines
|
|
35
|
-
};
|
|
36
|
-
//# sourceMappingURL=plan-lines.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/plan-lines.ts"],"sourcesContent":["import type { PlatformRevenue } from '@withpotter/lib/admin-api';\nimport type { PriceCurrency } from '@withpotter/lib/admin-pricing-api';\nimport { byCurrency, formatPlanPrice } from '@withpotter/lib/plan-price';\nimport type { PlanSummary } from '@withpotter/lib/pricing-plans-types';\n\n/** One line per currency, at that currency's exponent. */\nfunction priceList(prices: Record<string, string>, currencies: PriceCurrency[]): string[] {\n return Object.keys(prices)\n .sort(byCurrency)\n .map((code) =>\n formatPlanPrice(prices[code], code, currencies.find((c) => c.code === code)?.exponent),\n );\n}\n\n/** What one plan shows: its catalogue entry joined with what it earns. */\nexport interface PlanLine {\n id: string;\n name: string;\n slug: string;\n prices: string[];\n /** Nothing to pay: every price is zero. */\n free: boolean;\n /** Created but not live yet. */\n draft: boolean;\n subscribers: number;\n mrr: number;\n}\n\n/**\n * Every plan an admin has created, one line each, with the name it was given\n * (Free, Basic, Premium and so on). The plans list is the source of the rows, so\n * a plan nobody is on yet still shows, with 0 subscribers; the revenue figures\n * for it are joined in by plan id, code or name.\n */\nexport function planLines(\n plans: readonly PlanSummary[],\n currencies: PriceCurrency[],\n breakdown: PlatformRevenue['recurring']['planBreakdown'],\n): PlanLine[] {\n const earned = new Map<string, { subscribers: number; mrr: number }>();\n\n for (const row of breakdown) {\n const key = row.planId.toLowerCase();\n const seen = earned.get(key);\n\n // A plan billed monthly and yearly comes as two rows: add them up.\n earned.set(key, {\n subscribers: (seen?.subscribers ?? 0) + row.subscribers,\n mrr: (seen?.mrr ?? 0) + row.mrr,\n });\n }\n\n return plans\n .map((plan): PlanLine => {\n const version = plan.live ?? plan.open;\n const prices: Record<string, string> = version?.prices ?? {};\n // The revenue breakdown names a plan by its code (\"basic\"), the plans list by its id: match either, or the name.\n const hit =\n earned.get(plan.id.toLowerCase()) ??\n earned.get(plan.slug.toLowerCase()) ??\n (version ? earned.get(version.name.toLowerCase()) : undefined);\n\n return {\n id: plan.id,\n name: version?.name ?? plan.slug,\n slug: plan.slug,\n prices: version ? priceList(prices, currencies) : [],\n free:\n Object.keys(prices).length > 0 &&\n Object.values(prices).every((minor) => /^0*$/.test(minor)),\n draft: plan.live === null,\n subscribers: hit?.subscribers ?? plan.subscribers,\n mrr: hit?.mrr ?? 0,\n };\n })\n .sort((a, b) => b.mrr - a.mrr || b.subscribers - a.subscribers || a.name.localeCompare(b.name));\n}\n"],"mappings":"AAEA,SAAS,YAAY,uBAAuB;AAI5C,SAAS,UAAU,QAAgC,YAAuC;AACxF,SAAO,OAAO,KAAK,MAAM,EACtB,KAAK,UAAU,EACf;AAAA,IAAI,CAAC,SACJ,gBAAgB,OAAO,IAAI,GAAG,MAAM,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG,QAAQ;AAAA,EACvF;AACJ;AAsBO,SAAS,UACd,OACA,YACA,WACY;AACZ,QAAM,SAAS,oBAAI,IAAkD;AAErE,aAAW,OAAO,WAAW;AAC3B,UAAM,MAAM,IAAI,OAAO,YAAY;AACnC,UAAM,OAAO,OAAO,IAAI,GAAG;AAG3B,WAAO,IAAI,KAAK;AAAA,MACd,cAAc,MAAM,eAAe,KAAK,IAAI;AAAA,MAC5C,MAAM,MAAM,OAAO,KAAK,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,SAAO,MACJ,IAAI,CAAC,SAAmB;AACvB,UAAM,UAAU,KAAK,QAAQ,KAAK;AAClC,UAAM,SAAiC,SAAS,UAAU,CAAC;AAE3D,UAAM,MACJ,OAAO,IAAI,KAAK,GAAG,YAAY,CAAC,KAChC,OAAO,IAAI,KAAK,KAAK,YAAY,CAAC,MACjC,UAAU,OAAO,IAAI,QAAQ,KAAK,YAAY,CAAC,IAAI;AAEtD,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,SAAS,QAAQ,KAAK;AAAA,MAC5B,MAAM,KAAK;AAAA,MACX,QAAQ,UAAU,UAAU,QAAQ,UAAU,IAAI,CAAC;AAAA,MACnD,MACE,OAAO,KAAK,MAAM,EAAE,SAAS,KAC7B,OAAO,OAAO,MAAM,EAAE,MAAM,CAAC,UAAU,OAAO,KAAK,KAAK,CAAC;AAAA,MAC3D,OAAO,KAAK,SAAS;AAAA,MACrB,aAAa,KAAK,eAAe,KAAK;AAAA,MACtC,KAAK,KAAK,OAAO;AAAA,IACnB;AAAA,EACF,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAClG;","names":[]}
|