@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.
Files changed (262) hide show
  1. package/dist/admin/_money/cash-postings.js +3 -20
  2. package/dist/admin/_money/cash-postings.js.map +1 -1
  3. package/dist/admin/_money/cash.module.css +0 -10
  4. package/dist/admin/_money/finder-format.js +0 -1
  5. package/dist/admin/_money/finder-format.js.map +1 -1
  6. package/dist/admin/_money/format.js +0 -1
  7. package/dist/admin/_money/format.js.map +1 -1
  8. package/dist/admin/_overview/activation-panel.js +6 -6
  9. package/dist/admin/_overview/activation-panel.js.map +1 -1
  10. package/dist/admin/_overview/attention-groups.js +8 -8
  11. package/dist/admin/_overview/attention-groups.js.map +1 -1
  12. package/dist/admin/_overview/attention-panel.js +33 -37
  13. package/dist/admin/_overview/attention-panel.js.map +1 -1
  14. package/dist/admin/_overview/attention.module.css +3 -0
  15. package/dist/admin/_overview/control-room.js +13 -41
  16. package/dist/admin/_overview/control-room.js.map +1 -1
  17. package/dist/admin/_overview/kpi-row.js +7 -7
  18. package/dist/admin/_overview/kpi-row.js.map +1 -1
  19. package/dist/admin/_overview/leaders-panel.js +1 -1
  20. package/dist/admin/_overview/leaders-panel.js.map +1 -1
  21. package/dist/admin/_overview/overdue-outstanding.js +1 -2
  22. package/dist/admin/_overview/overdue-outstanding.js.map +1 -1
  23. package/dist/admin/_overview/portfolio-panel.js +5 -5
  24. package/dist/admin/_overview/portfolio-panel.js.map +1 -1
  25. package/dist/admin/_overview/position-panel.js +12 -8
  26. package/dist/admin/_overview/position-panel.js.map +1 -1
  27. package/dist/admin/_pos/pos-figures.js +5 -18
  28. package/dist/admin/_pos/pos-figures.js.map +1 -1
  29. package/dist/admin/_pos/pos-rules.js +0 -2
  30. package/dist/admin/_pos/pos-rules.js.map +1 -1
  31. package/dist/admin/_pos/pos-words.js +1 -6
  32. package/dist/admin/_pos/pos-words.js.map +1 -1
  33. package/dist/admin/_pos/use-pos.js +1 -70
  34. package/dist/admin/_pos/use-pos.js.map +1 -1
  35. package/dist/admin/_shell/nav-config.js +3 -3
  36. package/dist/admin/_shell/nav-config.js.map +1 -1
  37. package/dist/admin/_shell/nav-filter.js +2 -4
  38. package/dist/admin/_shell/nav-filter.js.map +1 -1
  39. package/dist/admin/_shell/nav-row.js +2 -5
  40. package/dist/admin/_shell/nav-row.js.map +1 -1
  41. package/dist/admin/_shell/sidebar-footer.js +0 -20
  42. package/dist/admin/_shell/sidebar-footer.js.map +1 -1
  43. package/dist/admin/_shell/sidebar-footer.module.css +0 -17
  44. package/dist/admin/_shell/sidebar.js +15 -25
  45. package/dist/admin/_shell/sidebar.js.map +1 -1
  46. package/dist/admin/_shell/sidebar.module.css +0 -69
  47. package/dist/admin/_shell/use-fluid-hover.js +6 -37
  48. package/dist/admin/_shell/use-fluid-hover.js.map +1 -1
  49. package/dist/admin/_ui/admin.css +0 -7
  50. package/dist/admin/_ui/data-table.js +1 -1
  51. package/dist/admin/_ui/data-table.js.map +1 -1
  52. package/dist/admin/_ui/list-card.js +1 -1
  53. package/dist/admin/_ui/list-card.js.map +1 -1
  54. package/dist/admin/_ui/list-card.module.css +16 -2
  55. package/dist/admin/_ui/money-view.js +0 -1
  56. package/dist/admin/_ui/money-view.js.map +1 -1
  57. package/dist/admin/_ui/page-header.js +4 -31
  58. package/dist/admin/_ui/page-header.js.map +1 -1
  59. package/dist/admin/_ui/panel.module.css +10 -0
  60. package/dist/admin/_ui/stat-card.js +2 -11
  61. package/dist/admin/_ui/stat-card.js.map +1 -1
  62. package/dist/admin/_ui/trend.js +2 -2
  63. package/dist/admin/_ui/trend.js.map +1 -1
  64. package/dist/admin/_ui/workspace-tab.js +1 -11
  65. package/dist/admin/_ui/workspace-tab.js.map +1 -1
  66. package/dist/admin/accounts/account-panel.js +6 -49
  67. package/dist/admin/accounts/account-panel.js.map +1 -1
  68. package/dist/admin/accounts/account-panel.module.css +0 -73
  69. package/dist/admin/accounts/account-row.js +2 -11
  70. package/dist/admin/accounts/account-row.js.map +1 -1
  71. package/dist/admin/accounts/account-vitals.js +13 -15
  72. package/dist/admin/accounts/account-vitals.js.map +1 -1
  73. package/dist/admin/accounts/accounts-table.js +8 -39
  74. package/dist/admin/accounts/accounts-table.js.map +1 -1
  75. package/dist/admin/accounts/accounts-workspace.js +2 -2
  76. package/dist/admin/accounts/accounts-workspace.js.map +1 -1
  77. package/dist/admin/accounts/owner-session.js +6 -0
  78. package/dist/admin/accounts/owner-session.js.map +1 -1
  79. package/dist/admin/accounts/rows.module.css +0 -17
  80. package/dist/admin/audit/status-pill.js +0 -6
  81. package/dist/admin/audit/status-pill.js.map +1 -1
  82. package/dist/admin/audit/status-pill.module.css +0 -4
  83. package/dist/admin/commerce/commerce-overview.js +2 -2
  84. package/dist/admin/commerce/commerce-overview.js.map +1 -1
  85. package/dist/admin/commerce/merchant-meta.js +0 -1
  86. package/dist/admin/commerce/merchant-meta.js.map +1 -1
  87. package/dist/admin/commerce/merchant-view.js +2 -2
  88. package/dist/admin/commerce/merchant-view.js.map +1 -1
  89. package/dist/admin/feedback/prompt-operations.module.css +2 -0
  90. package/dist/admin/feedback/prompt-preview.module.css +2 -0
  91. package/dist/admin/hooks.js +1 -57
  92. package/dist/admin/hooks.js.map +1 -1
  93. package/dist/admin/layout.js +2 -8
  94. package/dist/admin/layout.js.map +1 -1
  95. package/dist/admin/page.js +8 -28
  96. package/dist/admin/page.js.map +1 -1
  97. package/dist/admin/payout-tiers/_tier-dialog.js +21 -31
  98. package/dist/admin/payout-tiers/_tier-dialog.js.map +1 -1
  99. package/dist/admin/payout-tiers/tier-preview-result.js +9 -10
  100. package/dist/admin/payout-tiers/tier-preview-result.js.map +1 -1
  101. package/dist/admin/payout-tiers/tiers-table.js +17 -40
  102. package/dist/admin/payout-tiers/tiers-table.js.map +1 -1
  103. package/dist/admin/payout-tiers/tiers-workspace.js +17 -20
  104. package/dist/admin/payout-tiers/tiers-workspace.js.map +1 -1
  105. package/dist/admin/pos/pos-workspace.js +2 -6
  106. package/dist/admin/pos/pos-workspace.js.map +1 -1
  107. package/dist/admin/pos/shifts-tab.js +9 -16
  108. package/dist/admin/pos/shifts-tab.js.map +1 -1
  109. package/dist/admin/revenue/cohort-panel.js +6 -6
  110. package/dist/admin/revenue/cohort-panel.js.map +1 -1
  111. package/dist/admin/revenue/health-panel.js +21 -22
  112. package/dist/admin/revenue/health-panel.js.map +1 -1
  113. package/dist/admin/revenue/plan-mix-panel.js +3 -3
  114. package/dist/admin/revenue/plan-mix-panel.js.map +1 -1
  115. package/dist/admin/revenue/quality-panel.js +19 -19
  116. package/dist/admin/revenue/quality-panel.js.map +1 -1
  117. package/dist/admin/revenue/revenue-kpis.js +50 -111
  118. package/dist/admin/revenue/revenue-kpis.js.map +1 -1
  119. package/dist/admin/revenue/revenue-overview.js +46 -39
  120. package/dist/admin/revenue/revenue-overview.js.map +1 -1
  121. package/dist/admin/revenue/revenue-retention.js +13 -17
  122. package/dist/admin/revenue/revenue-retention.js.map +1 -1
  123. package/dist/admin/revenue/revenue-workspace.js +6 -53
  124. package/dist/admin/revenue/revenue-workspace.js.map +1 -1
  125. package/dist/admin/revenue/subscriptions-panel.js +14 -14
  126. package/dist/admin/revenue/subscriptions-panel.js.map +1 -1
  127. package/dist/admin/revenue/top-tenants-panel.js +4 -4
  128. package/dist/admin/revenue/top-tenants-panel.js.map +1 -1
  129. package/dist/admin/settings/account-tab.js +25 -1
  130. package/dist/admin/settings/account-tab.js.map +1 -1
  131. package/dist/admin/settings/general-tab.js +2 -2
  132. package/dist/admin/settings/general-tab.js.map +1 -1
  133. package/dist/admin/settings/system-status-card.js +30 -0
  134. package/dist/admin/settings/system-status-card.js.map +1 -0
  135. package/dist/admin/support/queue-card.module.css +4 -1
  136. package/dist/admin/tenants/[id]/commerce/format.js +1 -2
  137. package/dist/admin/tenants/[id]/commerce/format.js.map +1 -1
  138. package/dist/admin/tenants/[id]/commerce/recent-orders.js +1 -1
  139. package/dist/admin/tenants/[id]/commerce/recent-orders.js.map +1 -1
  140. package/dist/admin/tenants/[id]/page.js +10 -37
  141. package/dist/admin/tenants/[id]/page.js.map +1 -1
  142. package/dist/admin/tenants/[id]/tenant-layout.module.css +4 -1
  143. package/dist/admin/tenants/[id]/tenant-overview.js +2 -3
  144. package/dist/admin/tenants/[id]/tenant-overview.js.map +1 -1
  145. package/dist/admin/tenants/[id]/tenant-overview.module.css +1 -1
  146. package/dist/admin/tenants/[id]/tenant-pos.js +16 -64
  147. package/dist/admin/tenants/[id]/tenant-pos.js.map +1 -1
  148. package/dist/admin/tenants/[id]/tenant-wallet-card.js +2 -3
  149. package/dist/admin/tenants/[id]/tenant-wallet-card.js.map +1 -1
  150. package/dist/admin/tenants/[id]/verification/document-viewer.js +52 -3
  151. package/dist/admin/tenants/[id]/verification/document-viewer.js.map +1 -1
  152. package/dist/admin/tenants/[id]/verification/document-viewer.module.css +0 -51
  153. package/dist/admin/tenants/[id]/verification/tenant-verification.js +0 -10
  154. package/dist/admin/tenants/[id]/verification/tenant-verification.js.map +1 -1
  155. package/dist/admin/tenants/tenant-list-row.js +1 -2
  156. package/dist/admin/tenants/tenant-list-row.js.map +1 -1
  157. package/dist/admin/tenants/tenants-table.js +2 -3
  158. package/dist/admin/tenants/tenants-table.js.map +1 -1
  159. package/dist/admin/tenants/tenants-workspace.js +2 -2
  160. package/dist/admin/tenants/tenants-workspace.js.map +1 -1
  161. package/dist/admin/verification/verification-page.js +3 -19
  162. package/dist/admin/verification/verification-page.js.map +1 -1
  163. package/dist/admin/verification/verification.module.css +0 -10
  164. package/dist/admin/verification/waiting-case-actions.js +68 -0
  165. package/dist/admin/verification/waiting-case-actions.js.map +1 -0
  166. package/dist/admin/verification/waiting-cases.js +48 -66
  167. package/dist/admin/verification/waiting-cases.js.map +1 -1
  168. package/package.json +5 -5
  169. package/dist/admin/_pos/olive-balance.js +0 -40
  170. package/dist/admin/_pos/olive-balance.js.map +0 -1
  171. package/dist/admin/_pos/olive-state.js +0 -23
  172. package/dist/admin/_pos/olive-state.js.map +0 -1
  173. package/dist/admin/_pos/payments-model.js +0 -55
  174. package/dist/admin/_pos/payments-model.js.map +0 -1
  175. package/dist/admin/_pos/tenant-operators.js +0 -63
  176. package/dist/admin/_pos/tenant-operators.js.map +0 -1
  177. package/dist/admin/_pos/tenant-outlets.js +0 -77
  178. package/dist/admin/_pos/tenant-outlets.js.map +0 -1
  179. package/dist/admin/_pos/tenant-pos-figures.js +0 -82
  180. package/dist/admin/_pos/tenant-pos-figures.js.map +0 -1
  181. package/dist/admin/_pos/tenant-requests.js +0 -64
  182. package/dist/admin/_pos/tenant-requests.js.map +0 -1
  183. package/dist/admin/_pos/tenant-transactions.js +0 -145
  184. package/dist/admin/_pos/tenant-transactions.js.map +0 -1
  185. package/dist/admin/_shell/nav-fold.js +0 -14
  186. package/dist/admin/_shell/nav-fold.js.map +0 -1
  187. package/dist/admin/_shell/nav-group.js +0 -78
  188. package/dist/admin/_shell/nav-group.js.map +0 -1
  189. package/dist/admin/_shell/use-open-groups.js +0 -64
  190. package/dist/admin/_shell/use-open-groups.js.map +0 -1
  191. package/dist/admin/_ui/page-header.module.css +0 -45
  192. package/dist/admin/accounts/account-sessions.js +0 -70
  193. package/dist/admin/accounts/account-sessions.js.map +0 -1
  194. package/dist/admin/accounts/account-sessions.module.css +0 -55
  195. package/dist/admin/accounts/account-stage-filter.js +0 -29
  196. package/dist/admin/accounts/account-stage-filter.js.map +0 -1
  197. package/dist/admin/accounts/account-stage.js +0 -81
  198. package/dist/admin/accounts/account-stage.js.map +0 -1
  199. package/dist/admin/accounts/hint.js +0 -12
  200. package/dist/admin/accounts/hint.js.map +0 -1
  201. package/dist/admin/payout-tiers/requirement-labels.js +0 -70
  202. package/dist/admin/payout-tiers/requirement-labels.js.map +0 -1
  203. package/dist/admin/pos/transactions-tab.js +0 -214
  204. package/dist/admin/pos/transactions-tab.js.map +0 -1
  205. package/dist/admin/revenue/channel-panel.js +0 -49
  206. package/dist/admin/revenue/channel-panel.js.map +0 -1
  207. package/dist/admin/revenue/channel-rows.js +0 -17
  208. package/dist/admin/revenue/channel-rows.js.map +0 -1
  209. package/dist/admin/revenue/download-button.js +0 -40
  210. package/dist/admin/revenue/download-button.js.map +0 -1
  211. package/dist/admin/revenue/earnings/earnings-colors.js +0 -25
  212. package/dist/admin/revenue/earnings/earnings-colors.js.map +0 -1
  213. package/dist/admin/revenue/earnings/earnings-legend.js +0 -75
  214. package/dist/admin/revenue/earnings/earnings-legend.js.map +0 -1
  215. package/dist/admin/revenue/earnings/earnings-summary.js +0 -308
  216. package/dist/admin/revenue/earnings/earnings-summary.js.map +0 -1
  217. package/dist/admin/revenue/earnings/earnings-table.js +0 -143
  218. package/dist/admin/revenue/earnings/earnings-table.js.map +0 -1
  219. package/dist/admin/revenue/earnings/earnings.module.css +0 -201
  220. package/dist/admin/revenue/earnings/use-earnings.js +0 -27
  221. package/dist/admin/revenue/earnings/use-earnings.js.map +0 -1
  222. package/dist/admin/revenue/ledger-model.js +0 -104
  223. package/dist/admin/revenue/ledger-model.js.map +0 -1
  224. package/dist/admin/revenue/ledger.module.css +0 -144
  225. package/dist/admin/revenue/merchant-headline.js +0 -37
  226. package/dist/admin/revenue/merchant-headline.js.map +0 -1
  227. package/dist/admin/revenue/merchant-payments-view.js +0 -234
  228. package/dist/admin/revenue/merchant-payments-view.js.map +0 -1
  229. package/dist/admin/revenue/method-cards.js +0 -40
  230. package/dist/admin/revenue/method-cards.js.map +0 -1
  231. package/dist/admin/revenue/payment-drawer.js +0 -53
  232. package/dist/admin/revenue/payment-drawer.js.map +0 -1
  233. package/dist/admin/revenue/plan-lines.js +0 -36
  234. package/dist/admin/revenue/plan-lines.js.map +0 -1
  235. package/dist/admin/revenue/plan-payment-drawer.js +0 -42
  236. package/dist/admin/revenue/plan-payment-drawer.js.map +0 -1
  237. package/dist/admin/revenue/plans-panel.js +0 -48
  238. package/dist/admin/revenue/plans-panel.js.map +0 -1
  239. package/dist/admin/revenue/platform-fees.js +0 -9
  240. package/dist/admin/revenue/platform-fees.js.map +0 -1
  241. package/dist/admin/revenue/print-summary-button.js +0 -27
  242. package/dist/admin/revenue/print-summary-button.js.map +0 -1
  243. package/dist/admin/revenue/subscriptions-view.js +0 -213
  244. package/dist/admin/revenue/subscriptions-view.js.map +0 -1
  245. package/dist/admin/revenue/summary-download.js +0 -38
  246. package/dist/admin/revenue/summary-download.js.map +0 -1
  247. package/dist/admin/revenue/summary-print.js +0 -82
  248. package/dist/admin/revenue/summary-print.js.map +0 -1
  249. package/dist/admin/revenue/summary-rows.js +0 -44
  250. package/dist/admin/revenue/summary-rows.js.map +0 -1
  251. package/dist/admin/revenue/url-filters.js +0 -36
  252. package/dist/admin/revenue/url-filters.js.map +0 -1
  253. package/dist/admin/revenue/use-url-filters.js +0 -24
  254. package/dist/admin/revenue/use-url-filters.js.map +0 -1
  255. package/dist/admin/tenants/[id]/use-tenant-wallet.js +0 -23
  256. package/dist/admin/tenants/[id]/use-tenant-wallet.js.map +0 -1
  257. package/dist/admin/tenants/[id]/verification/document-file.js +0 -78
  258. package/dist/admin/tenants/[id]/verification/document-file.js.map +0 -1
  259. package/dist/admin/tenants/[id]/verification/sent-files.js +0 -33
  260. package/dist/admin/tenants/[id]/verification/sent-files.js.map +0 -1
  261. package/dist/admin/verification/preview-dialog.js +0 -111
  262. package/dist/admin/verification/preview-dialog.js.map +0 -1
