@withpotter/pages-admin 0.13.0-next.82 → 0.13.0-next.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/_money/finder-format.js +1 -0
- package/dist/admin/_money/finder-format.js.map +1 -1
- package/dist/admin/_money/format.js +1 -0
- package/dist/admin/_money/format.js.map +1 -1
- package/dist/admin/_overview/attention.module.css +0 -3
- package/dist/admin/_overview/overdue-outstanding.js +2 -1
- package/dist/admin/_overview/overdue-outstanding.js.map +1 -1
- package/dist/admin/_ui/list-card.module.css +2 -16
- package/dist/admin/_ui/money-view.js +1 -0
- package/dist/admin/_ui/money-view.js.map +1 -1
- package/dist/admin/_ui/page-header.js +31 -4
- package/dist/admin/_ui/page-header.js.map +1 -1
- package/dist/admin/_ui/page-header.module.css +45 -0
- package/dist/admin/_ui/panel.module.css +0 -10
- package/dist/admin/commerce/commerce-overview.js +2 -2
- package/dist/admin/commerce/commerce-overview.js.map +1 -1
- package/dist/admin/commerce/merchant-meta.js +1 -0
- package/dist/admin/commerce/merchant-meta.js.map +1 -1
- package/dist/admin/commerce/merchant-view.js +2 -2
- package/dist/admin/commerce/merchant-view.js.map +1 -1
- package/dist/admin/feedback/prompt-operations.module.css +0 -2
- package/dist/admin/feedback/prompt-preview.module.css +0 -2
- package/dist/admin/revenue/platform-fees.js +9 -0
- package/dist/admin/revenue/platform-fees.js.map +1 -0
- package/dist/admin/revenue/revenue-kpis.js +1 -1
- package/dist/admin/revenue/revenue-kpis.js.map +1 -1
- package/dist/admin/revenue/revenue-overview.js +20 -1
- package/dist/admin/revenue/revenue-overview.js.map +1 -1
- package/dist/admin/revenue/revenue-workspace.js +1 -1
- package/dist/admin/revenue/revenue-workspace.js.map +1 -1
- package/dist/admin/support/queue-card.module.css +1 -4
- package/dist/admin/tenants/[id]/commerce/format.js +2 -1
- package/dist/admin/tenants/[id]/commerce/format.js.map +1 -1
- package/dist/admin/tenants/[id]/page.js +31 -9
- package/dist/admin/tenants/[id]/page.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-layout.module.css +1 -4
- 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-overview.module.css +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/dist/admin/tenants/tenants-table.js +1 -1
- package/dist/admin/tenants/tenants-table.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* Identity + tabs, scrolling with the page. */
|
|
2
2
|
.header {
|
|
3
|
-
position: sticky;
|
|
4
|
-
top: var(--adm-topbar-h);
|
|
5
|
-
z-index: 15;
|
|
6
3
|
margin: -32px -40px 20px;
|
|
7
4
|
padding: 16px 40px 0;
|
|
8
5
|
border-bottom: 1px solid var(--border-subtle);
|
|
@@ -46,7 +46,8 @@ function TenantOverview({
|
|
|
46
46
|
recentOrders,
|
|
47
47
|
onTab,
|
|
48
48
|
currency = "",
|
|
49
|
-
currencyControl
|
|
49
|
+
currencyControl,
|
|
50
|
+
walletNote
|
|
50
51
|
}) {
|
|
51
52
|
const balances = byCurrency(
|
|
52
53
|
data.wallet?.balances ?? [],
|
|
@@ -77,7 +78,7 @@ function TenantOverview({
|
|
|
77
78
|
StatCard,
|
|
78
79
|
{
|
|
79
80
|
label: "Wallet available",
|
|
80
|
-
value: balances.length ? /* @__PURE__ */ jsx("span", { className: styles.moneyLines, children: balances.map((b) => /* @__PURE__ */ jsx("span", { children: formatMinor(b.availableMinor, b.currency) }, b.currency)) }) : data.wallet?.balances.length ? `No ${currency} balance` : "No wallet",
|
|
81
|
+
value: balances.length ? /* @__PURE__ */ jsx("span", { className: styles.moneyLines, children: balances.map((b) => /* @__PURE__ */ jsx("span", { children: formatMinor(b.availableMinor, b.currency) }, b.currency)) }) : data.wallet?.balances.length ? `No ${currency} balance` : walletNote ? walletNote : "No wallet",
|
|
81
82
|
sub: balances.length ? `Pending ${balances.map((b) => formatMinor(b.pendingMinor, b.currency)).join(" \xB7 ")}` : void 0,
|
|
82
83
|
hint: "Money in the merchant's Potter wallet that can be paid out now."
|
|
83
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-overview.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { Badge } from '@withpotter/ui-kit';\nimport type { TenantDetail } from '@withpotter/lib/admin-api';\nimport { formatMinor } from '@withpotter/lib/wallet-format';\nimport {\n MoneyLines,\n PICK_CURRENCY,\n StatCard,\n byCurrency,\n focusCurrency,\n moneyLines,\n noneText,\n rowFor,\n} from '../../_ui';\nimport { GrowthChart } from '../../growth-chart';\nimport { ActivityFeed } from './tenant-activity-feed';\nimport { JUMP, TenantAttention } from './tenant-attention';\nimport { TenantSidebar } from './tenant-sidebar';\nimport type { TenantTab } from './tenant-header';\nimport styles from './tenant-overview.module.css';\n\nfunction SetupCard({ data }: { data: TenantDetail }) {\n const onboarding = data.onboarding;\n\n if (!onboarding || onboarding.percent >= 100) return null;\n\n // Unfinished steps first: that's what the admin can help with.\n const steps = [...onboarding.steps].sort(\n (a, b) =>\n Number(a.status === 'completed') - Number(b.status === 'completed'),\n );\n\n return (\n <section id={JUMP.setup} className={`adm-panel ${styles.jumpTarget}`}>\n <div className={styles.cardHead}>\n <h2 className={styles.title}>Setup · {onboarding.percent}% done</h2>\n {onboarding.dismissed && (\n <Badge variant=\"secondary\">checklist dismissed</Badge>\n )}\n </div>\n <div className={styles.progress} aria-hidden>\n <span style={{ width: `${onboarding.percent}%` }} />\n </div>\n <ul className={styles.steps}>\n {steps.map((s) => (\n <li key={s.key} data-done={s.status === 'completed'}>\n {s.label}\n <span>{s.status.replaceAll('_', ' ')}</span>\n </li>\n ))}\n </ul>\n </section>\n );\n}\n\n/** The first tab: health and money first, facts on the side. */\nexport function TenantOverview({\n data,\n periodLabel,\n periodControl,\n recentOrders,\n onTab,\n currency = '',\n currencyControl,\n}: {\n data: TenantDetail;\n periodLabel: string;\n periodControl: ReactNode;\n recentOrders: ReactNode;\n onTab: (tab: TenantTab) => void;\n /** The currency filter's choice; narrows the wallet and recent orders. */\n currency?: string;\n currencyControl?: ReactNode;\n}) {\n // Every currency the wallet holds, one line each; it showed only the first.\n const balances = byCurrency(\n data.wallet?.balances ?? [],\n currency,\n (b) => b.currency,\n );\n const period = data.metrics.window;\n // Sales come back once per currency; the chart draws one of them.\n const sales = data.metrics.byCurrency;\n const lifetime = moneyLines(sales, currency, (r) => r.gmv);\n const focus = focusCurrency(data.currencies ?? [], currency);\n const series = rowFor(data.revenueSeriesByCurrency, focus)?.series;\n\n return (\n <>\n <TenantAttention data={data} onTab={onTab} />\n <div className={styles.layout}>\n <div className={styles.main}>\n <div className={styles.period}>\n <span>Numbers for {periodLabel}</span>\n <div className=\"adm-toolbar\">\n {currencyControl}\n {periodControl}\n </div>\n </div>\n <div className={styles.kpis}>\n <StatCard\n label=\"Wallet available\"\n value={\n balances.length ? (\n <span className={styles.moneyLines}>\n {balances.map((b) => (\n <span key={b.currency}>\n {formatMinor(b.availableMinor, b.currency)}\n </span>\n ))}\n </span>\n ) : data.wallet?.balances.length ? (\n `No ${currency} balance`\n ) : (\n 'No wallet'\n )\n }\n sub={\n balances.length\n ? `Pending ${balances\n .map((b) => formatMinor(b.pendingMinor, b.currency))\n .join(' · ')}`\n : undefined\n }\n hint=\"Money in the merchant's Potter wallet that can be paid out now.\"\n />\n <StatCard\n label=\"Sales\"\n value={\n <MoneyLines\n rows={sales}\n currency={currency}\n amount={(r) => r.window.gmv}\n />\n }\n sub={`Lifetime ${lifetime.length ? lifetime.join(' · ') : noneText(currency)}`}\n hint=\"Gross sales in the selected period; lifetime underneath.\"\n />\n <StatCard\n label=\"Orders\"\n value={String(period.orders)}\n sub={`${period.paidOrders} paid`}\n hint=\"Orders placed in the selected period.\"\n />\n <StatCard\n label=\"Customers\"\n value={String(period.customers)}\n sub={`${data.metrics.totalCustomers} all time`}\n hint=\"Distinct customers who ordered in the selected period.\"\n />\n <StatCard\n label=\"Avg order\"\n value={\n <MoneyLines\n rows={sales}\n currency={currency}\n amount={(r) => r.window.aov}\n />\n }\n sub={`Subscription: ${data.metrics.subscriptionStatus ?? 'none'}`}\n hint=\"Average order value in the selected period, and the plan status.\"\n />\n </div>\n <section className=\"adm-panel\">\n <h2 className={styles.title}>\n Revenue · last 30 days{focus ? ` · ${focus}` : ''}\n </h2>\n {focus || !data.currencies?.length ? (\n <GrowthChart\n data={(series ?? []).map((p) => ({\n date: p.date,\n count: p.amount,\n }))}\n emptyLabel=\"No revenue in the last 30 days\"\n />\n ) : (\n <p className=\"text-sm text-[var(--text-secondary)]\">\n {PICK_CURRENCY}\n </p>\n )}\n </section>\n <SetupCard data={data} />\n <section className=\"adm-panel\">\n <h2 className={styles.title}>Recent activity</h2>\n <ActivityFeed\n entries={data.activity ?? []}\n onViewAll={() => onTab('audit')}\n />\n </section>\n <section>\n <h2 className={styles.title}>Recent orders</h2>\n {recentOrders}\n </section>\n </div>\n <TenantSidebar\n data={data}\n onTeam={() =>\n document\n .getElementById('tenant-team')\n ?.scrollIntoView({ behavior: 'smooth', block: 'start' })\n }\n />\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-overview.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { Badge } from '@withpotter/ui-kit';\nimport type { TenantDetail } from '@withpotter/lib/admin-api';\nimport { formatMinor } from '@withpotter/lib/wallet-format';\nimport {\n MoneyLines,\n PICK_CURRENCY,\n StatCard,\n byCurrency,\n focusCurrency,\n moneyLines,\n noneText,\n rowFor,\n} from '../../_ui';\nimport { GrowthChart } from '../../growth-chart';\nimport { ActivityFeed } from './tenant-activity-feed';\nimport { JUMP, TenantAttention } from './tenant-attention';\nimport { TenantSidebar } from './tenant-sidebar';\nimport type { TenantTab } from './tenant-header';\nimport styles from './tenant-overview.module.css';\n\nfunction SetupCard({ data }: { data: TenantDetail }) {\n const onboarding = data.onboarding;\n\n if (!onboarding || onboarding.percent >= 100) return null;\n\n // Unfinished steps first: that's what the admin can help with.\n const steps = [...onboarding.steps].sort(\n (a, b) =>\n Number(a.status === 'completed') - Number(b.status === 'completed'),\n );\n\n return (\n <section id={JUMP.setup} className={`adm-panel ${styles.jumpTarget}`}>\n <div className={styles.cardHead}>\n <h2 className={styles.title}>Setup · {onboarding.percent}% done</h2>\n {onboarding.dismissed && (\n <Badge variant=\"secondary\">checklist dismissed</Badge>\n )}\n </div>\n <div className={styles.progress} aria-hidden>\n <span style={{ width: `${onboarding.percent}%` }} />\n </div>\n <ul className={styles.steps}>\n {steps.map((s) => (\n <li key={s.key} data-done={s.status === 'completed'}>\n {s.label}\n <span>{s.status.replaceAll('_', ' ')}</span>\n </li>\n ))}\n </ul>\n </section>\n );\n}\n\n/** The first tab: health and money first, facts on the side. */\nexport function TenantOverview({\n data,\n periodLabel,\n periodControl,\n recentOrders,\n onTab,\n currency = '',\n currencyControl,\n walletNote,\n}: {\n data: TenantDetail;\n periodLabel: string;\n periodControl: ReactNode;\n recentOrders: ReactNode;\n onTab: (tab: TenantTab) => void;\n /** The currency filter's choice; narrows the wallet and recent orders. */\n currency?: string;\n currencyControl?: ReactNode;\n /** Said in place of \"No wallet\" while the ledger loads or cannot be read. */\n walletNote?: string;\n}) {\n // Every currency the wallet holds, one line each; it showed only the first.\n const balances = byCurrency(\n data.wallet?.balances ?? [],\n currency,\n (b) => b.currency,\n );\n const period = data.metrics.window;\n // Sales come back once per currency; the chart draws one of them.\n const sales = data.metrics.byCurrency;\n const lifetime = moneyLines(sales, currency, (r) => r.gmv);\n const focus = focusCurrency(data.currencies ?? [], currency);\n const series = rowFor(data.revenueSeriesByCurrency, focus)?.series;\n\n return (\n <>\n <TenantAttention data={data} onTab={onTab} />\n <div className={styles.layout}>\n <div className={styles.main}>\n <div className={styles.period}>\n <span>Numbers for {periodLabel}</span>\n <div className=\"adm-toolbar\">\n {currencyControl}\n {periodControl}\n </div>\n </div>\n <div className={styles.kpis}>\n <StatCard\n label=\"Wallet available\"\n value={\n balances.length ? (\n <span className={styles.moneyLines}>\n {balances.map((b) => (\n <span key={b.currency}>\n {formatMinor(b.availableMinor, b.currency)}\n </span>\n ))}\n </span>\n ) : data.wallet?.balances.length ? (\n `No ${currency} balance`\n ) : walletNote ? (\n walletNote\n ) : (\n 'No wallet'\n )\n }\n sub={\n balances.length\n ? `Pending ${balances\n .map((b) => formatMinor(b.pendingMinor, b.currency))\n .join(' · ')}`\n : undefined\n }\n hint=\"Money in the merchant's Potter wallet that can be paid out now.\"\n />\n <StatCard\n label=\"Sales\"\n value={\n <MoneyLines\n rows={sales}\n currency={currency}\n amount={(r) => r.window.gmv}\n />\n }\n sub={`Lifetime ${lifetime.length ? lifetime.join(' · ') : noneText(currency)}`}\n hint=\"Gross sales in the selected period; lifetime underneath.\"\n />\n <StatCard\n label=\"Orders\"\n value={String(period.orders)}\n sub={`${period.paidOrders} paid`}\n hint=\"Orders placed in the selected period.\"\n />\n <StatCard\n label=\"Customers\"\n value={String(period.customers)}\n sub={`${data.metrics.totalCustomers} all time`}\n hint=\"Distinct customers who ordered in the selected period.\"\n />\n <StatCard\n label=\"Avg order\"\n value={\n <MoneyLines\n rows={sales}\n currency={currency}\n amount={(r) => r.window.aov}\n />\n }\n sub={`Subscription: ${data.metrics.subscriptionStatus ?? 'none'}`}\n hint=\"Average order value in the selected period, and the plan status.\"\n />\n </div>\n <section className=\"adm-panel\">\n <h2 className={styles.title}>\n Revenue · last 30 days{focus ? ` · ${focus}` : ''}\n </h2>\n {focus || !data.currencies?.length ? (\n <GrowthChart\n data={(series ?? []).map((p) => ({\n date: p.date,\n count: p.amount,\n }))}\n emptyLabel=\"No revenue in the last 30 days\"\n />\n ) : (\n <p className=\"text-sm text-[var(--text-secondary)]\">\n {PICK_CURRENCY}\n </p>\n )}\n </section>\n <SetupCard data={data} />\n <section className=\"adm-panel\">\n <h2 className={styles.title}>Recent activity</h2>\n <ActivityFeed\n entries={data.activity ?? []}\n onViewAll={() => onTab('audit')}\n />\n </section>\n <section>\n <h2 className={styles.title}>Recent orders</h2>\n {recentOrders}\n </section>\n </div>\n <TenantSidebar\n data={data}\n onTeam={() =>\n document\n .getElementById('tenant-team')\n ?.scrollIntoView({ behavior: 'smooth', block: 'start' })\n }\n />\n </div>\n </>\n );\n}\n"],"mappings":";AAqCQ,SAwDJ,UAtDM,KAFF;AAlCR,SAAS,aAAa;AAEtB,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,MAAM,uBAAuB;AACtC,SAAS,qBAAqB;AAE9B,OAAO,YAAY;AAEnB,SAAS,UAAU,EAAE,KAAK,GAA2B;AACnD,QAAM,aAAa,KAAK;AAExB,MAAI,CAAC,cAAc,WAAW,WAAW,IAAK,QAAO;AAGrD,QAAM,QAAQ,CAAC,GAAG,WAAW,KAAK,EAAE;AAAA,IAClC,CAAC,GAAG,MACF,OAAO,EAAE,WAAW,WAAW,IAAI,OAAO,EAAE,WAAW,WAAW;AAAA,EACtE;AAEA,SACE,qBAAC,aAAQ,IAAI,KAAK,OAAO,WAAW,aAAa,OAAO,UAAU,IAChE;AAAA,yBAAC,SAAI,WAAW,OAAO,UACrB;AAAA,2BAAC,QAAG,WAAW,OAAO,OAAO;AAAA;AAAA,QAAS,WAAW;AAAA,QAAQ;AAAA,SAAM;AAAA,MAC9D,WAAW,aACV,oBAAC,SAAM,SAAQ,aAAY,iCAAmB;AAAA,OAElD;AAAA,IACA,oBAAC,SAAI,WAAW,OAAO,UAAU,eAAW,MAC1C,8BAAC,UAAK,OAAO,EAAE,OAAO,GAAG,WAAW,OAAO,IAAI,GAAG,GACpD;AAAA,IACA,oBAAC,QAAG,WAAW,OAAO,OACnB,gBAAM,IAAI,CAAC,MACV,qBAAC,QAAe,aAAW,EAAE,WAAW,aACrC;AAAA,QAAE;AAAA,MACH,oBAAC,UAAM,YAAE,OAAO,WAAW,KAAK,GAAG,GAAE;AAAA,SAF9B,EAAE,GAGX,CACD,GACH;AAAA,KACF;AAEJ;AAGO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAWG;AAED,QAAM,WAAW;AAAA,IACf,KAAK,QAAQ,YAAY,CAAC;AAAA,IAC1B;AAAA,IACA,CAAC,MAAM,EAAE;AAAA,EACX;AACA,QAAM,SAAS,KAAK,QAAQ;AAE5B,QAAM,QAAQ,KAAK,QAAQ;AAC3B,QAAM,WAAW,WAAW,OAAO,UAAU,CAAC,MAAM,EAAE,GAAG;AACzD,QAAM,QAAQ,cAAc,KAAK,cAAc,CAAC,GAAG,QAAQ;AAC3D,QAAM,SAAS,OAAO,KAAK,yBAAyB,KAAK,GAAG;AAE5D,SACE,iCACE;AAAA,wBAAC,mBAAgB,MAAY,OAAc;AAAA,IAC3C,qBAAC,SAAI,WAAW,OAAO,QACrB;AAAA,2BAAC,SAAI,WAAW,OAAO,MACrB;AAAA,6BAAC,SAAI,WAAW,OAAO,QACrB;AAAA,+BAAC,UAAK;AAAA;AAAA,YAAa;AAAA,aAAY;AAAA,UAC/B,qBAAC,SAAI,WAAU,eACZ;AAAA;AAAA,YACA;AAAA,aACH;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAW,OAAO,MACrB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OACE,SAAS,SACP,oBAAC,UAAK,WAAW,OAAO,YACrB,mBAAS,IAAI,CAAC,MACb,oBAAC,UACE,sBAAY,EAAE,gBAAgB,EAAE,QAAQ,KADhC,EAAE,QAEb,CACD,GACH,IACE,KAAK,QAAQ,SAAS,SACxB,MAAM,QAAQ,aACZ,aACF,aAEA;AAAA,cAGJ,KACE,SAAS,SACL,WAAW,SACR,IAAI,CAAC,MAAM,YAAY,EAAE,cAAc,EAAE,QAAQ,CAAC,EAClD,KAAK,QAAK,CAAC,KACd;AAAA,cAEN,MAAK;AAAA;AAAA,UACP;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OACE;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM;AAAA,kBACN;AAAA,kBACA,QAAQ,CAAC,MAAM,EAAE,OAAO;AAAA;AAAA,cAC1B;AAAA,cAEF,KAAK,YAAY,SAAS,SAAS,SAAS,KAAK,QAAK,IAAI,SAAS,QAAQ,CAAC;AAAA,cAC5E,MAAK;AAAA;AAAA,UACP;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OAAO,OAAO,OAAO,MAAM;AAAA,cAC3B,KAAK,GAAG,OAAO,UAAU;AAAA,cACzB,MAAK;AAAA;AAAA,UACP;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OAAO,OAAO,OAAO,SAAS;AAAA,cAC9B,KAAK,GAAG,KAAK,QAAQ,cAAc;AAAA,cACnC,MAAK;AAAA;AAAA,UACP;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,OACE;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM;AAAA,kBACN;AAAA,kBACA,QAAQ,CAAC,MAAM,EAAE,OAAO;AAAA;AAAA,cAC1B;AAAA,cAEF,KAAK,iBAAiB,KAAK,QAAQ,sBAAsB,MAAM;AAAA,cAC/D,MAAK;AAAA;AAAA,UACP;AAAA,WACF;AAAA,QACA,qBAAC,aAAQ,WAAU,aACjB;AAAA,+BAAC,QAAG,WAAW,OAAO,OAAO;AAAA;AAAA,YACJ,QAAQ,SAAM,KAAK,KAAK;AAAA,aACjD;AAAA,UACC,SAAS,CAAC,KAAK,YAAY,SAC1B;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO;AAAA,gBAC/B,MAAM,EAAE;AAAA,gBACR,OAAO,EAAE;AAAA,cACX,EAAE;AAAA,cACF,YAAW;AAAA;AAAA,UACb,IAEA,oBAAC,OAAE,WAAU,wCACV,yBACH;AAAA,WAEJ;AAAA,QACA,oBAAC,aAAU,MAAY;AAAA,QACvB,qBAAC,aAAQ,WAAU,aACjB;AAAA,8BAAC,QAAG,WAAW,OAAO,OAAO,6BAAe;AAAA,UAC5C;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,KAAK,YAAY,CAAC;AAAA,cAC3B,WAAW,MAAM,MAAM,OAAO;AAAA;AAAA,UAChC;AAAA,WACF;AAAA,QACA,qBAAC,aACC;AAAA,8BAAC,QAAG,WAAW,OAAO,OAAO,2BAAa;AAAA,UACzC;AAAA,WACH;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,QAAQ,MACN,SACG,eAAe,aAAa,GAC3B,eAAe,EAAE,UAAU,UAAU,OAAO,QAAQ,CAAC;AAAA;AAAA,MAE7D;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
.attentionAction:hover {
|
|
124
124
|
background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
|
|
125
125
|
}
|
|
126
|
-
/* Where the banner jumps to:
|
|
126
|
+
/* Where the banner jumps to: then flash once. */
|
|
127
127
|
.jumpTarget {
|
|
128
128
|
scroll-margin-top: 200px;
|
|
129
129
|
}
|
|
@@ -6,7 +6,8 @@ import { byCurrency } from "../../_ui/currency-filter-model";
|
|
|
6
6
|
import styles from "./tenant-overview.module.css";
|
|
7
7
|
function TenantWalletCard({
|
|
8
8
|
data,
|
|
9
|
-
currency = ""
|
|
9
|
+
currency = "",
|
|
10
|
+
walletNote
|
|
10
11
|
}) {
|
|
11
12
|
const wallet = data.wallet;
|
|
12
13
|
const balances = byCurrency(
|
|
@@ -25,7 +26,7 @@ function TenantWalletCard({
|
|
|
25
26
|
}
|
|
26
27
|
)
|
|
27
28
|
] }),
|
|
28
|
-
!wallet ? /* @__PURE__ */ jsx("p", { children: "No wallet yet
|
|
29
|
+
!wallet && walletNote ? /* @__PURE__ */ jsx("p", { children: walletNote }) : !wallet ? /* @__PURE__ */ jsx("p", { children: "No wallet yet. It is created when the merchant activates payments." }) : balances.length === 0 ? /* @__PURE__ */ jsxs("p", { children: [
|
|
29
30
|
"The wallet holds no ",
|
|
30
31
|
currency,
|
|
31
32
|
" balance."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-wallet-card.tsx"],"sourcesContent":["'use client';\n\nimport { Badge } from '@withpotter/ui-kit';\nimport type { TenantDetail } from '@withpotter/lib/admin-api';\nimport { formatMinor, sameStatus } from '@withpotter/lib/wallet-format';\nimport { byCurrency } from '../../_ui/currency-filter-model';\nimport styles from './tenant-overview.module.css';\n\n/** Every balance the merchant's Potter wallet holds, per currency. */\nexport function TenantWalletCard({\n data,\n currency = '',\n}: {\n data: TenantDetail;\n /** The currency filter's choice; '' shows every currency. */\n currency?: string;\n}) {\n const wallet = data.wallet;\n const balances = byCurrency(\n wallet?.balances ?? [],\n currency,\n (b) => b.currency,\n );\n\n return (\n <section className=\"adm-panel\" style={{ marginBottom: 16 }}>\n <h2 className={styles.title}>\n Wallet\n {wallet && (\n <Badge\n variant={\n sameStatus(wallet.status) === 'active' ? 'success' : 'warning'\n }\n >\n {sameStatus(wallet.status).replaceAll('_', ' ')}\n </Badge>\n )}\n </h2>\n {!wallet ? (\n <p>\n No wallet yet
|
|
1
|
+
{"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-wallet-card.tsx"],"sourcesContent":["'use client';\n\nimport { Badge } from '@withpotter/ui-kit';\nimport type { TenantDetail } from '@withpotter/lib/admin-api';\nimport { formatMinor, sameStatus } from '@withpotter/lib/wallet-format';\nimport { byCurrency } from '../../_ui/currency-filter-model';\nimport styles from './tenant-overview.module.css';\n\n/** Every balance the merchant's Potter wallet holds, per currency. */\nexport function TenantWalletCard({\n data,\n currency = '',\n walletNote,\n}: {\n data: TenantDetail;\n /** The currency filter's choice; '' shows every currency. */\n currency?: string;\n /** Said in place of \"No wallet yet\" while the ledger loads or cannot be read. */\n walletNote?: string;\n}) {\n const wallet = data.wallet;\n const balances = byCurrency(\n wallet?.balances ?? [],\n currency,\n (b) => b.currency,\n );\n\n return (\n <section className=\"adm-panel\" style={{ marginBottom: 16 }}>\n <h2 className={styles.title}>\n Wallet\n {wallet && (\n <Badge\n variant={\n sameStatus(wallet.status) === 'active' ? 'success' : 'warning'\n }\n >\n {sameStatus(wallet.status).replaceAll('_', ' ')}\n </Badge>\n )}\n </h2>\n {!wallet && walletNote ? (\n <p>{walletNote}</p>\n ) : !wallet ? (\n <p>\n No wallet yet. It is created when the merchant activates payments.\n </p>\n ) : balances.length === 0 ? (\n <p>The wallet holds no {currency} balance.</p>\n ) : (\n <div className={styles.kpis}>\n {balances.flatMap((b) =>\n [\n ['Available', b.availableMinor],\n ['Pending', b.pendingMinor],\n ['Paying out', b.payoutPendingMinor],\n ['Restricted', b.restrictedMinor],\n ].map(([label, minor]) => (\n <div key={`${b.currency}-${label}`}>\n <div className={styles.title}>\n {b.currency} {label}\n </div>\n <strong>{formatMinor(minor, b.currency)}</strong>\n </div>\n )),\n )}\n </div>\n )}\n </section>\n );\n}\n"],"mappings":";AA6BM,SAGI,KAHJ;AA3BN,SAAS,aAAa;AAEtB,SAAS,aAAa,kBAAkB;AACxC,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AAGZ,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAMG;AACD,QAAM,SAAS,KAAK;AACpB,QAAM,WAAW;AAAA,IACf,QAAQ,YAAY,CAAC;AAAA,IACrB;AAAA,IACA,CAAC,MAAM,EAAE;AAAA,EACX;AAEA,SACE,qBAAC,aAAQ,WAAU,aAAY,OAAO,EAAE,cAAc,GAAG,GACvD;AAAA,yBAAC,QAAG,WAAW,OAAO,OAAO;AAAA;AAAA,MAE1B,UACC;AAAA,QAAC;AAAA;AAAA,UACC,SACE,WAAW,OAAO,MAAM,MAAM,WAAW,YAAY;AAAA,UAGtD,qBAAW,OAAO,MAAM,EAAE,WAAW,KAAK,GAAG;AAAA;AAAA,MAChD;AAAA,OAEJ;AAAA,IACC,CAAC,UAAU,aACV,oBAAC,OAAG,sBAAW,IACb,CAAC,SACH,oBAAC,OAAE,gFAEH,IACE,SAAS,WAAW,IACtB,qBAAC,OAAE;AAAA;AAAA,MAAqB;AAAA,MAAS;AAAA,OAAS,IAE1C,oBAAC,SAAI,WAAW,OAAO,MACpB,mBAAS;AAAA,MAAQ,CAAC,MACjB;AAAA,QACE,CAAC,aAAa,EAAE,cAAc;AAAA,QAC9B,CAAC,WAAW,EAAE,YAAY;AAAA,QAC1B,CAAC,cAAc,EAAE,kBAAkB;AAAA,QACnC,CAAC,cAAc,EAAE,eAAe;AAAA,MAClC,EAAE,IAAI,CAAC,CAAC,OAAO,KAAK,MAClB,qBAAC,SACC;AAAA,6BAAC,SAAI,WAAW,OAAO,OACpB;AAAA,YAAE;AAAA,UAAS;AAAA,UAAE;AAAA,WAChB;AAAA,QACA,oBAAC,YAAQ,sBAAY,OAAO,EAAE,QAAQ,GAAE;AAAA,WAJhC,GAAG,EAAE,QAAQ,IAAI,KAAK,EAKhC,CACD;AAAA,IACH,GACF;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useLedgerWallet } from "../../_money/use-ledger";
|
|
3
|
+
import { isNoWallet } from "../../_money/ledger";
|
|
4
|
+
function useTenantWallet(tenantId, enabled, fallback) {
|
|
5
|
+
const ledger = useLedgerWallet(enabled ? tenantId : "");
|
|
6
|
+
if (ledger.data)
|
|
7
|
+
return {
|
|
8
|
+
wallet: { status: ledger.data.status, balances: ledger.data.balances }
|
|
9
|
+
};
|
|
10
|
+
if (ledger.isError)
|
|
11
|
+
return {
|
|
12
|
+
wallet: isNoWallet(ledger.error) ? null : fallback,
|
|
13
|
+
walletNote: !isNoWallet(ledger.error) && !fallback ? "The wallet did not load." : void 0
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
wallet: fallback,
|
|
17
|
+
walletNote: enabled && !fallback ? "Loading" : void 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
useTenantWallet
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=use-tenant-wallet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/use-tenant-wallet.ts"],"sourcesContent":["'use client';\n\nimport type { TenantDetail } from '@withpotter/lib/admin-api';\nimport { useLedgerWallet } from '../../_money/use-ledger';\nimport { isNoWallet } from '../../_money/ledger';\n\ntype TenantWallet = TenantDetail['wallet'];\n\n/**\n * The wallet the Overview and Wallet cards show. The tenant detail does not\n * carry one, so it is read from the money ledger, the same read the Money\n * pages use. `null` only when the ledger says the business has none.\n */\nexport function useTenantWallet(\n tenantId: string,\n enabled: boolean,\n fallback: TenantWallet,\n): { wallet: TenantWallet; walletNote?: string } {\n const ledger = useLedgerWallet(enabled ? tenantId : '');\n\n if (ledger.data)\n return {\n wallet: { status: ledger.data.status, balances: ledger.data.balances },\n };\n\n if (ledger.isError)\n return {\n wallet: isNoWallet(ledger.error) ? null : fallback,\n walletNote:\n !isNoWallet(ledger.error) && !fallback\n ? 'The wallet did not load.'\n : undefined,\n };\n\n // Loading says so rather than \"No wallet\"; with the ledger off, nothing to add.\n return {\n wallet: fallback,\n walletNote: enabled && !fallback ? 'Loading' : undefined,\n };\n}\n"],"mappings":";AAGA,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AASpB,SAAS,gBACd,UACA,SACA,UAC+C;AAC/C,QAAM,SAAS,gBAAgB,UAAU,WAAW,EAAE;AAEtD,MAAI,OAAO;AACT,WAAO;AAAA,MACL,QAAQ,EAAE,QAAQ,OAAO,KAAK,QAAQ,UAAU,OAAO,KAAK,SAAS;AAAA,IACvE;AAEF,MAAI,OAAO;AACT,WAAO;AAAA,MACL,QAAQ,WAAW,OAAO,KAAK,IAAI,OAAO;AAAA,MAC1C,YACE,CAAC,WAAW,OAAO,KAAK,KAAK,CAAC,WAC1B,6BACA;AAAA,IACR;AAGF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY,WAAW,CAAC,WAAW,YAAY;AAAA,EACjD;AACF;","names":[]}
|
|
@@ -102,7 +102,7 @@ function TenantsTable() {
|
|
|
102
102
|
/* @__PURE__ */ jsx(Th, { children: "Email" }),
|
|
103
103
|
/* @__PURE__ */ jsx(Th, { children: "Country" }),
|
|
104
104
|
/* @__PURE__ */ jsx(Th, { children: "Industry" }),
|
|
105
|
-
/* @__PURE__ */ jsx(Th, { align: "right", children: "GMV
|
|
105
|
+
/* @__PURE__ */ jsx(Th, { align: "right", children: "GMV" }),
|
|
106
106
|
/* @__PURE__ */ jsx(Th, { children: "Status" }),
|
|
107
107
|
/* @__PURE__ */ jsx(Th, { children: "Account officer" }),
|
|
108
108
|
/* @__PURE__ */ jsx(Th, { children: "Joined" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/tenants/tenants-table.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Th } from '@withpotter/ui-kit';\nimport type { TenantStatus } from '@withpotter/lib/admin-api';\nimport { usePlatformTenants } from '../hooks';\nimport { ListCard, ListPager } from '../_ui/list-card';\nimport { TenantListRow } from './tenant-list-row';\nimport { FilterSearch, FilterSelect, Filters } from '../_ui';\nimport { CountryFilter, useCountryParam } from './country-filter';\nimport { AccountOfficerFilter, useOfficerParam } from './account-officer-filter';\n\nconst STATUS_OPTIONS: TenantStatus[] = [\n 'active',\n 'pending',\n 'suspended',\n 'deleted',\n];\n\nconst sentence = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);\n\n/** Every business, searchable by name or slug, narrowed by status, country or officer. */\nexport function TenantsTable() {\n const router = useRouter();\n const [search, setSearch] = useState('');\n const [status, setStatus] = useState<TenantStatus | ''>('');\n const [country, setCountry] = useCountryParam();\n const [officer, setOfficer] = useOfficerParam();\n const [page, setPage] = useState(1);\n const { data, isLoading, isError, refetch } = usePlatformTenants({\n page,\n pageSize: 20,\n search: search || undefined,\n status: status || undefined,\n country: country || undefined,\n accountOfficerId: officer || undefined,\n });\n const totalPages = data\n ? Math.max(1, Math.ceil(data.total / data.pageSize))\n : 1;\n\n return (\n <ListCard\n title=\"All merchants\"\n count={data?.total}\n columns={9}\n minWidth={1000}\n isLoading={isLoading}\n isError={isError}\n onRetry={() => void refetch()}\n errorTitle=\"Failed to load merchants.\"\n isEmpty={data?.rows.length === 0}\n emptyTitle=\"No merchants found\"\n emptyDescription=\"Try a different search, status, country or officer.\"\n controls={\n <Filters label=\"Filter merchants\">\n <FilterSearch\n value={search}\n onChange={(v) => {\n setSearch(v);\n setPage(1);\n }}\n placeholder=\"Search by name or slug…\"\n />\n <FilterSelect\n label=\"Status\"\n value={status}\n onChange={(v) => {\n setStatus(v as TenantStatus | '');\n setPage(1);\n }}\n options={[\n { value: '', label: 'All statuses' },\n ...STATUS_OPTIONS.map((s) => ({\n value: s,\n label: sentence(s),\n })),\n ]}\n />\n <CountryFilter\n value={country}\n onChange={(v) => {\n setCountry(v);\n setPage(1);\n }}\n />\n <AccountOfficerFilter\n value={officer}\n onChange={(v) => {\n setOfficer(v);\n setPage(1);\n }}\n />\n </Filters>\n }\n head={\n <tr>\n <Th>Business</Th>\n <Th>Email</Th>\n <Th>Country</Th>\n <Th>Industry</Th>\n <Th align=\"right\">GMV
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/tenants/tenants-table.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Th } from '@withpotter/ui-kit';\nimport type { TenantStatus } from '@withpotter/lib/admin-api';\nimport { usePlatformTenants } from '../hooks';\nimport { ListCard, ListPager } from '../_ui/list-card';\nimport { TenantListRow } from './tenant-list-row';\nimport { FilterSearch, FilterSelect, Filters } from '../_ui';\nimport { CountryFilter, useCountryParam } from './country-filter';\nimport { AccountOfficerFilter, useOfficerParam } from './account-officer-filter';\n\nconst STATUS_OPTIONS: TenantStatus[] = [\n 'active',\n 'pending',\n 'suspended',\n 'deleted',\n];\n\nconst sentence = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);\n\n/** Every business, searchable by name or slug, narrowed by status, country or officer. */\nexport function TenantsTable() {\n const router = useRouter();\n const [search, setSearch] = useState('');\n const [status, setStatus] = useState<TenantStatus | ''>('');\n const [country, setCountry] = useCountryParam();\n const [officer, setOfficer] = useOfficerParam();\n const [page, setPage] = useState(1);\n const { data, isLoading, isError, refetch } = usePlatformTenants({\n page,\n pageSize: 20,\n search: search || undefined,\n status: status || undefined,\n country: country || undefined,\n accountOfficerId: officer || undefined,\n });\n const totalPages = data\n ? Math.max(1, Math.ceil(data.total / data.pageSize))\n : 1;\n\n return (\n <ListCard\n title=\"All merchants\"\n count={data?.total}\n columns={9}\n minWidth={1000}\n isLoading={isLoading}\n isError={isError}\n onRetry={() => void refetch()}\n errorTitle=\"Failed to load merchants.\"\n isEmpty={data?.rows.length === 0}\n emptyTitle=\"No merchants found\"\n emptyDescription=\"Try a different search, status, country or officer.\"\n controls={\n <Filters label=\"Filter merchants\">\n <FilterSearch\n value={search}\n onChange={(v) => {\n setSearch(v);\n setPage(1);\n }}\n placeholder=\"Search by name or slug…\"\n />\n <FilterSelect\n label=\"Status\"\n value={status}\n onChange={(v) => {\n setStatus(v as TenantStatus | '');\n setPage(1);\n }}\n options={[\n { value: '', label: 'All statuses' },\n ...STATUS_OPTIONS.map((s) => ({\n value: s,\n label: sentence(s),\n })),\n ]}\n />\n <CountryFilter\n value={country}\n onChange={(v) => {\n setCountry(v);\n setPage(1);\n }}\n />\n <AccountOfficerFilter\n value={officer}\n onChange={(v) => {\n setOfficer(v);\n setPage(1);\n }}\n />\n </Filters>\n }\n head={\n <tr>\n <Th>Business</Th>\n <Th>Email</Th>\n <Th>Country</Th>\n <Th>Industry</Th>\n <Th align=\"right\">GMV</Th>\n <Th>Status</Th>\n <Th>Account officer</Th>\n <Th>Joined</Th>\n <Th align=\"right\">\n <span className=\"sr-only\">Actions</span>\n </Th>\n </tr>\n }\n footer={\n <ListPager\n page={page}\n totalPages={totalPages}\n total={data?.total}\n noun=\"merchants\"\n onPage={setPage}\n />\n }\n >\n {data?.rows.map((t) => (\n <TenantListRow\n key={t.id}\n t={t}\n onOpen={() => router.push(`/admin/tenants/${t.id}`)}\n />\n ))}\n </ListCard>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withpotter/pages-admin",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.85",
|
|
4
4
|
"description": "Box console platform-admin routes: overview, revenue, accounts, tenants, admins, roles, audit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@radix-ui/react-popover": "^1.1.23",
|
|
33
33
|
"@tanstack/react-query": "^5.90.20",
|
|
34
|
-
"@withpotter/lib": "^0.13.0-next.
|
|
35
|
-
"@withpotter/platform-admin": "^0.13.0-next.
|
|
36
|
-
"@withpotter/ui-kit": "^0.13.0-next.
|
|
34
|
+
"@withpotter/lib": "^0.13.0-next.85",
|
|
35
|
+
"@withpotter/platform-admin": "^0.13.0-next.85",
|
|
36
|
+
"@withpotter/ui-kit": "^0.13.0-next.85",
|
|
37
37
|
"lucide-react": "^0.562.0",
|
|
38
38
|
"motion": "^12.42.2"
|
|
39
39
|
},
|