@withpotter/pages-admin 0.13.0-next.76 → 0.13.0-next.78

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.
@@ -7,6 +7,9 @@ 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 { ACTIVE_HINT, STAGE_LABELS, stoppedAt } from "./account-stage";
11
+ import { AccountSessions } from "./account-sessions";
12
+ import { Hint } from "./hint";
10
13
  import { AccountVitals } from "./account-vitals";
11
14
  import { useAccountActions } from "./use-account-actions";
12
15
  import { useModuleOn } from "../_shell/modules-context";
@@ -56,11 +59,11 @@ function AccountPanel({
56
59
  label: account.emailVerified ? "Email verified" : "Email unverified"
57
60
  }
58
61
  ),
59
- account.mfaEnabled !== void 0 && /* @__PURE__ */ jsx(
62
+ account.stage && /* @__PURE__ */ jsx(
60
63
  StatusPill,
61
64
  {
62
- status: account.mfaEnabled ? "active" : "deleted",
63
- label: account.mfaEnabled ? "2FA on" : "2FA off"
65
+ status: account.stage === "active" ? "active" : "pending",
66
+ label: STAGE_LABELS[account.stage]
64
67
  }
65
68
  ),
66
69
  account.authProvider && /* @__PURE__ */ jsx("span", { className: styles.muted, children: account.authProvider })
@@ -106,6 +109,13 @@ function AccountPanel({
106
109
  (action, i) => action.isError && /* @__PURE__ */ jsx(Banner, { tone: "error", title: "That didn\u2019t work", children: action.error.message }, i)
107
110
  ),
108
111
  /* @__PURE__ */ jsx(AccountVitals, { account, workspaces: liveWorkspaces }),
112
+ account.stage && /* @__PURE__ */ jsxs("p", { className: styles.stopped, children: [
113
+ /* @__PURE__ */ jsxs("span", { className: styles.stoppedLabel, children: [
114
+ "Where this account stopped",
115
+ /* @__PURE__ */ jsx(Hint, { label: ACTIVE_HINT })
116
+ ] }),
117
+ /* @__PURE__ */ jsx("span", { children: stoppedAt(account) ?? "Active" })
118
+ ] }),
109
119
  /* @__PURE__ */ jsx(
110
120
  Section,
111
121
  {
@@ -123,7 +133,8 @@ function AccountPanel({
123
133
  }
124
134
  ),
125
135
  /* @__PURE__ */ jsx(Section, { title: "Workspaces", index: 1, count: liveWorkspaces, children: /* @__PURE__ */ jsx(Workspaces, { accountId: account.id }) }),
126
- /* @__PURE__ */ jsx(Section, { title: "Account ID", index: 2, children: /* @__PURE__ */ jsx(CopyText, { value: account.id }) })
136
+ /* @__PURE__ */ jsx(Section, { title: "Sessions", index: 2, children: /* @__PURE__ */ jsx(AccountSessions, { accountId: account.id }) }),
137
+ /* @__PURE__ */ jsx(Section, { title: "Account ID", index: 3, children: /* @__PURE__ */ jsx(CopyText, { value: account.id }) })
127
138
  ] })
128
139
  }
129
140
  );
@@ -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 { 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":[]}
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 { ACTIVE_HINT, STAGE_LABELS, stoppedAt } 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 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.stage && (\n <StatusPill\n status={account.stage === 'active' ? 'active' : 'pending'}\n label={STAGE_LABELS[account.stage]}\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 {account.stage && (\n <p className={styles.stopped}>\n <span className={styles.stoppedLabel}>\n Where this account stopped\n <Hint label={ACTIVE_HINT} />\n </span>\n <span>{stoppedAt(account) ?? 'Active'}</span>\n </p>\n )}\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=\"Sessions\" index={2}>\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":[]}
@@ -138,3 +138,24 @@
138
138
  line-height: 16px;
139
139
  color: var(--text-tertiary);
140
140
  }
141
+
142
+ .stopped {
143
+ display: grid;
144
+ gap: 4px;
145
+ margin: 0;
146
+ padding: 12px 16px;
147
+ border-radius: var(--adm-card-radius);
148
+ border: 1px solid var(--adm-line);
149
+ background: var(--bg-base);
150
+ font-size: 14px;
151
+ font-weight: 500;
152
+ line-height: 20px;
153
+ color: var(--text-primary);
154
+ }
155
+
156
+ .stoppedLabel {
157
+ font-size: 12px;
158
+ font-weight: 400;
159
+ line-height: 16px;
160
+ color: var(--text-tertiary);
161
+ }
@@ -11,6 +11,7 @@ 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 } from "./account-stage";
14
15
  import { useAccountActions } from "./use-account-actions";
15
16
  import { useModuleOn } from "../_shell/modules-context";
16
17
  import list from "../_ui/list-card.module.css";
@@ -76,11 +77,19 @@ function AccountListRow({
76
77
  ] }) }),
77
78
  visible.has("provider") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: PROVIDER_LABELS[a.authProvider] ?? (a.authProvider || EMPTY) }),
78
79
  /* @__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: a.stage === "active" ? "active" : "pending",
85
+ label: STAGE_LABELS[a.stage]
86
+ }
87
+ ) : /* @__PURE__ */ jsx("span", { className: list.muted, children: EMPTY }) }),
79
88
  visible.has("emailVerified") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(StatusPill, { status: a.emailVerified ? "verified" : "unverified" }) }),
80
89
  visible.has("workspaces") && /* @__PURE__ */ jsx("td", { className: list.num, children: a.workspaceCount ?? EMPTY }),
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" }),
90
+ visible.has("lastLogin") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: day(a.lastLoginAt) ?? "Never" }),
82
91
  visible.has("joined") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: day(a.createdAt) }),
83
- visible.has("mfa") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: a.mfaEnabled ? "Yes" : "No" }),
92
+ visible.has("firstSale") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: day(a.firstSaleAt) ?? "None yet" }),
84
93
  visible.has("activeSessions") && /* @__PURE__ */ jsx("td", { className: list.num, children: a.activeSessions ?? EMPTY }),
85
94
  visible.has("lastSeen") && /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: a.lastSeenAt ? /* @__PURE__ */ jsx(When, { iso: a.lastSeenAt, className: styles.when }) : "Never" }),
