@withpotter/pages-admin 0.13.0-next.123 → 0.13.0-next.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/_money/cash-postings.js +3 -20
- package/dist/admin/_money/cash-postings.js.map +1 -1
- package/dist/admin/_money/cash.module.css +0 -10
- package/dist/admin/_money/finder-format.js +0 -1
- package/dist/admin/_money/finder-format.js.map +1 -1
- package/dist/admin/_money/format.js +0 -1
- package/dist/admin/_money/format.js.map +1 -1
- package/dist/admin/_overview/activation-panel.js +6 -6
- package/dist/admin/_overview/activation-panel.js.map +1 -1
- package/dist/admin/_overview/attention-groups.js +8 -8
- package/dist/admin/_overview/attention-groups.js.map +1 -1
- package/dist/admin/_overview/attention-panel.js +33 -37
- package/dist/admin/_overview/attention-panel.js.map +1 -1
- package/dist/admin/_overview/attention.module.css +3 -0
- package/dist/admin/_overview/control-room.js +13 -41
- package/dist/admin/_overview/control-room.js.map +1 -1
- package/dist/admin/_overview/kpi-row.js +7 -7
- package/dist/admin/_overview/kpi-row.js.map +1 -1
- package/dist/admin/_overview/leaders-panel.js +1 -1
- package/dist/admin/_overview/leaders-panel.js.map +1 -1
- package/dist/admin/_overview/overdue-outstanding.js +1 -2
- package/dist/admin/_overview/overdue-outstanding.js.map +1 -1
- package/dist/admin/_overview/portfolio-panel.js +5 -5
- package/dist/admin/_overview/portfolio-panel.js.map +1 -1
- package/dist/admin/_overview/position-panel.js +12 -8
- package/dist/admin/_overview/position-panel.js.map +1 -1
- package/dist/admin/_pos/pos-figures.js +5 -18
- package/dist/admin/_pos/pos-figures.js.map +1 -1
- package/dist/admin/_pos/pos-rules.js +0 -2
- package/dist/admin/_pos/pos-rules.js.map +1 -1
- package/dist/admin/_pos/pos-words.js +1 -6
- package/dist/admin/_pos/pos-words.js.map +1 -1
- package/dist/admin/_pos/use-pos.js +1 -70
- package/dist/admin/_pos/use-pos.js.map +1 -1
- package/dist/admin/_shell/nav-config.js +3 -3
- package/dist/admin/_shell/nav-config.js.map +1 -1
- package/dist/admin/_shell/nav-filter.js +2 -4
- package/dist/admin/_shell/nav-filter.js.map +1 -1
- package/dist/admin/_shell/nav-row.js +2 -5
- package/dist/admin/_shell/nav-row.js.map +1 -1
- package/dist/admin/_shell/sidebar-footer.js +0 -20
- package/dist/admin/_shell/sidebar-footer.js.map +1 -1
- package/dist/admin/_shell/sidebar-footer.module.css +0 -17
- package/dist/admin/_shell/sidebar.js +15 -25
- package/dist/admin/_shell/sidebar.js.map +1 -1
- package/dist/admin/_shell/sidebar.module.css +0 -69
- package/dist/admin/_shell/use-fluid-hover.js +6 -37
- package/dist/admin/_shell/use-fluid-hover.js.map +1 -1
- package/dist/admin/_ui/admin.css +0 -7
- package/dist/admin/_ui/data-table.js +1 -1
- package/dist/admin/_ui/data-table.js.map +1 -1
- package/dist/admin/_ui/list-card.js +1 -1
- package/dist/admin/_ui/list-card.js.map +1 -1
- package/dist/admin/_ui/list-card.module.css +16 -2
- package/dist/admin/_ui/money-view.js +0 -1
- package/dist/admin/_ui/money-view.js.map +1 -1
- package/dist/admin/_ui/page-header.js +4 -31
- package/dist/admin/_ui/page-header.js.map +1 -1
- package/dist/admin/_ui/panel.module.css +10 -0
- package/dist/admin/_ui/stat-card.js +2 -11
- package/dist/admin/_ui/stat-card.js.map +1 -1
- package/dist/admin/_ui/trend.js +2 -2
- package/dist/admin/_ui/trend.js.map +1 -1
- package/dist/admin/_ui/workspace-tab.js +1 -11
- package/dist/admin/_ui/workspace-tab.js.map +1 -1
- package/dist/admin/accounts/account-panel.js +6 -49
- package/dist/admin/accounts/account-panel.js.map +1 -1
- package/dist/admin/accounts/account-panel.module.css +0 -73
- package/dist/admin/accounts/account-row.js +2 -11
- package/dist/admin/accounts/account-row.js.map +1 -1
- package/dist/admin/accounts/account-vitals.js +13 -15
- package/dist/admin/accounts/account-vitals.js.map +1 -1
- package/dist/admin/accounts/accounts-table.js +8 -39
- package/dist/admin/accounts/accounts-table.js.map +1 -1
- package/dist/admin/accounts/accounts-workspace.js +2 -2
- package/dist/admin/accounts/accounts-workspace.js.map +1 -1
- package/dist/admin/accounts/owner-session.js +6 -0
- package/dist/admin/accounts/owner-session.js.map +1 -1
- package/dist/admin/accounts/rows.module.css +0 -17
- package/dist/admin/audit/status-pill.js +0 -6
- package/dist/admin/audit/status-pill.js.map +1 -1
- package/dist/admin/audit/status-pill.module.css +0 -4
- package/dist/admin/commerce/commerce-overview.js +2 -2
- package/dist/admin/commerce/commerce-overview.js.map +1 -1
- package/dist/admin/commerce/merchant-meta.js +0 -1
- package/dist/admin/commerce/merchant-meta.js.map +1 -1
- package/dist/admin/commerce/merchant-view.js +2 -2
- package/dist/admin/commerce/merchant-view.js.map +1 -1
- package/dist/admin/feedback/prompt-operations.module.css +2 -0
- package/dist/admin/feedback/prompt-preview.module.css +2 -0
- package/dist/admin/hooks.js +1 -57
- package/dist/admin/hooks.js.map +1 -1
- package/dist/admin/layout.js +2 -8
- package/dist/admin/layout.js.map +1 -1
- package/dist/admin/page.js +8 -28
- package/dist/admin/page.js.map +1 -1
- package/dist/admin/payout-tiers/_tier-dialog.js +21 -31
- package/dist/admin/payout-tiers/_tier-dialog.js.map +1 -1
- package/dist/admin/payout-tiers/tier-preview-result.js +9 -10
- package/dist/admin/payout-tiers/tier-preview-result.js.map +1 -1
- package/dist/admin/payout-tiers/tiers-table.js +17 -40
- package/dist/admin/payout-tiers/tiers-table.js.map +1 -1
- package/dist/admin/payout-tiers/tiers-workspace.js +17 -20
- package/dist/admin/payout-tiers/tiers-workspace.js.map +1 -1
- package/dist/admin/pos/pos-workspace.js +2 -6
- package/dist/admin/pos/pos-workspace.js.map +1 -1
- package/dist/admin/pos/shifts-tab.js +9 -16
- package/dist/admin/pos/shifts-tab.js.map +1 -1
- package/dist/admin/revenue/cohort-panel.js +6 -6
- package/dist/admin/revenue/cohort-panel.js.map +1 -1
- package/dist/admin/revenue/health-panel.js +21 -22
- package/dist/admin/revenue/health-panel.js.map +1 -1
- package/dist/admin/revenue/plan-mix-panel.js +3 -3
- package/dist/admin/revenue/plan-mix-panel.js.map +1 -1
- package/dist/admin/revenue/quality-panel.js +19 -19
- package/dist/admin/revenue/quality-panel.js.map +1 -1
- package/dist/admin/revenue/revenue-kpis.js +50 -111
- package/dist/admin/revenue/revenue-kpis.js.map +1 -1
- package/dist/admin/revenue/revenue-overview.js +46 -39
- package/dist/admin/revenue/revenue-overview.js.map +1 -1
- package/dist/admin/revenue/revenue-retention.js +13 -17
- package/dist/admin/revenue/revenue-retention.js.map +1 -1
- package/dist/admin/revenue/revenue-workspace.js +6 -53
- package/dist/admin/revenue/revenue-workspace.js.map +1 -1
- package/dist/admin/revenue/subscriptions-panel.js +14 -14
- package/dist/admin/revenue/subscriptions-panel.js.map +1 -1
- package/dist/admin/revenue/top-tenants-panel.js +4 -4
- package/dist/admin/revenue/top-tenants-panel.js.map +1 -1
- package/dist/admin/settings/account-tab.js +25 -1
- package/dist/admin/settings/account-tab.js.map +1 -1
- package/dist/admin/settings/general-tab.js +2 -2
- package/dist/admin/settings/general-tab.js.map +1 -1
- package/dist/admin/settings/system-status-card.js +30 -0
- package/dist/admin/settings/system-status-card.js.map +1 -0
- package/dist/admin/support/queue-card.module.css +4 -1
- package/dist/admin/tenants/[id]/commerce/format.js +1 -2
- package/dist/admin/tenants/[id]/commerce/format.js.map +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js +1 -1
- package/dist/admin/tenants/[id]/commerce/recent-orders.js.map +1 -1
- package/dist/admin/tenants/[id]/page.js +10 -37
- package/dist/admin/tenants/[id]/page.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-layout.module.css +4 -1
- package/dist/admin/tenants/[id]/tenant-overview.js +2 -3
- package/dist/admin/tenants/[id]/tenant-overview.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-overview.module.css +1 -1
- package/dist/admin/tenants/[id]/tenant-pos.js +16 -64
- package/dist/admin/tenants/[id]/tenant-pos.js.map +1 -1
- package/dist/admin/tenants/[id]/tenant-wallet-card.js +2 -3
- package/dist/admin/tenants/[id]/tenant-wallet-card.js.map +1 -1
- package/dist/admin/tenants/[id]/verification/document-viewer.js +52 -3
- package/dist/admin/tenants/[id]/verification/document-viewer.js.map +1 -1
- package/dist/admin/tenants/[id]/verification/document-viewer.module.css +0 -51
- package/dist/admin/tenants/[id]/verification/tenant-verification.js +0 -10
- package/dist/admin/tenants/[id]/verification/tenant-verification.js.map +1 -1
- package/dist/admin/tenants/tenant-list-row.js +1 -2
- package/dist/admin/tenants/tenant-list-row.js.map +1 -1
- package/dist/admin/tenants/tenants-table.js +2 -3
- package/dist/admin/tenants/tenants-table.js.map +1 -1
- package/dist/admin/tenants/tenants-workspace.js +2 -2
- package/dist/admin/tenants/tenants-workspace.js.map +1 -1
- package/dist/admin/verification/verification-page.js +3 -19
- package/dist/admin/verification/verification-page.js.map +1 -1
- package/dist/admin/verification/verification.module.css +0 -10
- package/dist/admin/verification/waiting-case-actions.js +68 -0
- package/dist/admin/verification/waiting-case-actions.js.map +1 -0
- package/dist/admin/verification/waiting-cases.js +48 -66
- package/dist/admin/verification/waiting-cases.js.map +1 -1
- package/package.json +5 -5
- package/dist/admin/_pos/olive-balance.js +0 -40
- package/dist/admin/_pos/olive-balance.js.map +0 -1
- package/dist/admin/_pos/olive-state.js +0 -23
- package/dist/admin/_pos/olive-state.js.map +0 -1
- package/dist/admin/_pos/payments-model.js +0 -55
- package/dist/admin/_pos/payments-model.js.map +0 -1
- package/dist/admin/_pos/tenant-operators.js +0 -63
- package/dist/admin/_pos/tenant-operators.js.map +0 -1
- package/dist/admin/_pos/tenant-outlets.js +0 -77
- package/dist/admin/_pos/tenant-outlets.js.map +0 -1
- package/dist/admin/_pos/tenant-pos-figures.js +0 -82
- package/dist/admin/_pos/tenant-pos-figures.js.map +0 -1
- package/dist/admin/_pos/tenant-requests.js +0 -64
- package/dist/admin/_pos/tenant-requests.js.map +0 -1
- package/dist/admin/_pos/tenant-transactions.js +0 -145
- package/dist/admin/_pos/tenant-transactions.js.map +0 -1
- package/dist/admin/_shell/nav-fold.js +0 -14
- package/dist/admin/_shell/nav-fold.js.map +0 -1
- package/dist/admin/_shell/nav-group.js +0 -78
- package/dist/admin/_shell/nav-group.js.map +0 -1
- package/dist/admin/_shell/use-open-groups.js +0 -64
- package/dist/admin/_shell/use-open-groups.js.map +0 -1
- package/dist/admin/_ui/page-header.module.css +0 -45
- package/dist/admin/accounts/account-sessions.js +0 -70
- package/dist/admin/accounts/account-sessions.js.map +0 -1
- package/dist/admin/accounts/account-sessions.module.css +0 -55
- package/dist/admin/accounts/account-stage-filter.js +0 -29
- package/dist/admin/accounts/account-stage-filter.js.map +0 -1
- package/dist/admin/accounts/account-stage.js +0 -81
- package/dist/admin/accounts/account-stage.js.map +0 -1
- package/dist/admin/accounts/hint.js +0 -12
- package/dist/admin/accounts/hint.js.map +0 -1
- package/dist/admin/payout-tiers/requirement-labels.js +0 -70
- package/dist/admin/payout-tiers/requirement-labels.js.map +0 -1
- package/dist/admin/pos/transactions-tab.js +0 -214
- package/dist/admin/pos/transactions-tab.js.map +0 -1
- package/dist/admin/revenue/channel-panel.js +0 -49
- package/dist/admin/revenue/channel-panel.js.map +0 -1
- package/dist/admin/revenue/channel-rows.js +0 -17
- package/dist/admin/revenue/channel-rows.js.map +0 -1
- package/dist/admin/revenue/download-button.js +0 -40
- package/dist/admin/revenue/download-button.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-colors.js +0 -25
- package/dist/admin/revenue/earnings/earnings-colors.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-legend.js +0 -75
- package/dist/admin/revenue/earnings/earnings-legend.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-summary.js +0 -296
- package/dist/admin/revenue/earnings/earnings-summary.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings-table.js +0 -143
- package/dist/admin/revenue/earnings/earnings-table.js.map +0 -1
- package/dist/admin/revenue/earnings/earnings.module.css +0 -201
- package/dist/admin/revenue/earnings/use-earnings.js +0 -27
- package/dist/admin/revenue/earnings/use-earnings.js.map +0 -1
- package/dist/admin/revenue/ledger-model.js +0 -104
- package/dist/admin/revenue/ledger-model.js.map +0 -1
- package/dist/admin/revenue/ledger.module.css +0 -144
- package/dist/admin/revenue/merchant-headline.js +0 -37
- package/dist/admin/revenue/merchant-headline.js.map +0 -1
- package/dist/admin/revenue/merchant-payments-view.js +0 -234
- package/dist/admin/revenue/merchant-payments-view.js.map +0 -1
- package/dist/admin/revenue/method-cards.js +0 -40
- package/dist/admin/revenue/method-cards.js.map +0 -1
- package/dist/admin/revenue/payment-drawer.js +0 -53
- package/dist/admin/revenue/payment-drawer.js.map +0 -1
- package/dist/admin/revenue/plan-lines.js +0 -36
- package/dist/admin/revenue/plan-lines.js.map +0 -1
- package/dist/admin/revenue/plan-payment-drawer.js +0 -42
- package/dist/admin/revenue/plan-payment-drawer.js.map +0 -1
- package/dist/admin/revenue/plans-panel.js +0 -48
- package/dist/admin/revenue/plans-panel.js.map +0 -1
- package/dist/admin/revenue/platform-fees.js +0 -9
- package/dist/admin/revenue/platform-fees.js.map +0 -1
- package/dist/admin/revenue/print-summary-button.js +0 -27
- package/dist/admin/revenue/print-summary-button.js.map +0 -1
- package/dist/admin/revenue/subscriptions-view.js +0 -213
- package/dist/admin/revenue/subscriptions-view.js.map +0 -1
- package/dist/admin/revenue/summary-download.js +0 -38
- package/dist/admin/revenue/summary-download.js.map +0 -1
- package/dist/admin/revenue/summary-print.js +0 -82
- package/dist/admin/revenue/summary-print.js.map +0 -1
- package/dist/admin/revenue/summary-rows.js +0 -44
- package/dist/admin/revenue/summary-rows.js.map +0 -1
- package/dist/admin/revenue/url-filters.js +0 -36
- package/dist/admin/revenue/url-filters.js.map +0 -1
- package/dist/admin/revenue/use-url-filters.js +0 -24
- package/dist/admin/revenue/use-url-filters.js.map +0 -1
- package/dist/admin/tenants/[id]/use-tenant-wallet.js +0 -23
- package/dist/admin/tenants/[id]/use-tenant-wallet.js.map +0 -1
- package/dist/admin/tenants/[id]/verification/document-file.js +0 -78
- package/dist/admin/tenants/[id]/verification/document-file.js.map +0 -1
- package/dist/admin/tenants/[id]/verification/sent-files.js +0 -33
- package/dist/admin/tenants/[id]/verification/sent-files.js.map +0 -1
- package/dist/admin/verification/preview-dialog.js +0 -111
- package/dist/admin/verification/preview-dialog.js.map +0 -1
|
@@ -1,33 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as Popover from "@radix-ui/react-popover";
|
|
4
|
-
import { Info } from "lucide-react";
|
|
5
|
-
import styles from "./page-header.module.css";
|
|
6
3
|
import { useInWorkspaceTab } from "./workspace-tab";
|
|
7
|
-
function PageInfo({ title, text }) {
|
|
8
|
-
return /* @__PURE__ */ jsx("span", { className: `adm-page-desc ${styles.info}`, children: /* @__PURE__ */ jsxs(Popover.Root, { children: [
|
|
9
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
10
|
-
"button",
|
|
11
|
-
{
|
|
12
|
-
type: "button",
|
|
13
|
-
className: styles.trigger,
|
|
14
|
-
"aria-label": `About ${title}`,
|
|
15
|
-
children: /* @__PURE__ */ jsx(Info, { size: 16, "aria-hidden": true })
|
|
16
|
-
}
|
|
17
|
-
) }),
|
|
18
|
-
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
19
|
-
Popover.Content,
|
|
20
|
-
{
|
|
21
|
-
side: "bottom",
|
|
22
|
-
align: "start",
|
|
23
|
-
sideOffset: 6,
|
|
24
|
-
collisionPadding: 16,
|
|
25
|
-
className: styles.popover,
|
|
26
|
-
children: text
|
|
27
|
-
}
|
|
28
|
-
) })
|
|
29
|
-
] }) });
|
|
30
|
-
}
|
|
31
4
|
function PageHeader({
|
|
32
5
|
title,
|
|
33
6
|
description,
|
|
@@ -36,15 +9,15 @@ function PageHeader({
|
|
|
36
9
|
if (useInWorkspaceTab()) {
|
|
37
10
|
if (!description && !actions) return null;
|
|
38
11
|
return /* @__PURE__ */ jsxs("div", { className: "adm-page-header adm-tab-lead", children: [
|
|
39
|
-
description && /* @__PURE__ */ jsx(
|
|
12
|
+
description && /* @__PURE__ */ jsx("p", { className: "adm-page-desc", children: description }),
|
|
40
13
|
actions && /* @__PURE__ */ jsx("div", { className: "adm-page-actions", children: actions })
|
|
41
14
|
] });
|
|
42
15
|
}
|
|
43
16
|
return /* @__PURE__ */ jsxs("div", { className: "adm-page-header", children: [
|
|
44
|
-
/* @__PURE__ */
|
|
17
|
+
/* @__PURE__ */ jsx("div", { className: "adm-page-lead", children: /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
|
|
45
18
|
/* @__PURE__ */ jsx("h1", { className: "adm-page-title", children: title }),
|
|
46
|
-
description && /* @__PURE__ */ jsx(
|
|
47
|
-
] }),
|
|
19
|
+
description && /* @__PURE__ */ jsx("p", { className: "adm-page-desc", children: description })
|
|
20
|
+
] }) }),
|
|
48
21
|
actions && /* @__PURE__ */ jsx("div", { className: "adm-page-actions", children: actions })
|
|
49
22
|
] });
|
|
50
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_ui/page-header.tsx"],"sourcesContent":["'use client';\n\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_ui/page-header.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport type { LucideIcon } from 'lucide-react';\nimport type { Tone } from '../_fx';\nimport { useInWorkspaceTab } from './workspace-tab';\n\n/**\n * Standard page header: title, one line of context, actions on the right.\n * No icon: the nav already says where you are. `icon`/`tone` are accepted\n * and ignored so existing callers compile.\n */\nexport function PageHeader({\n title,\n description,\n actions,\n}: {\n title: string;\n description?: string;\n actions?: ReactNode;\n icon?: LucideIcon;\n tone?: Tone;\n}) {\n // Inside a workspace tab the tab is the title: keep the context and actions.\n if (useInWorkspaceTab()) {\n if (!description && !actions) return null;\n return (\n <div className=\"adm-page-header adm-tab-lead\">\n {description && <p className=\"adm-page-desc\">{description}</p>}\n {actions && <div className=\"adm-page-actions\">{actions}</div>}\n </div>\n );\n }\n\n return (\n <div className=\"adm-page-header\">\n <div className=\"adm-page-lead\">\n <div style={{ minWidth: 0 }}>\n <h1 className=\"adm-page-title\">{title}</h1>\n {description && <p className=\"adm-page-desc\">{description}</p>}\n </div>\n </div>\n {actions && <div className=\"adm-page-actions\">{actions}</div>}\n </div>\n );\n}\n"],"mappings":";AA2BM,SACkB,KADlB;AAtBN,SAAS,yBAAyB;AAO3B,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AACF,GAMG;AAED,MAAI,kBAAkB,GAAG;AACvB,QAAI,CAAC,eAAe,CAAC,QAAS,QAAO;AACrC,WACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,qBAAe,oBAAC,OAAE,WAAU,iBAAiB,uBAAY;AAAA,MACzD,WAAW,oBAAC,SAAI,WAAU,oBAAoB,mBAAQ;AAAA,OACzD;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,mBACb;AAAA,wBAAC,SAAI,WAAU,iBACb,+BAAC,SAAI,OAAO,EAAE,UAAU,EAAE,GACxB;AAAA,0BAAC,QAAG,WAAU,kBAAkB,iBAAM;AAAA,MACrC,eAAe,oBAAC,OAAE,WAAU,iBAAiB,uBAAY;AAAA,OAC5D,GACF;AAAA,IACC,WAAW,oBAAC,SAAI,WAAU,oBAAoB,mBAAQ;AAAA,KACzD;AAEJ;","names":[]}
|
|
@@ -75,7 +75,17 @@
|
|
|
75
75
|
padding: 0 0 24px;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/* A table's own x-scroll wrapper would capture the sticky header; let the
|
|
79
|
+
panel body be the one scroller so column headers stay pinned. */
|
|
80
|
+
.body div:has(> table) {
|
|
81
|
+
overflow: visible;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Sticky column headers when a table scrolls inside the card. */
|
|
78
85
|
.body thead th {
|
|
86
|
+
position: sticky;
|
|
87
|
+
top: 0;
|
|
88
|
+
z-index: 1;
|
|
79
89
|
background: var(--bg-base);
|
|
80
90
|
}
|
|
81
91
|
|
|
@@ -14,8 +14,7 @@ function StatCard({
|
|
|
14
14
|
href,
|
|
15
15
|
compact,
|
|
16
16
|
note,
|
|
17
|
-
alert
|
|
18
|
-
accent
|
|
17
|
+
alert
|
|
19
18
|
}) {
|
|
20
19
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21
20
|
/* @__PURE__ */ jsxs("div", { className: "adm-stat-head", children: [
|
|
@@ -61,15 +60,7 @@ function StatCard({
|
|
|
61
60
|
}
|
|
62
61
|
);
|
|
63
62
|
}
|
|
64
|
-
return /* @__PURE__ */ jsx(
|
|
65
|
-
"div",
|
|
66
|
-
{
|
|
67
|
-
className: `adm-stat${compact ? " adm-stat--compact" : ""}`,
|
|
68
|
-
"data-accent": accent ? "" : void 0,
|
|
69
|
-
style: accent ? { "--adm-stat-accent": accent } : void 0,
|
|
70
|
-
children: body
|
|
71
|
-
}
|
|
72
|
-
);
|
|
63
|
+
return /* @__PURE__ */ jsx("div", { className: `adm-stat${compact ? " adm-stat--compact" : ""}`, children: body });
|
|
73
64
|
}
|
|
74
65
|
const isWord = (value) => typeof value === "string" && !/\d/.test(value) && value.length > 10;
|
|
75
66
|
function DeltaPill({ delta }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_ui/stat-card.tsx"],"sourcesContent":["import type { ComponentType,
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_ui/stat-card.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport Link from 'next/link';\nimport { Skeleton } from './skeleton';\nimport { Sparkline } from './sparkline';\n\nexport interface StatCardDelta {\n /** Period-over-period change. `null` = no prior baseline to compare against. */\n pct: number | null;\n /** When false, an increase is rendered red and a decrease green. Default true. */\n positiveIsGood?: boolean;\n /** Optional caption, e.g. \"vs prev 30d\". */\n caption?: string;\n}\n\n/**\n * KPI stat card: label, value (weight 500), optional sub-line or plain-text\n * delta, and an optional sparkline. No icon chip. Becomes a link with `href`.\n */\nexport function StatCard({\n label,\n value,\n sub,\n hint,\n isLoading,\n delta,\n spark,\n sparkColor,\n href,\n compact,\n note,\n alert,\n}: {\n label: string;\n value: ReactNode;\n sub?: string;\n /** Accepted for older callers; KPI cards no longer draw an icon chip. */\n icon?: ComponentType<{ size?: number; color?: string }>;\n hint?: string;\n isLoading?: boolean;\n delta?: StatCardDelta;\n spark?: number[];\n sparkColor?: string;\n href?: string;\n compact?: boolean;\n /** Quiet secondary text under the label. */\n note?: string;\n /** Paint the value red: a queue that needs someone now. */\n alert?: boolean;\n}) {\n const body = (\n <>\n <div className=\"adm-stat-head\">\n {hint ? (\n <span\n className=\"adm-stat-label adm-hint\"\n tabIndex={0}\n role=\"note\"\n aria-label={hint}\n >\n {label}\n <span className=\"adm-hint-bubble\" role=\"tooltip\">\n {hint}\n </span>\n </span>\n ) : (\n <span className=\"adm-stat-label\" title={label}>\n {label}\n </span>\n )}\n {note && <span className=\"adm-stat-note\">{note}</span>}\n </div>\n {isLoading ? (\n <Skeleton width={96} height={30} />\n ) : (\n <div\n className={`adm-stat-value${isWord(value) ? ' adm-stat-value--text' : ''}`}\n data-alert={alert || undefined}\n title={typeof value === 'string' ? value : undefined}\n >\n {value}\n </div>\n )}\n {!isLoading && (delta || sub || (spark && spark.length > 0)) && (\n <div className=\"adm-stat-foot\">\n <div className=\"adm-stat-foot-meta\">\n {delta ? (\n <DeltaPill delta={delta} />\n ) : sub ? (\n <span className=\"adm-stat-sub\" title={sub}>\n {sub}\n </span>\n ) : null}\n {delta?.caption && (\n <span className=\"adm-stat-caption\">{delta.caption}</span>\n )}\n </div>\n {spark && spark.length > 0 && (\n <Sparkline values={spark} color={sparkColor} />\n )}\n </div>\n )}\n </>\n );\n\n if (href) {\n return (\n <Link\n href={href}\n className={`adm-stat adm-stat-link${\n compact ? ' adm-stat--compact' : ''\n }`}\n >\n {body}\n </Link>\n );\n }\n return (\n <div className={`adm-stat${compact ? ' adm-stat--compact' : ''}`}>\n {body}\n </div>\n );\n}\n\n/** A label rather than a number: set at body size so it never shouts. */\nconst isWord = (value: ReactNode) =>\n typeof value === 'string' && !/\\d/.test(value) && value.length > 10;\n\nfunction DeltaPill({ delta }: { delta: StatCardDelta }) {\n const positiveIsGood = delta.positiveIsGood ?? true;\n if (delta.pct === null) {\n return <span className=\"adm-delta adm-delta--flat\">New</span>;\n }\n const up = delta.pct > 0;\n const flat = delta.pct === 0;\n const good = flat ? false : up === positiveIsGood;\n const tone = flat ? 'flat' : good ? 'good' : 'bad';\n // The sign carries the direction in words, so no arrow glyph is needed.\n const sign = flat ? '' : up ? '+' : '\\u2212';\n return (\n <span className={`adm-delta adm-delta--${tone}`}>\n {sign}\n {Math.abs(delta.pct)}%\n </span>\n );\n}\n"],"mappings":"AAkDI,mBAUQ,KAPF,YAHN;AAjDJ,OAAO,UAAU;AACjB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAenB,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAiBG;AACD,QAAM,OACJ,iCACE;AAAA,yBAAC,SAAI,WAAU,iBACZ;AAAA,aACC;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,UAAU;AAAA,UACV,MAAK;AAAA,UACL,cAAY;AAAA,UAEX;AAAA;AAAA,YACD,oBAAC,UAAK,WAAU,mBAAkB,MAAK,WACpC,gBACH;AAAA;AAAA;AAAA,MACF,IAEA,oBAAC,UAAK,WAAU,kBAAiB,OAAO,OACrC,iBACH;AAAA,MAED,QAAQ,oBAAC,UAAK,WAAU,iBAAiB,gBAAK;AAAA,OACjD;AAAA,IACC,YACC,oBAAC,YAAS,OAAO,IAAI,QAAQ,IAAI,IAEjC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,iBAAiB,OAAO,KAAK,IAAI,0BAA0B,EAAE;AAAA,QACxE,cAAY,SAAS;AAAA,QACrB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,QAE1C;AAAA;AAAA,IACH;AAAA,IAED,CAAC,cAAc,SAAS,OAAQ,SAAS,MAAM,SAAS,MACvD,qBAAC,SAAI,WAAU,iBACb;AAAA,2BAAC,SAAI,WAAU,sBACZ;AAAA,gBACC,oBAAC,aAAU,OAAc,IACvB,MACF,oBAAC,UAAK,WAAU,gBAAe,OAAO,KACnC,eACH,IACE;AAAA,QACH,OAAO,WACN,oBAAC,UAAK,WAAU,oBAAoB,gBAAM,SAAQ;AAAA,SAEtD;AAAA,MACC,SAAS,MAAM,SAAS,KACvB,oBAAC,aAAU,QAAQ,OAAO,OAAO,YAAY;AAAA,OAEjD;AAAA,KAEJ;AAGF,MAAI,MAAM;AACR,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW,yBACT,UAAU,uBAAuB,EACnC;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,EAEJ;AACA,SACE,oBAAC,SAAI,WAAW,WAAW,UAAU,uBAAuB,EAAE,IAC3D,gBACH;AAEJ;AAGA,MAAM,SAAS,CAAC,UACd,OAAO,UAAU,YAAY,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,SAAS;AAEnE,SAAS,UAAU,EAAE,MAAM,GAA6B;AACtD,QAAM,iBAAiB,MAAM,kBAAkB;AAC/C,MAAI,MAAM,QAAQ,MAAM;AACtB,WAAO,oBAAC,UAAK,WAAU,6BAA4B,iBAAG;AAAA,EACxD;AACA,QAAM,KAAK,MAAM,MAAM;AACvB,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,OAAO,OAAO,QAAQ,OAAO;AACnC,QAAM,OAAO,OAAO,SAAS,OAAO,SAAS;AAE7C,QAAM,OAAO,OAAO,KAAK,KAAK,MAAM;AACpC,SACE,qBAAC,UAAK,WAAW,wBAAwB,IAAI,IAC1C;AAAA;AAAA,IACA,KAAK,IAAI,MAAM,GAAG;AAAA,IAAE;AAAA,KACvB;AAEJ;","names":[]}
|
package/dist/admin/_ui/trend.js
CHANGED
|
@@ -20,9 +20,9 @@ function GmvOrdersChart({
|
|
|
20
20
|
data: gmvTrend,
|
|
21
21
|
compare: compare ? previousGmvTrend : null,
|
|
22
22
|
compareLabel: `Previous ${span}`,
|
|
23
|
-
label: "
|
|
23
|
+
label: "GMV",
|
|
24
24
|
formatValue: money,
|
|
25
|
-
emptyMessage: "No
|
|
25
|
+
emptyMessage: "No merchant GMV in this period",
|
|
26
26
|
height: 160
|
|
27
27
|
}
|
|
28
28
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_ui/trend.tsx"],"sourcesContent":["'use client';\n\nimport { TimeSeriesChart } from '@withpotter/components/charts';\n\n/** A `{date, N}[]` series reshaped into the `{date, value}` TimeSeriesChart wants. */\nexport function toTrend<K extends string>(\n series: Array<{ date: string } & Record<K, number>> | undefined,\n key: K,\n): Array<{ date: string; value: number }> {\n return (series ?? []).map((p) => ({ date: p.date, value: p[key] }));\n}\n\n/**\n * GMV over orders: the two measures share a window but not a scale, so\n * they're two stacked charts rather than one dual-axis one. Used wherever a\n * page shows both for the same range (Overview, Merchants, Commerce).\n */\nexport function GmvOrdersChart({\n gmvTrend,\n ordersTrend,\n previousGmvTrend,\n previousOrdersTrend,\n compare,\n span,\n money,\n}: {\n gmvTrend: Array<{ date: string; value: number }>;\n ordersTrend: Array<{ date: string; value: number }>;\n previousGmvTrend: Array<{ date: string; value: number }>;\n previousOrdersTrend: Array<{ date: string; value: number }>;\n /** False for \"all time\": there is no earlier period to compare with. */\n compare: boolean;\n span: string;\n money: (value: number) => string;\n}) {\n return (\n <>\n <TimeSeriesChart\n data={gmvTrend}\n compare={compare ? previousGmvTrend : null}\n compareLabel={`Previous ${span}`}\n label=\"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_ui/trend.tsx"],"sourcesContent":["'use client';\n\nimport { TimeSeriesChart } from '@withpotter/components/charts';\n\n/** A `{date, N}[]` series reshaped into the `{date, value}` TimeSeriesChart wants. */\nexport function toTrend<K extends string>(\n series: Array<{ date: string } & Record<K, number>> | undefined,\n key: K,\n): Array<{ date: string; value: number }> {\n return (series ?? []).map((p) => ({ date: p.date, value: p[key] }));\n}\n\n/**\n * GMV over orders: the two measures share a window but not a scale, so\n * they're two stacked charts rather than one dual-axis one. Used wherever a\n * page shows both for the same range (Overview, Merchants, Commerce).\n */\nexport function GmvOrdersChart({\n gmvTrend,\n ordersTrend,\n previousGmvTrend,\n previousOrdersTrend,\n compare,\n span,\n money,\n}: {\n gmvTrend: Array<{ date: string; value: number }>;\n ordersTrend: Array<{ date: string; value: number }>;\n previousGmvTrend: Array<{ date: string; value: number }>;\n previousOrdersTrend: Array<{ date: string; value: number }>;\n /** False for \"all time\": there is no earlier period to compare with. */\n compare: boolean;\n span: string;\n money: (value: number) => string;\n}) {\n return (\n <>\n <TimeSeriesChart\n data={gmvTrend}\n compare={compare ? previousGmvTrend : null}\n compareLabel={`Previous ${span}`}\n label=\"GMV\"\n formatValue={money}\n emptyMessage=\"No merchant GMV in this period\"\n height={160}\n />\n <div\n style={{\n margin: '20px 0 8px',\n fontSize: 12,\n fontWeight: 600,\n color: 'var(--text-tertiary)',\n }}\n >\n Orders\n </div>\n <TimeSeriesChart\n data={ordersTrend}\n compare={compare ? previousOrdersTrend : null}\n compareLabel={`Previous ${span}`}\n label=\"Orders\"\n emptyMessage=\"No orders in this period\"\n height={100}\n />\n </>\n );\n}\n"],"mappings":";AAoCI,mBACE,KADF;AAlCJ,SAAS,uBAAuB;AAGzB,SAAS,QACd,QACA,KACwC;AACxC,UAAQ,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,GAAG,EAAE,EAAE;AACpE;AAOO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,UAAU,mBAAmB;AAAA,QACtC,cAAc,YAAY,IAAI;AAAA,QAC9B,OAAM;AAAA,QACN,aAAa;AAAA,QACb,cAAa;AAAA,QACb,QAAQ;AAAA;AAAA,IACV;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,OAAO;AAAA,QACT;AAAA,QACD;AAAA;AAAA,IAED;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,UAAU,sBAAsB;AAAA,QACzC,cAAc,YAAY,IAAI;AAAA,QAC9B,OAAM;AAAA,QACN,cAAa;AAAA,QACb,QAAQ;AAAA;AAAA,IACV;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useContext } from "react";
|
|
4
|
-
import { motion, useReducedMotion } from "motion/react";
|
|
5
4
|
const NESTED_TAB_PARAM = "view";
|
|
6
5
|
const InWorkspaceTab = createContext(false);
|
|
7
6
|
function WorkspaceTab({ children }) {
|
|
8
|
-
|
|
9
|
-
return /* @__PURE__ */ jsx(InWorkspaceTab.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
10
|
-
motion.div,
|
|
11
|
-
{
|
|
12
|
-
initial: calm ? false : { opacity: 0, y: 8 },
|
|
13
|
-
animate: { opacity: 1, y: 0 },
|
|
14
|
-
transition: { duration: 0.2, ease: [0, 0, 0.2, 1] },
|
|
15
|
-
children
|
|
16
|
-
}
|
|
17
|
-
) });
|
|
7
|
+
return /* @__PURE__ */ jsx(InWorkspaceTab.Provider, { value: true, children });
|
|
18
8
|
}
|
|
19
9
|
const useInWorkspaceTab = () => useContext(InWorkspaceTab);
|
|
20
10
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_ui/workspace-tab.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, type ReactNode } from 'react';\
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_ui/workspace-tab.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, type ReactNode } from 'react';\n\n/** The address parameter tabs inside a workspace tab use, so they never fight the workspace's `?tab=`. */\nexport const NESTED_TAB_PARAM = 'view';\n\nconst InWorkspaceTab = createContext(false);\n\n/**\n * Wraps a page shown as one tab of a workspace (Money, say). Inside it the\n * page's header drops its title, which the tab already shows, and keeps its\n * one line of context and its actions; tabs within the page move to `?view=`.\n */\nexport function WorkspaceTab({ children }: { children: ReactNode }) {\n return (\n <InWorkspaceTab.Provider value={true}>{children}</InWorkspaceTab.Provider>\n );\n}\n\nexport const useInWorkspaceTab = () => useContext(InWorkspaceTab);\n"],"mappings":";AAgBI;AAdJ,SAAS,eAAe,kBAAkC;AAGnD,MAAM,mBAAmB;AAEhC,MAAM,iBAAiB,cAAc,KAAK;AAOnC,SAAS,aAAa,EAAE,SAAS,GAA4B;AAClE,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,MAAO,UAAS;AAEpD;AAEO,MAAM,oBAAoB,MAAM,WAAW,cAAc;","names":[]}
|
|
@@ -7,16 +7,6 @@ import { StatusPill } from "../audit/status-pill";
|
|
|
7
7
|
import { useAccountTenants } from "../hooks";
|
|
8
8
|
import { CopyText } from "../tenants/[id]/copy-text";
|
|
9
9
|
import { AccountActivity } from "./account-activity";
|
|
10
|
-
import {
|
|
11
|
-
ACTIVE_HINT,
|
|
12
|
-
providerLabel,
|
|
13
|
-
STAGE_LABELS,
|
|
14
|
-
stageStatus,
|
|
15
|
-
stepStates,
|
|
16
|
-
stoppedAt
|
|
17
|
-
} from "./account-stage";
|
|
18
|
-
import { AccountSessions } from "./account-sessions";
|
|
19
|
-
import { Hint } from "./hint";
|
|
20
10
|
import { AccountVitals } from "./account-vitals";
|
|
21
11
|
import { useAccountActions } from "./use-account-actions";
|
|
22
12
|
import { useModuleOn } from "../_shell/modules-context";
|
|
@@ -27,14 +17,12 @@ function Section({
|
|
|
27
17
|
title,
|
|
28
18
|
index,
|
|
29
19
|
count,
|
|
30
|
-
hint,
|
|
31
20
|
children
|
|
32
21
|
}) {
|
|
33
22
|
return /* @__PURE__ */ jsxs(Reveal, { as: "section", index, className: styles.section, children: [
|
|
34
23
|
/* @__PURE__ */ jsxs("h3", { className: styles.heading, children: [
|
|
35
24
|
title,
|
|
36
|
-
count !== void 0 && /* @__PURE__ */ jsx("span", { className: styles.count, children: count })
|
|
37
|
-
hint && /* @__PURE__ */ jsx(Hint, { label: hint })
|
|
25
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", { className: styles.count, children: count })
|
|
38
26
|
] }),
|
|
39
27
|
children
|
|
40
28
|
] });
|
|
@@ -68,14 +56,14 @@ function AccountPanel({
|
|
|
68
56
|
label: account.emailVerified ? "Email verified" : "Email unverified"
|
|
69
57
|
}
|
|
70
58
|
),
|
|
71
|
-
account.
|
|
59
|
+
account.mfaEnabled !== void 0 && /* @__PURE__ */ jsx(
|
|
72
60
|
StatusPill,
|
|
73
61
|
{
|
|
74
|
-
status:
|
|
75
|
-
label:
|
|
62
|
+
status: account.mfaEnabled ? "active" : "deleted",
|
|
63
|
+
label: account.mfaEnabled ? "2FA on" : "2FA off"
|
|
76
64
|
}
|
|
77
65
|
),
|
|
78
|
-
account.authProvider && /* @__PURE__ */ jsx("span", { className: styles.muted, children:
|
|
66
|
+
account.authProvider && /* @__PURE__ */ jsx("span", { className: styles.muted, children: account.authProvider })
|
|
79
67
|
] }),
|
|
80
68
|
footer: account && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
81
69
|
account.status && /* @__PURE__ */ jsx(
|
|
@@ -114,27 +102,6 @@ function AccountPanel({
|
|
|
114
102
|
)
|
|
115
103
|
] }),
|
|
116
104
|
children: account && /* @__PURE__ */ jsxs("div", { className: styles.panel, children: [
|
|
117
|
-
account.stage && /* @__PURE__ */ jsxs("div", { className: styles.stopped, children: [
|
|
118
|
-
/* @__PURE__ */ jsxs("span", { className: styles.stoppedLabel, children: [
|
|
119
|
-
"Where this account stopped",
|
|
120
|
-
/* @__PURE__ */ jsx(Hint, { label: ACTIVE_HINT })
|
|
121
|
-
] }),
|
|
122
|
-
/* @__PURE__ */ jsx("ol", { className: styles.steps, children: stepStates(account).map(
|
|
123
|
-
(step) => /* @__PURE__ */ jsxs(
|
|
124
|
-
"li",
|
|
125
|
-
{
|
|
126
|
-
className: styles.step,
|
|
127
|
-
"data-state": step.state,
|
|
128
|
-
children: [
|
|
129
|
-
/* @__PURE__ */ jsx("span", { className: styles.stepDot, "aria-hidden": true }),
|
|
130
|
-
step.label
|
|
131
|
-
]
|
|
132
|
-
},
|
|
133
|
-
step.label
|
|
134
|
-
)
|
|
135
|
-
) }),
|
|
136
|
-
/* @__PURE__ */ jsx("span", { className: styles.stoppedText, children: stoppedAt(account) ?? "Active" })
|
|
137
|
-
] }),
|
|
138
105
|
[verify, impersonate, setStatus].map(
|
|
139
106
|
(action, i) => action.isError && /* @__PURE__ */ jsx(Banner, { tone: "error", title: "That didn\u2019t work", children: action.error.message }, i)
|
|
140
107
|
),
|
|
@@ -156,17 +123,7 @@ function AccountPanel({
|
|
|
156
123
|
}
|
|
157
124
|
),
|
|
158
125
|
/* @__PURE__ */ jsx(Section, { title: "Workspaces", index: 1, count: liveWorkspaces, children: /* @__PURE__ */ jsx(Workspaces, { accountId: account.id }) }),
|
|
159
|
-
/* @__PURE__ */ jsx(
|
|
160
|
-
Section,
|
|
161
|
-
{
|
|
162
|
-
title: "Sessions",
|
|
163
|
-
index: 2,
|
|
164
|
-
count: account.activeSessions,
|
|
165
|
-
hint: "The number is devices signed in now.",
|
|
166
|
-
children: /* @__PURE__ */ jsx(AccountSessions, { accountId: account.id })
|
|
167
|
-
}
|
|
168
|
-
),
|
|
169
|
-
/* @__PURE__ */ jsx(Section, { title: "Account ID", index: 3, children: /* @__PURE__ */ jsx(CopyText, { value: account.id }) })
|
|
126
|
+
/* @__PURE__ */ jsx(Section, { title: "Account ID", index: 2, children: /* @__PURE__ */ jsx(CopyText, { value: account.id }) })
|
|
170
127
|
] })
|
|
171
128
|
}
|
|
172
129
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-panel.tsx"],"sourcesContent":["'use client';\n\nimport { Banner, Button } from '@withpotter/ui-kit';\nimport type { AccountRow } from '@withpotter/lib/admin-api';\nimport { Reveal } from '../_fx';\nimport { Drawer } from '../_ui';\nimport { StatusPill } from '../audit/status-pill';\nimport { useAccountTenants } from '../hooks';\nimport { CopyText } from '../tenants/[id]/copy-text';\nimport { AccountActivity } from './account-activity';\nimport {\n ACTIVE_HINT,\n providerLabel,\n STAGE_LABELS,\n stageStatus,\n stepStates,\n stoppedAt,\n} from './account-stage';\nimport type { StepState } from './account-stage';\nimport { AccountSessions } from './account-sessions';\nimport { Hint } from './hint';\nimport { AccountVitals } from './account-vitals';\nimport { useAccountActions } from './use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useAccountActivity } from './use-account-activity';\nimport { Workspaces } from './account-workspaces';\nimport styles from './account-panel.module.css';\n\n/** Enough to open the panel from anywhere; the table row adds the rest. */\nexport type AccountSummary = Pick<AccountRow, 'id' | 'email' | 'name'> &\n Partial<AccountRow>;\n\nfunction Section({\n title,\n index,\n count,\n hint,\n children,\n}: {\n title: string;\n index: number;\n count?: number;\n hint?: string;\n children: React.ReactNode;\n}) {\n return (\n <Reveal as=\"section\" index={index} className={styles.section}>\n <h3 className={styles.heading}>\n {title}\n {count !== undefined && <span className={styles.count}>{count}</span>}\n {hint && <Hint label={hint} />}\n </h3>\n {children}\n </Reveal>\n );\n}\n\nexport function AccountPanel({\n account,\n onClose,\n}: {\n account: AccountSummary | null;\n onClose: () => void;\n}) {\n const { setStatus, verify, impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const tenants = useAccountTenants(account?.id ?? null);\n const activity = useAccountActivity(account?.id ?? null, tenants.data);\n const suspended = account?.status === 'suspended';\n const liveWorkspaces = tenants.data?.filter(\n (w) => w.status !== 'deleted' && w.memberStatus !== 'removed',\n ).length;\n\n return (\n <Drawer\n open={Boolean(account)}\n onClose={onClose}\n width={560}\n title={account?.name || account?.email || 'Account'}\n subtitle={account?.name ? account.email : undefined}\n meta={\n account && (\n <>\n {account.status && <StatusPill status={account.status} />}\n {account.emailVerified !== undefined && (\n <StatusPill\n status={account.emailVerified ? 'verified' : 'unverified'}\n label={\n account.emailVerified ? 'Email verified' : 'Email unverified'\n }\n />\n )}\n {account.stage && (\n <StatusPill\n status={stageStatus(account.stage)}\n label={STAGE_LABELS[account.stage]}\n />\n )}\n {account.authProvider && (\n <span className={styles.muted}>\n {providerLabel(account.authProvider)}\n </span>\n )}\n </>\n )\n }\n footer={\n account && (\n <>\n {account.status && (\n <Button\n size=\"sm\"\n variant={suspended ? 'secondary' : 'ghostDanger'}\n loading={setStatus.isPending}\n onClick={() =>\n setStatus.mutate({\n id: account.id,\n status: suspended ? 'active' : 'suspended',\n })\n }\n >\n {suspended ? 'Restore' : 'Suspend'}\n </Button>\n )}\n <span className={styles.spacer} />\n {account.emailVerified === false && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n loading={verify.isPending}\n onClick={() => verify.mutate(account.id)}\n >\n Verify email\n </Button>\n )}\n {ownerView && (\n <Button\n size=\"sm\"\n variant=\"primary\"\n loading={impersonate.isPending}\n onClick={() => impersonate.mutate(account.id)}\n >\n View as user\n </Button>\n )}\n </>\n )\n }\n >\n {account && (\n <div className={styles.panel}>\n {account.stage && (\n <div className={styles.stopped}>\n <span className={styles.stoppedLabel}>\n Where this account stopped\n <Hint label={ACTIVE_HINT} />\n </span>\n <ol className={styles.steps}>\n {stepStates(account).map(\n (step: { label: string; state: StepState }) => (\n <li\n key={step.label}\n className={styles.step}\n data-state={step.state}\n >\n <span className={styles.stepDot} aria-hidden />\n {step.label}\n </li>\n ),\n )}\n </ol>\n <span className={styles.stoppedText}>\n {stoppedAt(account) ?? 'Active'}\n </span>\n </div>\n )}\n\n {[verify, impersonate, setStatus].map(\n (action, i) =>\n action.isError && (\n <Banner key={i} tone=\"error\" title=\"That didn’t work\">\n {action.error.message}\n </Banner>\n ),\n )}\n\n <AccountVitals account={account} workspaces={liveWorkspaces} />\n\n <Section\n title=\"Recent activity\"\n index={0}\n count={activity.entries.length}\n >\n <AccountActivity\n entries={activity.entries}\n isLoading={activity.isLoading}\n skipped={activity.skipped}\n />\n </Section>\n\n <Section title=\"Workspaces\" index={1} count={liveWorkspaces}>\n <Workspaces accountId={account.id} />\n </Section>\n\n <Section\n title=\"Sessions\"\n index={2}\n count={account.activeSessions}\n hint=\"The number is devices signed in now.\"\n >\n <AccountSessions accountId={account.id} />\n </Section>\n\n <Section title=\"Account ID\" index={3}>\n <CopyText value={account.id} />\n </Section>\n </div>\n )}\n </Drawer>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-panel.tsx"],"sourcesContent":["'use client';\n\nimport { Banner, Button } from '@withpotter/ui-kit';\nimport type { AccountRow } from '@withpotter/lib/admin-api';\nimport { Reveal } from '../_fx';\nimport { Drawer } from '../_ui';\nimport { StatusPill } from '../audit/status-pill';\nimport { useAccountTenants } from '../hooks';\nimport { CopyText } from '../tenants/[id]/copy-text';\nimport { AccountActivity } from './account-activity';\nimport { AccountVitals } from './account-vitals';\nimport { useAccountActions } from './use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useAccountActivity } from './use-account-activity';\nimport { Workspaces } from './account-workspaces';\nimport styles from './account-panel.module.css';\n\n/** Enough to open the panel from anywhere; the table row adds the rest. */\nexport type AccountSummary = Pick<AccountRow, 'id' | 'email' | 'name'> &\n Partial<AccountRow>;\n\nfunction Section({\n title,\n index,\n count,\n children,\n}: {\n title: string;\n index: number;\n count?: number;\n children: React.ReactNode;\n}) {\n return (\n <Reveal as=\"section\" index={index} className={styles.section}>\n <h3 className={styles.heading}>\n {title}\n {count !== undefined && <span className={styles.count}>{count}</span>}\n </h3>\n {children}\n </Reveal>\n );\n}\n\nexport function AccountPanel({\n account,\n onClose,\n}: {\n account: AccountSummary | null;\n onClose: () => void;\n}) {\n const { setStatus, verify, impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const tenants = useAccountTenants(account?.id ?? null);\n const activity = useAccountActivity(account?.id ?? null, tenants.data);\n const suspended = account?.status === 'suspended';\n const liveWorkspaces = tenants.data?.filter(\n (w) => w.status !== 'deleted' && w.memberStatus !== 'removed',\n ).length;\n\n return (\n <Drawer\n open={Boolean(account)}\n onClose={onClose}\n width={560}\n title={account?.name || account?.email || 'Account'}\n subtitle={account?.name ? account.email : undefined}\n meta={\n account && (\n <>\n {account.status && <StatusPill status={account.status} />}\n {account.emailVerified !== undefined && (\n <StatusPill\n status={account.emailVerified ? 'verified' : 'unverified'}\n label={\n account.emailVerified ? 'Email verified' : 'Email unverified'\n }\n />\n )}\n {account.mfaEnabled !== undefined && (\n <StatusPill\n status={account.mfaEnabled ? 'active' : 'deleted'}\n label={account.mfaEnabled ? '2FA on' : '2FA off'}\n />\n )}\n {account.authProvider && (\n <span className={styles.muted}>{account.authProvider}</span>\n )}\n </>\n )\n }\n footer={\n account && (\n <>\n {account.status && (\n <Button\n size=\"sm\"\n variant={suspended ? 'secondary' : 'ghostDanger'}\n loading={setStatus.isPending}\n onClick={() =>\n setStatus.mutate({\n id: account.id,\n status: suspended ? 'active' : 'suspended',\n })\n }\n >\n {suspended ? 'Restore' : 'Suspend'}\n </Button>\n )}\n <span className={styles.spacer} />\n {account.emailVerified === false && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n loading={verify.isPending}\n onClick={() => verify.mutate(account.id)}\n >\n Verify email\n </Button>\n )}\n {ownerView && (\n <Button\n size=\"sm\"\n variant=\"primary\"\n loading={impersonate.isPending}\n onClick={() => impersonate.mutate(account.id)}\n >\n View as user\n </Button>\n )}\n </>\n )\n }\n >\n {account && (\n <div className={styles.panel}>\n {[verify, impersonate, setStatus].map(\n (action, i) =>\n action.isError && (\n <Banner key={i} tone=\"error\" title=\"That didn’t work\">\n {action.error.message}\n </Banner>\n ),\n )}\n\n <AccountVitals account={account} workspaces={liveWorkspaces} />\n\n <Section\n title=\"Recent activity\"\n index={0}\n count={activity.entries.length}\n >\n <AccountActivity\n entries={activity.entries}\n isLoading={activity.isLoading}\n skipped={activity.skipped}\n />\n </Section>\n\n <Section title=\"Workspaces\" index={1} count={liveWorkspaces}>\n <Workspaces accountId={account.id} />\n </Section>\n\n <Section title=\"Account ID\" index={2}>\n <CopyText value={account.id} />\n </Section>\n </div>\n )}\n </Drawer>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -16,10 +16,6 @@
|
|
|
16
16
|
background: var(--bg-base);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
.vitals[data-count='3'] {
|
|
20
|
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
19
|
.vital {
|
|
24
20
|
display: grid;
|
|
25
21
|
gap: 4px;
|
|
@@ -142,72 +138,3 @@
|
|
|
142
138
|
line-height: 16px;
|
|
143
139
|
color: var(--text-tertiary);
|
|
144
140
|
}
|
|
145
|
-
|
|
146
|
-
.stopped {
|
|
147
|
-
display: grid;
|
|
148
|
-
gap: 12px;
|
|
149
|
-
padding: 16px;
|
|
150
|
-
border-radius: var(--adm-card-radius);
|
|
151
|
-
border: 1px solid var(--adm-line);
|
|
152
|
-
background: var(--bg-base);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.stoppedLabel {
|
|
156
|
-
display: flex;
|
|
157
|
-
align-items: center;
|
|
158
|
-
gap: 4px;
|
|
159
|
-
font-size: 12px;
|
|
160
|
-
font-weight: 400;
|
|
161
|
-
line-height: 16px;
|
|
162
|
-
color: var(--text-tertiary);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.steps {
|
|
166
|
-
display: grid;
|
|
167
|
-
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
168
|
-
gap: 8px;
|
|
169
|
-
margin: 0;
|
|
170
|
-
padding: 0;
|
|
171
|
-
list-style: none;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.step {
|
|
175
|
-
display: grid;
|
|
176
|
-
gap: 8px;
|
|
177
|
-
font-size: 12px;
|
|
178
|
-
line-height: 16px;
|
|
179
|
-
color: var(--text-tertiary);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.stepDot {
|
|
183
|
-
width: 8px;
|
|
184
|
-
height: 8px;
|
|
185
|
-
border-radius: 999px;
|
|
186
|
-
box-shadow: inset 0 0 0 1px var(--border-subtle);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.step[data-state='done'] {
|
|
190
|
-
color: var(--text-secondary);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.step[data-state='done'] .stepDot {
|
|
194
|
-
background: var(--adm-ok);
|
|
195
|
-
box-shadow: none;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.step[data-state='current'] {
|
|
199
|
-
font-weight: 600;
|
|
200
|
-
color: var(--text-primary);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.step[data-state='current'] .stepDot {
|
|
204
|
-
background: var(--adm-wait);
|
|
205
|
-
box-shadow: none;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.stoppedText {
|
|
209
|
-
font-size: 14px;
|
|
210
|
-
font-weight: 500;
|
|
211
|
-
line-height: 20px;
|
|
212
|
-
color: var(--text-primary);
|
|
213
|
-
}
|
|
@@ -11,7 +11,6 @@ import { EntityAvatar } from "../_fx";
|
|
|
11
11
|
import { EMPTY } from "../_ui/list-card";
|
|
12
12
|
import { StatusPill } from "../audit/status-pill";
|
|
13
13
|
import { When } from "../audit/when";
|
|
14
|
-
import { kycLabel, STAGE_LABELS, stageStatus } from "./account-stage";
|
|
15
14
|
import { useAccountActions } from "./use-account-actions";
|
|
16
15
|
import { useModuleOn } from "../_shell/modules-context";
|
|
17
16
|
import list from "../_ui/list-card.module.css";
|
|
@@ -77,19 +76,11 @@ function AccountListRow({
|
|
|
77
76
|
] }) }),
|
|
78
77
|
visible.has("provider") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: PROVIDER_LABELS[a.authProvider] ?? (a.authProvider || EMPTY) }),
|
|
79
78
|
/* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(StatusPill, { status: a.status }) }),
|
|
80
|
-
visible.has("kyc") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: kycLabel(a.kycStatus) ?? /* @__PURE__ */ jsx("span", { className: list.muted, children: EMPTY }) }),
|
|
81
|
-
visible.has("stage") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: a.stage ? /* @__PURE__ */ jsx(
|
|
82
|
-
StatusPill,
|
|
83
|
-
{
|
|
84
|
-
status: stageStatus(a.stage),
|
|
85
|
-
label: STAGE_LABELS[a.stage]
|
|
86
|
-
}
|
|
87
|
-
) : /* @__PURE__ */ jsx("span", { className: list.muted, children: EMPTY }) }),
|
|
88
79
|
visible.has("emailVerified") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(StatusPill, { status: a.emailVerified ? "verified" : "unverified" }) }),
|
|
89
80
|
visible.has("workspaces") && /* @__PURE__ */ jsx("td", { className: list.num, children: a.workspaceCount ?? EMPTY }),
|
|
90
|
-
visible.has("lastLogin") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children:
|
|
81
|
+
visible.has("lastLogin") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: a.lastLoginAt ? /* @__PURE__ */ jsx(When, { iso: a.lastLoginAt, className: styles.when }) : "Never" }),
|
|
91
82
|
visible.has("joined") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: day(a.createdAt) }),
|
|
92
|
-
visible.has("
|
|
83
|
+
visible.has("mfa") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: a.mfaEnabled ? "Yes" : "No" }),
|
|
93
84
|
visible.has("activeSessions") && /* @__PURE__ */ jsx("td", { className: list.num, children: a.activeSessions ?? EMPTY }),
|
|
94
85
|
visible.has("lastSeen") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: a.lastSeenAt ? /* @__PURE__ */ jsx(When, { iso: a.lastSeenAt, className: styles.when }) : "Never" }),
|
|
95
86
|
/* @__PURE__ */ jsx("td", { className: styles.menuCell, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(OverflowMenu, { label: `Actions for ${label}`, children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-row.tsx"],"sourcesContent":["'use client';\n\nimport {\n OverflowItem,\n OverflowMenu,\n TableRow,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport type { AccountRow as Account } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { EMPTY } from '../_ui/list-card';\nimport { StatusPill } from '../audit/status-pill';\nimport { When } from '../audit/when';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-row.tsx"],"sourcesContent":["'use client';\n\nimport {\n OverflowItem,\n OverflowMenu,\n TableRow,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport type { AccountRow as Account } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { EMPTY } from '../_ui/list-card';\nimport { StatusPill } from '../audit/status-pill';\nimport { When } from '../audit/when';\nimport { useAccountActions } from './use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport list from '../_ui/list-card.module.css';\nimport styles from './rows.module.css';\n\nexport const PROVIDER_LABELS: Record<string, string> = {\n local: 'Email',\n google: 'Google',\n github: 'GitHub',\n};\n\nconst day = (iso?: string | null) =>\n iso ? new Date(iso).toLocaleDateString() : null;\n\n/** One account: who, how they sign in, status, activity and a ••• menu. */\nexport function AccountListRow({\n a,\n active,\n onOpen,\n visible,\n}: {\n a: Account;\n active: boolean;\n onOpen: () => void;\n /** Which optional columns are showing, in the order the header renders them. */\n visible: Set<string>;\n}) {\n const confirm = useConfirm();\n const { toast } = useToast();\n const { setStatus, verify, impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const suspended = a.status === 'suspended';\n const label = a.name ?? a.email;\n\n const toggleStatus = async () => {\n const ok = await confirm({\n title: suspended ? `Restore ${label}?` : `Suspend ${label}?`,\n description: suspended\n ? 'They can sign in and use their workspaces again.'\n : 'They are signed out everywhere and cannot sign in until restored.',\n confirmLabel: suspended ? 'Restore' : 'Suspend',\n tone: suspended ? 'default' : 'danger',\n });\n if (!ok) return;\n setStatus.mutate(\n { id: a.id, status: suspended ? 'active' : 'suspended' },\n {\n onSuccess: () =>\n toast({ title: `${label} ${suspended ? 'restored' : 'suspended'}` }),\n onError: (e) =>\n toast({\n title: 'The status was not changed',\n description: e.message,\n variant: 'destructive',\n }),\n },\n );\n };\n\n return (\n <TableRow active={active} onClick={onOpen}>\n <td>\n <span className={styles.identity}>\n <EntityAvatar name={label} size={32} round />\n <span className={styles.identityText}>\n <button\n type=\"button\"\n className={styles.nameButton}\n title={label}\n aria-label={`Open ${label}`}\n onClick={(e) => {\n e.stopPropagation();\n onOpen();\n }}\n >\n {a.name ?? EMPTY}\n </button>\n <span className={styles.secondary} title={a.email}>\n {a.email}\n </span>\n </span>\n </span>\n </td>\n {visible.has('provider') && (\n <td className={`${list.nowrap} ${list.muted}`}>\n {PROVIDER_LABELS[a.authProvider] ?? (a.authProvider || EMPTY)}\n </td>\n )}\n <td className={list.nowrap}>\n <StatusPill status={a.status} />\n </td>\n {visible.has('emailVerified') && (\n <td className={list.nowrap}>\n <StatusPill status={a.emailVerified ? 'verified' : 'unverified'} />\n </td>\n )}\n {visible.has('workspaces') && (\n <td className={list.num}>{a.workspaceCount ?? EMPTY}</td>\n )}\n {visible.has('lastLogin') && (\n <td className={`${list.nowrap} ${list.muted}`}>\n {a.lastLoginAt ? (\n <When iso={a.lastLoginAt} className={styles.when} />\n ) : (\n 'Never'\n )}\n </td>\n )}\n {visible.has('joined') && (\n <td className={`${list.nowrap} ${list.muted}`}>{day(a.createdAt)}</td>\n )}\n {visible.has('mfa') && (\n <td className={list.nowrap}>{a.mfaEnabled ? 'Yes' : 'No'}</td>\n )}\n {visible.has('activeSessions') && (\n <td className={list.num}>{a.activeSessions ?? EMPTY}</td>\n )}\n {visible.has('lastSeen') && (\n <td className={`${list.nowrap} ${list.muted}`}>\n {a.lastSeenAt ? (\n <When iso={a.lastSeenAt} className={styles.when} />\n ) : (\n 'Never'\n )}\n </td>\n )}\n <td className={styles.menuCell} onClick={(e) => e.stopPropagation()}>\n <OverflowMenu label={`Actions for ${label}`}>\n <OverflowItem onClick={onOpen}>View account</OverflowItem>\n {ownerView && (\n <OverflowItem onClick={() => impersonate.mutate(a.id)}>\n View as user\n </OverflowItem>\n )}\n {!a.emailVerified && (\n <OverflowItem\n onClick={() =>\n verify.mutate(a.id, {\n onSuccess: () => toast({ title: `${a.email} verified` }),\n })\n }\n >\n Verify email\n </OverflowItem>\n )}\n <OverflowItem\n onClick={() => void toggleStatus()}\n destructive={!suspended}\n >\n {suspended ? 'Restore' : 'Suspend'}\n </OverflowItem>\n </OverflowMenu>\n </td>\n </TableRow>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,37 +1,35 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { relativeTime, seenWithin } from "./relative-time";
|
|
3
3
|
import styles from "./account-panel.module.css";
|
|
4
|
-
const SEEN_GAP_MS = 5 * 60 * 1e3;
|
|
5
4
|
function AccountVitals({
|
|
6
5
|
account,
|
|
7
6
|
workspaces
|
|
8
7
|
}) {
|
|
9
|
-
const online =
|
|
10
|
-
const laterSeen = account.lastSeenAt && (!account.lastLoginAt || new Date(account.lastSeenAt).getTime() - new Date(account.lastLoginAt).getTime() > SEEN_GAP_MS);
|
|
8
|
+
const online = seenWithin(account.lastSeenAt, 15 * 60);
|
|
11
9
|
const tiles = [
|
|
10
|
+
{
|
|
11
|
+
label: "Last seen",
|
|
12
|
+
value: relativeTime(account.lastSeenAt ?? account.lastLoginAt),
|
|
13
|
+
hint: account.activeSessions !== void 0 ? `${account.activeSessions} signed-in device${account.activeSessions === 1 ? "" : "s"}` : void 0,
|
|
14
|
+
live: Boolean(online)
|
|
15
|
+
},
|
|
12
16
|
{
|
|
13
17
|
label: "Last login",
|
|
14
18
|
value: relativeTime(account.lastLoginAt),
|
|
15
|
-
hint: account.lastLoginAt ? new Date(account.lastLoginAt).toLocaleDateString() : void 0
|
|
16
|
-
live: online && !laterSeen
|
|
19
|
+
hint: account.lastLoginAt ? new Date(account.lastLoginAt).toLocaleDateString() : void 0
|
|
17
20
|
},
|
|
18
|
-
...laterSeen ? [
|
|
19
|
-
{
|
|
20
|
-
label: "Last seen",
|
|
21
|
-
value: relativeTime(account.lastSeenAt),
|
|
22
|
-
live: online
|
|
23
|
-
}
|
|
24
|
-
] : [],
|
|
25
21
|
{
|
|
26
22
|
label: "Workspaces",
|
|
27
|
-
value: workspaces === void 0 ? "\u2026" : String(workspaces)
|
|
23
|
+
value: workspaces === void 0 ? "\u2026" : String(workspaces),
|
|
24
|
+
hint: "Active memberships"
|
|
28
25
|
},
|
|
29
26
|
{
|
|
30
27
|
label: "Joined",
|
|
31
|
-
value: account.createdAt ?
|
|
28
|
+
value: account.createdAt ? relativeTime(account.createdAt) : "\u2014",
|
|
29
|
+
hint: account.createdAt ? new Date(account.createdAt).toLocaleDateString() : void 0
|
|
32
30
|
}
|
|
33
31
|
];
|
|
34
|
-
return /* @__PURE__ */ jsx("div", { className: styles.vitals,
|
|
32
|
+
return /* @__PURE__ */ jsx("div", { className: styles.vitals, children: tiles.map((tile) => /* @__PURE__ */ jsxs("div", { className: styles.vital, children: [
|
|
35
33
|
/* @__PURE__ */ jsxs("span", { className: styles.vitalLabel, children: [
|
|
36
34
|
tile.live && /* @__PURE__ */ jsx("span", { className: styles.live, "aria-label": "Online" }),
|
|
37
35
|
tile.label
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-vitals.tsx"],"sourcesContent":["import type { AccountRow } from '@withpotter/lib/admin-api';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-panel.module.css';\n\n/**
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-vitals.tsx"],"sourcesContent":["import type { AccountRow } from '@withpotter/lib/admin-api';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-panel.module.css';\n\n/** Four numbers that say whether this person is here and how deeply. */\nexport function AccountVitals({\n account,\n workspaces,\n}: {\n account: Pick<AccountRow, 'id'> & Partial<AccountRow>;\n workspaces: number | undefined;\n}) {\n const online = seenWithin(account.lastSeenAt, 15 * 60);\n const tiles = [\n {\n label: 'Last seen',\n value: relativeTime(account.lastSeenAt ?? account.lastLoginAt),\n hint:\n account.activeSessions !== undefined\n ? `${account.activeSessions} signed-in device${account.activeSessions === 1 ? '' : 's'}`\n : undefined,\n live: Boolean(online),\n },\n {\n label: 'Last login',\n value: relativeTime(account.lastLoginAt),\n hint: account.lastLoginAt\n ? new Date(account.lastLoginAt).toLocaleDateString()\n : undefined,\n },\n {\n label: 'Workspaces',\n value: workspaces === undefined ? '…' : String(workspaces),\n hint: 'Active memberships',\n },\n {\n label: 'Joined',\n value: account.createdAt ? relativeTime(account.createdAt) : '—',\n hint: account.createdAt\n ? new Date(account.createdAt).toLocaleDateString()\n : undefined,\n },\n ];\n\n return (\n <div className={styles.vitals}>\n {tiles.map((tile) => (\n <div key={tile.label} className={styles.vital}>\n <span className={styles.vitalLabel}>\n {tile.live && <span className={styles.live} aria-label=\"Online\" />}\n {tile.label}\n </span>\n <span className={styles.vitalValue}>{tile.value}</span>\n {tile.hint && <span className={styles.vitalHint}>{tile.hint}</span>}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"AAgDU,SACgB,KADhB;AA/CV,SAAS,cAAc,kBAAkB;AACzC,OAAO,YAAY;AAGZ,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,QAAM,SAAS,WAAW,QAAQ,YAAY,KAAK,EAAE;AACrD,QAAM,QAAQ;AAAA,IACZ;AAAA,MACE,OAAO;AAAA,MACP,OAAO,aAAa,QAAQ,cAAc,QAAQ,WAAW;AAAA,MAC7D,MACE,QAAQ,mBAAmB,SACvB,GAAG,QAAQ,cAAc,oBAAoB,QAAQ,mBAAmB,IAAI,KAAK,GAAG,KACpF;AAAA,MACN,MAAM,QAAQ,MAAM;AAAA,IACtB;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,OAAO,aAAa,QAAQ,WAAW;AAAA,MACvC,MAAM,QAAQ,cACV,IAAI,KAAK,QAAQ,WAAW,EAAE,mBAAmB,IACjD;AAAA,IACN;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,OAAO,eAAe,SAAY,WAAM,OAAO,UAAU;AAAA,MACzD,MAAM;AAAA,IACR;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,OAAO,QAAQ,YAAY,aAAa,QAAQ,SAAS,IAAI;AAAA,MAC7D,MAAM,QAAQ,YACV,IAAI,KAAK,QAAQ,SAAS,EAAE,mBAAmB,IAC/C;AAAA,IACN;AAAA,EACF;AAEA,SACE,oBAAC,SAAI,WAAW,OAAO,QACpB,gBAAM,IAAI,CAAC,SACV,qBAAC,SAAqB,WAAW,OAAO,OACtC;AAAA,yBAAC,UAAK,WAAW,OAAO,YACrB;AAAA,WAAK,QAAQ,oBAAC,UAAK,WAAW,OAAO,MAAM,cAAW,UAAS;AAAA,MAC/D,KAAK;AAAA,OACR;AAAA,IACA,oBAAC,UAAK,WAAW,OAAO,YAAa,eAAK,OAAM;AAAA,IAC/C,KAAK,QAAQ,oBAAC,UAAK,WAAW,OAAO,WAAY,eAAK,MAAK;AAAA,OANpD,KAAK,KAOf,CACD,GACH;AAEJ;","names":[]}
|