@@ -1,42 +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 { PURPOSE_WORDS, STATUS_WORDS, SUBSCRIPTION_METHOD_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 = { succeeded: "success", pending: "pending", failed: "failed" };
11
- function PlanPaymentDrawer({ row, onClose }) {
12
- return /* @__PURE__ */ jsx(
13
- Drawer,
14
- {
15
- open: Boolean(row),
16
- onClose,
17
- title: row ? minorToMoney(row.amountKobo, "NGN") : "Plan payment",
18
- subtitle: row ? row.merchantName ?? "Unknown merchant" : void 0,
19
- meta: row ? /* @__PURE__ */ jsx(StatusPill, { status: TONE[row.status] ?? "neutral", label: word(STATUS_WORDS, row.status) }) : void 0,
20
- children: row ? /* @__PURE__ */ jsx("div", { className: styles.drawerBody, children: /* @__PURE__ */ jsxs(FactList, { columns: 2, children: [
21
- /* @__PURE__ */ jsx(Fact, { label: "Paid on", value: when(row.paidAt ?? row.createdAt) }),
22
- /* @__PURE__ */ jsx(Fact, { label: "Plan", value: row.plan.charAt(0).toUpperCase() + row.plan.slice(1) }),
23
- /* @__PURE__ */ jsx(Fact, { label: "What it was for", value: word(PURPOSE_WORDS, row.purpose) }),
24
- /* @__PURE__ */ jsx(Fact, { label: "How they paid", value: word(SUBSCRIPTION_METHOD_WORDS, row.method) }),
25
- /* @__PURE__ */ jsx(
26
- Fact,
27
- {
28
- label: "Merchant",
29
- value: /* @__PURE__ */ jsx(Link, { href: `/admin/tenants/${row.tenantId}`, children: row.merchantName ?? "Open merchant" })
30
- }
31
- ),
32
- /* @__PURE__ */ jsx(Fact, { label: "Email", value: row.merchantEmail ?? "\u2013" }),
33
- /* @__PURE__ */ jsx(Fact, { label: "Reference", value: row.reference }),
34
- row.status === "failed" ? /* @__PURE__ */ jsx(Fact, { label: "Why it failed", value: row.failureReason ?? "Not recorded" }) : null
35
- ] }) }) : null
36
- }
37
- );
38
- }
39
- export {
40
- PlanPaymentDrawer
41
- };
42
- //# sourceMappingURL=plan-payment-drawer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/plan-payment-drawer.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { Fact, FactList } from '@withpotter/ui-kit';\nimport type { SubscriptionPaymentRow } from '@withpotter/lib/admin-api';\nimport { StatusPill } from '../audit/status-pill';\nimport { Drawer } from '../_ui';\nimport { PURPOSE_WORDS, STATUS_WORDS, SUBSCRIPTION_METHOD_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> = { succeeded: 'success', pending: 'pending', failed: 'failed' };\n\n/** One plan payment in full: which merchant paid, for which plan, and how it went. */\nexport function PlanPaymentDrawer({ row, onClose }: { row: SubscriptionPaymentRow | null; onClose: () => void }) {\n return (\n <Drawer\n open={Boolean(row)}\n onClose={onClose}\n title={row ? minorToMoney(row.amountKobo, 'NGN') : 'Plan payment'}\n subtitle={row ? (row.merchantName ?? 'Unknown merchant') : undefined}\n meta={\n row ? <StatusPill status={TONE[row.status] ?? 'neutral'} label={word(STATUS_WORDS, row.status)} /> : undefined\n }\n >\n {row ? (\n <div className={styles.drawerBody}>\n <FactList columns={2}>\n <Fact label=\"Paid on\" value={when(row.paidAt ?? row.createdAt)} />\n <Fact label=\"Plan\" value={row.plan.charAt(0).toUpperCase() + row.plan.slice(1)} />\n <Fact label=\"What it was for\" value={word(PURPOSE_WORDS, row.purpose)} />\n <Fact label=\"How they paid\" value={word(SUBSCRIPTION_METHOD_WORDS, row.method)} />\n <Fact\n label=\"Merchant\"\n value={<Link href={`/admin/tenants/${row.tenantId}`}>{row.merchantName ?? 'Open merchant'}</Link>}\n />\n <Fact label=\"Email\" value={row.merchantEmail ?? '–'} />\n <Fact label=\"Reference\" value={row.reference} />\n {row.status === 'failed' ? <Fact label=\"Why it failed\" value={row.failureReason ?? 'Not recorded'} /> : null}\n </FactList>\n </div>\n ) : null}\n </Drawer>\n );\n}\n"],"mappings":";AAwBc,cAKJ,YALI;AAtBd,OAAO,UAAU;AACjB,SAAS,MAAM,gBAAgB;AAE/B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,eAAe,cAAc,2BAA2B,cAAc,YAAY;AAC3F,OAAO,YAAY;AAEnB,MAAM,OAAO,CAAC,QACZ,IAAI,KAAK,GAAG,EAAE,eAAe,SAAS,EAAE,WAAW,UAAU,WAAW,QAAQ,CAAC;AAEnF,MAAM,OAA+B,EAAE,WAAW,WAAW,SAAS,WAAW,QAAQ,SAAS;AAG3F,SAAS,kBAAkB,EAAE,KAAK,QAAQ,GAAgE;AAC/G,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,QAAQ,GAAG;AAAA,MACjB;AAAA,MACA,OAAO,MAAM,aAAa,IAAI,YAAY,KAAK,IAAI;AAAA,MACnD,UAAU,MAAO,IAAI,gBAAgB,qBAAsB;AAAA,MAC3D,MACE,MAAM,oBAAC,cAAW,QAAQ,KAAK,IAAI,MAAM,KAAK,WAAW,OAAO,KAAK,cAAc,IAAI,MAAM,GAAG,IAAK;AAAA,MAGtG,gBACC,oBAAC,SAAI,WAAW,OAAO,YACrB,+BAAC,YAAS,SAAS,GACjB;AAAA,4BAAC,QAAK,OAAM,WAAU,OAAO,KAAK,IAAI,UAAU,IAAI,SAAS,GAAG;AAAA,QAChE,oBAAC,QAAK,OAAM,QAAO,OAAO,IAAI,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,IAAI,KAAK,MAAM,CAAC,GAAG;AAAA,QAChF,oBAAC,QAAK,OAAM,mBAAkB,OAAO,KAAK,eAAe,IAAI,OAAO,GAAG;AAAA,QACvE,oBAAC,QAAK,OAAM,iBAAgB,OAAO,KAAK,2BAA2B,IAAI,MAAM,GAAG;AAAA,QAChF;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO,oBAAC,QAAK,MAAM,kBAAkB,IAAI,QAAQ,IAAK,cAAI,gBAAgB,iBAAgB;AAAA;AAAA,QAC5F;AAAA,QACA,oBAAC,QAAK,OAAM,SAAQ,OAAO,IAAI,iBAAiB,UAAK;AAAA,QACrD,oBAAC,QAAK,OAAM,aAAY,OAAO,IAAI,WAAW;AAAA,QAC7C,IAAI,WAAW,WAAW,oBAAC,QAAK,OAAM,iBAAgB,OAAO,IAAI,iBAAiB,gBAAgB,IAAK;AAAA,SAC1G,GACF,IACE;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -1,48 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { EmptyNote, TableRow, TableShell, Th } from "@withpotter/ui-kit";
3
- import { Panel } from "../_ui";
4
- import styles from "./revenue.module.css";
5
- const integer = new Intl.NumberFormat("en-US");
6
- function PlansPanel({
7
- lines,
8
- money,
9
- caption
10
- }) {
11
- const totalMrr = lines.reduce((sum, p) => sum + p.mrr, 0);
12
- return /* @__PURE__ */ jsx(Panel, { title: "Plans", count: lines.length || void 0, caption, flush: true, children: lines.length === 0 ? /* @__PURE__ */ jsx(EmptyNote, { className: styles.empty, children: "No plans created yet." }) : /* @__PURE__ */ jsx(
13
- TableShell,
14
- {
15
- bare: true,
16
- minWidth: 520,
17
- density: "compact",
18
- head: /* @__PURE__ */ jsxs("tr", { children: [
19
- /* @__PURE__ */ jsx(Th, { children: "Plan" }),
20
- /* @__PURE__ */ jsx(Th, { align: "right", children: "Price a month" }),
21
- /* @__PURE__ */ jsx(Th, { align: "right", children: "Subscribers" }),
22
- /* @__PURE__ */ jsx(Th, { align: "right", children: "Monthly income" }),
23
- /* @__PURE__ */ jsx(Th, { align: "right", children: "Share" })
24
- ] }),
25
- children: lines.map((p) => {
26
- const share = totalMrr > 0 ? p.mrr / totalMrr * 100 : 0;
27
- return /* @__PURE__ */ jsxs(TableRow, { children: [
28
- /* @__PURE__ */ jsx("td", { className: styles.name, children: /* @__PURE__ */ jsxs("span", { className: styles.tenantText, children: [
29
- p.name,
30
- /* @__PURE__ */ jsx("span", { className: styles.slug, children: p.draft ? `${p.slug} \xB7 not live` : p.slug })
31
- ] }) }),
32
- /* @__PURE__ */ jsx("td", { className: styles.num, children: p.free ? "Free" : p.prices.length ? p.prices.map((l) => /* @__PURE__ */ jsx("div", { children: l }, l)) : "\u2013" }),
33
- /* @__PURE__ */ jsx("td", { className: styles.num, children: integer.format(p.subscribers) }),
34
- /* @__PURE__ */ jsx("td", { className: styles.numStrong, children: money(p.mrr) }),
35
- /* @__PURE__ */ jsx("td", { className: styles.num, children: /* @__PURE__ */ jsxs("span", { className: styles.share, children: [
36
- /* @__PURE__ */ jsx("span", { className: styles.shareTrack, "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: styles.shareFill, style: { width: `${Math.min(share, 100)}%` } }) }),
37
- share.toFixed(1),
38
- "%"
39
- ] }) })
40
- ] }, p.id);
41
- })
42
- }
43
- ) });
44
- }
45
- export {
46
- PlansPanel
47
- };
48
- //# sourceMappingURL=plans-panel.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/plans-panel.tsx"],"sourcesContent":["import { EmptyNote, TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { Panel } from '../_ui';\nimport type { PlanLine } from './plan-lines';\nimport styles from './revenue.module.css';\n\nconst integer = new Intl.NumberFormat('en-US');\n\nexport function PlansPanel({\n lines,\n money,\n caption,\n}: {\n lines: readonly PlanLine[];\n money: (value: number) => string;\n caption: string;\n}) {\n const totalMrr = lines.reduce((sum, p) => sum + p.mrr, 0);\n\n return (\n <Panel title=\"Plans\" count={lines.length || undefined} caption={caption} flush>\n {lines.length === 0 ? (\n <EmptyNote className={styles.empty}>No plans created yet.</EmptyNote>\n ) : (\n <TableShell\n bare\n minWidth={520}\n density=\"compact\"\n head={\n <tr>\n <Th>Plan</Th>\n <Th align=\"right\">Price a month</Th>\n <Th align=\"right\">Subscribers</Th>\n <Th align=\"right\">Monthly income</Th>\n <Th align=\"right\">Share</Th>\n </tr>\n }\n >\n {lines.map((p) => {\n const share = totalMrr > 0 ? (p.mrr / totalMrr) * 100 : 0;\n\n return (\n <TableRow key={p.id}>\n <td className={styles.name}>\n <span className={styles.tenantText}>\n {p.name}\n <span className={styles.slug}>{p.draft ? `${p.slug} · not live` : p.slug}</span>\n </span>\n </td>\n <td className={styles.num}>\n {p.free ? 'Free' : p.prices.length ? p.prices.map((l) => <div key={l}>{l}</div>) : '–'}\n </td>\n <td className={styles.num}>{integer.format(p.subscribers)}</td>\n <td className={styles.numStrong}>{money(p.mrr)}</td>\n <td className={styles.num}>\n <span className={styles.share}>\n <span className={styles.shareTrack} aria-hidden>\n <span className={styles.shareFill} style={{ width: `${Math.min(share, 100)}%` }} />\n </span>\n {share.toFixed(1)}%\n </span>\n </td>\n </TableRow>\n );\n })}\n </TableShell>\n )}\n </Panel>\n );\n}\n"],"mappings":"AAqBQ,cAOI,YAPJ;AArBR,SAAS,WAAW,UAAU,YAAY,UAAU;AACpD,SAAS,aAAa;AAEtB,OAAO,YAAY;AAEnB,MAAM,UAAU,IAAI,KAAK,aAAa,OAAO;AAEtC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,WAAW,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,KAAK,CAAC;AAExD,SACE,oBAAC,SAAM,OAAM,SAAQ,OAAO,MAAM,UAAU,QAAW,SAAkB,OAAK,MAC3E,gBAAM,WAAW,IAChB,oBAAC,aAAU,WAAW,OAAO,OAAO,mCAAqB,IAEzD;AAAA,IAAC;AAAA;AAAA,MACC,MAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAQ;AAAA,MACR,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,kBAAI;AAAA,QACR,oBAAC,MAAG,OAAM,SAAQ,2BAAa;AAAA,QAC/B,oBAAC,MAAG,OAAM,SAAQ,yBAAW;AAAA,QAC7B,oBAAC,MAAG,OAAM,SAAQ,4BAAc;AAAA,QAChC,oBAAC,MAAG,OAAM,SAAQ,mBAAK;AAAA,SACzB;AAAA,MAGD,gBAAM,IAAI,CAAC,MAAM;AAChB,cAAM,QAAQ,WAAW,IAAK,EAAE,MAAM,WAAY,MAAM;AAExD,eACE,qBAAC,YACC;AAAA,8BAAC,QAAG,WAAW,OAAO,MACpB,+BAAC,UAAK,WAAW,OAAO,YACrB;AAAA,cAAE;AAAA,YACH,oBAAC,UAAK,WAAW,OAAO,MAAO,YAAE,QAAQ,GAAG,EAAE,IAAI,mBAAgB,EAAE,MAAK;AAAA,aAC3E,GACF;AAAA,UACA,oBAAC,QAAG,WAAW,OAAO,KACnB,YAAE,OAAO,SAAS,EAAE,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC,MAAM,oBAAC,SAAa,eAAJ,CAAM,CAAM,IAAI,UACrF;AAAA,UACA,oBAAC,QAAG,WAAW,OAAO,KAAM,kBAAQ,OAAO,EAAE,WAAW,GAAE;AAAA,UAC1D,oBAAC,QAAG,WAAW,OAAO,WAAY,gBAAM,EAAE,GAAG,GAAE;AAAA,UAC/C,oBAAC,QAAG,WAAW,OAAO,KACpB,+BAAC,UAAK,WAAW,OAAO,OACtB;AAAA,gCAAC,UAAK,WAAW,OAAO,YAAY,eAAW,MAC7C,8BAAC,UAAK,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,GAAG,CAAC,IAAI,GAAG,GACnF;AAAA,YACC,MAAM,QAAQ,CAAC;AAAA,YAAE;AAAA,aACpB,GACF;AAAA,aAnBa,EAAE,EAoBjB;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH,GAEJ;AAEJ;","names":[]}
@@ -1,9 +0,0 @@
1
- import { shownRows } from "../_ui/money-view";
2
- function platformFeeRows(rows, selected) {
3
- const shown = shownRows(rows, selected);
4
- return shown.length ? shown : null;
5
- }
6
- export {
7
- platformFeeRows
8
- };
9
- //# sourceMappingURL=platform-fees.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/platform-fees.ts"],"sourcesContent":["import { shownRows } from '../_ui/money-view';\n\n/**\n * The platform fees rows to show, or null when the figure should be hidden.\n * The api field is optional: absent or empty means nothing to show, never a\n * zero. Rows follow the currency filter and are never added together.\n */\nexport function platformFeeRows<T extends { currency: string }>(\n rows: readonly T[] | undefined,\n selected: string,\n): T[] | null {\n const shown = shownRows(rows, selected);\n return shown.length ? shown : null;\n}\n"],"mappings":"AAAA,SAAS,iBAAiB;AAOnB,SAAS,gBACd,MACA,UACY;AACZ,QAAM,QAAQ,UAAU,MAAM,QAAQ;AACtC,SAAO,MAAM,SAAS,QAAQ;AAChC;","names":[]}
@@ -1,27 +0,0 @@
1
- import { ADMIN_NAME } from "../_shell/admin-brand";
2
- import { summaryHtml } from "./summary-print";
3
- function printSummary(model, onFail) {
4
- const frame = document.createElement("iframe");
5
- frame.setAttribute("aria-hidden", "true");
6
- frame.style.cssText = "position:fixed;width:0;height:0;border:0;visibility:hidden";
7
- document.body.appendChild(frame);
8
- const doc = frame.contentDocument;
9
- const view = frame.contentWindow;
10
- if (!doc || !view) {
11
- frame.remove();
12
- onFail();
13
- return;
14
- }
15
- doc.open();
16
- doc.write(summaryHtml(model, ADMIN_NAME, (/* @__PURE__ */ new Date()).toLocaleDateString("en-GB", { dateStyle: "long" })));
17
- doc.close();
18
- setTimeout(() => {
19
- view.focus();
20
- view.print();
21
- setTimeout(() => frame.remove(), 1e3);
22
- }, 150);
23
- }
24
- export {
25
- printSummary
26
- };
27
- //# sourceMappingURL=print-summary-button.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/print-summary-button.tsx"],"sourcesContent":["import { ADMIN_NAME } from '../_shell/admin-brand';\nimport { summaryHtml, type SummaryModel } from './summary-print';\n\n/**\n * Opens the one-page summary in the browser's print window, where \"Save as PDF\" makes the file. The page is drawn in a\n * hidden frame, so the admin itself is never printed and no pop-up is needed. `onFail` runs when the frame cannot open.\n */\nexport function printSummary(model: SummaryModel, onFail: () => void): void {\n const frame = document.createElement('iframe');\n frame.setAttribute('aria-hidden', 'true');\n frame.style.cssText = 'position:fixed;width:0;height:0;border:0;visibility:hidden';\n document.body.appendChild(frame);\n\n const doc = frame.contentDocument;\n const view = frame.contentWindow;\n\n if (!doc || !view) {\n frame.remove();\n onFail();\n\n return;\n }\n\n doc.open();\n doc.write(summaryHtml(model, ADMIN_NAME, new Date().toLocaleDateString('en-GB', { dateStyle: 'long' })));\n doc.close();\n\n // Print once the page has laid out, and tidy the frame up when the print window closes.\n setTimeout(() => {\n view.focus();\n view.print();\n setTimeout(() => frame.remove(), 1000);\n }, 150);\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,mBAAsC;AAMxC,SAAS,aAAa,OAAqB,QAA0B;AAC1E,QAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,QAAM,aAAa,eAAe,MAAM;AACxC,QAAM,MAAM,UAAU;AACtB,WAAS,KAAK,YAAY,KAAK;AAE/B,QAAM,MAAM,MAAM;AAClB,QAAM,OAAO,MAAM;AAEnB,MAAI,CAAC,OAAO,CAAC,MAAM;AACjB,UAAM,OAAO;AACb,WAAO;AAEP;AAAA,EACF;AAEA,MAAI,KAAK;AACT,MAAI,MAAM,YAAY,OAAO,aAAY,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,WAAW,OAAO,CAAC,CAAC,CAAC;AACvG,MAAI,MAAM;AAGV,aAAW,MAAM;AACf,SAAK,MAAM;AACX,SAAK,MAAM;AACX,eAAW,MAAM,MAAM,OAAO,GAAG,GAAI;AAAA,EACvC,GAAG,GAAG;AACR;","names":[]}
@@ -1,213 +0,0 @@
1
- "use client";
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useEffect, useMemo, useState } from "react";
4
- import { EmptyNote, TableRow, TableShell, Th } from "@withpotter/ui-kit";
5
- import {
6
- platformAdminApi
7
- } from "@withpotter/lib/admin-api";
8
- import { useSubscriptionPayments } from "../hooks";
9
- import { CountUp, Reveal } from "../_fx";
10
- import { AdmLoading, FilterSearch, FilterSelect, Filters, Pagination, StatCard, moneyIn } from "../_ui";
11
- import { DownloadButton } from "./download-button";
12
- import {
13
- PURPOSE_WORDS,
14
- STATUS_WORDS,
15
- SUBSCRIPTION_METHOD_WORDS,
16
- planPaymentTotals,
17
- downloadName,
18
- minorToMoney,
19
- word
20
- } from "./ledger-model";
21
- import { MethodCards } from "./method-cards";
22
- import { PlanMixPanel } from "./plan-mix-panel";
23
- import { PlanPaymentDrawer } from "./plan-payment-drawer";
24
- import { useUrlFilters } from "./use-url-filters";
25
- import styles from "./ledger.module.css";
26
- import table from "./revenue.module.css";
27
- const PAGE_SIZE = 25;
28
- const FILTERS = { q: "", status: "", method: "" };
29
- const integer = new Intl.NumberFormat("en-US");
30
- const STATUSES = [
31
- { value: "", label: "Every result" },
32
- { value: "succeeded", label: "Paid" },
33
- { value: "failed", label: "Failed" },
34
- { value: "pending", label: "Waiting" }
35
- ];
36
- const METHOD_HINTS = {
37
- wallet: "Plan payments taken from the merchant's Box wallet.",
38
- gateway: "Plan payments the merchant paid through the Olive payment gateway (card or bank).",
39
- manual: "Plan payments an admin recorded by hand, for example a bank transfer."
40
- };
41
- const METHODS = [
42
- { value: "", label: "Every way of paying" },
43
- { value: "gateway", label: "Olive payment gateway" },
44
- { value: "wallet", label: "Box wallet" },
45
- { value: "manual", label: "Recorded by hand" }
46
- ];
47
- const tone = (status) => status === "succeeded" ? styles.statusGood : status === "failed" ? styles.statusBad : styles.statusWait;
48
- function SubscriptionsView({
49
- startDate,
50
- endDate,
51
- revenue
52
- }) {
53
- const { values, page, set } = useUrlFilters("sp", FILTERS);
54
- const [search, setSearch] = useState(values.q);
55
- const [open, setOpen] = useState(null);
56
- useEffect(() => {
57
- const timer = setTimeout(() => {
58
- if (search.trim() !== values.q) set({ q: search.trim() });
59
- }, 300);
60
- return () => clearTimeout(timer);
61
- }, [search]);
62
- const filters = useMemo(
63
- () => ({ startDate, endDate, status: values.status, method: values.method, q: values.q, page, limit: PAGE_SIZE }),
64
- [startDate, endDate, values, page]
65
- );
66
- const { recurring } = revenue;
67
- const tracked = recurring.tracked !== false;
68
- const currency = recurring.currency ?? revenue.currency ?? "NGN";
69
- const money = moneyIn(currency);
70
- const money2 = moneyIn(currency, 2);
71
- const notSet = "Not set up yet";
72
- const why = "The plan payments have not been connected to this page yet.";
73
- const payments = useSubscriptionPayments(filters);
74
- return /* @__PURE__ */ jsxs("div", { children: [
75
- /* @__PURE__ */ jsx("p", { className: styles.lede, children: "This is money that merchants pay Olive for their plan. It is kept apart from the money customers pay merchants." }),
76
- /* @__PURE__ */ jsxs("section", { "aria-label": "Plan income", className: "adm-grid", children: [
77
- /* @__PURE__ */ jsx(Reveal, { index: 0, className: "adm-span-3", children: /* @__PURE__ */ jsx(
78
- StatCard,
79
- {
80
- label: "Plan income each month",
81
- value: tracked ? /* @__PURE__ */ jsx(CountUp, { value: recurring.mrr, format: money }) : notSet,
82
- sub: tracked ? `${integer.format(recurring.payingMerchants ?? recurring.activeSubscriptions)} merchants paying` : why,
83
- hint: "What merchants on a paid plan are expected to pay Olive every month. Merchants on a free plan, a trial or a pause are not counted."
84
- }
85
- ) }),
86
- /* @__PURE__ */ jsx(Reveal, { index: 1, className: "adm-span-3", children: /* @__PURE__ */ jsx(
87
- StatCard,
88
- {
89
- label: "Plan income over a year",
90
- value: tracked ? /* @__PURE__ */ jsx(CountUp, { value: recurring.arr, format: money }) : notSet,
91
- sub: tracked ? "Each month times 12" : why,
92
- hint: "What plan income adds up to over a year if nothing changes: the monthly figure times 12."
93
- }
94
- ) }),
95
- /* @__PURE__ */ jsx(Reveal, { index: 2, className: "adm-span-3", children: /* @__PURE__ */ jsx(
96
- StatCard,
97
- {
98
- label: "Average per paying merchant",
99
- value: tracked ? /* @__PURE__ */ jsx(CountUp, { value: recurring.arpa, format: money2 }) : notSet,
100
- sub: tracked ? "Each month" : why,
101
- hint: "What one paying merchant pays Olive in a month, on average."
102
- }
103
- ) }),
104
- /* @__PURE__ */ jsx(Reveal, { index: 3, className: "adm-span-3", children: /* @__PURE__ */ jsx(
105
- StatCard,
106
- {
107
- label: "Joined and left",
108
- value: tracked ? `${integer.format(recurring.newInRange)} joined` : notSet,
109
- sub: tracked ? `${integer.format(recurring.churnedInRange)} left or cancelled in this period` : why,
110
- hint: "Merchants who started a plan in this period, and merchants whose plan was cancelled, ended or switched off."
111
- }
112
- ) })
113
- ] }),
114
- tracked && recurring.collectedByMethod ? /* @__PURE__ */ jsxs(Fragment, { children: [
115
- /* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "How merchants pay Olive" }),
116
- /* @__PURE__ */ jsx("p", { className: styles.sectionNote, children: "Plan payments that went through in this period, by the way the merchant paid." }),
117
- /* @__PURE__ */ jsx(
118
- MethodCards,
119
- {
120
- now: planPaymentTotals(recurring.collectedByMethod, currency),
121
- before: null,
122
- currency,
123
- order: ["wallet", "gateway"],
124
- labels: SUBSCRIPTION_METHOD_WORDS,
125
- title: "How merchants pay Olive",
126
- received: "of the plan income collected",
127
- describe: (_label, key) => METHOD_HINTS[key] ?? "Plan payments paid this way.",
128
- hide: ["manual"]
129
- }
130
- )
131
- ] }) : null,
132
- tracked && recurring.planBreakdown.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
133
- /* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "Who pays what" }),
134
- /* @__PURE__ */ jsx("p", { className: styles.sectionNote, children: "How the monthly plan income splits across plans." }),
135
- /* @__PURE__ */ jsx(PlanMixPanel, { planBreakdown: recurring.planBreakdown, money, caption: `Each month \xB7 ${currency}` })
136
- ] }) : null,
137
- /* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "Every plan payment" }),
138
- payments.isError ? /* @__PURE__ */ jsx("div", { className: styles.notice, children: "Plan payments are not available on this box yet. Once the subscription module is connected, every payment will be listed here." }) : payments.isLoading || !payments.data ? /* @__PURE__ */ jsx(AdmLoading, { label: "Loading plan payments\u2026" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
139
- /* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
140
- /* @__PURE__ */ jsxs(Filters, { label: "Filter plan payments", children: [
141
- /* @__PURE__ */ jsx(
142
- FilterSearch,
143
- {
144
- value: search,
145
- onChange: setSearch,
146
- placeholder: "Search a merchant's name or email, or a reference",
147
- label: "Search plan payments"
148
- }
149
- ),
150
- /* @__PURE__ */ jsx(FilterSelect, { value: values.status, onChange: (status) => set({ status }), options: STATUSES, label: "Payment result" }),
151
- /* @__PURE__ */ jsx(FilterSelect, { value: values.method, onChange: (method) => set({ method }), options: METHODS, label: "Way of paying" })
152
- ] }),
153
- /* @__PURE__ */ jsx(
154
- DownloadButton,
155
- {
156
- filename: downloadName("plan-payments", startDate, endDate),
157
- load: () => platformAdminApi.subscriptionPaymentsCsv({ ...filters, page: void 0, limit: void 0 })
158
- }
159
- )
160
- ] }),
161
- payments.data.items.length === 0 ? /* @__PURE__ */ jsx(EmptyNote, { className: table.empty, children: "No plan payments match. Try a wider date range or clear a filter." }) : /* @__PURE__ */ jsx(
162
- TableShell,
163
- {
164
- minWidth: 860,
165
- density: "compact",
166
- head: /* @__PURE__ */ jsxs("tr", { children: [
167
- /* @__PURE__ */ jsx(Th, { children: "When" }),
168
- /* @__PURE__ */ jsx(Th, { children: "Merchant" }),
169
- /* @__PURE__ */ jsx(Th, { children: "Plan" }),
170
- /* @__PURE__ */ jsx(Th, { children: "For" }),
171
- /* @__PURE__ */ jsx(Th, { children: "How" }),
172
- /* @__PURE__ */ jsx(Th, { children: "Result" }),
173
- /* @__PURE__ */ jsx(Th, { align: "right", children: "Amount" })
174
- ] }),
175
- children: payments.data.items.map((row) => /* @__PURE__ */ jsx(PlanPaymentRow, { row, onOpen: setOpen }, row.id))
176
- }
177
- ),
178
- /* @__PURE__ */ jsx(
179
- Pagination,
180
- {
181
- page,
182
- totalPages: Math.max(Math.ceil(payments.data.total / PAGE_SIZE), 1),
183
- total: payments.data.total,
184
- noun: "plan payments",
185
- onPage: (next) => set({ page: next })
186
- }
187
- ),
188
- /* @__PURE__ */ jsx(PlanPaymentDrawer, { row: open, onClose: () => setOpen(null) })
189
- ] })
190
- ] });
191
- }
192
- function PlanPaymentRow({ row, onOpen }) {
193
- const when = row.paidAt ?? row.createdAt;
194
- return /* @__PURE__ */ jsxs(TableRow, { onClick: () => onOpen(row), children: [
195
- /* @__PURE__ */ jsx("td", { className: table.num, children: new Date(when).toLocaleString(void 0, { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }) }),
196
- /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
197
- /* @__PURE__ */ jsx("span", { className: styles.whoName, children: row.merchantName || "Unknown merchant" }),
198
- row.merchantEmail ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: row.merchantEmail }) : null
199
- ] }) }),
200
- /* @__PURE__ */ jsx("td", { className: table.name, children: row.plan.charAt(0).toUpperCase() + row.plan.slice(1) }),
201
- /* @__PURE__ */ jsx("td", { children: word(PURPOSE_WORDS, row.purpose) }),
202
- /* @__PURE__ */ jsx("td", { children: word(SUBSCRIPTION_METHOD_WORDS, row.method) }),
203
- /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
204
- /* @__PURE__ */ jsx("span", { className: `${styles.status} ${tone(row.status)}`, children: word(STATUS_WORDS, row.status) }),
205
- row.status === "failed" && row.failureReason ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: row.failureReason }) : null
206
- ] }) }),
207
- /* @__PURE__ */ jsx("td", { className: table.numStrong, children: minorToMoney(row.amountKobo, "NGN") })
208
- ] });
209
- }
210
- export {
211
- SubscriptionsView
212
- };
213
- //# sourceMappingURL=subscriptions-view.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/subscriptions-view.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { EmptyNote, TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport {\n platformAdminApi,\n type PlatformRevenue,\n type SubscriptionPaymentRow,\n} from '@withpotter/lib/admin-api';\nimport { useSubscriptionPayments } from '../hooks';\nimport { LoadFailed } from '../audit/load-failed';\nimport { CountUp, Reveal } from '../_fx';\nimport { AdmLoading, FilterSearch, FilterSelect, Filters, Pagination, StatCard, moneyIn } from '../_ui';\nimport { DownloadButton } from './download-button';\nimport {\n PURPOSE_WORDS,\n STATUS_WORDS,\n SUBSCRIPTION_METHOD_WORDS,\n planPaymentTotals,\n downloadName,\n minorToMoney,\n word,\n} from './ledger-model';\nimport { MethodCards } from './method-cards';\nimport { PlanMixPanel } from './plan-mix-panel';\nimport { PlanPaymentDrawer } from './plan-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: '', status: '', method: '' };\nconst integer = new Intl.NumberFormat('en-US');\n\nconst STATUSES = [\n { value: '', label: 'Every result' },\n { value: 'succeeded', label: 'Paid' },\n { value: 'failed', label: 'Failed' },\n { value: 'pending', label: 'Waiting' },\n];\nconst METHOD_HINTS: Record<string, string> = {\n wallet: \"Plan payments taken from the merchant's Box wallet.\",\n gateway: 'Plan payments the merchant paid through the Olive payment gateway (card or bank).',\n manual: 'Plan payments an admin recorded by hand, for example a bank transfer.',\n};\n\nconst METHODS = [\n { value: '', label: 'Every way of paying' },\n { value: 'gateway', label: 'Olive payment gateway' },\n { value: 'wallet', label: 'Box wallet' },\n { value: 'manual', label: 'Recorded by hand' },\n];\n\nconst tone = (status: string) =>\n status === 'succeeded' ? styles.statusGood : status === 'failed' ? styles.statusBad : styles.statusWait;\n\n/** What merchants pay Olive for their plan: the income figures, who pays what, and every plan payment. */\nexport function SubscriptionsView({\n startDate,\n endDate,\n revenue,\n}: {\n startDate?: string;\n endDate?: string;\n revenue: PlatformRevenue;\n}) {\n const { values, page, set } = useUrlFilters('sp', FILTERS);\n const [search, setSearch] = useState(values.q);\n const [open, setOpen] = useState<SubscriptionPaymentRow | null>(null);\n\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 () => ({ startDate, endDate, status: values.status, method: values.method, q: values.q, page, limit: PAGE_SIZE }),\n [startDate, endDate, values, page],\n );\n\n const { recurring } = revenue;\n const tracked = recurring.tracked !== false;\n const currency = recurring.currency ?? revenue.currency ?? 'NGN';\n const money = moneyIn(currency);\n const money2 = moneyIn(currency, 2);\n const notSet = 'Not set up yet';\n const why = 'The plan payments have not been connected to this page yet.';\n const payments = useSubscriptionPayments(filters);\n\n return (\n <div>\n <p className={styles.lede}>\n This is money that merchants pay Olive for their plan. It is kept apart from the money customers pay merchants.\n </p>\n\n <section aria-label=\"Plan income\" className=\"adm-grid\">\n <Reveal index={0} className=\"adm-span-3\">\n <StatCard\n label=\"Plan income each month\"\n value={tracked ? <CountUp value={recurring.mrr} format={money} /> : notSet}\n sub={tracked ? `${integer.format(recurring.payingMerchants ?? recurring.activeSubscriptions)} merchants paying` : why}\n hint=\"What merchants on a paid plan are expected to pay Olive every month. Merchants on a free plan, a trial or a pause are not counted.\"\n />\n </Reveal>\n <Reveal index={1} className=\"adm-span-3\">\n <StatCard\n label=\"Plan income over a year\"\n value={tracked ? <CountUp value={recurring.arr} format={money} /> : notSet}\n sub={tracked ? 'Each month times 12' : why}\n hint=\"What plan income adds up to over a year if nothing changes: the monthly figure times 12.\"\n />\n </Reveal>\n <Reveal index={2} className=\"adm-span-3\">\n <StatCard\n label=\"Average per paying merchant\"\n value={tracked ? <CountUp value={recurring.arpa} format={money2} /> : notSet}\n sub={tracked ? 'Each month' : why}\n hint=\"What one paying merchant pays Olive in a month, on average.\"\n />\n </Reveal>\n <Reveal index={3} className=\"adm-span-3\">\n <StatCard\n label=\"Joined and left\"\n value={tracked ? `${integer.format(recurring.newInRange)} joined` : notSet}\n sub={tracked ? `${integer.format(recurring.churnedInRange)} left or cancelled in this period` : why}\n hint=\"Merchants who started a plan in this period, and merchants whose plan was cancelled, ended or switched off.\"\n />\n </Reveal>\n </section>\n\n {tracked && recurring.collectedByMethod ? (\n <>\n <h2 className={styles.sectionTitle}>How merchants pay Olive</h2>\n <p className={styles.sectionNote}>Plan payments that went through in this period, by the way the merchant paid.</p>\n <MethodCards\n now={planPaymentTotals(recurring.collectedByMethod, currency)}\n before={null}\n currency={currency}\n order={['wallet', 'gateway']}\n labels={SUBSCRIPTION_METHOD_WORDS}\n title=\"How merchants pay Olive\"\n received=\"of the plan income collected\"\n describe={(_label, key) => METHOD_HINTS[key] ?? 'Plan payments paid this way.'}\n hide={['manual']}\n />\n </>\n ) : null}\n\n {tracked && recurring.planBreakdown.length > 0 ? (\n <>\n <h2 className={styles.sectionTitle}>Who pays what</h2>\n <p className={styles.sectionNote}>How the monthly plan income splits across plans.</p>\n <PlanMixPanel planBreakdown={recurring.planBreakdown} money={money} caption={`Each month · ${currency}`} />\n </>\n ) : null}\n\n <h2 className={styles.sectionTitle}>Every plan payment</h2>\n {payments.isError ? (\n <div className={styles.notice}>\n Plan payments are not available on this box yet. Once the subscription module is connected, every payment will\n be listed here.\n </div>\n ) : payments.isLoading || !payments.data ? (\n <AdmLoading label=\"Loading plan payments…\" />\n ) : (\n <>\n <div className={styles.toolbar}>\n <Filters label=\"Filter plan payments\">\n <FilterSearch\n value={search}\n onChange={setSearch}\n placeholder=\"Search a merchant's name or email, or a reference\"\n label=\"Search plan payments\"\n />\n <FilterSelect value={values.status} onChange={(status) => set({ status })} options={STATUSES} label=\"Payment result\" />\n <FilterSelect value={values.method} onChange={(method) => set({ method })} options={METHODS} label=\"Way of paying\" />\n </Filters>\n <DownloadButton\n filename={downloadName('plan-payments', startDate, endDate)}\n load={() => platformAdminApi.subscriptionPaymentsCsv({ ...filters, page: undefined, limit: undefined })}\n />\n </div>\n\n {payments.data.items.length === 0 ? (\n <EmptyNote className={table.empty}>No plan payments match. Try a wider date range or clear a filter.</EmptyNote>\n ) : (\n <TableShell\n minWidth={860}\n density=\"compact\"\n head={\n <tr>\n <Th>When</Th>\n <Th>Merchant</Th>\n <Th>Plan</Th>\n <Th>For</Th>\n <Th>How</Th>\n <Th>Result</Th>\n <Th align=\"right\">Amount</Th>\n </tr>\n }\n >\n {payments.data.items.map((row) => (\n <PlanPaymentRow key={row.id} row={row} onOpen={setOpen} />\n ))}\n </TableShell>\n )}\n <Pagination\n page={page}\n totalPages={Math.max(Math.ceil(payments.data.total / PAGE_SIZE), 1)}\n total={payments.data.total}\n noun=\"plan payments\"\n onPage={(next) => set({ page: next })}\n />\n <PlanPaymentDrawer row={open} onClose={() => setOpen(null)} />\n </>\n )}\n </div>\n );\n}\n\nfunction PlanPaymentRow({ row, onOpen }: { row: SubscriptionPaymentRow; onOpen: (row: SubscriptionPaymentRow) => void }) {\n const when = row.paidAt ?? row.createdAt;\n\n return (\n <TableRow onClick={() => onOpen(row)}>\n <td className={table.num}>\n {new Date(when).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}>{row.merchantName || 'Unknown merchant'}</span>\n {row.merchantEmail ? <span className={styles.whoSub}>{row.merchantEmail}</span> : null}\n </span>\n </td>\n <td className={table.name}>{row.plan.charAt(0).toUpperCase() + row.plan.slice(1)}</td>\n <td>{word(PURPOSE_WORDS, row.purpose)}</td>\n <td>{word(SUBSCRIPTION_METHOD_WORDS, row.method)}</td>\n <td>\n <span className={styles.who}>\n <span className={`${styles.status} ${tone(row.status)}`}>{word(STATUS_WORDS, row.status)}</span>\n {row.status === 'failed' && row.failureReason ? <span className={styles.whoSub}>{row.failureReason}</span> : null}\n </span>\n </td>\n <td className={table.numStrong}>{minorToMoney(row.amountKobo, 'NGN')}</td>\n </TableRow>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,38 +0,0 @@
1
- "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { DropdownMenu, useToast } from "@withpotter/ui-kit";
4
- import { toCsv } from "@withpotter/lib/csv";
5
- import { saveCsv } from "./download-button";
6
- import { downloadName } from "./ledger-model";
7
- import { printSummary } from "./print-summary-button";
8
- import { SUMMARY_HEAD, summaryRows } from "./summary-rows";
9
- import { summaryModel } from "./summary-print";
10
- const CSV = "Download CSV";
11
- const PDF = "Save PDF";
12
- function SummaryDownload({
13
- ledger,
14
- revenue,
15
- currency,
16
- startDate,
17
- endDate,
18
- period
19
- }) {
20
- const { toast } = useToast();
21
- const rows = summaryRows(ledger, revenue, currency);
22
- if (rows.length === 0) return null;
23
- const choose = (item) => {
24
- if (item === CSV) {
25
- saveCsv(downloadName("revenue-summary", startDate, endDate), toCsv([SUMMARY_HEAD, ...rows]));
26
- } else {
27
- printSummary(
28
- summaryModel(ledger, revenue, currency, period),
29
- () => toast({ title: "The summary could not be opened. Please try again." })
30
- );
31
- }
32
- };
33
- return /* @__PURE__ */ jsx(DropdownMenu, { label: "Download", items: [CSV, PDF], onSelect: choose });
34
- }
35
- export {
36
- SummaryDownload
37
- };
38
- //# sourceMappingURL=summary-download.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/summary-download.tsx"],"sourcesContent":["'use client';\n\nimport { DropdownMenu, useToast } from '@withpotter/ui-kit';\nimport { toCsv } from '@withpotter/lib/csv';\nimport type { MerchantPaymentsPage, PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { saveCsv } from './download-button';\nimport { downloadName } from './ledger-model';\nimport { printSummary } from './print-summary-button';\nimport { SUMMARY_HEAD, summaryRows } from './summary-rows';\nimport { summaryModel } from './summary-print';\n\nconst CSV = 'Download CSV';\nconst PDF = 'Save PDF';\n\n/** The figures on the Overview as a small spreadsheet or a one-page PDF (through the print window), from one menu. */\nexport function SummaryDownload({\n ledger,\n revenue,\n currency,\n startDate,\n endDate,\n period,\n}: {\n ledger?: MerchantPaymentsPage;\n revenue?: PlatformRevenue;\n currency: string;\n startDate?: string;\n endDate?: string;\n /** The period in words, such as \"last 30 days\". */\n period: string;\n}) {\n const { toast } = useToast();\n const rows = summaryRows(ledger, revenue, currency);\n\n if (rows.length === 0) return null;\n\n const choose = (item: string) => {\n if (item === CSV) {\n saveCsv(downloadName('revenue-summary', startDate, endDate), toCsv([SUMMARY_HEAD, ...rows]));\n } else {\n printSummary(summaryModel(ledger, revenue, currency, period), () =>\n toast({ title: 'The summary could not be opened. Please try again.' }),\n );\n }\n };\n\n return <DropdownMenu label=\"Download\" items={[CSV, PDF]} onSelect={choose} />;\n}\n"],"mappings":";AA8CS;AA5CT,SAAS,cAAc,gBAAgB;AACvC,SAAS,aAAa;AAEtB,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,cAAc,mBAAmB;AAC1C,SAAS,oBAAoB;AAE7B,MAAM,MAAM;AACZ,MAAM,MAAM;AAGL,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,OAAO,YAAY,QAAQ,SAAS,QAAQ;AAElD,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,SAAS,CAAC,SAAiB;AAC/B,QAAI,SAAS,KAAK;AAChB,cAAQ,aAAa,mBAAmB,WAAW,OAAO,GAAG,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;AAAA,IAC7F,OAAO;AACL;AAAA,QAAa,aAAa,QAAQ,SAAS,UAAU,MAAM;AAAA,QAAG,MAC5D,MAAM,EAAE,OAAO,qDAAqD,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAEA,SAAO,oBAAC,gBAAa,OAAM,YAAW,OAAO,CAAC,KAAK,GAAG,GAAG,UAAU,QAAQ;AAC7E;","names":[]}
@@ -1,82 +0,0 @@
1
- import { methodCards, minorToMoney, pctChange } from "./ledger-model";
2
- const integer = new Intl.NumberFormat("en-US");
3
- const change = (now, before) => {
4
- const pct = pctChange(now, before);
5
- return pct === null ? void 0 : `${pct > 0 ? "+" : ""}${pct}% since the period before`;
6
- };
7
- function summaryModel(ledger, revenue, currency, period) {
8
- const groups = [];
9
- const now = ledger?.cards.current.find((c) => c.currency === currency);
10
- const before = ledger?.cards.previous?.find((c) => c.currency === currency);
11
- const money = (minor) => minorToMoney(minor, currency);
12
- if (ledger && now) {
13
- groups.push({
14
- heading: "Money merchants earned",
15
- note: "What customers paid to merchants. This is the merchants' money, not Olive's own income.",
16
- rows: [
17
- { label: "Money received", value: money(now.grossMinor), detail: change(now.grossMinor, before?.grossMinor) },
18
- { label: "Fees taken", value: money(now.feeMinor), detail: change(now.feeMinor, before?.feeMinor) },
19
- { label: "Merchants keep", value: money(now.netMinor), detail: change(now.netMinor, before?.netMinor) },
20
- { label: "Refunded", value: money(now.refundsMinor) },
21
- { label: "Payments that went through", value: integer.format(now.payments) },
22
- { label: "Payments that worked", value: now.successRate === null ? "\u2013" : `${now.successRate}%` }
23
- ]
24
- });
25
- groups.push({
26
- heading: "How customers paid",
27
- note: "Money received, by way of paying.",
28
- rows: methodCards(ledger.byChannel.current, ledger.byChannel.previous, currency).map((card) => ({
29
- label: card.label,
30
- value: money(card.amountMinor),
31
- detail: card.payments === 0 ? "No payments this way" : [
32
- `${integer.format(card.payments)} payments`,
33
- `${card.share}% of the money received`,
34
- card.change === null ? "" : `${card.change > 0 ? "+" : ""}${card.change}% since the period before`
35
- ].filter(Boolean).join(" \xB7 ")
36
- }))
37
- });
38
- }
39
- const plan = revenue?.recurring;
40
- const naira = (value) => minorToMoney(Math.round(value * 100), plan?.currency ?? "NGN");
41
- groups.push({
42
- heading: "Money Olive earns",
43
- note: "What merchants pay Olive for their plan.",
44
- rows: plan && plan.tracked !== false ? [
45
- { label: "Plan income each month", value: naira(plan.mrr) },
46
- { label: "Plan income over a year", value: naira(plan.arr) },
47
- { label: "Average per paying merchant", value: naira(plan.arpa) },
48
- { label: "Merchants paying", value: integer.format(plan.payingMerchants ?? plan.activeSubscriptions) },
49
- { label: "Joined this period", value: integer.format(plan.newInRange) },
50
- { label: "Left this period", value: integer.format(plan.churnedInRange) }
51
- ] : [{ label: "Plan income", value: "Not set up yet", detail: "The plan payments are not connected on this box." }]
52
- });
53
- return { title: "Revenue summary", period, groups };
54
- }
55
- const esc = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
56
- function summaryHtml(model, brand, printedOn) {
57
- const groups = model.groups.map(
58
- (group) => `<section><h2>${esc(group.heading)}</h2><p class="note">${esc(group.note)}</p><table>${group.rows.map(
59
- (row) => `<tr><td class="label">${esc(row.label)}${row.detail ? `<div class="detail">${esc(row.detail)}</div>` : ""}</td><td class="value">${esc(row.value)}</td></tr>`
60
- ).join("")}</table></section>`
61
- ).join("");
62
- return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${esc(model.title)}</title><style>
63
- @page { size: A4; margin: 14mm; }
64
- * { box-sizing: border-box; }
65
- body { margin: 0; font: 12px/1.45 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color: #1f2a14; }
66
- h1 { margin: 0 0 2px; font-size: 22px; color: #3d5a1e; }
67
- .period { margin: 0 0 14px; color: #5b6356; font-size: 13px; }
68
- section { margin: 0 0 14px; break-inside: avoid; }
69
- h2 { margin: 0 0 2px; font-size: 14px; color: #3d5a1e; }
70
- .note { margin: 0 0 6px; color: #5b6356; }
71
- table { width: 100%; border-collapse: collapse; }
72
- td { padding: 5px 0; border-bottom: 1px solid #d9ddd3; vertical-align: top; }
73
- .value { text-align: right; font-weight: 600; white-space: nowrap; padding-left: 12px; }
74
- .detail { color: #5b6356; font-size: 11px; }
75
- footer { margin-top: 10px; color: #5b6356; font-size: 10px; }
76
- </style></head><body><h1>${esc(model.title)}</h1><p class="period">${esc(brand)} \xB7 ${esc(model.period)}</p>${groups}<footer>Printed ${esc(printedOn)}. Amounts are in the currency shown; different currencies are never added together.</footer></body></html>`;
77
- }
78
- export {
79
- summaryHtml,
80
- summaryModel
81
- };
82
- //# sourceMappingURL=summary-print.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/revenue/summary-print.ts"],"sourcesContent":["import type { MerchantPaymentsPage, PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { methodCards, minorToMoney, pctChange } from './ledger-model';\n\nexport interface SummaryRow {\n label: string;\n value: string;\n /** A short second line: the change since the period before, or a count. */\n detail?: string;\n}\n\nexport interface SummaryGroup {\n heading: string;\n note: string;\n rows: SummaryRow[];\n}\n\nexport interface SummaryModel {\n title: string;\n period: string;\n groups: SummaryGroup[];\n}\n\nconst integer = new Intl.NumberFormat('en-US');\n\nconst change = (now: string | number, before?: string | number): string | undefined => {\n const pct = pctChange(now, before);\n\n return pct === null ? undefined : `${pct > 0 ? '+' : ''}${pct}% since the period before`;\n};\n\n/** The Overview's figures as words and amounts a person can read on one printed page. */\nexport function summaryModel(\n ledger: MerchantPaymentsPage | undefined,\n revenue: PlatformRevenue | undefined,\n currency: string,\n period: string,\n): SummaryModel {\n const groups: SummaryGroup[] = [];\n const now = ledger?.cards.current.find((c) => c.currency === currency);\n const before = ledger?.cards.previous?.find((c) => c.currency === currency);\n const money = (minor: string) => minorToMoney(minor, currency);\n\n if (ledger && now) {\n groups.push({\n heading: 'Money merchants earned',\n note: \"What customers paid to merchants. This is the merchants' money, not Olive's own income.\",\n rows: [\n { label: 'Money received', value: money(now.grossMinor), detail: change(now.grossMinor, before?.grossMinor) },\n { label: 'Fees taken', value: money(now.feeMinor), detail: change(now.feeMinor, before?.feeMinor) },\n { label: 'Merchants keep', value: money(now.netMinor), detail: change(now.netMinor, before?.netMinor) },\n { label: 'Refunded', value: money(now.refundsMinor) },\n { label: 'Payments that went through', value: integer.format(now.payments) },\n { label: 'Payments that worked', value: now.successRate === null ? '–' : `${now.successRate}%` },\n ],\n });\n\n groups.push({\n heading: 'How customers paid',\n note: 'Money received, by way of paying.',\n rows: methodCards(ledger.byChannel.current, ledger.byChannel.previous, currency).map((card) => ({\n label: card.label,\n value: money(card.amountMinor),\n detail:\n card.payments === 0\n ? 'No payments this way'\n : [\n `${integer.format(card.payments)} payments`,\n `${card.share}% of the money received`,\n card.change === null ? '' : `${card.change > 0 ? '+' : ''}${card.change}% since the period before`,\n ]\n .filter(Boolean)\n .join(' · '),\n })),\n });\n }\n\n const plan = revenue?.recurring;\n const naira = (value: number) => minorToMoney(Math.round(value * 100), plan?.currency ?? 'NGN');\n\n groups.push({\n heading: 'Money Olive earns',\n note: 'What merchants pay Olive for their plan.',\n rows:\n plan && plan.tracked !== false\n ? [\n { label: 'Plan income each month', value: naira(plan.mrr) },\n { label: 'Plan income over a year', value: naira(plan.arr) },\n { label: 'Average per paying merchant', value: naira(plan.arpa) },\n { label: 'Merchants paying', value: integer.format(plan.payingMerchants ?? plan.activeSubscriptions) },\n { label: 'Joined this period', value: integer.format(plan.newInRange) },\n { label: 'Left this period', value: integer.format(plan.churnedInRange) },\n ]\n : [{ label: 'Plan income', value: 'Not set up yet', detail: 'The plan payments are not connected on this box.' }],\n });\n\n return { title: 'Revenue summary', period, groups };\n}\n\nconst esc = (text: string): string =>\n text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n\n/** A self-contained page that prints on one A4 sheet. Every value is escaped. */\nexport function summaryHtml(model: SummaryModel, brand: string, printedOn: string): string {\n const groups = model.groups\n .map(\n (group) => `<section><h2>${esc(group.heading)}</h2><p class=\"note\">${esc(group.note)}</p><table>${group.rows\n .map(\n (row) =>\n `<tr><td class=\"label\">${esc(row.label)}${row.detail ? `<div class=\"detail\">${esc(row.detail)}</div>` : ''}</td><td class=\"value\">${esc(row.value)}</td></tr>`,\n )\n .join('')}</table></section>`,\n )\n .join('');\n\n return `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>${esc(model.title)}</title><style>\n@page { size: A4; margin: 14mm; }\n* { box-sizing: border-box; }\nbody { margin: 0; font: 12px/1.45 -apple-system, \"Segoe UI\", Helvetica, Arial, sans-serif; color: #1f2a14; }\nh1 { margin: 0 0 2px; font-size: 22px; color: #3d5a1e; }\n.period { margin: 0 0 14px; color: #5b6356; font-size: 13px; }\nsection { margin: 0 0 14px; break-inside: avoid; }\nh2 { margin: 0 0 2px; font-size: 14px; color: #3d5a1e; }\n.note { margin: 0 0 6px; color: #5b6356; }\ntable { width: 100%; border-collapse: collapse; }\ntd { padding: 5px 0; border-bottom: 1px solid #d9ddd3; vertical-align: top; }\n.value { text-align: right; font-weight: 600; white-space: nowrap; padding-left: 12px; }\n.detail { color: #5b6356; font-size: 11px; }\nfooter { margin-top: 10px; color: #5b6356; font-size: 10px; }\n</style></head><body><h1>${esc(model.title)}</h1><p class=\"period\">${esc(brand)} · ${esc(model.period)}</p>${groups}<footer>Printed ${esc(printedOn)}. Amounts are in the currency shown; different currencies are never added together.</footer></body></html>`;\n}\n"],"mappings":"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","names":[]}
@@ -1,44 +0,0 @@
1
- import { methodCards, pctChange, plainAmount } from "./ledger-model";
2
- const SUMMARY_HEAD = ["Section", "Figure", "This period", "Period before", "Change (%)"];
3
- const naira = (kobo) => plainAmount(Math.round(kobo * 100));
4
- function summaryRows(ledger, revenue, currency) {
5
- const rows = [];
6
- const now = ledger?.cards.current.find((c) => c.currency === currency);
7
- const before = ledger?.cards.previous?.find((c) => c.currency === currency);
8
- if (now) {
9
- const money = (label, a, b) => rows.push(["Merchant payments", label, plainAmount(a), b === void 0 ? "" : plainAmount(b), String(pctChange(a, b) ?? "")]);
10
- money("Money received", now.grossMinor, before?.grossMinor);
11
- money("Fees taken", now.feeMinor, before?.feeMinor);
12
- money("Merchants keep", now.netMinor, before?.netMinor);
13
- money("Refunded", now.refundsMinor, before?.refundsMinor);
14
- rows.push(["Merchant payments", "Payments that went through", String(now.payments), before ? String(before.payments) : "", String(pctChange(now.payments, before?.payments) ?? "")]);
15
- rows.push(["Merchant payments", "Payments that worked (%)", String(now.successRate ?? ""), String(before?.successRate ?? ""), ""]);
16
- }
17
- if (ledger) {
18
- for (const card of methodCards(ledger.byChannel.current, ledger.byChannel.previous, currency)) {
19
- const prior = ledger.byChannel.previous?.find((r) => r.currency === currency && r.channel.key === card.key);
20
- rows.push([
21
- "How customers paid",
22
- card.label,
23
- plainAmount(card.amountMinor),
24
- ledger.byChannel.previous ? plainAmount(prior?.amountMinor ?? "0") : "",
25
- String(card.change ?? "")
26
- ]);
27
- }
28
- }
29
- const plan = revenue?.recurring;
30
- if (plan && plan.tracked !== false) {
31
- rows.push(["Plan income", "Income each month", naira(plan.mrr), "", ""]);
32
- rows.push(["Plan income", "Income over a year", naira(plan.arr), "", ""]);
33
- rows.push(["Plan income", "Average per paying merchant", naira(plan.arpa), "", ""]);
34
- rows.push(["Plan income", "Merchants paying", String(plan.payingMerchants ?? plan.activeSubscriptions), "", ""]);
35
- rows.push(["Plan income", "Joined in this period", String(plan.newInRange), "", ""]);
36
- rows.push(["Plan income", "Left in this period", String(plan.churnedInRange), "", ""]);
37
- }
38
- return rows;
39
- }
40
- export {
41
- SUMMARY_HEAD,
42
- summaryRows
43
- };
44
- //# sourceMappingURL=summary-rows.js.map