86
95
  /* @__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 { 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
+ {"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 { kycLabel, STAGE_LABELS } from './account-stage';\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('kyc') && (\n <td className={list.nowrap}>\n {kycLabel(a.kycStatus) ?? <span className={list.muted}>{EMPTY}</span>}\n </td>\n )}\n {visible.has('stage') && (\n <td className={list.nowrap}>\n {a.stage ? (\n <StatusPill\n status={a.stage === 'active' ? 'active' : 'pending'}\n label={STAGE_LABELS[a.stage]}\n />\n ) : (\n <span className={list.muted}>{EMPTY}</span>\n )}\n </td>\n )}\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 {day(a.lastLoginAt) ?? 'Never'}\n </td>\n )}\n {visible.has('joined') && (\n <td className={`${list.nowrap} ${list.muted}`}>{day(a.createdAt)}</td>\n )}\n {visible.has('firstSale') && (\n <td className={`${list.nowrap} ${list.muted}`}>\n {day(a.firstSaleAt) ?? 'None yet'}\n </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":[]}
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Button, EmptyNote } from "@withpotter/ui-kit";
4
+ import { AdmLoading } from "../_ui";
5
+ import { useAccountSessions } from "../hooks";
6
+ import styles from "./account-sessions.module.css";
7
+ const ENDED = {
8
+ active: "Active now",
9
+ revoked: "Signed out",
10
+ expired: "Expired"
11
+ };
12
+ const when = (iso) => iso ? new Date(iso).toLocaleString(void 0, {
13
+ dateStyle: "medium",
14
+ timeStyle: "short"
15
+ }) : null;
16
+ function deviceOf(s) {
17
+ return s.name || s.kind || s.userAgent || "Unknown device";
18
+ }
19
+ function AccountSessions({ accountId }) {
20
+ const { data, isLoading, isError, refetch } = useAccountSessions(accountId);
21
+ if (isLoading) return /* @__PURE__ */ jsx(AdmLoading, { label: "Loading sessions\u2026", size: "sm" });
22
+ if (isError) {
23
+ return /* @__PURE__ */ jsxs(EmptyNote, { children: [
24
+ "We could not load their sessions just now.",
25
+ " ",
26
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void refetch(), children: "Try again" })
27
+ ] });
28
+ }
29
+ if (!data?.length) {
30
+ return /* @__PURE__ */ jsx(EmptyNote, { children: "No sign-ins recorded for this person yet." });
31
+ }
32
+ const ordered = [...data].sort(
33
+ (a, b) => b.startedAt.localeCompare(a.startedAt)
34
+ );
35
+ return /* @__PURE__ */ jsx("ul", { className: styles.list, children: ordered.map((s) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
36
+ /* @__PURE__ */ jsxs("span", { className: styles.top, children: [
37
+ /* @__PURE__ */ jsx("span", { className: styles.device, title: s.userAgent ?? void 0, children: deviceOf(s) }),
38
+ /* @__PURE__ */ jsx("span", { className: styles.how, "data-live": s.endedHow === "active", children: ENDED[s.endedHow] })
39
+ ] }),
40
+ /* @__PURE__ */ jsx("span", { className: styles.meta, children: [s.ipAddress, s.location].filter(Boolean).join(" \xB7 ") || "No address recorded" }),
41
+ /* @__PURE__ */ jsxs("span", { className: styles.meta, children: [
42
+ "Started ",
43
+ when(s.startedAt),
44
+ s.endedAt && ` \xB7 Ended ${when(s.endedAt)}`
45
+ ] })
46
+ ] }, s.id)) });
47
+ }
48
+ export {
49
+ AccountSessions
50
+ };
51
+ //# sourceMappingURL=account-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/accounts/account-sessions.tsx"],"sourcesContent":["'use client';\n\nimport { Button, EmptyNote } from '@withpotter/ui-kit';\nimport type { AccountSession } from '@withpotter/lib/admin-api';\nimport { AdmLoading } from '../_ui';\nimport { useAccountSessions } from '../hooks';\nimport styles from './account-sessions.module.css';\n\nconst ENDED: Record<AccountSession['endedHow'], string> = {\n active: 'Active now',\n revoked: 'Signed out',\n expired: 'Expired',\n};\n\nconst when = (iso: string | null) =>\n iso\n ? new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n : null;\n\n/** A short device name from the session, falling back to the raw agent. */\nfunction deviceOf(s: AccountSession): string {\n return s.name || s.kind || s.userAgent || 'Unknown device';\n}\n\n/** Where and when this person signed in, newest first. */\nexport function AccountSessions({ accountId }: { accountId: string }) {\n const { data, isLoading, isError, refetch } = useAccountSessions(accountId);\n\n if (isLoading) return <AdmLoading label=\"Loading sessions…\" size=\"sm\" />;\n\n if (isError) {\n return (\n <EmptyNote>\n We could not load their sessions just now.{' '}\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void refetch()}>\n Try again\n </Button>\n </EmptyNote>\n );\n }\n\n if (!data?.length) {\n return <EmptyNote>No sign-ins recorded for this person yet.</EmptyNote>;\n }\n\n const ordered = [...data].sort((a: AccountSession, b: AccountSession) =>\n b.startedAt.localeCompare(a.startedAt),\n );\n\n return (\n <ul className={styles.list}>\n {ordered.map((s: AccountSession) => (\n <li key={s.id} className={styles.item}>\n <span className={styles.top}>\n <span className={styles.device} title={s.userAgent ?? undefined}>\n {deviceOf(s)}\n </span>\n <span className={styles.how} data-live={s.endedHow === 'active'}>\n {ENDED[s.endedHow]}\n </span>\n </span>\n <span className={styles.meta}>\n {[s.ipAddress, s.location].filter(Boolean).join(' · ') ||\n 'No address recorded'}\n </span>\n <span className={styles.meta}>\n Started {when(s.startedAt)}\n {s.endedAt && ` · Ended ${when(s.endedAt)}`}\n </span>\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";AA+BwB,cAIlB,YAJkB;AA7BxB,SAAS,QAAQ,iBAAiB;AAElC,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,OAAO,YAAY;AAEnB,MAAM,QAAoD;AAAA,EACxD,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAEA,MAAM,OAAO,CAAC,QACZ,MACI,IAAI,KAAK,GAAG,EAAE,eAAe,QAAW;AAAA,EACtC,WAAW;AAAA,EACX,WAAW;AACb,CAAC,IACD;AAGN,SAAS,SAAS,GAA2B;AAC3C,SAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa;AAC5C;AAGO,SAAS,gBAAgB,EAAE,UAAU,GAA0B;AACpE,QAAM,EAAE,MAAM,WAAW,SAAS,QAAQ,IAAI,mBAAmB,SAAS;AAE1E,MAAI,UAAW,QAAO,oBAAC,cAAW,OAAM,0BAAoB,MAAK,MAAK;AAEtE,MAAI,SAAS;AACX,WACE,qBAAC,aAAU;AAAA;AAAA,MACkC;AAAA,MAC3C,oBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,QAAQ,GAAG,uBAEjE;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM,QAAQ;AACjB,WAAO,oBAAC,aAAU,uDAAyC;AAAA,EAC7D;AAEA,QAAM,UAAU,CAAC,GAAG,IAAI,EAAE;AAAA,IAAK,CAAC,GAAmB,MACjD,EAAE,UAAU,cAAc,EAAE,SAAS;AAAA,EACvC;AAEA,SACE,oBAAC,QAAG,WAAW,OAAO,MACnB,kBAAQ,IAAI,CAAC,MACZ,qBAAC,QAAc,WAAW,OAAO,MAC/B;AAAA,yBAAC,UAAK,WAAW,OAAO,KACtB;AAAA,0BAAC,UAAK,WAAW,OAAO,QAAQ,OAAO,EAAE,aAAa,QACnD,mBAAS,CAAC,GACb;AAAA,MACA,oBAAC,UAAK,WAAW,OAAO,KAAK,aAAW,EAAE,aAAa,UACpD,gBAAM,EAAE,QAAQ,GACnB;AAAA,OACF;AAAA,IACA,oBAAC,UAAK,WAAW,OAAO,MACrB,WAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK,KACnD,uBACJ;AAAA,IACA,qBAAC,UAAK,WAAW,OAAO,MAAM;AAAA;AAAA,MACnB,KAAK,EAAE,SAAS;AAAA,MACxB,EAAE,WAAW,eAAY,KAAK,EAAE,OAAO,CAAC;AAAA,OAC3C;AAAA,OAhBO,EAAE,EAiBX,CACD,GACH;AAEJ;","names":[]}
@@ -0,0 +1,53 @@
1
+ .list {
2
+ display: grid;
3
+ gap: 8px;
4
+ max-height: 320px;
5
+ margin: 0;
6
+ padding: 0;
7
+ overflow-y: auto;
8
+ list-style: none;
9
+ }
10
+
11
+ .item {
12
+ display: grid;
13
+ gap: 2px;
14
+ padding: 12px 16px;
15
+ border-radius: var(--adm-card-radius);
16
+ border: 1px solid var(--adm-line);
17
+ background: var(--bg-base);
18
+ }
19
+
20
+ .top {
21
+ display: flex;
22
+ align-items: baseline;
23
+ justify-content: space-between;
24
+ gap: 12px;
25
+ }
26
+
27
+ .device {
28
+ min-width: 0;
29
+ overflow: hidden;
30
+ text-overflow: ellipsis;
31
+ white-space: nowrap;
32
+ font-size: 14px;
33
+ font-weight: 500;
34
+ line-height: 20px;
35
+ color: var(--text-primary);
36
+ }
37
+
38
+ .how {
39
+ flex-shrink: 0;
40
+ font-size: 12px;
41
+ line-height: 16px;
42
+ color: var(--text-tertiary);
43
+ }
44
+
45
+ .how[data-live='true'] {
46
+ color: var(--success);
47
+ }
48
+
49
+ .meta {
50
+ font-size: 12px;
51
+ line-height: 16px;
52
+ color: var(--text-tertiary);
53
+ }
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { FilterSelect } from "../_ui";
4
+ import { STAGE_LABELS, STAGE_ORDER } from "./account-stage";
5
+ const OPTIONS = [
6
+ { value: "", label: "All stages" },
7
+ ...STAGE_ORDER.map((s) => ({
8
+ value: s,
9
+ label: STAGE_LABELS[s]
10
+ }))
11
+ ];
12
+ function AccountStageFilter({
13
+ value,
14
+ onChange
15
+ }) {
16
+ return /* @__PURE__ */ jsx(
17
+ FilterSelect,
18
+ {
19
+ label: "Stage",
20
+ value,
21
+ onChange: (v) => onChange(v),
22
+ options: OPTIONS
23
+ }
24
+ );
25
+ }
26
+ export {
27
+ AccountStageFilter
28
+ };
29
+ //# sourceMappingURL=account-stage-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/accounts/account-stage-filter.tsx"],"sourcesContent":["'use client';\n\nimport type { AccountStage } from '@withpotter/lib/admin-api';\nimport { FilterSelect } from '../_ui';\nimport { STAGE_LABELS, STAGE_ORDER } from './account-stage';\n\nconst OPTIONS = [\n { value: '', label: 'All stages' },\n ...STAGE_ORDER.map((s: AccountStage) => ({\n value: s,\n label: STAGE_LABELS[s],\n })),\n];\n\n/** Narrow the accounts list to everyone who stopped at one stage. */\nexport function AccountStageFilter({\n value,\n onChange,\n}: {\n value: AccountStage | '';\n onChange: (value: AccountStage | '') => void;\n}) {\n return (\n <FilterSelect\n label=\"Stage\"\n value={value}\n onChange={(v: string) => onChange(v as AccountStage | '')}\n options={OPTIONS}\n />\n );\n}\n"],"mappings":";AAuBI;AApBJ,SAAS,oBAAoB;AAC7B,SAAS,cAAc,mBAAmB;AAE1C,MAAM,UAAU;AAAA,EACd,EAAE,OAAO,IAAI,OAAO,aAAa;AAAA,EACjC,GAAG,YAAY,IAAI,CAAC,OAAqB;AAAA,IACvC,OAAO;AAAA,IACP,OAAO,aAAa,CAAC;AAAA,EACvB,EAAE;AACJ;AAGO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN;AAAA,MACA,UAAU,CAAC,MAAc,SAAS,CAAsB;AAAA,MACxD,SAAS;AAAA;AAAA,EACX;AAEJ;","names":[]}
@@ -0,0 +1,47 @@
1
+ const STAGE_LABELS = {
2
+ signed_up: "Signed up",
3
+ verified: "Email verified",
4
+ onboarding: "Onboarding",
5
+ kyc_approved: "KYC approved",
6
+ active: "Active"
7
+ };
8
+ const STAGE_ORDER = [
9
+ "signed_up",
10
+ "verified",
11
+ "onboarding",
12
+ "kyc_approved",
13
+ "active"
14
+ ];
15
+ const KYC_LABELS = {
16
+ NOT_STARTED: "Not started",
17
+ CHECKING: "Checking",
18
+ WAITING_REVIEW: "In review",
19
+ NEEDS_MORE: "Needs more",
20
+ APPROVED: "Approved",
21
+ DENIED: "Denied"
22
+ };
23
+ const ACTIVE_HINT = "Active means the email is verified, KYC or KYB is approved, and at least one sale was paid.";
24
+ function kycLabel(status) {
25
+ if (!status) return null;
26
+ return KYC_LABELS[status] ?? status;
27
+ }
28
+ function stoppedAt(a) {
29
+ if (a.stage === void 0 && a.emailVerified === void 0) return null;
30
+ if (a.stage === "active") return null;
31
+ if (a.emailVerified === false) return "Verify their email";
32
+ if (a.workspaceCount === 0) return "Add a business";
33
+ if (a.kycStatus !== "APPROVED") {
34
+ return a.kycStatus === "DENIED" ? "Finish KYC or KYB (denied)" : "Finish KYC or KYB";
35
+ }
36
+ if (!a.firstSaleAt) return "Make a first sale";
37
+ return null;
38
+ }
39
+ export {
40
+ ACTIVE_HINT,
41
+ KYC_LABELS,
42
+ STAGE_LABELS,
43
+ STAGE_ORDER,
44
+ kycLabel,
45
+ stoppedAt
46
+ };
47
+ //# sourceMappingURL=account-stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/accounts/account-stage.ts"],"sourcesContent":["import type {\n AccountKycStatus,\n AccountRow,\n AccountStage,\n} from '@withpotter/lib/admin-api';\n\nexport const STAGE_LABELS: Record<AccountStage, string> = {\n signed_up: 'Signed up',\n verified: 'Email verified',\n onboarding: 'Onboarding',\n kyc_approved: 'KYC approved',\n active: 'Active',\n};\n\nexport const STAGE_ORDER: AccountStage[] = [\n 'signed_up',\n 'verified',\n 'onboarding',\n 'kyc_approved',\n 'active',\n];\n\nexport const KYC_LABELS: Record<AccountKycStatus, string> = {\n NOT_STARTED: 'Not started',\n CHECKING: 'Checking',\n WAITING_REVIEW: 'In review',\n NEEDS_MORE: 'Needs more',\n APPROVED: 'Approved',\n DENIED: 'Denied',\n};\n\n/** What \"active\" means, said once for every place that needs it. */\nexport const ACTIVE_HINT =\n 'Active means the email is verified, KYC or KYB is approved, and at least one sale was paid.';\n\n/** Plain words for a KYC or KYB status; the raw value for unknown ones. */\nexport function kycLabel(status: string | null | undefined): string | null {\n if (!status) return null;\n\n return KYC_LABELS[status as AccountKycStatus] ?? status;\n}\n\ntype Progress = Pick<\n AccountRow,\n 'emailVerified' | 'workspaceCount' | 'kycStatus' | 'firstSaleAt' | 'stage'\n>;\n\n/**\n * The first step this account has not done, from the fields the list sends.\n * Returns null when every step is done (or when nothing is known yet).\n */\nexport function stoppedAt(a: Partial<Progress>): string | null {\n if (a.stage === undefined && a.emailVerified === undefined) return null;\n if (a.stage === 'active') return null;\n if (a.emailVerified === false) return 'Verify their email';\n if (a.workspaceCount === 0) return 'Add a business';\n if (a.kycStatus !== 'APPROVED') {\n return a.kycStatus === 'DENIED'\n ? 'Finish KYC or KYB (denied)'\n : 'Finish KYC or KYB';\n }\n if (!a.firstSaleAt) return 'Make a first sale';\n\n return null;\n}\n"],"mappings":"AAMO,MAAM,eAA6C;AAAA,EACxD,WAAW;AAAA,EACX,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,QAAQ;AACV;AAEO,MAAM,cAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,aAA+C;AAAA,EAC1D,aAAa;AAAA,EACb,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,QAAQ;AACV;AAGO,MAAM,cACX;AAGK,SAAS,SAAS,QAAkD;AACzE,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,WAAW,MAA0B,KAAK;AACnD;AAWO,SAAS,UAAU,GAAqC;AAC7D,MAAI,EAAE,UAAU,UAAa,EAAE,kBAAkB,OAAW,QAAO;AACnE,MAAI,EAAE,UAAU,SAAU,QAAO;AACjC,MAAI,EAAE,kBAAkB,MAAO,QAAO;AACtC,MAAI,EAAE,mBAAmB,EAAG,QAAO;AACnC,MAAI,EAAE,cAAc,YAAY;AAC9B,WAAO,EAAE,cAAc,WACnB,+BACA;AAAA,EACN;AACA,MAAI,CAAC,EAAE,YAAa,QAAO;AAE3B,SAAO;AACT;","names":[]}
@@ -3,7 +3,9 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Button, OverflowItem, OverflowMenu, Th } from "@withpotter/ui-kit";
5
5
  import { toCsv } from "@withpotter/lib/csv";
