@withpotter/pages-admin 0.13.0-next.81 → 0.13.0-next.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/_overview/control-room.js +1 -19
- package/dist/admin/_overview/control-room.js.map +1 -1
- package/dist/admin/accounts/account-sessions.js +7 -3
- package/dist/admin/accounts/account-sessions.js.map +1 -1
- package/dist/admin/revenue/platform-fees.js +9 -0
- package/dist/admin/revenue/platform-fees.js.map +1 -0
- package/dist/admin/revenue/revenue-overview.js +20 -1
- package/dist/admin/revenue/revenue-overview.js.map +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js.map +1 -1
- package/dist/admin/tenants/[id]/page.js +18 -3
- package/dist/admin/tenants/[id]/page.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-overview.js +3 -2
- package/dist/admin/tenants/[id]/tenant-overview.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-wallet-card.js +3 -2
- package/dist/admin/tenants/[id]/tenant-wallet-card.js.map +1 -1
- package/dist/admin/tenants/[id]/use-tenant-wallet.js +23 -0
- package/dist/admin/tenants/[id]/use-tenant-wallet.js.map +1 -0
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { CountUp } from "../_fx";
|
|
4
|
-
import {
|
|
4
|
+
import { StatCard, formatMoney } from "../_ui";
|
|
5
5
|
import { usePlatformRevenue } from "../hooks";
|
|
6
6
|
import { useModuleOn } from "../_shell/modules-context";
|
|
7
7
|
import styles from "./control-room.module.css";
|
|
@@ -79,24 +79,6 @@ function ControlRoom({
|
|
|
79
79
|
sub: "Businesses that passed KYC or KYB"
|
|
80
80
|
}
|
|
81
81
|
),
|
|
82
|
-
totals?.gatewayFeesByCurrency !== void 0 && /* @__PURE__ */ jsx(
|
|
83
|
-
StatCard,
|
|
84
|
-
{
|
|
85
|
-
compact: true,
|
|
86
|
-
label: "Gateway revenue",
|
|
87
|
-
value: /* @__PURE__ */ jsx(
|
|
88
|
-
MoneyLines,
|
|
89
|
-
{
|
|
90
|
-
rows: totals.gatewayFeesByCurrency,
|
|
91
|
-
currency,
|
|
92
|
-
amount: (row) => row.amount,
|
|
93
|
-
empty: "No fees yet"
|
|
94
|
-
}
|
|
95
|
-
),
|
|
96
|
-
sub: "Fees on card payments",
|
|
97
|
-
href: "/admin/revenue"
|
|
98
|
-
}
|
|
99
|
-
),
|
|
100
82
|
/* @__PURE__ */ jsx(SubscriptionsCard, {})
|
|
101
83
|
] });
|
|
102
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_overview/control-room.tsx"],"sourcesContent":["'use client';\n\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { CountUp } from '../_fx';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_overview/control-room.tsx"],"sourcesContent":["'use client';\n\nimport type { PlatformCockpit } from '@withpotter/lib/admin-api';\nimport { CountUp } from '../_fx';\nimport { StatCard, formatMoney } from '../_ui';\nimport { usePlatformRevenue } from '../hooks';\nimport { useModuleOn } from '../_shell/modules-context';\nimport styles from './control-room.module.css';\nimport { useWaitingCases } from './use-waiting-cases';\nimport { overdueOutstanding } from './overdue-outstanding';\n\n/** KYC / KYB waiting count; mounted only where Verification is served. */\nfunction VerificationCard() {\n const waiting = useWaitingCases().data;\n\n return (\n <StatCard\n compact\n label=\"KYC / KYB decisions\"\n value={waiting === undefined ? '–' : <CountUp value={waiting} />}\n sub=\"Businesses waiting for review\"\n href=\"/admin/verification\"\n alert={!!waiting}\n />\n );\n}\n\n/** Monthly recurring revenue; shown only where billing feeds the figure, never as a made-up zero. */\nfunction SubscriptionsCard() {\n const revenue = usePlatformRevenue().data;\n\n if (!revenue?.recurring.tracked) return null;\n\n return (\n <StatCard\n compact\n label=\"Subscriptions\"\n value={formatMoney(revenue.recurring.mrr, revenue.currency)}\n sub=\"Monthly recurring revenue\"\n href=\"/admin/revenue\"\n />\n );\n}\n\n/**\n * The control room: every queue that needs a decision, then the revenue and\n * wallet figures, as one row of identical metric cards that wraps. A card opens its screen when this Box\n * serves that screen; a queue whose module is off is not shown.\n */\nexport function ControlRoom({\n attention,\n currency = '',\n totals,\n}: {\n attention: PlatformCockpit['attention'];\n /** Lifetime figures; a card whose figure the api does not send is left out. */\n totals?: PlatformCockpit['totals'];\n /** The currency filter's choice; narrows what overdue invoices owe. */\n currency?: string;\n}) {\n const failed = attention.failedSettlements.count;\n const overdue = attention.overdueInvoices.count;\n // A feed that failed to load is unknown, not zero.\n const failedUnknown = !!attention.unavailable?.includes('failedSettlements');\n const overdueUnknown = !!attention.unavailable?.includes('overdueInvoices');\n const withdrawals = useModuleOn('withdrawals');\n const verification = useModuleOn('verification');\n\n return (\n <section className={styles.room} aria-label=\"Control room\">\n <StatCard\n compact\n label=\"Failed payouts\"\n value={failedUnknown ? '–' : <CountUp value={failed} />}\n sub={\n failedUnknown\n ? 'Could not be loaded'\n : 'Settlements that did not land'\n }\n href={withdrawals ? '/admin/withdrawals?tab=failed' : undefined}\n alert={failedUnknown || failed > 0}\n />\n <StatCard\n compact\n label=\"Overdue invoices\"\n value={overdueUnknown ? '–' : <CountUp value={overdue} />}\n sub={\n overdueUnknown\n ? 'Could not be loaded'\n : (overdueOutstanding(attention.overdueInvoices, currency) ??\n 'Invoices past their due date')\n }\n href=\"/admin/revenue\"\n alert={overdueUnknown || overdue > 0}\n />\n {verification && <VerificationCard />}\n {totals?.verifiedWallets !== undefined && (\n <StatCard\n compact\n label=\"Verified wallets\"\n value={<CountUp value={totals.verifiedWallets} />}\n sub=\"Businesses that passed KYC or KYB\"\n />\n )}\n <SubscriptionsCard />\n </section>\n );\n}\n"],"mappings":";AAmB2C,cAkDvC,YAlDuC;AAhB3C,SAAS,eAAe;AACxB,SAAS,UAAU,mBAAmB;AACtC,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B,OAAO,YAAY;AACnB,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AAGnC,SAAS,mBAAmB;AAC1B,QAAM,UAAU,gBAAgB,EAAE;AAElC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAO;AAAA,MACP,OAAM;AAAA,MACN,OAAO,YAAY,SAAY,WAAM,oBAAC,WAAQ,OAAO,SAAS;AAAA,MAC9D,KAAI;AAAA,MACJ,MAAK;AAAA,MACL,OAAO,CAAC,CAAC;AAAA;AAAA,EACX;AAEJ;AAGA,SAAS,oBAAoB;AAC3B,QAAM,UAAU,mBAAmB,EAAE;AAErC,MAAI,CAAC,SAAS,UAAU,QAAS,QAAO;AAExC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAO;AAAA,MACP,OAAM;AAAA,MACN,OAAO,YAAY,QAAQ,UAAU,KAAK,QAAQ,QAAQ;AAAA,MAC1D,KAAI;AAAA,MACJ,MAAK;AAAA;AAAA,EACP;AAEJ;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAMG;AACD,QAAM,SAAS,UAAU,kBAAkB;AAC3C,QAAM,UAAU,UAAU,gBAAgB;AAE1C,QAAM,gBAAgB,CAAC,CAAC,UAAU,aAAa,SAAS,mBAAmB;AAC3E,QAAM,iBAAiB,CAAC,CAAC,UAAU,aAAa,SAAS,iBAAiB;AAC1E,QAAM,cAAc,YAAY,aAAa;AAC7C,QAAM,eAAe,YAAY,cAAc;AAE/C,SACE,qBAAC,aAAQ,WAAW,OAAO,MAAM,cAAW,gBAC1C;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,OAAO,gBAAgB,WAAM,oBAAC,WAAQ,OAAO,QAAQ;AAAA,QACrD,KACE,gBACI,wBACA;AAAA,QAEN,MAAM,cAAc,kCAAkC;AAAA,QACtD,OAAO,iBAAiB,SAAS;AAAA;AAAA,IACnC;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,OAAO,iBAAiB,WAAM,oBAAC,WAAQ,OAAO,SAAS;AAAA,QACvD,KACE,iBACI,wBACC,mBAAmB,UAAU,iBAAiB,QAAQ,KACvD;AAAA,QAEN,MAAK;AAAA,QACL,OAAO,kBAAkB,UAAU;AAAA;AAAA,IACrC;AAAA,IACC,gBAAgB,oBAAC,oBAAiB;AAAA,IAClC,QAAQ,oBAAoB,UAC3B;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,OAAO,oBAAC,WAAQ,OAAO,OAAO,iBAAiB;AAAA,QAC/C,KAAI;AAAA;AAAA,IACN;AAAA,IAEF,oBAAC,qBAAkB;AAAA,KACrB;AAEJ;","names":[]}
|
|
@@ -3,12 +3,16 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Button, EmptyNote } from "@withpotter/ui-kit";
|
|
4
4
|
import { AdmLoading } from "../_ui";
|
|
5
5
|
import { useAccountSessions } from "../hooks";
|
|
6
|
+
import { relativeTime, seenWithin } from "./relative-time";
|
|
6
7
|
import styles from "./account-sessions.module.css";
|
|
7
8
|
const ENDED = {
|
|
8
|
-
active: "
|
|
9
|
+
active: "Signed in",
|
|
9
10
|
revoked: "Signed out",
|
|
10
11
|
expired: "Expired"
|
|
11
12
|
};
|
|
13
|
+
const IN_USE_SECONDS = 300;
|
|
14
|
+
const inUse = (s) => s.endedHow === "active" && seenWithin(s.lastSeenAt, IN_USE_SECONDS);
|
|
15
|
+
const stateOf = (s) => inUse(s) ? "Active now" : ENDED[s.endedHow];
|
|
12
16
|
const when = (iso) => iso ? new Date(iso).toLocaleString(void 0, {
|
|
13
17
|
dateStyle: "medium",
|
|
14
18
|
timeStyle: "short"
|
|
@@ -35,13 +39,13 @@ function AccountSessions({ accountId }) {
|
|
|
35
39
|
return /* @__PURE__ */ jsx("ul", { className: styles.list, children: ordered.map((s) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
|
|
36
40
|
/* @__PURE__ */ jsxs("span", { className: styles.top, children: [
|
|
37
41
|
/* @__PURE__ */ jsx("span", { className: styles.device, title: s.userAgent ?? void 0, children: deviceOf(s) }),
|
|
38
|
-
/* @__PURE__ */ jsx("span", { className: styles.how, "data-live": s
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: styles.how, "data-live": inUse(s), children: stateOf(s) })
|
|
39
43
|
] }),
|
|
40
44
|
/* @__PURE__ */ jsx("span", { className: styles.meta, children: [s.ipAddress, s.location].filter(Boolean).join(" \xB7 ") || "No address recorded" }),
|
|
41
45
|
/* @__PURE__ */ jsxs("span", { className: styles.meta, children: [
|
|
42
46
|
"Started ",
|
|
43
47
|
when(s.startedAt),
|
|
44
|
-
s.endedAt
|
|
48
|
+
s.endedAt ? ` \xB7 Ended ${when(s.endedAt)}` : ` \xB7 Last seen ${relativeTime(s.lastSeenAt)}`
|
|
45
49
|
] })
|
|
46
50
|
] }, s.id)) });
|
|
47
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-sessions.tsx"],"sourcesContent":["'use client';\n\nimport { Button, EmptyNote } from '@withpotter/ui-kit';\nimport type { AccountSession } from '@withpotter/lib/admin-api';\nimport { AdmLoading } from '../_ui';\nimport { useAccountSessions } from '../hooks';\nimport styles from './account-sessions.module.css';\n\nconst ENDED: Record<AccountSession['endedHow'], string> = {\n active: '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-sessions.tsx"],"sourcesContent":["'use client';\n\nimport { Button, EmptyNote } from '@withpotter/ui-kit';\nimport type { AccountSession } from '@withpotter/lib/admin-api';\nimport { AdmLoading } from '../_ui';\nimport { useAccountSessions } from '../hooks';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-sessions.module.css';\n\nconst ENDED: Record<AccountSession['endedHow'], string> = {\n active: 'Signed in',\n revoked: 'Signed out',\n expired: 'Expired',\n};\n\n/** A device seen in the last few minutes is in use; a session not ended yet is only signed in. */\nconst IN_USE_SECONDS = 300;\nconst inUse = (s: AccountSession) =>\n s.endedHow === 'active' && seenWithin(s.lastSeenAt, IN_USE_SECONDS);\nconst stateOf = (s: AccountSession) =>\n inUse(s) ? 'Active now' : ENDED[s.endedHow];\n\nconst when = (iso: string | null) =>\n iso\n ? new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n : null;\n\n/** A short device name from the session, falling back to the raw agent. */\nfunction deviceOf(s: AccountSession): string {\n return s.name || s.kind || s.userAgent || 'Unknown device';\n}\n\n/** Where and when this person signed in, newest first. */\nexport function AccountSessions({ accountId }: { accountId: string }) {\n const { data, isLoading, isError, refetch } = useAccountSessions(accountId);\n\n if (isLoading) return <AdmLoading label=\"Loading sessions…\" size=\"sm\" />;\n\n if (isError) {\n return (\n <EmptyNote>\n We could not load their sessions just now.{' '}\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void refetch()}>\n Try again\n </Button>\n </EmptyNote>\n );\n }\n\n if (!data?.length) {\n return <EmptyNote>No sign-ins recorded for this person yet.</EmptyNote>;\n }\n\n const ordered = [...data].sort((a: AccountSession, b: AccountSession) =>\n b.startedAt.localeCompare(a.startedAt),\n );\n\n return (\n <ul className={styles.list}>\n {ordered.map((s: AccountSession) => (\n <li key={s.id} className={styles.item}>\n <span className={styles.top}>\n <span className={styles.device} title={s.userAgent ?? undefined}>\n {deviceOf(s)}\n </span>\n <span className={styles.how} data-live={inUse(s)}>\n {stateOf(s)}\n </span>\n </span>\n <span className={styles.meta}>\n {[s.ipAddress, s.location].filter(Boolean).join(' · ') ||\n 'No address recorded'}\n </span>\n <span className={styles.meta}>\n Started {when(s.startedAt)}\n {s.endedAt\n ? ` · Ended ${when(s.endedAt)}`\n : ` · Last seen ${relativeTime(s.lastSeenAt)}`}\n </span>\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";AAuCwB,cAIlB,YAJkB;AArCxB,SAAS,QAAQ,iBAAiB;AAElC,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,cAAc,kBAAkB;AACzC,OAAO,YAAY;AAEnB,MAAM,QAAoD;AAAA,EACxD,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAGA,MAAM,iBAAiB;AACvB,MAAM,QAAQ,CAAC,MACb,EAAE,aAAa,YAAY,WAAW,EAAE,YAAY,cAAc;AACpE,MAAM,UAAU,CAAC,MACf,MAAM,CAAC,IAAI,eAAe,MAAM,EAAE,QAAQ;AAE5C,MAAM,OAAO,CAAC,QACZ,MACI,IAAI,KAAK,GAAG,EAAE,eAAe,QAAW;AAAA,EACtC,WAAW;AAAA,EACX,WAAW;AACb,CAAC,IACD;AAGN,SAAS,SAAS,GAA2B;AAC3C,SAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa;AAC5C;AAGO,SAAS,gBAAgB,EAAE,UAAU,GAA0B;AACpE,QAAM,EAAE,MAAM,WAAW,SAAS,QAAQ,IAAI,mBAAmB,SAAS;AAE1E,MAAI,UAAW,QAAO,oBAAC,cAAW,OAAM,0BAAoB,MAAK,MAAK;AAEtE,MAAI,SAAS;AACX,WACE,qBAAC,aAAU;AAAA;AAAA,MACkC;AAAA,MAC3C,oBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,QAAQ,GAAG,uBAEjE;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM,QAAQ;AACjB,WAAO,oBAAC,aAAU,uDAAyC;AAAA,EAC7D;AAEA,QAAM,UAAU,CAAC,GAAG,IAAI,EAAE;AAAA,IAAK,CAAC,GAAmB,MACjD,EAAE,UAAU,cAAc,EAAE,SAAS;AAAA,EACvC;AAEA,SACE,oBAAC,QAAG,WAAW,OAAO,MACnB,kBAAQ,IAAI,CAAC,MACZ,qBAAC,QAAc,WAAW,OAAO,MAC/B;AAAA,yBAAC,UAAK,WAAW,OAAO,KACtB;AAAA,0BAAC,UAAK,WAAW,OAAO,QAAQ,OAAO,EAAE,aAAa,QACnD,mBAAS,CAAC,GACb;AAAA,MACA,oBAAC,UAAK,WAAW,OAAO,KAAK,aAAW,MAAM,CAAC,GAC5C,kBAAQ,CAAC,GACZ;AAAA,OACF;AAAA,IACA,oBAAC,UAAK,WAAW,OAAO,MACrB,WAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK,KACnD,uBACJ;AAAA,IACA,qBAAC,UAAK,WAAW,OAAO,MAAM;AAAA;AAAA,MACnB,KAAK,EAAE,SAAS;AAAA,MACxB,EAAE,UACC,eAAY,KAAK,EAAE,OAAO,CAAC,KAC3B,mBAAgB,aAAa,EAAE,UAAU,CAAC;AAAA,OAChD;AAAA,OAlBO,EAAE,EAmBX,CACD,GACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { TimeSeriesChart } from "@withpotter/components/charts";
|
|
5
|
-
import { PICK_CURRENCY, Panel, moneyIn, rowFor, toTrend } from "../_ui";
|
|
5
|
+
import { MoneyLines, PICK_CURRENCY, Panel, StatCard, moneyIn, rowFor, toTrend } from "../_ui";
|
|
6
|
+
import { Reveal } from "../_fx";
|
|
6
7
|
import { Tinted } from "../audit/tinted";
|
|
7
8
|
import { HealthPanel } from "./health-panel";
|
|
8
9
|
import { PlanMixPanel } from "./plan-mix-panel";
|
|
10
|
+
import { platformFeeRows } from "./platform-fees";
|
|
9
11
|
import { RevenueKpis } from "./revenue-kpis";
|
|
10
12
|
import { SubscriptionsPanel } from "./subscriptions-panel";
|
|
11
13
|
import { TopTenantsPanel } from "./top-tenants-panel";
|
|
@@ -25,6 +27,7 @@ function RevenueOverview({
|
|
|
25
27
|
const gmvSeries = useMemo(() => toTrend(gmv?.series, "gmv"), [gmv]);
|
|
26
28
|
const gmvCaption = focus ? `${period} \xB7 ${focus}` : period;
|
|
27
29
|
const recurringTracked = data.recurring.tracked !== false;
|
|
30
|
+
const platformFees = platformFeeRows(data.platformFeesEarnedByCurrency, currency);
|
|
28
31
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
32
|
/* @__PURE__ */ jsx(
|
|
30
33
|
RevenueKpis,
|
|
@@ -37,6 +40,22 @@ function RevenueOverview({
|
|
|
37
40
|
gmvSpark: gmvSeries.map((p) => p.value)
|
|
38
41
|
}
|
|
39
42
|
),
|
|
43
|
+
platformFees && /* @__PURE__ */ jsx("section", { "aria-label": "Platform fees", className: "adm-grid", children: /* @__PURE__ */ jsx(Reveal, { index: 0, className: "adm-span-3", children: /* @__PURE__ */ jsx(
|
|
44
|
+
StatCard,
|
|
45
|
+
{
|
|
46
|
+
label: "Platform fees earned",
|
|
47
|
+
value: /* @__PURE__ */ jsx(
|
|
48
|
+
MoneyLines,
|
|
49
|
+
{
|
|
50
|
+
rows: platformFees,
|
|
51
|
+
currency: "",
|
|
52
|
+
amount: (r) => r.amount,
|
|
53
|
+
decimals: 2
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
hint: "What Olive keeps from payouts, settlements and subscriptions."
|
|
57
|
+
}
|
|
58
|
+
) }) }),
|
|
40
59
|
/* @__PURE__ */ jsx("div", { className: "adm-grid", children: /* @__PURE__ */ jsx("div", { className: "adm-span-12", children: /* @__PURE__ */ jsx(
|
|
41
60
|
Panel,
|
|
42
61
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/revenue/revenue-overview.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { TimeSeriesChart } from '@withpotter/components/charts';\nimport type { PlatformCockpit, PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { PICK_CURRENCY, Panel, moneyIn, rowFor, toTrend } from '../_ui';\nimport { Tinted } from '../audit/tinted';\nimport { HealthPanel } from './health-panel';\nimport { PlanMixPanel } from './plan-mix-panel';\nimport { RevenueKpis } from './revenue-kpis';\nimport { SubscriptionsPanel } from './subscriptions-panel';\nimport { TopTenantsPanel } from './top-tenants-panel';\nimport styles from './revenue.module.css';\n\n/**\n * Overview tab: the four headline figures, then the GMV trend as the one\n * full-width primary chart, then revenue health, plan mix, top merchants\n * and subscriptions beneath it. Merchant GMV is per currency: the chart and\n * the leaderboard follow one currency (`focus`), never a mix.\n */\nexport function RevenueOverview({\n data,\n health,\n money,\n money2,\n currency,\n focus,\n period,\n span,\n recurringCaption,\n}: {\n data: PlatformRevenue;\n health: PlatformCockpit['health'] | undefined;\n /** Formats recurring revenue, in the currency the API names for it. */\n money: (value: number) => string;\n money2: (value: number) => string;\n /** The currency filter's choice; empty shows a line per currency. */\n currency: string;\n /** The one currency the chart and leaderboard show, or null. */\n focus: string | null;\n period: string;\n span: string;\n recurringCaption: string;\n}) {\n const gmv = rowFor(data.gmvByCurrency, focus);\n const gmvSeries = useMemo(() => toTrend(gmv?.series, 'gmv'), [gmv]);\n const gmvCaption = focus ? `${period} · ${focus}` : period;\n const recurringTracked = data.recurring.tracked !== false;\n\n return (\n <>\n <RevenueKpis\n data={data}\n money={money}\n money2={money2}\n currency={currency}\n focus={focus}\n gmvSpark={gmvSeries.map((p) => p.value)}\n />\n\n <div className=\"adm-grid\">\n <div className=\"adm-span-12\">\n <Panel\n title=\"GMV trend\"\n caption={gmvCaption}\n height=\"calc(var(--adm-panel-h) + 32px)\"\n >\n {focus ? (\n <div className={styles.flatChart}>\n <Tinted tone=\"money\">\n <TimeSeriesChart\n data={gmvSeries}\n compare={null}\n compareLabel={`Previous ${span}`}\n formatValue={moneyIn(focus)}\n emptyMessage={`No paid ${focus} orders in this window yet`}\n height={240}\n />\n </Tinted>\n </div>\n ) : (\n <p className={styles.empty}>{PICK_CURRENCY}</p>\n )}\n </Panel>\n </div>\n </div>\n\n {recurringTracked && (\n <div className=\"adm-grid\">\n <div className=\"adm-span-7\">\n <HealthPanel\n health={health}\n money={money}\n caption={recurringCaption}\n />\n </div>\n <div className=\"adm-span-5\">\n <PlanMixPanel\n planBreakdown={data.recurring.planBreakdown}\n money={money}\n caption={recurringCaption}\n />\n </div>\n </div>\n )}\n\n <div className=\"adm-grid\">\n <div className={recurringTracked ? 'adm-span-8' : 'adm-span-12'}>\n <TopTenantsPanel\n tenants={gmv?.topTenants ?? []}\n lifetime={gmv?.lifetime ?? 0}\n focus={focus}\n />\n </div>\n {recurringTracked && (\n <div className=\"adm-span-4\">\n <SubscriptionsPanel recurring={data.recurring} span={span} />\n </div>\n )}\n </div>\n </>\n );\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/revenue/revenue-overview.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { TimeSeriesChart } from '@withpotter/components/charts';\nimport type { PlatformCockpit, PlatformRevenue } from '@withpotter/lib/admin-api';\nimport { MoneyLines, PICK_CURRENCY, Panel, StatCard, moneyIn, rowFor, toTrend } from '../_ui';\nimport { Reveal } from '../_fx';\nimport { Tinted } from '../audit/tinted';\nimport { HealthPanel } from './health-panel';\nimport { PlanMixPanel } from './plan-mix-panel';\nimport { platformFeeRows } from './platform-fees';\nimport { RevenueKpis } from './revenue-kpis';\nimport { SubscriptionsPanel } from './subscriptions-panel';\nimport { TopTenantsPanel } from './top-tenants-panel';\nimport styles from './revenue.module.css';\n\n/**\n * Overview tab: the four headline figures, then the GMV trend as the one\n * full-width primary chart, then revenue health, plan mix, top merchants\n * and subscriptions beneath it. Merchant GMV is per currency: the chart and\n * the leaderboard follow one currency (`focus`), never a mix.\n */\nexport function RevenueOverview({\n data,\n health,\n money,\n money2,\n currency,\n focus,\n period,\n span,\n recurringCaption,\n}: {\n data: PlatformRevenue;\n health: PlatformCockpit['health'] | undefined;\n /** Formats recurring revenue, in the currency the API names for it. */\n money: (value: number) => string;\n money2: (value: number) => string;\n /** The currency filter's choice; empty shows a line per currency. */\n currency: string;\n /** The one currency the chart and leaderboard show, or null. */\n focus: string | null;\n period: string;\n span: string;\n recurringCaption: string;\n}) {\n const gmv = rowFor(data.gmvByCurrency, focus);\n const gmvSeries = useMemo(() => toTrend(gmv?.series, 'gmv'), [gmv]);\n const gmvCaption = focus ? `${period} · ${focus}` : period;\n const recurringTracked = data.recurring.tracked !== false;\n // Hidden until the api sends the field; never an invented zero.\n const platformFees = platformFeeRows(data.platformFeesEarnedByCurrency, currency);\n\n return (\n <>\n <RevenueKpis\n data={data}\n money={money}\n money2={money2}\n currency={currency}\n focus={focus}\n gmvSpark={gmvSeries.map((p) => p.value)}\n />\n\n {platformFees && (\n <section aria-label=\"Platform fees\" className=\"adm-grid\">\n <Reveal index={0} className=\"adm-span-3\">\n <StatCard\n label=\"Platform fees earned\"\n value={\n <MoneyLines\n rows={platformFees}\n currency=\"\"\n amount={(r) => r.amount}\n decimals={2}\n />\n }\n hint=\"What Olive keeps from payouts, settlements and subscriptions.\"\n />\n </Reveal>\n </section>\n )}\n\n <div className=\"adm-grid\">\n <div className=\"adm-span-12\">\n <Panel\n title=\"GMV trend\"\n caption={gmvCaption}\n height=\"calc(var(--adm-panel-h) + 32px)\"\n >\n {focus ? (\n <div className={styles.flatChart}>\n <Tinted tone=\"money\">\n <TimeSeriesChart\n data={gmvSeries}\n compare={null}\n compareLabel={`Previous ${span}`}\n formatValue={moneyIn(focus)}\n emptyMessage={`No paid ${focus} orders in this window yet`}\n height={240}\n />\n </Tinted>\n </div>\n ) : (\n <p className={styles.empty}>{PICK_CURRENCY}</p>\n )}\n </Panel>\n </div>\n </div>\n\n {recurringTracked && (\n <div className=\"adm-grid\">\n <div className=\"adm-span-7\">\n <HealthPanel\n health={health}\n money={money}\n caption={recurringCaption}\n />\n </div>\n <div className=\"adm-span-5\">\n <PlanMixPanel\n planBreakdown={data.recurring.planBreakdown}\n money={money}\n caption={recurringCaption}\n />\n </div>\n </div>\n )}\n\n <div className=\"adm-grid\">\n <div className={recurringTracked ? 'adm-span-8' : 'adm-span-12'}>\n <TopTenantsPanel\n tenants={gmv?.topTenants ?? []}\n lifetime={gmv?.lifetime ?? 0}\n focus={focus}\n />\n </div>\n {recurringTracked && (\n <div className=\"adm-span-4\">\n <SubscriptionsPanel recurring={data.recurring} span={span} />\n </div>\n )}\n </div>\n </>\n );\n}\n"],"mappings":";AAsDI,mBACE,KAwDE,YAzDJ;AApDJ,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAEhC,SAAS,YAAY,eAAe,OAAO,UAAU,SAAS,QAAQ,eAAe;AACrF,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,OAAO,YAAY;AAQZ,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAaG;AACD,QAAM,MAAM,OAAO,KAAK,eAAe,KAAK;AAC5C,QAAM,YAAY,QAAQ,MAAM,QAAQ,KAAK,QAAQ,KAAK,GAAG,CAAC,GAAG,CAAC;AAClE,QAAM,aAAa,QAAQ,GAAG,MAAM,SAAM,KAAK,KAAK;AACpD,QAAM,mBAAmB,KAAK,UAAU,YAAY;AAEpD,QAAM,eAAe,gBAAgB,KAAK,8BAA8B,QAAQ;AAEhF,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA;AAAA,IACxC;AAAA,IAEC,gBACC,oBAAC,aAAQ,cAAW,iBAAgB,WAAU,YAC5C,8BAAC,UAAO,OAAO,GAAG,WAAU,cAC1B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OACE;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,UAAS;AAAA,YACT,QAAQ,CAAC,MAAM,EAAE;AAAA,YACjB,UAAU;AAAA;AAAA,QACZ;AAAA,QAEF,MAAK;AAAA;AAAA,IACP,GACF,GACF;AAAA,IAGF,oBAAC,SAAI,WAAU,YACb,8BAAC,SAAI,WAAU,eACb;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,SAAS;AAAA,QACT,QAAO;AAAA,QAEN,kBACC,oBAAC,SAAI,WAAW,OAAO,WACrB,8BAAC,UAAO,MAAK,SACX;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,SAAS;AAAA,YACT,cAAc,YAAY,IAAI;AAAA,YAC9B,aAAa,QAAQ,KAAK;AAAA,YAC1B,cAAc,WAAW,KAAK;AAAA,YAC9B,QAAQ;AAAA;AAAA,QACV,GACF,GACF,IAEA,oBAAC,OAAE,WAAW,OAAO,OAAQ,yBAAc;AAAA;AAAA,IAE/C,GACF,GACF;AAAA,IAEC,oBACC,qBAAC,SAAI,WAAU,YACb;AAAA,0BAAC,SAAI,WAAU,cACb;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,SAAS;AAAA;AAAA,MACX,GACF;AAAA,MACA,oBAAC,SAAI,WAAU,cACb;AAAA,QAAC;AAAA;AAAA,UACC,eAAe,KAAK,UAAU;AAAA,UAC9B;AAAA,UACA,SAAS;AAAA;AAAA,MACX,GACF;AAAA,OACF;AAAA,IAGF,qBAAC,SAAI,WAAU,YACb;AAAA,0BAAC,SAAI,WAAW,mBAAmB,eAAe,eAChD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,KAAK,cAAc,CAAC;AAAA,UAC7B,UAAU,KAAK,YAAY;AAAA,UAC3B;AAAA;AAAA,MACF,GACF;AAAA,MACC,oBACC,oBAAC,SAAI,WAAU,cACb,8BAAC,sBAAmB,WAAW,KAAK,WAAW,MAAY,GAC7D;AAAA,OAEJ;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -39,7 +39,7 @@ function RecentOrders({
|
|
|
39
39
|
/* @__PURE__ */ jsx(Th, { children: "Customer" }),
|
|
40
40
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Total" }),
|
|
41
41
|
/* @__PURE__ */ jsx(Th, { children: "Payment" }),
|
|
42
|
-
/* @__PURE__ */ jsx(Th, { children: "
|
|
42
|
+
/* @__PURE__ */ jsx(Th, { children: "Fulfilment" }),
|
|
43
43
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Placed" })
|
|
44
44
|
] }),
|
|
45
45
|
children: loading ? Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx("tr", { "aria-hidden": true, children: /* @__PURE__ */ jsx("td", { colSpan: 6, children: /* @__PURE__ */ jsx(Skeleton, { height: 20 }) }) }, i)) : orders.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: styles.emptyCell, children: "No orders yet. They show here as soon as a customer checks out." }) }) : orders.map((o) => /* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/recent-orders.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport type {\n DashboardStats,\n TenantActivityOrder,\n Transaction,\n} from '@withpotter/lib/admin-api';\nimport { TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { Panel, Skeleton } from '../../../_ui';\nimport { moneyIn, shortDate } from './format';\nimport { OrderDrawer } from './order-drawer';\nimport { OrderPipeline } from './order-pipeline';\nimport { StatusPill } from './status-pill';\nimport styles from './orders.module.css';\n\n/** The order pipeline over the latest orders; a row opens the order. */\nexport function RecentOrders({\n orders,\n stats,\n transactions,\n loading,\n statsLoading,\n currency,\n}: {\n orders: TenantActivityOrder[];\n stats: DashboardStats['orderStats'] | undefined;\n transactions: Transaction[];\n loading: boolean;\n statsLoading: boolean;\n currency: string;\n}) {\n const [open, setOpen] = useState<TenantActivityOrder | null>(null);\n\n return (\n <>\n <Panel\n title=\"Orders\"\n count={stats?.total}\n caption={stats ? `${stats.today.toLocaleString()} today` : undefined}\n height=\"calc(var(--adm-panel-h) + 120px)\"\n flush\n >\n <OrderPipeline stats={stats} loading={statsLoading} />\n <TableShell\n bare\n minWidth={640}\n density=\"compact\"\n head={\n <tr>\n <Th>Order</Th>\n <Th>Customer</Th>\n <Th align=\"right\">Total</Th>\n <Th>Payment</Th>\n <Th>
|
|
1
|
+
{"version":3,"sources":["../../../../../src/admin/tenants/%5Bid%5D/commerce/recent-orders.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport type {\n DashboardStats,\n TenantActivityOrder,\n Transaction,\n} from '@withpotter/lib/admin-api';\nimport { TableRow, TableShell, Th } from '@withpotter/ui-kit';\nimport { Panel, Skeleton } from '../../../_ui';\nimport { moneyIn, shortDate } from './format';\nimport { OrderDrawer } from './order-drawer';\nimport { OrderPipeline } from './order-pipeline';\nimport { StatusPill } from './status-pill';\nimport styles from './orders.module.css';\n\n/** The order pipeline over the latest orders; a row opens the order. */\nexport function RecentOrders({\n orders,\n stats,\n transactions,\n loading,\n statsLoading,\n currency,\n}: {\n orders: TenantActivityOrder[];\n stats: DashboardStats['orderStats'] | undefined;\n transactions: Transaction[];\n loading: boolean;\n statsLoading: boolean;\n currency: string;\n}) {\n const [open, setOpen] = useState<TenantActivityOrder | null>(null);\n\n return (\n <>\n <Panel\n title=\"Orders\"\n count={stats?.total}\n caption={stats ? `${stats.today.toLocaleString()} today` : undefined}\n height=\"calc(var(--adm-panel-h) + 120px)\"\n flush\n >\n <OrderPipeline stats={stats} loading={statsLoading} />\n <TableShell\n bare\n minWidth={640}\n density=\"compact\"\n head={\n <tr>\n <Th>Order</Th>\n <Th>Customer</Th>\n <Th align=\"right\">Total</Th>\n <Th>Payment</Th>\n <Th>Fulfilment</Th>\n <Th align=\"right\">Placed</Th>\n </tr>\n }\n >\n {loading ? (\n Array.from({ length: 5 }, (_, i) => (\n <tr key={i} aria-hidden>\n <td colSpan={6}>\n <Skeleton height={20} />\n </td>\n </tr>\n ))\n ) : orders.length === 0 ? (\n <tr>\n <td colSpan={6} className={styles.emptyCell}>\n No orders yet. They show here as soon as a customer checks out.\n </td>\n </tr>\n ) : (\n orders.map((o) => (\n <TableRow\n key={o.id}\n active={open?.id === o.id}\n onClick={() => setOpen(o)}\n >\n <td className={styles.mono} title={o.orderNumber}>\n {/* The row opens on click; this is its keyboard way in. */}\n <button\n type=\"button\"\n className={styles.orderLink}\n onClick={(e) => {\n e.stopPropagation();\n setOpen(o);\n }}\n >\n {o.orderNumber}\n </button>\n </td>\n <td>\n <span\n className={styles.truncate}\n title={o.customerName || o.customerEmail}\n >\n {o.customerName || o.customerEmail}\n </span>\n </td>\n <td className={styles.num}>\n {moneyIn(o.currency || currency, 2)(o.total)}\n </td>\n <td>\n <StatusPill status={o.paymentStatus} />\n </td>\n <td>\n <StatusPill status={o.status} />\n </td>\n <td className={styles.num}>{shortDate(o.createdAt)}</td>\n </TableRow>\n ))\n )}\n </TableShell>\n </Panel>\n <OrderDrawer\n order={open}\n transactions={transactions}\n currency={currency}\n onClose={() => setOpen(null)}\n />\n </>\n );\n}\n"],"mappings":";AAmCI,mBAQI,KAMI,YAdR;AAjCJ,SAAS,gBAAgB;AAMzB,SAAS,UAAU,YAAY,UAAU;AACzC,SAAS,OAAO,gBAAgB;AAChC,SAAS,SAAS,iBAAiB;AACnC,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AAGZ,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAqC,IAAI;AAEjE,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,OAAO;AAAA,QACd,SAAS,QAAQ,GAAG,MAAM,MAAM,eAAe,CAAC,WAAW;AAAA,QAC3D,QAAO;AAAA,QACP,OAAK;AAAA,QAEL;AAAA,8BAAC,iBAAc,OAAc,SAAS,cAAc;AAAA,UACpD;AAAA,YAAC;AAAA;AAAA,cACC,MAAI;AAAA,cACJ,UAAU;AAAA,cACV,SAAQ;AAAA,cACR,MACE,qBAAC,QACC;AAAA,oCAAC,MAAG,mBAAK;AAAA,gBACT,oBAAC,MAAG,sBAAQ;AAAA,gBACZ,oBAAC,MAAG,OAAM,SAAQ,mBAAK;AAAA,gBACvB,oBAAC,MAAG,qBAAO;AAAA,gBACX,oBAAC,MAAG,wBAAU;AAAA,gBACd,oBAAC,MAAG,OAAM,SAAQ,oBAAM;AAAA,iBAC1B;AAAA,cAGD,oBACC,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAC5B,oBAAC,QAAW,eAAW,MACrB,8BAAC,QAAG,SAAS,GACX,8BAAC,YAAS,QAAQ,IAAI,GACxB,KAHO,CAIT,CACD,IACC,OAAO,WAAW,IACpB,oBAAC,QACC,8BAAC,QAAG,SAAS,GAAG,WAAW,OAAO,WAAW,6EAE7C,GACF,IAEA,OAAO,IAAI,CAAC,MACV;AAAA,gBAAC;AAAA;AAAA,kBAEC,QAAQ,MAAM,OAAO,EAAE;AAAA,kBACvB,SAAS,MAAM,QAAQ,CAAC;AAAA,kBAExB;AAAA,wCAAC,QAAG,WAAW,OAAO,MAAM,OAAO,EAAE,aAEnC;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAK;AAAA,wBACL,WAAW,OAAO;AAAA,wBAClB,SAAS,CAAC,MAAM;AACd,4BAAE,gBAAgB;AAClB,kCAAQ,CAAC;AAAA,wBACX;AAAA,wBAEC,YAAE;AAAA;AAAA,oBACL,GACF;AAAA,oBACA,oBAAC,QACC;AAAA,sBAAC;AAAA;AAAA,wBACC,WAAW,OAAO;AAAA,wBAClB,OAAO,EAAE,gBAAgB,EAAE;AAAA,wBAE1B,YAAE,gBAAgB,EAAE;AAAA;AAAA,oBACvB,GACF;AAAA,oBACA,oBAAC,QAAG,WAAW,OAAO,KACnB,kBAAQ,EAAE,YAAY,UAAU,CAAC,EAAE,EAAE,KAAK,GAC7C;AAAA,oBACA,oBAAC,QACC,8BAAC,cAAW,QAAQ,EAAE,eAAe,GACvC;AAAA,oBACA,oBAAC,QACC,8BAAC,cAAW,QAAQ,EAAE,QAAQ,GAChC;AAAA,oBACA,oBAAC,QAAG,WAAW,OAAO,KAAM,oBAAU,EAAE,SAAS,GAAE;AAAA;AAAA;AAAA,gBAlC9C,EAAE;AAAA,cAmCT,CACD;AAAA;AAAA,UAEL;AAAA;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA,SAAS,MAAM,QAAQ,IAAI;AAAA;AAAA,IAC7B;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -14,6 +14,7 @@ import { moduleOn } from "../../_shell/nav-modules";
|
|
|
14
14
|
import { TenantOverview } from "./tenant-overview";
|
|
15
15
|
import { TenantPeoplePanel } from "./tenant-people-panel";
|
|
16
16
|
import { TenantWalletCard } from "./tenant-wallet-card";
|
|
17
|
+
import { useTenantWallet } from "./use-tenant-wallet";
|
|
17
18
|
import { LedgerCurrencyFilter, MerchantLedger } from "./merchant-ledger";
|
|
18
19
|
import { TenantSupport } from "./tenant-support";
|
|
19
20
|
import { TenantVerification } from "./verification/tenant-verification";
|
|
@@ -153,6 +154,11 @@ function TenantDetailPage({
|
|
|
153
154
|
startDate: range.startDate,
|
|
154
155
|
endDate: range.endDate
|
|
155
156
|
});
|
|
157
|
+
const { wallet, walletNote } = useTenantWallet(
|
|
158
|
+
id,
|
|
159
|
+
walletLedger,
|
|
160
|
+
data?.wallet
|
|
161
|
+
);
|
|
156
162
|
const queryClient = useQueryClient();
|
|
157
163
|
const statusMutation = useMutation({
|
|
158
164
|
mutationFn: (status) => platformAdminApi.setTenantStatus(id, status),
|
|
@@ -169,6 +175,7 @@ function TenantDetailPage({
|
|
|
169
175
|
return /* @__PURE__ */ jsx("p", { style: { color: "var(--error)" }, children: "Failed to load tenant." });
|
|
170
176
|
}
|
|
171
177
|
if (isLoading || !data) return /* @__PURE__ */ jsx(AdmLoading, { label: "Loading tenant\u2026" });
|
|
178
|
+
const view = { ...data, wallet };
|
|
172
179
|
const currency = data.currency ?? "USD";
|
|
173
180
|
const currencyControl = /* @__PURE__ */ jsx(
|
|
174
181
|
CurrencyFilter,
|
|
@@ -177,7 +184,7 @@ function TenantDetailPage({
|
|
|
177
184
|
onChange: setShown,
|
|
178
185
|
currencies: currenciesIn(
|
|
179
186
|
data.currencies,
|
|
180
|
-
|
|
187
|
+
view.wallet?.balances.map((b) => b.currency),
|
|
181
188
|
data.recentOrders.map((o) => o.currency)
|
|
182
189
|
)
|
|
183
190
|
}
|
|
@@ -208,7 +215,8 @@ function TenantDetailPage({
|
|
|
208
215
|
tab === "overview" && /* @__PURE__ */ jsx(
|
|
209
216
|
TenantOverview,
|
|
210
217
|
{
|
|
211
|
-
data,
|
|
218
|
+
data: view,
|
|
219
|
+
walletNote,
|
|
212
220
|
periodLabel: periodPhrase(range),
|
|
213
221
|
periodControl,
|
|
214
222
|
recentOrders: /* @__PURE__ */ jsx(
|
|
@@ -265,7 +273,14 @@ function TenantDetailPage({
|
|
|
265
273
|
]
|
|
266
274
|
}
|
|
267
275
|
),
|
|
268
|
-
group === "wallet" && current !== "money" && /* @__PURE__ */ jsx(
|
|
276
|
+
group === "wallet" && current !== "money" && /* @__PURE__ */ jsx(
|
|
277
|
+
TenantWalletCard,
|
|
278
|
+
{
|
|
279
|
+
data: view,
|
|
280
|
+
currency: shown,
|
|
281
|
+
walletNote
|
|
282
|
+
}
|
|
283
|
+
),
|
|
269
284
|
current === "money" && /* @__PURE__ */ jsx(
|
|
270
285
|
MerchantLedger,
|
|
271
286
|
{
|