@withpotter/pages-admin 0.13.0-next.96 → 0.13.0-next.98

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.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useQuery } from "@tanstack/react-query";
2
+ import { keepPreviousData, useQuery } from "@tanstack/react-query";
3
3
  import { platformAdminApi } from "@withpotter/lib/admin-api";
4
4
  function useAdminSession(enabled = true) {
5
5
  return useQuery({
@@ -36,6 +36,24 @@ function useGatewayChannels(range = {}) {
36
36
  staleTime: 6e4
37
37
  });
38
38
  }
39
+ function useMerchantPayments(filters) {
40
+ return useQuery({
41
+ queryKey: ["platform-admin", "revenue", "merchant-payments", filters],
42
+ queryFn: () => platformAdminApi.merchantPayments(filters),
43
+ retry: false,
44
+ staleTime: 3e4,
45
+ placeholderData: keepPreviousData
46
+ });
47
+ }
48
+ function useSubscriptionPayments(filters) {
49
+ return useQuery({
50
+ queryKey: ["platform-admin", "revenue", "subscription-payments", filters],
51
+ queryFn: () => platformAdminApi.subscriptionPayments(filters),
52
+ retry: false,
53
+ staleTime: 3e4,
54
+ placeholderData: keepPreviousData
55
+ });
56
+ }
39
57
  function usePlatformTenantsAnalytics(range = {}) {
40
58
  return useQuery({
41
59
  queryKey: ["platform-admin", "tenants-analytics", range],
@@ -209,6 +227,7 @@ export {
209
227
  useAdminRoles,
210
228
  useAdminSession,
211
229
  useGatewayChannels,
230
+ useMerchantPayments,
212
231
  usePlatformAccounts,
213
232
  usePlatformAccountsAnalytics,
214
233
  usePlatformAdmins,
@@ -221,6 +240,7 @@ export {
221
240
  usePlatformTenant,
222
241
  usePlatformTenants,
223
242
  usePlatformTenantsAnalytics,
243
+ useSubscriptionPayments,
224
244
  useTenantAnalytics,
225
245
  useTenantCustomerInsights,
226
246
  useTenantCustomers,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { platformAdminApi } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\nimport type { AccountStage } from '@withpotter/lib/admin-account-types';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\n/**\n * How customers paid, per channel. Not every box can say: where the route is\n * absent the query fails once (no retry) and `data` stays undefined, which the\n * panels read as \"nothing to show\".\n */\nexport function useGatewayChannels(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'gateway-channels', range],\n queryFn: () => platformAdminApi.gatewayChannels(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n stage?: AccountStage;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useAccountSessions(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-sessions', id],\n queryFn: () => platformAdminApi.accountSessions(id!),\n enabled: !!id,\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { keepPreviousData, useQuery } from '@tanstack/react-query';\nimport { platformAdminApi, type LedgerFilters } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\nimport type { AccountStage } from '@withpotter/lib/admin-account-types';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\n/**\n * How customers paid, per channel. Not every box can say: where the route is\n * absent the query fails once (no retry) and `data` stays undefined, which the\n * panels read as \"nothing to show\".\n */\nexport function useGatewayChannels(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'gateway-channels', range],\n queryFn: () => platformAdminApi.gatewayChannels(range),\n retry: false,\n staleTime: 60_000,\n });\n}\n\n/**\n * Revenue's Merchant payments view. A box without the route answers 404; the\n * view reads that as \"not available here\", never as a failure to retry.\n */\nexport function useMerchantPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'merchant-payments', filters],\n queryFn: () => platformAdminApi.merchantPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\n/** Revenue's Subscriptions view: every plan payment merchants made to Olive. */\nexport function useSubscriptionPayments(filters: LedgerFilters) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', 'subscription-payments', filters],\n queryFn: () => platformAdminApi.subscriptionPayments(filters),\n retry: false,\n staleTime: 30_000,\n placeholderData: keepPreviousData,\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n stage?: AccountStage;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useAccountSessions(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-sessions', id],\n queryFn: () => platformAdminApi.accountSessions(id!),\n enabled: !!id,\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Button, useToast } from "@withpotter/ui-kit";
5
+ function saveCsv(filename, text) {
6
+ const url = URL.createObjectURL(
7
+ new Blob([`\uFEFF${text}`], { type: "text/csv;charset=utf-8" })
8
+ );
9
+ const link = document.createElement("a");
10
+ link.href = url;
11
+ link.download = filename;
12
+ document.body.appendChild(link);
13
+ link.click();
14
+ link.remove();
15
+ URL.revokeObjectURL(url);
16
+ }
17
+ function DownloadButton({
18
+ filename,
19
+ load,
20
+ label = "Download as CSV"
21
+ }) {
22
+ const [busy, setBusy] = useState(false);
23
+ const { toast } = useToast();
24
+ const run = async () => {
25
+ setBusy(true);
26
+ try {
27
+ saveCsv(filename, await load());
28
+ } catch {
29
+ toast({ title: "The download did not start. Please try again." });
30
+ } finally {
31
+ setBusy(false);
32
+ }
33
+ };
34
+ return /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", loading: busy, onClick: () => void run(), children: label });
35
+ }
36
+ export {
37
+ DownloadButton,
38
+ saveCsv
39
+ };
40
+ //# sourceMappingURL=download-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/revenue/download-button.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, useToast } from '@withpotter/ui-kit';\n\n/** Saves what `load` returns (CSV text) as a file. A byte-order mark is put first so Excel reads names and ₦ correctly. */\nexport function saveCsv(filename: string, text: string): void {\n const url = URL.createObjectURL(\n new Blob([`${text}`], { type: 'text/csv;charset=utf-8' }),\n );\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n link.remove();\n URL.revokeObjectURL(url);\n}\n\n/** One button that downloads the table as it is filtered. */\nexport function DownloadButton({\n filename,\n load,\n label = 'Download as CSV',\n}: {\n filename: string;\n load: () => Promise<string>;\n label?: string;\n}) {\n const [busy, setBusy] = useState(false);\n const { toast } = useToast();\n\n const run = async () => {\n setBusy(true);\n try {\n saveCsv(filename, await load());\n } catch {\n toast({ title: 'The download did not start. Please try again.' });\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Button variant=\"secondary\" size=\"sm\" loading={busy} onClick={() => void run()}>\n {label}\n </Button>\n );\n}\n"],"mappings":";AA4CI;AA1CJ,SAAS,gBAAgB;AACzB,SAAS,QAAQ,gBAAgB;AAG1B,SAAS,QAAQ,UAAkB,MAAoB;AAC5D,QAAM,MAAM,IAAI;AAAA,IACd,IAAI,KAAK,CAAC,SAAI,IAAI,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAAA,EAC3D;AACA,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,MAAI,gBAAgB,GAAG;AACzB;AAGO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,QAAQ;AACV,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,EAAE,MAAM,IAAI,SAAS;AAE3B,QAAM,MAAM,YAAY;AACtB,YAAQ,IAAI;AACZ,QAAI;AACF,cAAQ,UAAU,MAAM,KAAK,CAAC;AAAA,IAChC,QAAQ;AACN,YAAM,EAAE,OAAO,gDAAgD,CAAC;AAAA,IAClE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,SACE,oBAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,SAAS,MAAM,KAAK,IAAI,GAC1E,iBACH;AAEJ;","names":[]}
@@ -0,0 +1,84 @@
1
+ import { formatMoney } from "../_ui/money-view";
2
+ function minorToMoney(minor, currency) {
3
+ return formatMoney(Number(minor) / 100, currency, 2);
4
+ }
5
+ function pctChange(now, before) {
6
+ const a = Number(now ?? 0);
7
+ const b = Number(before ?? 0);
8
+ if (b === 0) return null;
9
+ return Number(((a - b) / Math.abs(b) * 100).toFixed(1));
10
+ }
11
+ const METHOD_ORDER = ["card", "ussd", "transfer", "opay"];
12
+ function methodCards(now, before, currency) {
13
+ const pick = (rows) => (rows ?? []).filter((r) => r.currency === currency);
14
+ const current = pick(now);
15
+ const total = current.reduce((sum, r) => sum + BigInt(r.amountMinor), BigInt(0));
16
+ const labels = { card: "Card", ussd: "USSD", transfer: "Bank transfer", opay: "Opay" };
17
+ const keys = [
18
+ ...METHOD_ORDER,
19
+ ...current.map((r) => r.channel.key).filter((k) => !METHOD_ORDER.includes(k))
20
+ ];
21
+ return [...new Set(keys)].map((key) => {
22
+ const mine = current.find((r) => r.channel.key === key);
23
+ const prior = pick(before).find((r) => r.channel.key === key);
24
+ const amount = BigInt(mine?.amountMinor ?? "0");
25
+ return {
26
+ key,
27
+ label: mine?.channel.label ?? labels[key] ?? key,
28
+ payments: mine?.count ?? 0,
29
+ amountMinor: amount.toString(),
30
+ share: total > BigInt(0) ? Number((Number(amount) / Number(total) * 100).toFixed(1)) : 0,
31
+ change: before ? pctChange(amount.toString(), prior?.amountMinor ?? "0") : null
32
+ };
33
+ });
34
+ }
35
+ const STATUS_WORDS = {
36
+ success: "Paid",
37
+ pending: "Waiting",
38
+ failed: "Failed",
39
+ refunded: "Refunded",
40
+ cancelled: "Cancelled",
41
+ abandoned: "Abandoned",
42
+ succeeded: "Paid"
43
+ };
44
+ const TYPE_WORDS = {
45
+ order: "Order",
46
+ invoice: "Invoice",
47
+ booking: "Booking",
48
+ membership: "Membership",
49
+ subscription: "Subscription",
50
+ deposit: "Deposit",
51
+ tip: "Tip",
52
+ refund: "Refund",
53
+ payout: "Payout"
54
+ };
55
+ const PURPOSE_WORDS = {
56
+ select_plan: "First payment",
57
+ upgrade: "Upgrade",
58
+ renewal: "Renewal",
59
+ reactivation: "Came back",
60
+ mandate_setup: "Card saved"
61
+ };
62
+ const SUBSCRIPTION_METHOD_WORDS = {
63
+ gateway: "Card or bank",
64
+ wallet: "Wallet",
65
+ manual: "Recorded by hand"
66
+ };
67
+ const word = (words, key) => words[key] ?? key.replace(/_/g, " ");
68
+ function downloadName(prefix, startDate, endDate) {
69
+ const day = (iso) => iso ? iso.slice(0, 10) : "all";
70
+ return `${prefix}_${day(startDate)}_${day(endDate)}.csv`;
71
+ }
72
+ export {
73
+ METHOD_ORDER,
74
+ PURPOSE_WORDS,
75
+ STATUS_WORDS,
76
+ SUBSCRIPTION_METHOD_WORDS,
77
+ TYPE_WORDS,
78
+ downloadName,
79
+ methodCards,
80
+ minorToMoney,
81
+ pctChange,
82
+ word
83
+ };
84
+ //# sourceMappingURL=ledger-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/revenue/ledger-model.ts"],"sourcesContent":["import type { GatewayChannelTotal } from '@withpotter/lib/admin-api';\nimport { formatMoney } from '../_ui/money-view';\n\n/** A kobo amount (a string, so no float is involved on the server) shown as naira. */\nexport function minorToMoney(minor: string | number, currency: string): string {\n return formatMoney(Number(minor) / 100, currency, 2);\n}\n\n/** Percent change from before to now; null when there was nothing before to compare with. */\nexport function pctChange(\n now: string | number | null | undefined,\n before: string | number | null | undefined,\n): number | null {\n const a = Number(now ?? 0);\n const b = Number(before ?? 0);\n if (b === 0) return null;\n\n return Number((((a - b) / Math.abs(b)) * 100).toFixed(1));\n}\n\n/** The four ways a customer can pay at the checkout, in the order the checkout shows them. */\nexport const METHOD_ORDER = ['card', 'ussd', 'transfer', 'opay'] as const;\n\nexport interface MethodCardRow {\n key: string;\n label: string;\n payments: number;\n amountMinor: string;\n /** Share of all money received in this currency, in percent. */\n share: number;\n /** Change in amount since the period before; null when there is no comparison. */\n change: number | null;\n}\n\n/**\n * One card per way of paying, for one currency. The four checkout methods are always there, with zero when nobody\n * used one, so a quiet method is visible rather than missing. Any other way found in the data follows.\n */\nexport function methodCards(\n now: readonly GatewayChannelTotal[],\n before: readonly GatewayChannelTotal[] | null,\n currency: string,\n): MethodCardRow[] {\n const pick = (rows: readonly GatewayChannelTotal[] | null) => (rows ?? []).filter((r) => r.currency === currency);\n const current = pick(now);\n const total = current.reduce((sum, r) => sum + BigInt(r.amountMinor), BigInt(0));\n const labels: Record<string, string> = { card: 'Card', ussd: 'USSD', transfer: 'Bank transfer', opay: 'Opay' };\n\n const keys = [\n ...METHOD_ORDER,\n ...current.map((r) => r.channel.key).filter((k) => !(METHOD_ORDER as readonly string[]).includes(k)),\n ];\n\n return [...new Set(keys)].map((key) => {\n const mine = current.find((r) => r.channel.key === key);\n const prior = pick(before).find((r) => r.channel.key === key);\n const amount = BigInt(mine?.amountMinor ?? '0');\n\n return {\n key,\n label: mine?.channel.label ?? labels[key] ?? key,\n payments: mine?.count ?? 0,\n amountMinor: amount.toString(),\n share: total > BigInt(0) ? Number(((Number(amount) / Number(total)) * 100).toFixed(1)) : 0,\n change: before ? pctChange(amount.toString(), prior?.amountMinor ?? '0') : null,\n };\n });\n}\n\n/** Words an accountant would use, not the database's. */\nexport const STATUS_WORDS: Record<string, string> = {\n success: 'Paid',\n pending: 'Waiting',\n failed: 'Failed',\n refunded: 'Refunded',\n cancelled: 'Cancelled',\n abandoned: 'Abandoned',\n succeeded: 'Paid',\n};\n\nexport const TYPE_WORDS: Record<string, string> = {\n order: 'Order',\n invoice: 'Invoice',\n booking: 'Booking',\n membership: 'Membership',\n subscription: 'Subscription',\n deposit: 'Deposit',\n tip: 'Tip',\n refund: 'Refund',\n payout: 'Payout',\n};\n\nexport const PURPOSE_WORDS: Record<string, string> = {\n select_plan: 'First payment',\n upgrade: 'Upgrade',\n renewal: 'Renewal',\n reactivation: 'Came back',\n mandate_setup: 'Card saved',\n};\n\nexport const SUBSCRIPTION_METHOD_WORDS: Record<string, string> = {\n gateway: 'Card or bank',\n wallet: 'Wallet',\n manual: 'Recorded by hand',\n};\n\nexport const word = (words: Record<string, string>, key: string): string => words[key] ?? key.replace(/_/g, ' ');\n\n/** The dates in a download's file name, so a file says what it holds. */\nexport function downloadName(prefix: string, startDate?: string, endDate?: string): string {\n const day = (iso?: string) => (iso ? iso.slice(0, 10) : 'all');\n\n return `${prefix}_${day(startDate)}_${day(endDate)}.csv`;\n}\n"],"mappings":"AACA,SAAS,mBAAmB;AAGrB,SAAS,aAAa,OAAwB,UAA0B;AAC7E,SAAO,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU,CAAC;AACrD;AAGO,SAAS,UACd,KACA,QACe;AACf,QAAM,IAAI,OAAO,OAAO,CAAC;AACzB,QAAM,IAAI,OAAO,UAAU,CAAC;AAC5B,MAAI,MAAM,EAAG,QAAO;AAEpB,SAAO,SAAU,IAAI,KAAK,KAAK,IAAI,CAAC,IAAK,KAAK,QAAQ,CAAC,CAAC;AAC1D;AAGO,MAAM,eAAe,CAAC,QAAQ,QAAQ,YAAY,MAAM;AAiBxD,SAAS,YACd,KACA,QACA,UACiB;AACjB,QAAM,OAAO,CAAC,UAAiD,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAChH,QAAM,UAAU,KAAK,GAAG;AACxB,QAAM,QAAQ,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC;AAC/E,QAAM,SAAiC,EAAE,MAAM,QAAQ,MAAM,QAAQ,UAAU,iBAAiB,MAAM,OAAO;AAE7G,QAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,EAAE,OAAO,CAAC,MAAM,CAAE,aAAmC,SAAS,CAAC,CAAC;AAAA,EACrG;AAEA,SAAO,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ;AACrC,UAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,QAAQ,QAAQ,GAAG;AACtD,UAAM,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,QAAQ,GAAG;AAC5D,UAAM,SAAS,OAAO,MAAM,eAAe,GAAG;AAE9C,WAAO;AAAA,MACL;AAAA,MACA,OAAO,MAAM,QAAQ,SAAS,OAAO,GAAG,KAAK;AAAA,MAC7C,UAAU,MAAM,SAAS;AAAA,MACzB,aAAa,OAAO,SAAS;AAAA,MAC7B,OAAO,QAAQ,OAAO,CAAC,IAAI,QAAS,OAAO,MAAM,IAAI,OAAO,KAAK,IAAK,KAAK,QAAQ,CAAC,CAAC,IAAI;AAAA,MACzF,QAAQ,SAAS,UAAU,OAAO,SAAS,GAAG,OAAO,eAAe,GAAG,IAAI;AAAA,IAC7E;AAAA,EACF,CAAC;AACH;AAGO,MAAM,eAAuC;AAAA,EAClD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AACb;AAEO,MAAM,aAAqC;AAAA,EAChD,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,SAAS;AAAA,EACT,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AACV;AAEO,MAAM,gBAAwC;AAAA,EACnD,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAAA,EACd,eAAe;AACjB;AAEO,MAAM,4BAAoD;AAAA,EAC/D,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AACV;AAEO,MAAM,OAAO,CAAC,OAA+B,QAAwB,MAAM,GAAG,KAAK,IAAI,QAAQ,MAAM,GAAG;AAGxG,SAAS,aAAa,QAAgB,WAAoB,SAA0B;AACzF,QAAM,MAAM,CAAC,QAAkB,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI;AAExD,SAAO,GAAG,MAAM,IAAI,IAAI,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC;AACpD;","names":[]}
@@ -0,0 +1,101 @@
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
+ }
@@ -0,0 +1,231 @@
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 { 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 styles from "./ledger.module.css";
30
+ import table from "./revenue.module.css";
31
+ const PAGE_SIZE = 25;
32
+ const integer = new Intl.NumberFormat("en-US");
33
+ const METHODS = [
34
+ { value: "", label: "Every way of paying" },
35
+ { value: "card", label: "Card" },
36
+ { value: "ussd", label: "USSD" },
37
+ { value: "transfer", label: "Bank transfer" },
38
+ { value: "opay", label: "Opay" }
39
+ ];
40
+ const STATUSES = [
41
+ { value: "", label: "Paid" },
42
+ { value: "pending", label: "Waiting" },
43
+ { value: "failed", label: "Failed" },
44
+ { value: "refunded", label: "Refunded" }
45
+ ];
46
+ const TYPES = [
47
+ { value: "", label: "Every kind" },
48
+ ...["order", "invoice", "booking", "membership", "subscription", "deposit", "tip", "refund"].map((value) => ({
49
+ value,
50
+ label: word(TYPE_WORDS, value)
51
+ }))
52
+ ];
53
+ const tone = (status) => status === "success" ? styles.statusGood : status === "failed" ? styles.statusBad : status === "pending" ? styles.statusWait : styles.statusMuted;
54
+ function MerchantPaymentsView({
55
+ startDate,
56
+ endDate,
57
+ currency
58
+ }) {
59
+ const [search, setSearch] = useState("");
60
+ const [q, setQ] = useState("");
61
+ const [method, setMethod] = useState("");
62
+ const [status, setStatus] = useState("");
63
+ const [type, setType] = useState("");
64
+ const [page, setPage] = useState(1);
65
+ useEffect(() => {
66
+ const timer = setTimeout(() => setQ(search.trim()), 300);
67
+ return () => clearTimeout(timer);
68
+ }, [search]);
69
+ const filters = useMemo(
70
+ () => ({ startDate, endDate, method, status, type, q, page, limit: PAGE_SIZE }),
71
+ [startDate, endDate, method, status, type, q, page]
72
+ );
73
+ const change = (set) => (value) => {
74
+ set(value);
75
+ setPage(1);
76
+ };
77
+ const { data, isLoading, isError, refetch } = useMerchantPayments(filters);
78
+ if (isError) {
79
+ return /* @__PURE__ */ jsx(
80
+ LoadFailed,
81
+ {
82
+ message: "Merchant payments could not be loaded. This box may not have them yet.",
83
+ onRetry: () => void refetch()
84
+ }
85
+ );
86
+ }
87
+ if (isLoading || !data) return /* @__PURE__ */ jsx(AdmLoading, { label: "Loading merchant payments\u2026" });
88
+ const currencies = [...new Set(data.cards.current.map((c) => c.currency))];
89
+ const focus = focusCurrency(currencies, currency) ?? "NGN";
90
+ const now = data.cards.current.find((c) => c.currency === focus);
91
+ const before = data.cards.previous?.find((c) => c.currency === focus) ?? null;
92
+ const strip = data.strip.find((s) => s.currency === focus);
93
+ const money = (minor) => minorToMoney(minor, focus);
94
+ const delta = (a, b, goodWhenUp = true) => before ? { pct: pctChange(a, b), positiveIsGood: goodWhenUp, caption: "vs the period before" } : void 0;
95
+ const totalPages = Math.max(Math.ceil(data.total / PAGE_SIZE), 1);
96
+ return /* @__PURE__ */ jsxs("div", { children: [
97
+ /* @__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." }),
98
+ /* @__PURE__ */ jsxs("section", { "aria-label": "Merchant payments at a glance", className: "adm-grid", children: [
99
+ /* @__PURE__ */ jsx(Reveal, { index: 0, className: "adm-span-3", children: /* @__PURE__ */ jsx(
100
+ StatCard,
101
+ {
102
+ label: "Money received",
103
+ value: /* @__PURE__ */ jsx(CountUp, { value: Number(now?.grossMinor ?? 0) / 100, format: (v) => money(Math.round(v * 100)) }),
104
+ sub: now ? `${integer.format(now.payments)} payments that went through` : "No payments in this period",
105
+ hint: "Everything customers paid to merchants that went through, before any fees are taken.",
106
+ delta: delta(now?.grossMinor, before?.grossMinor)
107
+ }
108
+ ) }),
109
+ /* @__PURE__ */ jsx(Reveal, { index: 1, className: "adm-span-3", children: /* @__PURE__ */ jsx(
110
+ StatCard,
111
+ {
112
+ label: "Fees taken",
113
+ value: money(now?.feeMinor ?? 0),
114
+ hint: "What the payment gateway and Olive kept from those payments.",
115
+ delta: delta(now?.feeMinor, before?.feeMinor, false)
116
+ }
117
+ ) }),
118
+ /* @__PURE__ */ jsx(Reveal, { index: 2, className: "adm-span-3", children: /* @__PURE__ */ jsx(
119
+ StatCard,
120
+ {
121
+ label: "Merchants keep",
122
+ value: money(now?.netMinor ?? 0),
123
+ hint: "What is left for merchants after the fees: money received minus fees taken.",
124
+ delta: delta(now?.netMinor, before?.netMinor)
125
+ }
126
+ ) }),
127
+ /* @__PURE__ */ jsx(Reveal, { index: 3, className: "adm-span-3", children: /* @__PURE__ */ jsx(
128
+ StatCard,
129
+ {
130
+ label: "Payments that worked",
131
+ value: now?.successRate == null ? "\u2013" : `${now.successRate}%`,
132
+ sub: now?.successRate == null ? "Nothing was tried in this period" : `Refunded: ${money(now.refundsMinor)}`,
133
+ hint: "Out of every payment that was tried, the share that went through. A low number can mean a way of paying is broken."
134
+ }
135
+ ) })
136
+ ] }),
137
+ /* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "How customers paid" }),
138
+ /* @__PURE__ */ jsx("p", { className: styles.sectionNote, children: "The ways of paying on the checkout, and how much came in through each." }),
139
+ /* @__PURE__ */ jsx(MethodCards, { now: data.byChannel.current, before: data.byChannel.previous, currency: focus }),
140
+ /* @__PURE__ */ jsx("h2", { className: styles.sectionTitle, children: "Every payment" }),
141
+ /* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
142
+ /* @__PURE__ */ jsxs(Filters, { label: "Filter payments", children: [
143
+ /* @__PURE__ */ jsx(
144
+ FilterSearch,
145
+ {
146
+ value: search,
147
+ onChange: change(setSearch),
148
+ placeholder: "Search a name, email, phone, reference or merchant",
149
+ label: "Search payments"
150
+ }
151
+ ),
152
+ /* @__PURE__ */ jsx(FilterSelect, { value: method, onChange: change(setMethod), options: METHODS, label: "Way of paying" }),
153
+ /* @__PURE__ */ jsx(FilterSelect, { value: status, onChange: change(setStatus), options: STATUSES, label: "Payment result" }),
154
+ /* @__PURE__ */ jsx(FilterSelect, { value: type, onChange: change(setType), options: TYPES, label: "What it was for" })
155
+ ] }),
156
+ /* @__PURE__ */ jsx(
157
+ DownloadButton,
158
+ {
159
+ filename: downloadName("merchant-payments", startDate, endDate),
160
+ load: () => platformAdminApi.merchantPaymentsCsv({ ...filters, page: void 0, limit: void 0 })
161
+ }
162
+ )
163
+ ] }),
164
+ strip ? /* @__PURE__ */ jsxs("p", { className: styles.strip, "aria-live": "polite", children: [
165
+ /* @__PURE__ */ jsxs("span", { children: [
166
+ "Showing ",
167
+ /* @__PURE__ */ jsx("strong", { children: integer.format(data.total) }),
168
+ " ",
169
+ data.total === 1 ? "payment" : "payments"
170
+ ] }),
171
+ /* @__PURE__ */ jsxs("span", { children: [
172
+ /* @__PURE__ */ jsx("strong", { children: money(strip.amountMinor) }),
173
+ " received"
174
+ ] }),
175
+ /* @__PURE__ */ jsxs("span", { children: [
176
+ /* @__PURE__ */ jsx("strong", { children: money(strip.feeMinor) }),
177
+ " in fees"
178
+ ] }),
179
+ /* @__PURE__ */ jsxs("span", { children: [
180
+ /* @__PURE__ */ jsx("strong", { children: money(strip.netMinor) }),
181
+ " to merchants"
182
+ ] })
183
+ ] }) : null,
184
+ 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(
185
+ TableShell,
186
+ {
187
+ minWidth: 980,
188
+ density: "compact",
189
+ head: /* @__PURE__ */ jsxs("tr", { children: [
190
+ /* @__PURE__ */ jsx(Th, { children: "When" }),
191
+ /* @__PURE__ */ jsx(Th, { children: "Paid by" }),
192
+ /* @__PURE__ */ jsx(Th, { children: "How" }),
193
+ /* @__PURE__ */ jsx(Th, { children: "Merchant" }),
194
+ /* @__PURE__ */ jsx(Th, { children: "For" }),
195
+ /* @__PURE__ */ jsx(Th, { children: "Result" }),
196
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Amount" }),
197
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Fee" }),
198
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Merchant gets" })
199
+ ] }),
200
+ children: data.rows.map((row) => /* @__PURE__ */ jsx(PaymentRow, { row }, row.id))
201
+ }
202
+ ),
203
+ /* @__PURE__ */ jsx(Pagination, { page, totalPages, total: data.total, noun: "payments", onPage: setPage })
204
+ ] });
205
+ }
206
+ function PaymentRow({ row }) {
207
+ const { payer } = row;
208
+ const contact = [payer.email, payer.phone].filter(Boolean).join(" \xB7 ");
209
+ const money = (minor) => minorToMoney(minor, row.currency);
210
+ return /* @__PURE__ */ jsxs(TableRow, { children: [
211
+ /* @__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" }) }),
212
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
213
+ /* @__PURE__ */ jsx("span", { className: styles.whoName, children: payer.name || "Name not recorded" }),
214
+ contact ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: contact }) : null
215
+ ] }) }),
216
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: styles.who, children: [
217
+ /* @__PURE__ */ jsx("span", { className: styles.whoName, children: row.channel.label }),
218
+ row.methodDetail ? /* @__PURE__ */ jsx("span", { className: styles.whoSub, children: row.methodDetail }) : null
219
+ ] }) }),
220
+ /* @__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" }),
221
+ /* @__PURE__ */ jsx("td", { children: word(TYPE_WORDS, row.type) }),
222
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: `${styles.status} ${tone(row.status)}`, children: word(STATUS_WORDS, row.status) }) }),
223
+ /* @__PURE__ */ jsx("td", { className: table.numStrong, children: money(row.amountMinor) }),
224
+ /* @__PURE__ */ jsx("td", { className: table.num, children: money(row.feeMinor) }),
225
+ /* @__PURE__ */ jsx("td", { className: table.num, children: money(row.netMinor) })
226
+ ] });
227
+ }
228
+ export {
229
+ MerchantPaymentsView
230
+ };
231
+ //# sourceMappingURL=merchant-payments-view.js.map
@@ -0,0 +1 @@
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 { 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 styles from './ledger.module.css';\nimport table from './revenue.module.css';\n\nconst PAGE_SIZE = 25;\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 const [search, setSearch] = useState('');\n const [q, setQ] = useState('');\n const [method, setMethod] = useState('');\n const [status, setStatus] = useState('');\n const [type, setType] = useState('');\n const [page, setPage] = useState(1);\n\n // Typing waits a moment, so a search is one request and not one per key.\n useEffect(() => {\n const timer = setTimeout(() => setQ(search.trim()), 300);\n\n return () => clearTimeout(timer);\n }, [search]);\n\n const filters = useMemo(\n () => ({ startDate, endDate, method, status, type, q, page, limit: PAGE_SIZE }),\n [startDate, endDate, method, status, type, q, page],\n );\n // Any change of what is shown starts again from the first page.\n const change = <T,>(set: (v: T) => void) => (value: T) => {\n set(value);\n setPage(1);\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 strip = data.strip.find((s) => s.currency === focus);\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}>Every payment</h2>\n <div className={styles.toolbar}>\n <Filters label=\"Filter payments\">\n <FilterSearch\n value={search}\n onChange={change(setSearch)}\n placeholder=\"Search a name, email, phone, reference or merchant\"\n label=\"Search payments\"\n />\n <FilterSelect value={method} onChange={change(setMethod)} options={METHODS} label=\"Way of paying\" />\n <FilterSelect value={status} onChange={change(setStatus)} options={STATUSES} label=\"Payment result\" />\n <FilterSelect value={type} onChange={change(setType)} 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 {strip ? (\n <p className={styles.strip} aria-live=\"polite\">\n <span>\n Showing <strong>{integer.format(data.total)}</strong> {data.total === 1 ? 'payment' : 'payments'}\n </span>\n <span>\n <strong>{money(strip.amountMinor)}</strong> received\n </span>\n <span>\n <strong>{money(strip.feeMinor)}</strong> in fees\n </span>\n <span>\n <strong>{money(strip.netMinor)}</strong> to merchants\n </span>\n </p>\n ) : null}\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} />\n ))}\n </TableShell>\n )}\n\n <Pagination page={page} totalPages={totalPages} total={data.total} noun=\"payments\" onPage={setPage} />\n </div>\n );\n}\n\nfunction PaymentRow({ row }: { row: MerchantPaymentRow }) {\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>\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":[]}