6
- import { platformAdminApi } from "@withpotter/lib/admin-api";
6
+ import {
7
+ platformAdminApi
8
+ } from "@withpotter/lib/admin-api";
7
9
  import {
8
10
  ColumnPicker,
9
11
  FilterDates,
@@ -13,20 +15,27 @@ import {
13
15
  import { usePlatformAccounts } from "../hooks";
14
16
  import { AccountPanel } from "./account-panel";
15
17
  import { ListCard, ListPager } from "../_ui/list-card";
18
+ import { ACTIVE_HINT, kycLabel, STAGE_LABELS } from "./account-stage";
19
+ import { AccountStageFilter } from "./account-stage-filter";
20
+ import { Hint } from "./hint";
16
21
  import { AccountListRow, PROVIDER_LABELS } from "./account-row";
17
22
  import { PROVIDER_LABELS as PROVIDER_LABELS2 } from "./account-row";
18
23
  const OPTIONAL_COLUMNS = [
19
24
  { key: "provider", label: "Provider" },
25
+ { key: "kyc", label: "KYC / KYB" },
26
+ { key: "stage", label: "Stage" },
20
27
  { key: "emailVerified", label: "Email verified" },
21
28
  { key: "workspaces", label: "Workspaces" },
22
29
  { key: "lastLogin", label: "Last login" },
23
30
  { key: "joined", label: "Joined" },
24
- { key: "mfa", label: "MFA enabled" },
31
+ { key: "firstSale", label: "First sale" },
25
32
  { key: "activeSessions", label: "Active sessions" },
26
33
  { key: "lastSeen", label: "Last seen" }
27
34
  ];
28
35
  const DEFAULT_VISIBLE = [
29
36
  "provider",
37
+ "kyc",
38
+ "stage",
30
39
  "emailVerified",
31
40
  "workspaces",
32
41
  "lastLogin",
@@ -55,13 +64,15 @@ function toCsvRow(a, visible) {
55
64
  cells.push(PROVIDER_LABELS[a.authProvider] ?? a.authProvider);
56
65
  }
57
66
  cells.push(a.status);
67
+ if (visible.has("kyc")) cells.push(kycLabel(a.kycStatus) ?? "");
68
+ if (visible.has("stage")) cells.push(a.stage ? STAGE_LABELS[a.stage] : "");
58
69
  if (visible.has("emailVerified")) {
59
70
  cells.push(a.emailVerified ? "Verified" : "Unverified");
60
71
  }
61
72
  if (visible.has("workspaces")) cells.push(String(a.workspaceCount ?? 0));
62
73
  if (visible.has("lastLogin")) cells.push(day(a.lastLoginAt) || "Never");
63
74
  if (visible.has("joined")) cells.push(day(a.createdAt));
64
- if (visible.has("mfa")) cells.push(a.mfaEnabled ? "Yes" : "No");
75
+ if (visible.has("firstSale")) cells.push(day(a.firstSaleAt) || "None yet");
65
76
  if (visible.has("activeSessions")) cells.push(String(a.activeSessions ?? 0));
66
77
  if (visible.has("lastSeen")) cells.push(day(a.lastSeenAt) || "Never");
67
78
  return cells;
@@ -73,6 +84,7 @@ function AccountsTable({
73
84
  const [search, setSearch] = useState("");
74
85
  const [joinedFrom, setJoinedFrom] = useState("");
75
86
  const [joinedTo, setJoinedTo] = useState("");
87
+ const [stage, setStage] = useState("");
76
88
  const [page, setPage] = useState(1);
77
89
  const [visible, setVisible] = useState(readVisibleColumns);
78
90
  const [exporting, setExporting] = useState(false);
@@ -81,7 +93,8 @@ function AccountsTable({
81
93
  pageSize: 20,
82
94
  search: search || void 0,
83
95
  joinedFrom: joinedFrom || void 0,
84
- joinedTo: joinedTo || void 0
96
+ joinedTo: joinedTo || void 0,
97
+ stage: stage || void 0
85
98
  });
86
99
  const current = data?.rows.find((row) => row.id === selected?.id) ?? selected;
87
100
  const totalPages = data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1;
@@ -100,16 +113,19 @@ function AccountsTable({
100
113
  pageSize: 5e3,
101
114
  search: search || void 0,
102
115
  joinedFrom: joinedFrom || void 0,
103
- joinedTo: joinedTo || void 0
116
+ joinedTo: joinedTo || void 0,
117
+ stage: stage || void 0
104
118
  });
105
119
  const header = ["Name", "Email"];
106
120
  if (visible.has("provider")) header.push("Provider");
107
121
  header.push("Status");
122
+ if (visible.has("kyc")) header.push("KYC / KYB");
123
+ if (visible.has("stage")) header.push("Stage");
108
124
  if (visible.has("emailVerified")) header.push("Email verified");
109
125
  if (visible.has("workspaces")) header.push("Workspaces");
110
126
  if (visible.has("lastLogin")) header.push("Last login");
111
127
  if (visible.has("joined")) header.push("Joined");
112
- if (visible.has("mfa")) header.push("MFA enabled");
128
+ if (visible.has("firstSale")) header.push("First sale");
113
129
  if (visible.has("activeSessions")) header.push("Active sessions");
114
130
  if (visible.has("lastSeen")) header.push("Last seen");
115
131
  const rows = [header, ...page2.rows.map((a2) => toCsvRow(a2, visible))];
@@ -139,7 +155,7 @@ function AccountsTable({
139
155
  onRetry: () => void refetch(),
140
156
  errorTitle: "Failed to load accounts.",
141
157
  isEmpty: data?.rows.length === 0,
142
- emptyTitle: "No accounts match this search or join date.",
158
+ emptyTitle: "No accounts match these filters.",
143
159
  controls: /* @__PURE__ */ jsxs(Filters, { label: "Filter accounts", children: [
144
160
  /* @__PURE__ */ jsx(
145
161
  FilterSearch,
@@ -149,6 +165,16 @@ function AccountsTable({
149
165
  placeholder: "Search by email or name\u2026"
150
166
  }
151
167
  ),
168
+ /* @__PURE__ */ jsx(
169
+ AccountStageFilter,
170
+ {
171
+ value: stage,
172
+ onChange: (v) => {
173
+ setStage(v);
174
+ setPage(1);
175
+ }
176
+ }
177
+ ),
152
178
  /* @__PURE__ */ jsx(
153
179
  FilterDates,
154
180
  {
@@ -194,11 +220,16 @@ function AccountsTable({
194
220
  /* @__PURE__ */ jsx(Th, { children: "Account" }),
195
221
  visible.has("provider") && /* @__PURE__ */ jsx(Th, { children: "Provider" }),
196
222
  /* @__PURE__ */ jsx(Th, { children: "Status" }),
223
+ visible.has("kyc") && /* @__PURE__ */ jsx(Th, { children: "KYC / KYB" }),
224
+ visible.has("stage") && /* @__PURE__ */ jsxs(Th, { children: [
225
+ "Stage",
226
+ /* @__PURE__ */ jsx(Hint, { label: ACTIVE_HINT })
227
+ ] }),
197
228
  visible.has("emailVerified") && /* @__PURE__ */ jsx(Th, { children: "Email" }),
198
229
  visible.has("workspaces") && /* @__PURE__ */ jsx(Th, { align: "right", children: "Workspaces" }),
199
230
  visible.has("lastLogin") && /* @__PURE__ */ jsx(Th, { children: "Last login" }),
200
231
  visible.has("joined") && /* @__PURE__ */ jsx(Th, { children: "Joined" }),
201
- visible.has("mfa") && /* @__PURE__ */ jsx(Th, { children: "MFA" }),
232
+ visible.has("firstSale") && /* @__PURE__ */ jsx(Th, { children: "First sale" }),
202
233
  visible.has("activeSessions") && /* @__PURE__ */ jsx(Th, { align: "right", children: "Sessions" }),
203
234
  visible.has("lastSeen") && /* @__PURE__ */ jsx(Th, { children: "Last seen" }),
204
235
  /* @__PURE__ */ jsx(Th, { align: "right", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/accounts/accounts-table.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, OverflowItem, OverflowMenu, Th } from '@withpotter/ui-kit';\nimport { toCsv } from '@withpotter/lib/csv';\nimport { platformAdminApi, type AccountRow } from '@withpotter/lib/admin-api';\nimport {\n ColumnPicker,\n FilterDates,\n FilterSearch,\n Filters,\n type ColumnOption,\n} from '../_ui';\nimport { usePlatformAccounts } from '../hooks';\nimport { AccountPanel, type AccountSummary } from './account-panel';\nimport { ListCard, ListPager } from '../_ui/list-card';\nimport { AccountListRow, PROVIDER_LABELS } from './account-row';\n\nexport { PROVIDER_LABELS } from './account-row';\n\nconst OPTIONAL_COLUMNS: ColumnOption[] = [\n { key: 'provider', label: 'Provider' },\n { key: 'emailVerified', label: 'Email verified' },\n { key: 'workspaces', label: 'Workspaces' },\n { key: 'lastLogin', label: 'Last login' },\n { key: 'joined', label: 'Joined' },\n { key: 'mfa', label: 'MFA enabled' },\n { key: 'activeSessions', label: 'Active sessions' },\n { key: 'lastSeen', label: 'Last seen' },\n];\nconst DEFAULT_VISIBLE = [\n 'provider',\n 'emailVerified',\n 'workspaces',\n 'lastLogin',\n 'joined',\n];\nconst COLUMNS_KEY = 'adm-accounts-columns';\n\nfunction readVisibleColumns(): Set<string> {\n if (typeof window === 'undefined') return new Set(DEFAULT_VISIBLE);\n try {\n const raw = window.localStorage.getItem(COLUMNS_KEY);\n return raw\n ? new Set(raw.split(',').filter(Boolean))\n : new Set(DEFAULT_VISIBLE);\n } catch {\n return new Set(DEFAULT_VISIBLE);\n }\n}\n\nfunction writeVisibleColumns(cols: Set<string>): void {\n try {\n window.localStorage.setItem(COLUMNS_KEY, [...cols].join(','));\n } catch {\n /* storage unavailable (private mode / quota) — non-fatal */\n }\n}\n\nconst day = (iso?: string | null) =>\n iso ? new Date(iso).toLocaleDateString() : '';\n\n/** One row's values, in the same column order the table renders. */\nfunction toCsvRow(a: AccountRow, visible: Set<string>): string[] {\n const cells: string[] = [a.name ?? '', a.email];\n if (visible.has('provider')) {\n cells.push(PROVIDER_LABELS[a.authProvider] ?? a.authProvider);\n }\n cells.push(a.status);\n if (visible.has('emailVerified')) {\n cells.push(a.emailVerified ? 'Verified' : 'Unverified');\n }\n if (visible.has('workspaces')) cells.push(String(a.workspaceCount ?? 0));\n if (visible.has('lastLogin')) cells.push(day(a.lastLoginAt) || 'Never');\n if (visible.has('joined')) cells.push(day(a.createdAt));\n if (visible.has('mfa')) cells.push(a.mfaEnabled ? 'Yes' : 'No');\n if (visible.has('activeSessions')) cells.push(String(a.activeSessions ?? 0));\n if (visible.has('lastSeen')) cells.push(day(a.lastSeenAt) || 'Never');\n return cells;\n}\n\n/** Every account, searchable and filterable by join date. */\nexport function AccountsTable({\n selected,\n onSelect,\n}: {\n selected: AccountSummary | null;\n onSelect: (account: AccountSummary | null) => void;\n}) {\n const [search, setSearch] = useState('');\n const [joinedFrom, setJoinedFrom] = useState('');\n const [joinedTo, setJoinedTo] = useState('');\n const [page, setPage] = useState(1);\n const [visible, setVisible] = useState<Set<string>>(readVisibleColumns);\n const [exporting, setExporting] = useState(false);\n const { data, isLoading, isError, refetch } = usePlatformAccounts({\n page,\n pageSize: 20,\n search: search || undefined,\n joinedFrom: joinedFrom || undefined,\n joinedTo: joinedTo || undefined,\n });\n // Show the freshest copy of the selected account after an action refetches.\n const current = data?.rows.find((row) => row.id === selected?.id) ?? selected;\n const totalPages = data\n ? Math.max(1, Math.ceil(data.total / data.pageSize))\n : 1;\n const filter = (set: (v: string) => void) => (v: string) => {\n set(v);\n setPage(1);\n };\n\n const changeColumns = (next: Set<string>) => {\n setVisible(next);\n writeVisibleColumns(next);\n };\n\n // Every account matching the current search/join-date filter, not just\n // this page — a export of \"what I'm looking at\" that stops at 20 rows\n // because of pagination would just be confusing.\n const exportCsv = async () => {\n setExporting(true);\n try {\n const page = await platformAdminApi.accounts({\n pageSize: 5000,\n search: search || undefined,\n joinedFrom: joinedFrom || undefined,\n joinedTo: joinedTo || undefined,\n });\n const header = ['Name', 'Email'];\n if (visible.has('provider')) header.push('Provider');\n header.push('Status');\n if (visible.has('emailVerified')) header.push('Email verified');\n if (visible.has('workspaces')) header.push('Workspaces');\n if (visible.has('lastLogin')) header.push('Last login');\n if (visible.has('joined')) header.push('Joined');\n if (visible.has('mfa')) header.push('MFA enabled');\n if (visible.has('activeSessions')) header.push('Active sessions');\n if (visible.has('lastSeen')) header.push('Last seen');\n const rows = [header, ...page.rows.map((a) => toCsvRow(a, visible))];\n const url = URL.createObjectURL(\n new Blob([toCsv(rows)], { type: 'text/csv' }),\n );\n const a = document.createElement('a');\n a.href = url;\n a.download = 'accounts.csv';\n a.click();\n URL.revokeObjectURL(url);\n } finally {\n setExporting(false);\n }\n };\n\n const columns = 3 + visible.size; // Account + Status + Actions, plus optional\n\n return (\n <>\n <ListCard\n title=\"All accounts\"\n count={data?.total}\n columns={columns}\n minWidth={880}\n isLoading={isLoading}\n isError={isError}\n onRetry={() => void refetch()}\n errorTitle=\"Failed to load accounts.\"\n isEmpty={data?.rows.length === 0}\n emptyTitle=\"No accounts match this search or join date.\"\n controls={\n <Filters label=\"Filter accounts\">\n <FilterSearch\n value={search}\n onChange={filter(setSearch)}\n placeholder=\"Search by email or name…\"\n />\n <FilterDates\n label=\"Joined\"\n fromLabel=\"Joined from\"\n toLabel=\"Joined to\"\n from={joinedFrom}\n to={joinedTo}\n onFrom={filter(setJoinedFrom)}\n onTo={filter(setJoinedTo)}\n />\n {(joinedFrom || joinedTo) && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setJoinedFrom('');\n setJoinedTo('');\n setPage(1);\n }}\n >\n Clear\n </Button>\n )}\n <ColumnPicker\n options={OPTIONAL_COLUMNS}\n visible={visible}\n onChange={changeColumns}\n />\n <OverflowMenu label=\"Export\">\n <OverflowItem\n onClick={() => void exportCsv()}\n disabled={exporting || !data?.total}\n >\n {exporting ? 'Exporting…' : 'Export CSV'}\n </OverflowItem>\n </OverflowMenu>\n </Filters>\n }\n head={\n <tr>\n <Th>Account</Th>\n {visible.has('provider') && <Th>Provider</Th>}\n <Th>Status</Th>\n {visible.has('emailVerified') && <Th>Email</Th>}\n {visible.has('workspaces') && <Th align=\"right\">Workspaces</Th>}\n {visible.has('lastLogin') && <Th>Last login</Th>}\n {visible.has('joined') && <Th>Joined</Th>}\n {visible.has('mfa') && <Th>MFA</Th>}\n {visible.has('activeSessions') && <Th align=\"right\">Sessions</Th>}\n {visible.has('lastSeen') && <Th>Last seen</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=\"accounts\"\n onPage={setPage}\n />\n }\n >\n {data?.rows.map((a) => (\n <AccountListRow\n key={a.id}\n a={a}\n active={a.id === selected?.id}\n onOpen={() => onSelect(a)}\n visible={visible}\n />\n ))}\n </ListCard>\n <AccountPanel account={current} onClose={() => onSelect(null)} />\n </>\n );\n}\n"],"mappings":"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","names":["PROVIDER_LABELS","page","a"]}
1
+ {"version":3,"sources":["../../../src/admin/accounts/accounts-table.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, OverflowItem, OverflowMenu, Th } from '@withpotter/ui-kit';\nimport { toCsv } from '@withpotter/lib/csv';\nimport {\n platformAdminApi,\n type AccountRow,\n type AccountStage,\n} from '@withpotter/lib/admin-api';\nimport {\n ColumnPicker,\n FilterDates,\n FilterSearch,\n Filters,\n type ColumnOption,\n} from '../_ui';\nimport { usePlatformAccounts } from '../hooks';\nimport { AccountPanel, type AccountSummary } from './account-panel';\nimport { ListCard, ListPager } from '../_ui/list-card';\nimport { ACTIVE_HINT, kycLabel, STAGE_LABELS } from './account-stage';\nimport { AccountStageFilter } from './account-stage-filter';\nimport { Hint } from './hint';\nimport { AccountListRow, PROVIDER_LABELS } from './account-row';\n\nexport { PROVIDER_LABELS } from './account-row';\n\nconst OPTIONAL_COLUMNS: ColumnOption[] = [\n { key: 'provider', label: 'Provider' },\n { key: 'kyc', label: 'KYC / KYB' },\n { key: 'stage', label: 'Stage' },\n { key: 'emailVerified', label: 'Email verified' },\n { key: 'workspaces', label: 'Workspaces' },\n { key: 'lastLogin', label: 'Last login' },\n { key: 'joined', label: 'Joined' },\n { key: 'firstSale', label: 'First sale' },\n { key: 'activeSessions', label: 'Active sessions' },\n { key: 'lastSeen', label: 'Last seen' },\n];\nconst DEFAULT_VISIBLE = [\n 'provider',\n 'kyc',\n 'stage',\n 'emailVerified',\n 'workspaces',\n 'lastLogin',\n 'joined',\n];\nconst COLUMNS_KEY = 'adm-accounts-columns';\n\nfunction readVisibleColumns(): Set<string> {\n if (typeof window === 'undefined') return new Set(DEFAULT_VISIBLE);\n try {\n const raw = window.localStorage.getItem(COLUMNS_KEY);\n return raw\n ? new Set(raw.split(',').filter(Boolean))\n : new Set(DEFAULT_VISIBLE);\n } catch {\n return new Set(DEFAULT_VISIBLE);\n }\n}\n\nfunction writeVisibleColumns(cols: Set<string>): void {\n try {\n window.localStorage.setItem(COLUMNS_KEY, [...cols].join(','));\n } catch {\n /* storage unavailable (private mode / quota) — non-fatal */\n }\n}\n\nconst day = (iso?: string | null) =>\n iso ? new Date(iso).toLocaleDateString() : '';\n\n/** One row's values, in the same column order the table renders. */\nfunction toCsvRow(a: AccountRow, visible: Set<string>): string[] {\n const cells: string[] = [a.name ?? '', a.email];\n if (visible.has('provider')) {\n cells.push(PROVIDER_LABELS[a.authProvider] ?? a.authProvider);\n }\n cells.push(a.status);\n if (visible.has('kyc')) cells.push(kycLabel(a.kycStatus) ?? '');\n if (visible.has('stage')) cells.push(a.stage ? STAGE_LABELS[a.stage] : '');\n if (visible.has('emailVerified')) {\n cells.push(a.emailVerified ? 'Verified' : 'Unverified');\n }\n if (visible.has('workspaces')) cells.push(String(a.workspaceCount ?? 0));\n if (visible.has('lastLogin')) cells.push(day(a.lastLoginAt) || 'Never');\n if (visible.has('joined')) cells.push(day(a.createdAt));\n if (visible.has('firstSale')) cells.push(day(a.firstSaleAt) || 'None yet');\n if (visible.has('activeSessions')) cells.push(String(a.activeSessions ?? 0));\n if (visible.has('lastSeen')) cells.push(day(a.lastSeenAt) || 'Never');\n return cells;\n}\n\n/** Every account, searchable and filterable by stage and join date. */\nexport function AccountsTable({\n selected,\n onSelect,\n}: {\n selected: AccountSummary | null;\n onSelect: (account: AccountSummary | null) => void;\n}) {\n const [search, setSearch] = useState('');\n const [joinedFrom, setJoinedFrom] = useState('');\n const [joinedTo, setJoinedTo] = useState('');\n const [stage, setStage] = useState<AccountStage | ''>('');\n const [page, setPage] = useState(1);\n const [visible, setVisible] = useState<Set<string>>(readVisibleColumns);\n const [exporting, setExporting] = useState(false);\n const { data, isLoading, isError, refetch } = usePlatformAccounts({\n page,\n pageSize: 20,\n search: search || undefined,\n joinedFrom: joinedFrom || undefined,\n joinedTo: joinedTo || undefined,\n stage: stage || undefined,\n });\n // Show the freshest copy of the selected account after an action refetches.\n const current = data?.rows.find((row) => row.id === selected?.id) ?? selected;\n const totalPages = data\n ? Math.max(1, Math.ceil(data.total / data.pageSize))\n : 1;\n const filter = (set: (v: string) => void) => (v: string) => {\n set(v);\n setPage(1);\n };\n\n const changeColumns = (next: Set<string>) => {\n setVisible(next);\n writeVisibleColumns(next);\n };\n\n // Every account matching the current search/join-date filter, not just\n // this page — a export of \"what I'm looking at\" that stops at 20 rows\n // because of pagination would just be confusing.\n const exportCsv = async () => {\n setExporting(true);\n try {\n const page = await platformAdminApi.accounts({\n pageSize: 5000,\n search: search || undefined,\n joinedFrom: joinedFrom || undefined,\n joinedTo: joinedTo || undefined,\n stage: stage || undefined,\n });\n const header = ['Name', 'Email'];\n if (visible.has('provider')) header.push('Provider');\n header.push('Status');\n if (visible.has('kyc')) header.push('KYC / KYB');\n if (visible.has('stage')) header.push('Stage');\n if (visible.has('emailVerified')) header.push('Email verified');\n if (visible.has('workspaces')) header.push('Workspaces');\n if (visible.has('lastLogin')) header.push('Last login');\n if (visible.has('joined')) header.push('Joined');\n if (visible.has('firstSale')) header.push('First sale');\n if (visible.has('activeSessions')) header.push('Active sessions');\n if (visible.has('lastSeen')) header.push('Last seen');\n const rows = [header, ...page.rows.map((a) => toCsvRow(a, visible))];\n const url = URL.createObjectURL(\n new Blob([toCsv(rows)], { type: 'text/csv' }),\n );\n const a = document.createElement('a');\n a.href = url;\n a.download = 'accounts.csv';\n a.click();\n URL.revokeObjectURL(url);\n } finally {\n setExporting(false);\n }\n };\n\n const columns = 3 + visible.size; // Account + Status + Actions, plus optional\n\n return (\n <>\n <ListCard\n title=\"All accounts\"\n count={data?.total}\n columns={columns}\n minWidth={880}\n isLoading={isLoading}\n isError={isError}\n onRetry={() => void refetch()}\n errorTitle=\"Failed to load accounts.\"\n isEmpty={data?.rows.length === 0}\n emptyTitle=\"No accounts match these filters.\"\n controls={\n <Filters label=\"Filter accounts\">\n <FilterSearch\n value={search}\n onChange={filter(setSearch)}\n placeholder=\"Search by email or name…\"\n />\n <AccountStageFilter\n value={stage}\n onChange={(v: AccountStage | '') => {\n setStage(v);\n setPage(1);\n }}\n />\n <FilterDates\n label=\"Joined\"\n fromLabel=\"Joined from\"\n toLabel=\"Joined to\"\n from={joinedFrom}\n to={joinedTo}\n onFrom={filter(setJoinedFrom)}\n onTo={filter(setJoinedTo)}\n />\n {(joinedFrom || joinedTo) && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setJoinedFrom('');\n setJoinedTo('');\n setPage(1);\n }}\n >\n Clear\n </Button>\n )}\n <ColumnPicker\n options={OPTIONAL_COLUMNS}\n visible={visible}\n onChange={changeColumns}\n />\n <OverflowMenu label=\"Export\">\n <OverflowItem\n onClick={() => void exportCsv()}\n disabled={exporting || !data?.total}\n >\n {exporting ? 'Exporting…' : 'Export CSV'}\n </OverflowItem>\n </OverflowMenu>\n </Filters>\n }\n head={\n <tr>\n <Th>Account</Th>\n {visible.has('provider') && <Th>Provider</Th>}\n <Th>Status</Th>\n {visible.has('kyc') && <Th>KYC / KYB</Th>}\n {visible.has('stage') && (\n <Th>\n Stage\n <Hint label={ACTIVE_HINT} />\n </Th>\n )}\n {visible.has('emailVerified') && <Th>Email</Th>}\n {visible.has('workspaces') && <Th align=\"right\">Workspaces</Th>}\n {visible.has('lastLogin') && <Th>Last login</Th>}\n {visible.has('joined') && <Th>Joined</Th>}\n {visible.has('firstSale') && <Th>First sale</Th>}\n {visible.has('activeSessions') && <Th align=\"right\">Sessions</Th>}\n {visible.has('lastSeen') && <Th>Last seen</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=\"accounts\"\n onPage={setPage}\n />\n }\n >\n {data?.rows.map((a) => (\n <AccountListRow\n key={a.id}\n a={a}\n active={a.id === selected?.id}\n onOpen={() => onSelect(a)}\n visible={visible}\n />\n ))}\n </ListCard>\n <AccountPanel account={current} onClose={() => onSelect(null)} />\n </>\n );\n}\n"],"mappings":";AA8KI,mBAcQ,KADF,YAbN;AA5KJ,SAAS,gBAAgB;AACzB,SAAS,QAAQ,cAAc,cAAc,UAAU;AACvD,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,2BAA2B;AACpC,SAAS,oBAAyC;AAClD,SAAS,UAAU,iBAAiB;AACpC,SAAS,aAAa,UAAU,oBAAoB;AACpD,SAAS,0BAA0B;AACnC,SAAS,YAAY;AACrB,SAAS,gBAAgB,uBAAuB;AAEhD,SAAS,mBAAAA,wBAAuB;AAEhC,MAAM,mBAAmC;AAAA,EACvC,EAAE,KAAK,YAAY,OAAO,WAAW;AAAA,EACrC,EAAE,KAAK,OAAO,OAAO,YAAY;AAAA,EACjC,EAAE,KAAK,SAAS,OAAO,QAAQ;AAAA,EAC/B,EAAE,KAAK,iBAAiB,OAAO,iBAAiB;AAAA,EAChD,EAAE,KAAK,cAAc,OAAO,aAAa;AAAA,EACzC,EAAE,KAAK,aAAa,OAAO,aAAa;AAAA,EACxC,EAAE,KAAK,UAAU,OAAO,SAAS;AAAA,EACjC,EAAE,KAAK,aAAa,OAAO,aAAa;AAAA,EACxC,EAAE,KAAK,kBAAkB,OAAO,kBAAkB;AAAA,EAClD,EAAE,KAAK,YAAY,OAAO,YAAY;AACxC;AACA,MAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,MAAM,cAAc;AAEpB,SAAS,qBAAkC;AACzC,MAAI,OAAO,WAAW,YAAa,QAAO,IAAI,IAAI,eAAe;AACjE,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,WAAW;AACnD,WAAO,MACH,IAAI,IAAI,IAAI,MAAM,GAAG,EAAE,OAAO,OAAO,CAAC,IACtC,IAAI,IAAI,eAAe;AAAA,EAC7B,QAAQ;AACN,WAAO,IAAI,IAAI,eAAe;AAAA,EAChC;AACF;AAEA,SAAS,oBAAoB,MAAyB;AACpD,MAAI;AACF,WAAO,aAAa,QAAQ,aAAa,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,CAAC;AAAA,EAC9D,QAAQ;AAAA,EAER;AACF;AAEA,MAAM,MAAM,CAAC,QACX,MAAM,IAAI,KAAK,GAAG,EAAE,mBAAmB,IAAI;AAG7C,SAAS,SAAS,GAAe,SAAgC;AAC/D,QAAM,QAAkB,CAAC,EAAE,QAAQ,IAAI,EAAE,KAAK;AAC9C,MAAI,QAAQ,IAAI,UAAU,GAAG;AAC3B,UAAM,KAAK,gBAAgB,EAAE,YAAY,KAAK,EAAE,YAAY;AAAA,EAC9D;AACA,QAAM,KAAK,EAAE,MAAM;AACnB,MAAI,QAAQ,IAAI,KAAK,EAAG,OAAM,KAAK,SAAS,EAAE,SAAS,KAAK,EAAE;AAC9D,MAAI,QAAQ,IAAI,OAAO,EAAG,OAAM,KAAK,EAAE,QAAQ,aAAa,EAAE,KAAK,IAAI,EAAE;AACzE,MAAI,QAAQ,IAAI,eAAe,GAAG;AAChC,UAAM,KAAK,EAAE,gBAAgB,aAAa,YAAY;AAAA,EACxD;AACA,MAAI,QAAQ,IAAI,YAAY,EAAG,OAAM,KAAK,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACvE,MAAI,QAAQ,IAAI,WAAW,EAAG,OAAM,KAAK,IAAI,EAAE,WAAW,KAAK,OAAO;AACtE,MAAI,QAAQ,IAAI,QAAQ,EAAG,OAAM,KAAK,IAAI,EAAE,SAAS,CAAC;AACtD,MAAI,QAAQ,IAAI,WAAW,EAAG,OAAM,KAAK,IAAI,EAAE,WAAW,KAAK,UAAU;AACzE,MAAI,QAAQ,IAAI,gBAAgB,EAAG,OAAM,KAAK,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC3E,MAAI,QAAQ,IAAI,UAAU,EAAG,OAAM,KAAK,IAAI,EAAE,UAAU,KAAK,OAAO;AACpE,SAAO;AACT;AAGO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAC/C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B,EAAE;AACxD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,CAAC;AAClC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAsB,kBAAkB;AACtE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,EAAE,MAAM,WAAW,SAAS,QAAQ,IAAI,oBAAoB;AAAA,IAChE;AAAA,IACA,UAAU;AAAA,IACV,QAAQ,UAAU;AAAA,IAClB,YAAY,cAAc;AAAA,IAC1B,UAAU,YAAY;AAAA,IACtB,OAAO,SAAS;AAAA,EAClB,CAAC;AAED,QAAM,UAAU,MAAM,KAAK,KAAK,CAAC,QAAQ,IAAI,OAAO,UAAU,EAAE,KAAK;AACrE,QAAM,aAAa,OACf,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,QAAQ,CAAC,IACjD;AACJ,QAAM,SAAS,CAAC,QAA6B,CAAC,MAAc;AAC1D,QAAI,CAAC;AACL,YAAQ,CAAC;AAAA,EACX;AAEA,QAAM,gBAAgB,CAAC,SAAsB;AAC3C,eAAW,IAAI;AACf,wBAAoB,IAAI;AAAA,EAC1B;AAKA,QAAM,YAAY,YAAY;AAC5B,iBAAa,IAAI;AACjB,QAAI;AACF,YAAMC,QAAO,MAAM,iBAAiB,SAAS;AAAA,QAC3C,UAAU;AAAA,QACV,QAAQ,UAAU;AAAA,QAClB,YAAY,cAAc;AAAA,QAC1B,UAAU,YAAY;AAAA,QACtB,OAAO,SAAS;AAAA,MAClB,CAAC;AACD,YAAM,SAAS,CAAC,QAAQ,OAAO;AAC/B,UAAI,QAAQ,IAAI,UAAU,EAAG,QAAO,KAAK,UAAU;AACnD,aAAO,KAAK,QAAQ;AACpB,UAAI,QAAQ,IAAI,KAAK,EAAG,QAAO,KAAK,WAAW;AAC/C,UAAI,QAAQ,IAAI,OAAO,EAAG,QAAO,KAAK,OAAO;AAC7C,UAAI,QAAQ,IAAI,eAAe,EAAG,QAAO,KAAK,gBAAgB;AAC9D,UAAI,QAAQ,IAAI,YAAY,EAAG,QAAO,KAAK,YAAY;AACvD,UAAI,QAAQ,IAAI,WAAW,EAAG,QAAO,KAAK,YAAY;AACtD,UAAI,QAAQ,IAAI,QAAQ,EAAG,QAAO,KAAK,QAAQ;AAC/C,UAAI,QAAQ,IAAI,WAAW,EAAG,QAAO,KAAK,YAAY;AACtD,UAAI,QAAQ,IAAI,gBAAgB,EAAG,QAAO,KAAK,iBAAiB;AAChE,UAAI,QAAQ,IAAI,UAAU,EAAG,QAAO,KAAK,WAAW;AACpD,YAAM,OAAO,CAAC,QAAQ,GAAGA,MAAK,KAAK,IAAI,CAACC,OAAM,SAASA,IAAG,OAAO,CAAC,CAAC;AACnE,YAAM,MAAM,IAAI;AAAA,QACd,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,MAAM,WAAW,CAAC;AAAA,MAC9C;AACA,YAAM,IAAI,SAAS,cAAc,GAAG;AACpC,QAAE,OAAO;AACT,QAAE,WAAW;AACb,QAAE,MAAM;AACR,UAAI,gBAAgB,GAAG;AAAA,IACzB,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,QAAQ;AAE5B,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,SAAS,MAAM,KAAK,QAAQ;AAAA,QAC5B,YAAW;AAAA,QACX,SAAS,MAAM,KAAK,WAAW;AAAA,QAC/B,YAAW;AAAA,QACX,UACE,qBAAC,WAAQ,OAAM,mBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAO,SAAS;AAAA,cAC1B,aAAY;AAAA;AAAA,UACd;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAyB;AAClC,yBAAS,CAAC;AACV,wBAAQ,CAAC;AAAA,cACX;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,WAAU;AAAA,cACV,SAAQ;AAAA,cACR,MAAM;AAAA,cACN,IAAI;AAAA,cACJ,QAAQ,OAAO,aAAa;AAAA,cAC5B,MAAM,OAAO,WAAW;AAAA;AAAA,UAC1B;AAAA,WACE,cAAc,aACd;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,MAAM;AACb,8BAAc,EAAE;AAChB,4BAAY,EAAE;AACd,wBAAQ,CAAC;AAAA,cACX;AAAA,cACD;AAAA;AAAA,UAED;AAAA,UAEF;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT;AAAA,cACA,UAAU;AAAA;AAAA,UACZ;AAAA,UACA,oBAAC,gBAAa,OAAM,UAClB;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,KAAK,UAAU;AAAA,cAC9B,UAAU,aAAa,CAAC,MAAM;AAAA,cAE7B,sBAAY,oBAAe;AAAA;AAAA,UAC9B,GACF;AAAA,WACF;AAAA,QAEF,MACE,qBAAC,QACC;AAAA,8BAAC,MAAG,qBAAO;AAAA,UACV,QAAQ,IAAI,UAAU,KAAK,oBAAC,MAAG,sBAAQ;AAAA,UACxC,oBAAC,MAAG,oBAAM;AAAA,UACT,QAAQ,IAAI,KAAK,KAAK,oBAAC,MAAG,uBAAS;AAAA,UACnC,QAAQ,IAAI,OAAO,KAClB,qBAAC,MAAG;AAAA;AAAA,YAEF,oBAAC,QAAK,OAAO,aAAa;AAAA,aAC5B;AAAA,UAED,QAAQ,IAAI,eAAe,KAAK,oBAAC,MAAG,mBAAK;AAAA,UACzC,QAAQ,IAAI,YAAY,KAAK,oBAAC,MAAG,OAAM,SAAQ,wBAAU;AAAA,UACzD,QAAQ,IAAI,WAAW,KAAK,oBAAC,MAAG,wBAAU;AAAA,UAC1C,QAAQ,IAAI,QAAQ,KAAK,oBAAC,MAAG,oBAAM;AAAA,UACnC,QAAQ,IAAI,WAAW,KAAK,oBAAC,MAAG,wBAAU;AAAA,UAC1C,QAAQ,IAAI,gBAAgB,KAAK,oBAAC,MAAG,OAAM,SAAQ,sBAAQ;AAAA,UAC3D,QAAQ,IAAI,UAAU,KAAK,oBAAC,MAAG,uBAAS;AAAA,UACzC,oBAAC,MAAG,OAAM,SACR,8BAAC,UAAK,WAAU,WAAU,qBAAO,GACnC;AAAA,WACF;AAAA,QAEF,QACE;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,OAAO,MAAM;AAAA,YACb,MAAK;AAAA,YACL,QAAQ;AAAA;AAAA,QACV;AAAA,QAGD,gBAAM,KAAK,IAAI,CAAC,MACf;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,QAAQ,EAAE,OAAO,UAAU;AAAA,YAC3B,QAAQ,MAAM,SAAS,CAAC;AAAA,YACxB;AAAA;AAAA,UAJK,EAAE;AAAA,QAKT,CACD;AAAA;AAAA,IACH;AAAA,IACA,oBAAC,gBAAa,SAAS,SAAS,SAAS,MAAM,SAAS,IAAI,GAAG;AAAA,KACjE;AAEJ;","names":["PROVIDER_LABELS","page","a"]}
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Info } from "lucide-react";
4
+ import { Tooltip } from "@withpotter/ui-kit";
5
+ import styles from "./rows.module.css";
6
+ function Hint({ label }) {
7
+ return /* @__PURE__ */ jsx(Tooltip, { label, children: /* @__PURE__ */ jsx("button", { type: "button", className: styles.hint, "aria-label": label, children: /* @__PURE__ */ jsx(Info, { size: 13, "aria-hidden": true }) }) });
8
+ }
9
+ export {
10
+ Hint
11
+ };
12
+ //# sourceMappingURL=hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/accounts/hint.tsx"],"sourcesContent":["'use client';\n\nimport { Info } from 'lucide-react';\nimport { Tooltip } from '@withpotter/ui-kit';\nimport styles from './rows.module.css';\n\n/** A small info icon that explains a term on hover or focus. */\nexport function Hint({ label }: { label: string }) {\n return (\n <Tooltip label={label}>\n <button type=\"button\" className={styles.hint} aria-label={label}>\n <Info size={13} aria-hidden />\n </button>\n </Tooltip>\n );\n}\n"],"mappings":";AAWQ;AATR,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,OAAO,YAAY;AAGZ,SAAS,KAAK,EAAE,MAAM,GAAsB;AACjD,SACE,oBAAC,WAAQ,OACP,8BAAC,YAAO,MAAK,UAAS,WAAW,OAAO,MAAM,cAAY,OACxD,8BAAC,QAAK,MAAM,IAAI,eAAW,MAAC,GAC9B,GACF;AAEJ;","names":[]}
@@ -65,3 +65,20 @@
65
65
  font-variant-numeric: tabular-nums;
66
66
  cursor: default;
67
67
  }
68
+
69
+ .hint {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ margin-left: 4px;
73
+ padding: 0;
74
+ border: 0;
75
+ background: none;
76
+ vertical-align: middle;
77
+ color: var(--text-tertiary);
78
+ cursor: help;
79
+ }
80
+
81
+ .hint:hover,
82
+ .hint:focus-visible {
83
+ color: var(--text-primary);
84
+ }
@@ -168,6 +168,13 @@ function useAdminRoles() {
168
168
  queryFn: () => platformAdminApi.roles()
169
169
  });
170
170
  }
171
+ function useAccountSessions(id) {
172
+ return useQuery({
173
+ queryKey: ["platform-admin", "account-sessions", id],
174
+ queryFn: () => platformAdminApi.accountSessions(id),
175
+ enabled: !!id
176
+ });
177
+ }
171
178
  function useAccountTenants(id) {
172
179
  return useQuery({
173
180
  queryKey: ["platform-admin", "account-tenants", id],
@@ -188,6 +195,7 @@ function usePlatformAuditAnalytics(range = {}) {
188
195
  });
189
196
  }
190
197
  export {
198
+ useAccountSessions,
191
199
  useAccountTenants,
192
200
  useAdminPermissions,
193
201
  useAdminRoles,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { platformAdminApi } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/admin/hooks.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { platformAdminApi } from '@withpotter/lib/admin-api';\nimport type { TenantListParams } from '@withpotter/lib/admin-tenants-query';\nimport type { AccountStage } from '@withpotter/lib/admin-account-types';\n\nexport function useAdminSession(enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'session'],\n queryFn: () => platformAdminApi.me(),\n enabled,\n retry: false,\n staleTime: 60_000,\n });\n}\n\nexport function usePlatformOverview() {\n return useQuery({\n queryKey: ['platform-admin', 'overview'],\n queryFn: () => platformAdminApi.overview(),\n });\n}\n\nexport function usePlatformCockpit(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'cockpit', range],\n queryFn: () => platformAdminApi.cockpit(range),\n });\n}\n\nexport function usePlatformRevenue(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'revenue', range],\n queryFn: () => platformAdminApi.revenue(range),\n });\n}\n\nexport function usePlatformTenantsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants-analytics', range],\n queryFn: () => platformAdminApi.tenantsAnalytics(range),\n });\n}\n\nexport function usePlatformTenants(params: TenantListParams) {\n return useQuery({\n queryKey: ['platform-admin', 'tenants', params],\n queryFn: () => platformAdminApi.tenants(params),\n });\n}\n\nexport function usePlatformTenant(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant', id, range],\n queryFn: () => platformAdminApi.tenant(id, range),\n enabled: !!id,\n });\n}\n\nexport function useTenantAnalytics(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-analytics', id, range],\n queryFn: () => platformAdminApi.tenantAnalytics(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantFinance(\n id: string,\n range: { startDate?: string; endDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-finance', id, range],\n queryFn: () => platformAdminApi.tenantFinance(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantTransactions(\n id: string,\n params: {\n status?: string;\n type?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-transactions', id, params],\n queryFn: () => platformAdminApi.tenantTransactions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomers(\n id: string,\n params: {\n search?: string;\n type?: string;\n status?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customers', id, params],\n queryFn: () => platformAdminApi.tenantCustomers(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProducts(\n id: string,\n params: { type?: string; search?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-products', id, params],\n queryFn: () => platformAdminApi.tenantProducts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantProductInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-product-insights', id, range],\n queryFn: () => platformAdminApi.tenantProductInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantCustomerInsights(\n id: string,\n range: { startDate?: string } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-customer-insights', id, range],\n queryFn: () => platformAdminApi.tenantCustomerInsights(id, range),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantInvoices(\n id: string,\n params: {\n status?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-invoices', id, params],\n queryFn: () => platformAdminApi.tenantInvoices(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantLoyalty(id: string, enabled = true) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-loyalty', id],\n queryFn: () => platformAdminApi.tenantLoyalty(id),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantDiscounts(\n id: string,\n params: {\n status?: string;\n discountType?: string;\n search?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-discounts', id, params],\n queryFn: () => platformAdminApi.tenantDiscounts(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSettlements(\n id: string,\n params: {\n page?: number;\n limit?: number;\n startDate?: string;\n endDate?: string;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-settlements', id, params],\n queryFn: () => platformAdminApi.tenantSettlements(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function useTenantSubscriptions(\n id: string,\n params: {\n status?: string;\n planId?: string;\n page?: number;\n limit?: number;\n } = {},\n enabled = true,\n) {\n return useQuery({\n queryKey: ['platform-admin', 'tenant-subscriptions', id, params],\n queryFn: () => platformAdminApi.tenantSubscriptions(id, params),\n enabled: !!id && enabled,\n });\n}\n\nexport function usePlatformAccounts(params: {\n page?: number;\n pageSize?: number;\n search?: string;\n joinedFrom?: string;\n joinedTo?: string;\n stage?: AccountStage;\n}) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts', params],\n queryFn: () => platformAdminApi.accounts(params),\n });\n}\n\nexport function usePlatformAccountsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'accounts-analytics', range],\n queryFn: () => platformAdminApi.accountsAnalytics(range),\n });\n}\n\nexport function usePlatformAdmins() {\n return useQuery({\n queryKey: ['platform-admin', 'admins'],\n queryFn: () => platformAdminApi.admins(),\n });\n}\n\nexport function usePlatformAdminsAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'admins-analytics', range],\n queryFn: () => platformAdminApi.adminsAnalytics(range),\n });\n}\n\nexport function useAdminPermissions() {\n return useQuery({\n queryKey: ['platform-admin', 'admin-permissions'],\n queryFn: () => platformAdminApi.adminPermissions(),\n staleTime: 5 * 60_000,\n });\n}\n\nexport function useAdminRoles() {\n return useQuery({\n queryKey: ['platform-admin', 'roles'],\n queryFn: () => platformAdminApi.roles(),\n });\n}\n\nexport function useAccountSessions(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-sessions', id],\n queryFn: () => platformAdminApi.accountSessions(id!),\n enabled: !!id,\n });\n}\n\nexport function useAccountTenants(id: string | null) {\n return useQuery({\n queryKey: ['platform-admin', 'account-tenants', id],\n queryFn: () => platformAdminApi.accountTenants(id!),\n enabled: !!id,\n });\n}\n\nexport function usePlatformAudit(\n params: {\n page?: number;\n pageSize?: number;\n search?: string;\n action?: string;\n adminEmail?: string;\n from?: string;\n to?: string;\n } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit', params],\n queryFn: () => platformAdminApi.audit(params),\n });\n}\n\nexport function usePlatformAuditAnalytics(\n range: { startDate?: string; endDate?: string } = {},\n) {\n return useQuery({\n queryKey: ['platform-admin', 'audit-analytics', range],\n queryFn: () => platformAdminApi.auditAnalytics(range),\n });\n}\n"],"mappings":"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","names":[]}
@@ -2,21 +2,17 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { useRouter } from "next/navigation";
5
- import { Button, useConfirm, useToast } from "@withpotter/ui-kit";
5
+ import { Button } from "@withpotter/ui-kit";
6
6
  import { SUPPORT_INBOX_EMAIL } from "@withpotter/lib/support-inbox";
7
7
  import { useAdminSession } from "../hooks";
8
- import { settingsStore } from "../_settings";
9
8
  import { useModuleOn } from "../_shell/modules-context";
10
9
  import { signOutAdmin } from "../_shell/sign-out";
11
10
  import { ChangePasswordDialog } from "./change-password-dialog";
12
11
  import { ProfileCard } from "./profile-card";
13
12
  import { SettingRow, SettingsCard } from "./setting-row";
14
- import { SystemStatusCard } from "./system-status-card";
15
13
  import styles from "./settings.module.css";
16
14
  function AccountTab() {
17
15
  const router = useRouter();
18
- const confirm = useConfirm();
19
- const { toast } = useToast();
20
16
  const { data: session } = useAdminSession();
21
17
  const [changing, setChanging] = useState(false);
22
18
  const [leaving, setLeaving] = useState(false);
@@ -26,17 +22,6 @@ function AccountTab() {
26
22
  await signOutAdmin();
27
23
  router.replace("/admin/login");
28
24
  };
29
- const reset = async () => {
30
- const ok = await confirm({
31
- title: "Reset settings on this device?",
32
- description: "Density, accessibility, notification and shortcut choices go back to their defaults. Your account is not affected.",
33
- confirmLabel: "Reset settings",
34
- tone: "danger"
35
- });
36
- if (!ok) return;
37
- settingsStore.reset();
38
- toast({ title: "Settings reset on this device" });
39
- };
40
25
  return /* @__PURE__ */ jsxs("div", { className: styles.stack, children: [
41
26
  session && /* @__PURE__ */ jsx(ProfileCard, { session }),
42
27
  /* @__PURE__ */ jsxs(SettingsCard, { title: "Security", children: [
@@ -57,14 +42,6 @@ function AccountTab() {
57
42
  }
58
43
  )
59
44
  ] }),
60
- /* @__PURE__ */ jsx(SettingsCard, { title: "This device", children: /* @__PURE__ */ jsx(
61
- SettingRow,
62
- {
63
- label: "Reset all settings on this device",
64
- description: "Every preference on this page returns to its default.",
65
- children: /* @__PURE__ */ jsx(Button, { variant: "danger", onClick: reset, children: "Reset" })
66
- }
67
- ) }),
68
45
  supportOn && /* @__PURE__ */ jsx(SettingsCard, { title: "Support inbox", children: /* @__PURE__ */ jsx(
69
46
  SettingRow,
70
47
  {
@@ -73,7 +50,6 @@ function AccountTab() {
73
50
  children: /* @__PURE__ */ jsx("span", { className: styles.value, title: SUPPORT_INBOX_EMAIL, children: SUPPORT_INBOX_EMAIL })
74
51
  }
75
52
  ) }),
76
- /* @__PURE__ */ jsx(SystemStatusCard, {}),
77
53
  changing && /* @__PURE__ */ jsx(ChangePasswordDialog, { onClose: () => setChanging(false) })
78
54
  ] });
79
55
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/settings/account-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Button, useConfirm, useToast } from '@withpotter/ui-kit';\nimport { SUPPORT_INBOX_EMAIL } from '@withpotter/lib/support-inbox';\nimport { useAdminSession } from '../hooks';\nimport { settingsStore } from '../_settings';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { signOutAdmin } from '../_shell/sign-out';\nimport { ChangePasswordDialog } from './change-password-dialog';\nimport { ProfileCard } from './profile-card';\nimport { SettingRow, SettingsCard } from './setting-row';\nimport { SystemStatusCard } from './system-status-card';\nimport styles from './settings.module.css';\n\n/**\n * Who you are, how you sign in, and the box's own health. The support inbox\n * address shows only where this box serves Support.\n */\nexport function AccountTab() {\n const router = useRouter();\n const confirm = useConfirm();\n const { toast } = useToast();\n const { data: session } = useAdminSession();\n const [changing, setChanging] = useState(false);\n const [leaving, setLeaving] = useState(false);\n const supportOn = useModuleOn('support');\n\n const signOut = async () => {\n setLeaving(true);\n await signOutAdmin();\n router.replace('/admin/login');\n };\n\n const reset = async () => {\n const ok = await confirm({\n title: 'Reset settings on this device?',\n description:\n 'Density, accessibility, notification and shortcut choices go back to their defaults. Your account is not affected.',\n confirmLabel: 'Reset settings',\n tone: 'danger',\n });\n if (!ok) return;\n settingsStore.reset();\n toast({ title: 'Settings reset on this device' });\n };\n\n return (\n <div className={styles.stack}>\n {session && <ProfileCard session={session} />}\n <SettingsCard title=\"Security\">\n <SettingRow\n label=\"Password\"\n description=\"At least 12 characters. You stay signed in here.\"\n >\n <Button variant=\"secondary\" onClick={() => setChanging(true)}>\n Change password\n </Button>\n </SettingRow>\n <SettingRow\n label=\"Sign out\"\n description=\"Also ends any View as owner session.\"\n >\n <Button variant=\"secondary\" loading={leaving} onClick={signOut}>\n Sign out\n </Button>\n </SettingRow>\n </SettingsCard>\n <SettingsCard title=\"This device\">\n <SettingRow\n label=\"Reset all settings on this device\"\n description=\"Every preference on this page returns to its default.\"\n >\n <Button variant=\"danger\" onClick={reset}>\n Reset\n </Button>\n </SettingRow>\n </SettingsCard>\n {supportOn && (\n <SettingsCard title=\"Support inbox\">\n <SettingRow\n label=\"Inbound address\"\n description=\"Merchants write here; each email opens a case in Support.\"\n >\n <span className={styles.value} title={SUPPORT_INBOX_EMAIL}>\n {SUPPORT_INBOX_EMAIL}\n </span>\n </SettingRow>\n </SettingsCard>\n )}\n <SystemStatusCard />\n {changing && <ChangePasswordDialog onClose={() => setChanging(false)} />}\n </div>\n );\n}\n"],"mappings":";AAkDkB,cACZ,YADY;AAhDlB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,YAAY,gBAAgB;AAC7C,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,YAAY,oBAAoB;AACzC,SAAS,wBAAwB;AACjC,OAAO,YAAY;AAMZ,SAAS,aAAa;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,UAAU,WAAW;AAC3B,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,EAAE,MAAM,QAAQ,IAAI,gBAAgB;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,YAAY,YAAY,SAAS;AAEvC,QAAM,UAAU,YAAY;AAC1B,eAAW,IAAI;AACf,UAAM,aAAa;AACnB,WAAO,QAAQ,cAAc;AAAA,EAC/B;AAEA,QAAM,QAAQ,YAAY;AACxB,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO;AAAA,MACP,aACE;AAAA,MACF,cAAc;AAAA,MACd,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,GAAI;AACT,kBAAc,MAAM;AACpB,UAAM,EAAE,OAAO,gCAAgC,CAAC;AAAA,EAClD;AAEA,SACE,qBAAC,SAAI,WAAW,OAAO,OACpB;AAAA,eAAW,oBAAC,eAAY,SAAkB;AAAA,IAC3C,qBAAC,gBAAa,OAAM,YAClB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,MAAM,YAAY,IAAI,GAAG,6BAE9D;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,SAAS,SAAS,SAAS,sBAEhE;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACA,oBAAC,gBAAa,OAAM,eAClB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,aAAY;AAAA,QAEZ,8BAAC,UAAO,SAAQ,UAAS,SAAS,OAAO,mBAEzC;AAAA;AAAA,IACF,GACF;AAAA,IACC,aACC,oBAAC,gBAAa,OAAM,iBAClB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,aAAY;AAAA,QAEZ,8BAAC,UAAK,WAAW,OAAO,OAAO,OAAO,qBACnC,+BACH;AAAA;AAAA,IACF,GACF;AAAA,IAEF,oBAAC,oBAAiB;AAAA,IACjB,YAAY,oBAAC,wBAAqB,SAAS,MAAM,YAAY,KAAK,GAAG;AAAA,KACxE;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/settings/account-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { Button } from '@withpotter/ui-kit';\nimport { SUPPORT_INBOX_EMAIL } from '@withpotter/lib/support-inbox';\nimport { useAdminSession } from '../hooks';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { signOutAdmin } from '../_shell/sign-out';\nimport { ChangePasswordDialog } from './change-password-dialog';\nimport { ProfileCard } from './profile-card';\nimport { SettingRow, SettingsCard } from './setting-row';\nimport styles from './settings.module.css';\n\n/**\n * Who you are and how you sign in. The support inbox\n * address shows only where this box serves Support.\n */\nexport function AccountTab() {\n const router = useRouter();\n const { data: session } = useAdminSession();\n const [changing, setChanging] = useState(false);\n const [leaving, setLeaving] = useState(false);\n const supportOn = useModuleOn('support');\n\n const signOut = async () => {\n setLeaving(true);\n await signOutAdmin();\n router.replace('/admin/login');\n };\n\n return (\n <div className={styles.stack}>\n {session && <ProfileCard session={session} />}\n <SettingsCard title=\"Security\">\n <SettingRow\n label=\"Password\"\n description=\"At least 12 characters. You stay signed in here.\"\n >\n <Button variant=\"secondary\" onClick={() => setChanging(true)}>\n Change password\n </Button>\n </SettingRow>\n <SettingRow\n label=\"Sign out\"\n description=\"Also ends any View as owner session.\"\n >\n <Button variant=\"secondary\" loading={leaving} onClick={signOut}>\n Sign out\n </Button>\n </SettingRow>\n </SettingsCard>\n {supportOn && (\n <SettingsCard title=\"Support inbox\">\n <SettingRow\n label=\"Inbound address\"\n description=\"Merchants write here; each email opens a case in Support.\"\n >\n <span className={styles.value} title={SUPPORT_INBOX_EMAIL}>\n {SUPPORT_INBOX_EMAIL}\n </span>\n </SettingRow>\n </SettingsCard>\n )}\n {changing && <ChangePasswordDialog onClose={() => setChanging(false)} />}\n </div>\n );\n}\n"],"mappings":";AAiCkB,cACZ,YADY;AA/BlB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,YAAY,oBAAoB;AACzC,OAAO,YAAY;AAMZ,SAAS,aAAa;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,MAAM,QAAQ,IAAI,gBAAgB;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,YAAY,YAAY,SAAS;AAEvC,QAAM,UAAU,YAAY;AAC1B,eAAW,IAAI;AACf,UAAM,aAAa;AACnB,WAAO,QAAQ,cAAc;AAAA,EAC/B;AAEA,SACE,qBAAC,SAAI,WAAW,OAAO,OACpB;AAAA,eAAW,oBAAC,eAAY,SAAkB;AAAA,IAC3C,qBAAC,gBAAa,OAAM,YAClB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,MAAM,YAAY,IAAI,GAAG,6BAE9D;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UAEZ,8BAAC,UAAO,SAAQ,aAAY,SAAS,SAAS,SAAS,SAAS,sBAEhE;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACC,aACC,oBAAC,gBAAa,OAAM,iBAClB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,aAAY;AAAA,QAEZ,8BAAC,UAAK,WAAW,OAAO,OAAO,OAAO,qBACnC,+BACH;AAAA;AAAA,IACF,GACF;AAAA,IAED,YAAY,oBAAC,wBAAqB,SAAS,MAAM,YAAY,KAAK,GAAG;AAAA,KACxE;AAEJ;","names":[]}
@@ -95,9 +95,10 @@ function TenantListRow({
95
95
  children: t.businessName
96
96
  }
97
97
  ),
98
- /* @__PURE__ */ jsx("span", { className: rows.secondary, title: t.slug, children: t.slug })
98
+ /* @__PURE__ */ jsx("span", { className: rows.secondary, title: t.slug, children: [t.slug, t.phone].filter(Boolean).join(" \xB7 ") })
99
99
  ] })
100
100
  ] }) }),
101
+ /* @__PURE__ */ jsx("td", { className: `${list.clip} ${list.muted}`, children: /* @__PURE__ */ jsx("span", { title: t.email ?? t.ownerEmail ?? void 0, children: t.email ?? t.ownerEmail ?? EMPTY }) }),
101
102
  /* @__PURE__ */ jsx("td", { className: `${list.clip} ${list.muted}`, children: countryText(t.country, t.countryCode) ? /* @__PURE__ */ jsx(CountryLabel, { country: t.country, code: t.countryCode }) : EMPTY }),
102
103
  /* @__PURE__ */ jsx("td", { className: `${list.clip} ${list.muted}`, children: t.industryKey ?? EMPTY }),
103
104
  /* @__PURE__ */ jsx("td", { className: list.num, children: /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/tenants/tenant-list-row.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport {\n OverflowItem,\n OverflowMenu,\n TableRow,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport { platformAdminApi, type TenantRow } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { CountryLabel } from '../_ui/country-label';\nimport { countryText } from '../_ui/country-text';\nimport { EMPTY } from '../_ui/list-card';\nimport { MoneyLines } from '../_ui/money-lines';\nimport { useAccountActions } from '../accounts/use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useAdminSession } from '../hooks';\nimport { StatusPill } from '../audit/status-pill';\nimport { AccountOfficerDialog } from './[id]/account-officer-dialog';\nimport { officerAction } from './account-officer-action';\nimport { logoOf } from './tenants-logo';\nimport list from '../_ui/list-card.module.css';\nimport rows from '../accounts/rows.module.css';\n\n/** One business: identity, money, status and a ••• menu. */\nexport function TenantListRow({\n t,\n onOpen,\n}: {\n t: TenantRow;\n onOpen: () => void;\n}) {\n const queryClient = useQueryClient();\n const confirm = useConfirm();\n const { toast } = useToast();\n const { impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const { data: session } = useAdminSession();\n const officerChoice = officerAction(\n t.accountOfficer,\n session?.role === 'superadmin',\n t.status,\n );\n const [picking, setPicking] = useState(false);\n const href = `/admin/tenants/${t.id}`;\n const suspended = t.status === 'suspended';\n const logo = logoOf(t);\n const fail = (title: string) => (e: Error) =>\n toast({ title, description: e.message, variant: 'destructive' });\n\n const status = useMutation({\n mutationFn: () =>\n platformAdminApi.setTenantStatus(\n t.id,\n suspended ? 'active' : 'suspended',\n ),\n onSuccess: () => {\n toast({\n title: `${t.businessName} ${suspended ? 'restored' : 'suspended'}`,\n });\n void queryClient.invalidateQueries({\n queryKey: ['platform-admin', 'tenants'],\n });\n },\n onError: fail('The status was not changed'),\n });\n\n // The owner is looked up first, so the tab is reserved inside the click\n // (before the await); opened afterwards it would be blocked as a pop-up.\n const viewAsOwner = useMutation({\n mutationFn: async (tab: Window | null) => {\n const detail = await platformAdminApi.tenant(t.id);\n const owner = detail.owner?.accountId;\n\n if (!owner) throw new Error('This business has no owner account.');\n\n impersonate.mutate(owner, tab);\n },\n onError: (error, tab) => {\n tab?.close();\n fail('Could not open the owner’s view')(error);\n },\n });\n\n const toggle = async () => {\n const ok = await confirm({\n title: suspended\n ? `Restore ${t.businessName}?`\n : `Suspend ${t.businessName}?`,\n description: suspended\n ? 'Their store and dashboard come back online straight away.'\n : 'Their store goes offline and their team loses access until restored.',\n confirmLabel: suspended ? 'Restore' : 'Suspend',\n tone: suspended ? 'default' : 'danger',\n });\n if (ok) status.mutate();\n };\n\n return (\n <TableRow onClick={onOpen}>\n <td>\n <span className={rows.identity}>\n {logo && <EntityAvatar name={t.businessName} src={logo} size={32} />}\n <span className={rows.identityText}>\n <Link\n href={href}\n className={rows.primary}\n title={t.businessName}\n onClick={(e) => e.stopPropagation()}\n >\n {t.businessName}\n </Link>\n <span className={rows.secondary} title={t.slug}>\n {t.slug}\n </span>\n </span>\n </span>\n </td>\n <td className={`${list.clip} ${list.muted}`}>\n {countryText(t.country, t.countryCode) ? (\n <CountryLabel country={t.country} code={t.countryCode} />\n ) : (\n EMPTY\n )}\n </td>\n <td className={`${list.clip} ${list.muted}`}>{t.industryKey ?? EMPTY}</td>\n <td className={list.num}>\n {/* One line per currency the business sells in; never added up. */}\n <MoneyLines\n rows={t.gmvByCurrency}\n currency=\"\"\n amount={(r) => r.amount}\n empty={EMPTY}\n />\n </td>\n <td className={list.nowrap}>\n <StatusPill status={t.status} />\n </td>\n <td\n className={`${list.clip} ${list.muted}`}\n onClick={(e) => e.stopPropagation()}\n >\n {officerChoice ? (\n <button\n type=\"button\"\n className={rows.nameButton}\n title={\n t.accountOfficer\n ? `${t.accountOfficer.name}. Change the account officer`\n : 'Assign an account officer'\n }\n onClick={() => setPicking(true)}\n >\n {t.accountOfficer?.name ?? 'Assign'}\n </button>\n ) : (\n (t.accountOfficer?.name ?? EMPTY)\n )}\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {new Date(t.createdAt).toLocaleDateString()}\n </td>\n <td className={rows.menuCell} onClick={(e) => e.stopPropagation()}>\n <OverflowMenu label={`Actions for ${t.businessName}`}>\n <OverflowItem onClick={onOpen}>View merchant</OverflowItem>\n {ownerView && (\n <OverflowItem\n disabled={viewAsOwner.isPending}\n onClick={() => viewAsOwner.mutate(impersonate.reserveTab())}\n >\n View as owner\n </OverflowItem>\n )}\n {officerChoice && (\n <OverflowItem onClick={() => setPicking(true)}>\n {officerChoice === 'assign'\n ? 'Assign account officer'\n : 'Change account officer'}\n </OverflowItem>\n )}\n {t.status !== 'deleted' && (\n <OverflowItem\n onClick={() => void toggle()}\n destructive={!suspended}\n >\n {suspended ? 'Restore' : 'Suspend'}\n </OverflowItem>\n )}\n </OverflowMenu>\n {picking && (\n <AccountOfficerDialog\n open\n tenantId={t.id}\n name={t.businessName}\n currentId={t.accountOfficer?.id ?? null}\n onClose={() => setPicking(false)}\n />\n )}\n </td>\n </TableRow>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/tenants/tenant-list-row.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport {\n OverflowItem,\n OverflowMenu,\n TableRow,\n useConfirm,\n useToast,\n} from '@withpotter/ui-kit';\nimport { platformAdminApi, type TenantRow } from '@withpotter/lib/admin-api';\nimport { EntityAvatar } from '../_fx';\nimport { CountryLabel } from '../_ui/country-label';\nimport { countryText } from '../_ui/country-text';\nimport { EMPTY } from '../_ui/list-card';\nimport { MoneyLines } from '../_ui/money-lines';\nimport { useAccountActions } from '../accounts/use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useAdminSession } from '../hooks';\nimport { StatusPill } from '../audit/status-pill';\nimport { AccountOfficerDialog } from './[id]/account-officer-dialog';\nimport { officerAction } from './account-officer-action';\nimport { logoOf } from './tenants-logo';\nimport list from '../_ui/list-card.module.css';\nimport rows from '../accounts/rows.module.css';\n\n/** One business: identity, money, status and a ••• menu. */\nexport function TenantListRow({\n t,\n onOpen,\n}: {\n t: TenantRow;\n onOpen: () => void;\n}) {\n const queryClient = useQueryClient();\n const confirm = useConfirm();\n const { toast } = useToast();\n const { impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const { data: session } = useAdminSession();\n const officerChoice = officerAction(\n t.accountOfficer,\n session?.role === 'superadmin',\n t.status,\n );\n const [picking, setPicking] = useState(false);\n const href = `/admin/tenants/${t.id}`;\n const suspended = t.status === 'suspended';\n const logo = logoOf(t);\n const fail = (title: string) => (e: Error) =>\n toast({ title, description: e.message, variant: 'destructive' });\n\n const status = useMutation({\n mutationFn: () =>\n platformAdminApi.setTenantStatus(\n t.id,\n suspended ? 'active' : 'suspended',\n ),\n onSuccess: () => {\n toast({\n title: `${t.businessName} ${suspended ? 'restored' : 'suspended'}`,\n });\n void queryClient.invalidateQueries({\n queryKey: ['platform-admin', 'tenants'],\n });\n },\n onError: fail('The status was not changed'),\n });\n\n // The owner is looked up first, so the tab is reserved inside the click\n // (before the await); opened afterwards it would be blocked as a pop-up.\n const viewAsOwner = useMutation({\n mutationFn: async (tab: Window | null) => {\n const detail = await platformAdminApi.tenant(t.id);\n const owner = detail.owner?.accountId;\n\n if (!owner) throw new Error('This business has no owner account.');\n\n impersonate.mutate(owner, tab);\n },\n onError: (error, tab) => {\n tab?.close();\n fail('Could not open the owner’s view')(error);\n },\n });\n\n const toggle = async () => {\n const ok = await confirm({\n title: suspended\n ? `Restore ${t.businessName}?`\n : `Suspend ${t.businessName}?`,\n description: suspended\n ? 'Their store and dashboard come back online straight away.'\n : 'Their store goes offline and their team loses access until restored.',\n confirmLabel: suspended ? 'Restore' : 'Suspend',\n tone: suspended ? 'default' : 'danger',\n });\n if (ok) status.mutate();\n };\n\n return (\n <TableRow onClick={onOpen}>\n <td>\n <span className={rows.identity}>\n {logo && <EntityAvatar name={t.businessName} src={logo} size={32} />}\n <span className={rows.identityText}>\n <Link\n href={href}\n className={rows.primary}\n title={t.businessName}\n onClick={(e) => e.stopPropagation()}\n >\n {t.businessName}\n </Link>\n <span className={rows.secondary} title={t.slug}>\n {[t.slug, t.phone].filter(Boolean).join(' · ')}\n </span>\n </span>\n </span>\n </td>\n <td className={`${list.clip} ${list.muted}`}>\n <span title={t.email ?? t.ownerEmail ?? undefined}>\n {t.email ?? t.ownerEmail ?? EMPTY}\n </span>\n </td>\n <td className={`${list.clip} ${list.muted}`}>\n {countryText(t.country, t.countryCode) ? (\n <CountryLabel country={t.country} code={t.countryCode} />\n ) : (\n EMPTY\n )}\n </td>\n <td className={`${list.clip} ${list.muted}`}>{t.industryKey ?? EMPTY}</td>\n <td className={list.num}>\n {/* One line per currency the business sells in; never added up. */}\n <MoneyLines\n rows={t.gmvByCurrency}\n currency=\"\"\n amount={(r) => r.amount}\n empty={EMPTY}\n />\n </td>\n <td className={list.nowrap}>\n <StatusPill status={t.status} />\n </td>\n <td\n className={`${list.clip} ${list.muted}`}\n onClick={(e) => e.stopPropagation()}\n >\n {officerChoice ? (\n <button\n type=\"button\"\n className={rows.nameButton}\n title={\n t.accountOfficer\n ? `${t.accountOfficer.name}. Change the account officer`\n : 'Assign an account officer'\n }\n onClick={() => setPicking(true)}\n >\n {t.accountOfficer?.name ?? 'Assign'}\n </button>\n ) : (\n (t.accountOfficer?.name ?? EMPTY)\n )}\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {new Date(t.createdAt).toLocaleDateString()}\n </td>\n <td className={rows.menuCell} onClick={(e) => e.stopPropagation()}>\n <OverflowMenu label={`Actions for ${t.businessName}`}>\n <OverflowItem onClick={onOpen}>View merchant</OverflowItem>\n {ownerView && (\n <OverflowItem\n disabled={viewAsOwner.isPending}\n onClick={() => viewAsOwner.mutate(impersonate.reserveTab())}\n >\n View as owner\n </OverflowItem>\n )}\n {officerChoice && (\n <OverflowItem onClick={() => setPicking(true)}>\n {officerChoice === 'assign'\n ? 'Assign account officer'\n : 'Change account officer'}\n </OverflowItem>\n )}\n {t.status !== 'deleted' && (\n <OverflowItem\n onClick={() => void toggle()}\n destructive={!suspended}\n >\n {suspended ? 'Restore' : 'Suspend'}\n </OverflowItem>\n )}\n </OverflowMenu>\n {picking && (\n <AccountOfficerDialog\n open\n tenantId={t.id}\n name={t.businessName}\n currentId={t.accountOfficer?.id ?? null}\n onClose={() => setPicking(false)}\n />\n )}\n </td>\n </TableRow>\n );\n}\n"],"mappings":"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","names":[]}
@@ -37,7 +37,7 @@ function TenantsTable() {
37
37
  {
38
38
  title: "All merchants",
39
39
  count: data?.total,
40
- columns: 8,
40
+ columns: 9,
41
41
  minWidth: 1e3,
42
42
  isLoading,
43
43
  isError,
@@ -99,6 +99,7 @@ function TenantsTable() {
99
99
  ] }),
100
100
  head: /* @__PURE__ */ jsxs("tr", { children: [
101
101
  /* @__PURE__ */ jsx(Th, { children: "Business" }),
102
+ /* @__PURE__ */ jsx(Th, { children: "Email" }),
102
103
  /* @__PURE__ */ jsx(Th, { children: "Country" }),
103
104
  /* @__PURE__ */ jsx(Th, { children: "Industry" }),
104
105
  /* @__PURE__ */ jsx(Th, { align: "right", children: "GMV (USD)" }),
@@ -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={8}\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>Country</Th>\n <Th>Industry</Th>\n <Th align=\"right\">GMV (USD)</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":";AAwDQ,SACE,KADF;AAtDR,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AAEnB,SAAS,0BAA0B;AACnC,SAAS,UAAU,iBAAiB;AACpC,SAAS,qBAAqB;AAC9B,SAAS,cAAc,cAAc,eAAe;AACpD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,sBAAsB,uBAAuB;AAEtD,MAAM,iBAAiC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,WAAW,CAAC,MAAc,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAG9D,SAAS,eAAe;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA4B,EAAE;AAC1D,QAAM,CAAC,SAAS,UAAU,IAAI,gBAAgB;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,gBAAgB;AAC9C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,CAAC;AAClC,QAAM,EAAE,MAAM,WAAW,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IAC/D;AAAA,IACA,UAAU;AAAA,IACV,QAAQ,UAAU;AAAA,IAClB,QAAQ,UAAU;AAAA,IAClB,SAAS,WAAW;AAAA,IACpB,kBAAkB,WAAW;AAAA,EAC/B,CAAC;AACD,QAAM,aAAa,OACf,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,QAAQ,CAAC,IACjD;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,YAAW;AAAA,MACX,SAAS,MAAM,KAAK,WAAW;AAAA,MAC/B,YAAW;AAAA,MACX,kBAAiB;AAAA,MACjB,UACE,qBAAC,WAAQ,OAAM,oBACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,wBAAU,CAAC;AACX,sBAAQ,CAAC;AAAA,YACX;AAAA,YACA,aAAY;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,wBAAU,CAAsB;AAChC,sBAAQ,CAAC;AAAA,YACX;AAAA,YACA,SAAS;AAAA,cACP,EAAE,OAAO,IAAI,OAAO,eAAe;AAAA,cACnC,GAAG,eAAe,IAAI,CAAC,OAAO;AAAA,gBAC5B,OAAO;AAAA,gBACP,OAAO,SAAS,CAAC;AAAA,cACnB,EAAE;AAAA,YACJ;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,yBAAW,CAAC;AACZ,sBAAQ,CAAC;AAAA,YACX;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,yBAAW,CAAC;AACZ,sBAAQ,CAAC;AAAA,YACX;AAAA;AAAA,QACF;AAAA,SACF;AAAA,MAEF,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,qBAAO;AAAA,QACX,oBAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,OAAM,SAAQ,uBAAS;AAAA,QAC3B,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,6BAAe;AAAA,QACnB,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,OAAM,SACR,8BAAC,UAAK,WAAU,WAAU,qBAAO,GACnC;AAAA,SACF;AAAA,MAEF,QACE;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,OAAO,MAAM;AAAA,UACb,MAAK;AAAA,UACL,QAAQ;AAAA;AAAA,MACV;AAAA,MAGD,gBAAM,KAAK,IAAI,CAAC,MACf;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,QAAQ,MAAM,OAAO,KAAK,kBAAkB,EAAE,EAAE,EAAE;AAAA;AAAA,QAF7C,EAAE;AAAA,MAGT,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
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 (USD)</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":";AAwDQ,SACE,KADF;AAtDR,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AAEnB,SAAS,0BAA0B;AACnC,SAAS,UAAU,iBAAiB;AACpC,SAAS,qBAAqB;AAC9B,SAAS,cAAc,cAAc,eAAe;AACpD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,sBAAsB,uBAAuB;AAEtD,MAAM,iBAAiC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,WAAW,CAAC,MAAc,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAG9D,SAAS,eAAe;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA4B,EAAE;AAC1D,QAAM,CAAC,SAAS,UAAU,IAAI,gBAAgB;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAI,gBAAgB;AAC9C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,CAAC;AAClC,QAAM,EAAE,MAAM,WAAW,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IAC/D;AAAA,IACA,UAAU;AAAA,IACV,QAAQ,UAAU;AAAA,IAClB,QAAQ,UAAU;AAAA,IAClB,SAAS,WAAW;AAAA,IACpB,kBAAkB,WAAW;AAAA,EAC/B,CAAC;AACD,QAAM,aAAa,OACf,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,QAAQ,CAAC,IACjD;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,YAAW;AAAA,MACX,SAAS,MAAM,KAAK,WAAW;AAAA,MAC/B,YAAW;AAAA,MACX,kBAAiB;AAAA,MACjB,UACE,qBAAC,WAAQ,OAAM,oBACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,wBAAU,CAAC;AACX,sBAAQ,CAAC;AAAA,YACX;AAAA,YACA,aAAY;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,wBAAU,CAAsB;AAChC,sBAAQ,CAAC;AAAA,YACX;AAAA,YACA,SAAS;AAAA,cACP,EAAE,OAAO,IAAI,OAAO,eAAe;AAAA,cACnC,GAAG,eAAe,IAAI,CAAC,OAAO;AAAA,gBAC5B,OAAO;AAAA,gBACP,OAAO,SAAS,CAAC;AAAA,cACnB,EAAE;AAAA,YACJ;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,yBAAW,CAAC;AACZ,sBAAQ,CAAC;AAAA,YACX;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,yBAAW,CAAC;AACZ,sBAAQ,CAAC;AAAA,YACX;AAAA;AAAA,QACF;AAAA,SACF;AAAA,MAEF,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,mBAAK;AAAA,QACT,oBAAC,MAAG,qBAAO;AAAA,QACX,oBAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,OAAM,SAAQ,uBAAS;AAAA,QAC3B,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,6BAAe;AAAA,QACnB,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,OAAM,SACR,8BAAC,UAAK,WAAU,WAAU,qBAAO,GACnC;AAAA,SACF;AAAA,MAEF,QACE;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,OAAO,MAAM;AAAA,UACb,MAAK;AAAA,UACL,QAAQ;AAAA;AAAA,MACV;AAAA,MAGD,gBAAM,KAAK,IAAI,CAAC,MACf;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,QAAQ,MAAM,OAAO,KAAK,kBAAkB,EAAE,EAAE,EAAE;AAAA;AAAA,QAF7C,EAAE;AAAA,MAGT,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withpotter/pages-admin",
3
- "version": "0.13.0-next.76",
3
+ "version": "0.13.0-next.78",
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.76",
35
- "@withpotter/platform-admin": "^0.13.0-next.76",
36
- "@withpotter/ui-kit": "^0.13.0-next.76",
34
+ "@withpotter/lib": "^0.13.0-next.78",
35
+ "@withpotter/platform-admin": "^0.13.0-next.78",
36
+ "@withpotter/ui-kit": "^0.13.0-next.78",
37
37
  "lucide-react": "^0.562.0",
38
38
  "motion": "^12.42.2"
39
39
  },
@@ -1,30 +0,0 @@
1
- "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { useQuery } from "@tanstack/react-query";
4
- import { adminReadinessApi } from "@withpotter/lib/admin-readiness-api";
5
- import { overallOf, SystemStatusPanel } from "../_shell/system-status-panel";
6
- import { SettingsCard } from "./setting-row";
7
- function SystemStatusCard() {
8
- const query = useQuery({
9
- queryKey: ["platform-admin", "readiness"],
10
- queryFn: adminReadinessApi.readiness,
11
- staleTime: 6e4,
12
- retry: 1
13
- });
14
- return /* @__PURE__ */ jsx(SettingsCard, { title: "System status", children: /* @__PURE__ */ jsx(
15
- SystemStatusPanel,
16
- {
17
- data: query.data,
18
- overall: overallOf(query.data, query.isError),
19
- isLoading: query.isLoading,
20
- isError: query.isError,
21
- isFetching: query.isFetching,
22
- checkedAt: query.dataUpdatedAt,
23
- onRefresh: () => void query.refetch()
24
- }
25
- ) });
26
- }
27
- export {
28
- SystemStatusCard
29
- };
30
- //# sourceMappingURL=system-status-card.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/admin/settings/system-status-card.tsx"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { adminReadinessApi } from '@withpotter/lib/admin-readiness-api';\nimport { overallOf, SystemStatusPanel } from '../_shell/system-status-panel';\nimport { SettingsCard } from './setting-row';\n\n/** The deployment's integration checks, each with its state. */\nexport function SystemStatusCard() {\n const query = useQuery({\n queryKey: ['platform-admin', 'readiness'],\n queryFn: adminReadinessApi.readiness,\n staleTime: 60_000,\n retry: 1,\n });\n\n return (\n <SettingsCard title=\"System status\">\n <SystemStatusPanel\n data={query.data}\n overall={overallOf(query.data, query.isError)}\n isLoading={query.isLoading}\n isError={query.isError}\n isFetching={query.isFetching}\n checkedAt={query.dataUpdatedAt}\n onRefresh={() => void query.refetch()}\n />\n </SettingsCard>\n );\n}\n"],"mappings":";AAkBM;AAhBN,SAAS,gBAAgB;AACzB,SAAS,yBAAyB;AAClC,SAAS,WAAW,yBAAyB;AAC7C,SAAS,oBAAoB;AAGtB,SAAS,mBAAmB;AACjC,QAAM,QAAQ,SAAS;AAAA,IACrB,UAAU,CAAC,kBAAkB,WAAW;AAAA,IACxC,SAAS,kBAAkB;AAAA,IAC3B,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,SACE,oBAAC,gBAAa,OAAM,iBAClB;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,MAAM;AAAA,MACZ,SAAS,UAAU,MAAM,MAAM,MAAM,OAAO;AAAA,MAC5C,WAAW,MAAM;AAAA,MACjB,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM,KAAK,MAAM,QAAQ;AAAA;AAAA,EACtC,GACF;AAEJ;","names":[]}