@withpotter/pages-admin 0.13.0-next.85 → 0.13.0-next.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/accounts/account-panel.js +43 -11
- package/dist/admin/accounts/account-panel.js.map +1 -1
- package/dist/admin/accounts/account-panel.module.css +59 -7
- package/dist/admin/accounts/account-row.js +2 -2
- package/dist/admin/accounts/account-row.js.map +1 -1
- package/dist/admin/accounts/account-sessions.js +28 -13
- package/dist/admin/accounts/account-sessions.js.map +1 -1
- package/dist/admin/accounts/account-sessions.module.css +4 -2
- package/dist/admin/accounts/account-stage.js +34 -0
- package/dist/admin/accounts/account-stage.js.map +1 -1
- package/dist/admin/accounts/account-vitals.js +15 -13
- package/dist/admin/accounts/account-vitals.js.map +1 -1
- package/dist/admin/audit/status-pill.js +6 -0
- package/dist/admin/audit/status-pill.js.map +1 -1
- package/dist/admin/audit/status-pill.module.css +4 -0
- package/package.json +4 -4
|
@@ -7,7 +7,14 @@ 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 {
|
|
10
|
+
import {
|
|
11
|
+
ACTIVE_HINT,
|
|
12
|
+
providerLabel,
|
|
13
|
+
STAGE_LABELS,
|
|
14
|
+
stageStatus,
|
|
15
|
+
stepStates,
|
|
16
|
+
stoppedAt
|
|
17
|
+
} from "./account-stage";
|
|
11
18
|
import { AccountSessions } from "./account-sessions";
|
|
12
19
|
import { Hint } from "./hint";
|
|
13
20
|
import { AccountVitals } from "./account-vitals";
|
|
@@ -20,12 +27,14 @@ function Section({
|
|
|
20
27
|
title,
|
|
21
28
|
index,
|
|
22
29
|
count,
|
|
30
|
+
hint,
|
|
23
31
|
children
|
|
24
32
|
}) {
|
|
25
33
|
return /* @__PURE__ */ jsxs(Reveal, { as: "section", index, className: styles.section, children: [
|
|
26
34
|
/* @__PURE__ */ jsxs("h3", { className: styles.heading, children: [
|
|
27
35
|
title,
|
|
28
|
-
count !== void 0 && /* @__PURE__ */ jsx("span", { className: styles.count, children: count })
|
|
36
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", { className: styles.count, children: count }),
|
|
37
|
+
hint && /* @__PURE__ */ jsx(Hint, { label: hint })
|
|
29
38
|
] }),
|
|
30
39
|
children
|
|
31
40
|
] });
|
|
@@ -62,11 +71,11 @@ function AccountPanel({
|
|
|
62
71
|
account.stage && /* @__PURE__ */ jsx(
|
|
63
72
|
StatusPill,
|
|
64
73
|
{
|
|
65
|
-
status: account.stage
|
|
74
|
+
status: stageStatus(account.stage),
|
|
66
75
|
label: STAGE_LABELS[account.stage]
|
|
67
76
|
}
|
|
68
77
|
),
|
|
69
|
-
account.authProvider && /* @__PURE__ */ jsx("span", { className: styles.muted, children: account.authProvider })
|
|
78
|
+
account.authProvider && /* @__PURE__ */ jsx("span", { className: styles.muted, children: providerLabel(account.authProvider) })
|
|
70
79
|
] }),
|
|
71
80
|
footer: account && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
72
81
|
account.status && /* @__PURE__ */ jsx(
|
|
@@ -105,17 +114,31 @@ function AccountPanel({
|
|
|
105
114
|
)
|
|
106
115
|
] }),
|
|
107
116
|
children: account && /* @__PURE__ */ jsxs("div", { className: styles.panel, children: [
|
|
108
|
-
|
|
109
|
-
(action, i) => action.isError && /* @__PURE__ */ jsx(Banner, { tone: "error", title: "That didn\u2019t work", children: action.error.message }, i)
|
|
110
|
-
),
|
|
111
|
-
/* @__PURE__ */ jsx(AccountVitals, { account, workspaces: liveWorkspaces }),
|
|
112
|
-
account.stage && /* @__PURE__ */ jsxs("p", { className: styles.stopped, children: [
|
|
117
|
+
account.stage && /* @__PURE__ */ jsxs("div", { className: styles.stopped, children: [
|
|
113
118
|
/* @__PURE__ */ jsxs("span", { className: styles.stoppedLabel, children: [
|
|
114
119
|
"Where this account stopped",
|
|
115
120
|
/* @__PURE__ */ jsx(Hint, { label: ACTIVE_HINT })
|
|
116
121
|
] }),
|
|
117
|
-
/* @__PURE__ */ jsx("
|
|
122
|
+
/* @__PURE__ */ jsx("ol", { className: styles.steps, children: stepStates(account).map(
|
|
123
|
+
(step) => /* @__PURE__ */ jsxs(
|
|
124
|
+
"li",
|
|
125
|
+
{
|
|
126
|
+
className: styles.step,
|
|
127
|
+
"data-state": step.state,
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsx("span", { className: styles.stepDot, "aria-hidden": true }),
|
|
130
|
+
step.label
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
step.label
|
|
134
|
+
)
|
|
135
|
+
) }),
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: styles.stoppedText, children: stoppedAt(account) ?? "Active" })
|
|
118
137
|
] }),
|
|
138
|
+
[verify, impersonate, setStatus].map(
|
|
139
|
+
(action, i) => action.isError && /* @__PURE__ */ jsx(Banner, { tone: "error", title: "That didn\u2019t work", children: action.error.message }, i)
|
|
140
|
+
),
|
|
141
|
+
/* @__PURE__ */ jsx(AccountVitals, { account, workspaces: liveWorkspaces }),
|
|
119
142
|
/* @__PURE__ */ jsx(
|
|
120
143
|
Section,
|
|
121
144
|
{
|
|
@@ -133,7 +156,16 @@ function AccountPanel({
|
|
|
133
156
|
}
|
|
134
157
|
),
|
|
135
158
|
/* @__PURE__ */ jsx(Section, { title: "Workspaces", index: 1, count: liveWorkspaces, children: /* @__PURE__ */ jsx(Workspaces, { accountId: account.id }) }),
|
|
136
|
-
/* @__PURE__ */ jsx(
|
|
159
|
+
/* @__PURE__ */ jsx(
|
|
160
|
+
Section,
|
|
161
|
+
{
|
|
162
|
+
title: "Sessions",
|
|
163
|
+
index: 2,
|
|
164
|
+
count: account.activeSessions,
|
|
165
|
+
hint: "The number is devices signed in now.",
|
|
166
|
+
children: /* @__PURE__ */ jsx(AccountSessions, { accountId: account.id })
|
|
167
|
+
}
|
|
168
|
+
),
|
|
137
169
|
/* @__PURE__ */ jsx(Section, { title: "Account ID", index: 3, children: /* @__PURE__ */ jsx(CopyText, { value: account.id }) })
|
|
138
170
|
] })
|
|
139
171
|
}
|
|
@@ -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 { 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":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-panel.tsx"],"sourcesContent":["'use client';\n\nimport { Banner, Button } from '@withpotter/ui-kit';\nimport type { AccountRow } from '@withpotter/lib/admin-api';\nimport { Reveal } from '../_fx';\nimport { Drawer } from '../_ui';\nimport { StatusPill } from '../audit/status-pill';\nimport { useAccountTenants } from '../hooks';\nimport { CopyText } from '../tenants/[id]/copy-text';\nimport { AccountActivity } from './account-activity';\nimport {\n ACTIVE_HINT,\n providerLabel,\n STAGE_LABELS,\n stageStatus,\n stepStates,\n stoppedAt,\n} from './account-stage';\nimport type { StepState } from './account-stage';\nimport { AccountSessions } from './account-sessions';\nimport { Hint } from './hint';\nimport { AccountVitals } from './account-vitals';\nimport { useAccountActions } from './use-account-actions';\nimport { useModuleOn } from '../_shell/modules-context';\nimport { useAccountActivity } from './use-account-activity';\nimport { Workspaces } from './account-workspaces';\nimport styles from './account-panel.module.css';\n\n/** Enough to open the panel from anywhere; the table row adds the rest. */\nexport type AccountSummary = Pick<AccountRow, 'id' | 'email' | 'name'> &\n Partial<AccountRow>;\n\nfunction Section({\n title,\n index,\n count,\n hint,\n children,\n}: {\n title: string;\n index: number;\n count?: number;\n hint?: string;\n children: React.ReactNode;\n}) {\n return (\n <Reveal as=\"section\" index={index} className={styles.section}>\n <h3 className={styles.heading}>\n {title}\n {count !== undefined && <span className={styles.count}>{count}</span>}\n {hint && <Hint label={hint} />}\n </h3>\n {children}\n </Reveal>\n );\n}\n\nexport function AccountPanel({\n account,\n onClose,\n}: {\n account: AccountSummary | null;\n onClose: () => void;\n}) {\n const { setStatus, verify, impersonate } = useAccountActions();\n const ownerView = useModuleOn('ownerView');\n const tenants = useAccountTenants(account?.id ?? null);\n const activity = useAccountActivity(account?.id ?? null, tenants.data);\n const suspended = account?.status === 'suspended';\n const liveWorkspaces = tenants.data?.filter(\n (w) => w.status !== 'deleted' && w.memberStatus !== 'removed',\n ).length;\n\n return (\n <Drawer\n open={Boolean(account)}\n onClose={onClose}\n width={560}\n title={account?.name || account?.email || 'Account'}\n subtitle={account?.name ? account.email : undefined}\n meta={\n account && (\n <>\n {account.status && <StatusPill status={account.status} />}\n {account.emailVerified !== undefined && (\n <StatusPill\n status={account.emailVerified ? 'verified' : 'unverified'}\n label={\n account.emailVerified ? 'Email verified' : 'Email unverified'\n }\n />\n )}\n {account.stage && (\n <StatusPill\n status={stageStatus(account.stage)}\n label={STAGE_LABELS[account.stage]}\n />\n )}\n {account.authProvider && (\n <span className={styles.muted}>\n {providerLabel(account.authProvider)}\n </span>\n )}\n </>\n )\n }\n footer={\n account && (\n <>\n {account.status && (\n <Button\n size=\"sm\"\n variant={suspended ? 'secondary' : 'ghostDanger'}\n loading={setStatus.isPending}\n onClick={() =>\n setStatus.mutate({\n id: account.id,\n status: suspended ? 'active' : 'suspended',\n })\n }\n >\n {suspended ? 'Restore' : 'Suspend'}\n </Button>\n )}\n <span className={styles.spacer} />\n {account.emailVerified === false && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n loading={verify.isPending}\n onClick={() => verify.mutate(account.id)}\n >\n Verify email\n </Button>\n )}\n {ownerView && (\n <Button\n size=\"sm\"\n variant=\"primary\"\n loading={impersonate.isPending}\n onClick={() => impersonate.mutate(account.id)}\n >\n View as user\n </Button>\n )}\n </>\n )\n }\n >\n {account && (\n <div className={styles.panel}>\n {account.stage && (\n <div className={styles.stopped}>\n <span className={styles.stoppedLabel}>\n Where this account stopped\n <Hint label={ACTIVE_HINT} />\n </span>\n <ol className={styles.steps}>\n {stepStates(account).map(\n (step: { label: string; state: StepState }) => (\n <li\n key={step.label}\n className={styles.step}\n data-state={step.state}\n >\n <span className={styles.stepDot} aria-hidden />\n {step.label}\n </li>\n ),\n )}\n </ol>\n <span className={styles.stoppedText}>\n {stoppedAt(account) ?? 'Active'}\n </span>\n </div>\n )}\n\n {[verify, impersonate, setStatus].map(\n (action, i) =>\n action.isError && (\n <Banner key={i} tone=\"error\" title=\"That didn’t work\">\n {action.error.message}\n </Banner>\n ),\n )}\n\n <AccountVitals account={account} workspaces={liveWorkspaces} />\n\n <Section\n title=\"Recent activity\"\n index={0}\n count={activity.entries.length}\n >\n <AccountActivity\n entries={activity.entries}\n isLoading={activity.isLoading}\n skipped={activity.skipped}\n />\n </Section>\n\n <Section title=\"Workspaces\" index={1} count={liveWorkspaces}>\n <Workspaces accountId={account.id} />\n </Section>\n\n <Section\n title=\"Sessions\"\n index={2}\n count={account.activeSessions}\n hint=\"The number is devices signed in now.\"\n >\n <AccountSessions accountId={account.id} />\n </Section>\n\n <Section title=\"Account ID\" index={3}>\n <CopyText value={account.id} />\n </Section>\n </div>\n )}\n </Drawer>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
background: var(--bg-base);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
.vitals[data-count='3'] {
|
|
20
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
.vital {
|
|
20
24
|
display: grid;
|
|
21
25
|
gap: 4px;
|
|
@@ -141,21 +145,69 @@
|
|
|
141
145
|
|
|
142
146
|
.stopped {
|
|
143
147
|
display: grid;
|
|
144
|
-
gap:
|
|
145
|
-
|
|
146
|
-
padding: 12px 16px;
|
|
148
|
+
gap: 12px;
|
|
149
|
+
padding: 16px;
|
|
147
150
|
border-radius: var(--adm-card-radius);
|
|
148
151
|
border: 1px solid var(--adm-line);
|
|
149
152
|
background: var(--bg-base);
|
|
150
|
-
font-size: 14px;
|
|
151
|
-
font-weight: 500;
|
|
152
|
-
line-height: 20px;
|
|
153
|
-
color: var(--text-primary);
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
.stoppedLabel {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
gap: 4px;
|
|
157
159
|
font-size: 12px;
|
|
158
160
|
font-weight: 400;
|
|
159
161
|
line-height: 16px;
|
|
160
162
|
color: var(--text-tertiary);
|
|
161
163
|
}
|
|
164
|
+
|
|
165
|
+
.steps {
|
|
166
|
+
display: grid;
|
|
167
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
168
|
+
gap: 8px;
|
|
169
|
+
margin: 0;
|
|
170
|
+
padding: 0;
|
|
171
|
+
list-style: none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.step {
|
|
175
|
+
display: grid;
|
|
176
|
+
gap: 8px;
|
|
177
|
+
font-size: 12px;
|
|
178
|
+
line-height: 16px;
|
|
179
|
+
color: var(--text-tertiary);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.stepDot {
|
|
183
|
+
width: 8px;
|
|
184
|
+
height: 8px;
|
|
185
|
+
border-radius: 999px;
|
|
186
|
+
box-shadow: inset 0 0 0 1px var(--border-subtle);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.step[data-state='done'] {
|
|
190
|
+
color: var(--text-secondary);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.step[data-state='done'] .stepDot {
|
|
194
|
+
background: var(--adm-ok);
|
|
195
|
+
box-shadow: none;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.step[data-state='current'] {
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
color: var(--text-primary);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.step[data-state='current'] .stepDot {
|
|
204
|
+
background: var(--adm-wait);
|
|
205
|
+
box-shadow: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.stoppedText {
|
|
209
|
+
font-size: 14px;
|
|
210
|
+
font-weight: 500;
|
|
211
|
+
line-height: 20px;
|
|
212
|
+
color: var(--text-primary);
|
|
213
|
+
}
|
|
@@ -11,7 +11,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
|
+
import { kycLabel, STAGE_LABELS, stageStatus } from "./account-stage";
|
|
15
15
|
import { useAccountActions } from "./use-account-actions";
|
|
16
16
|
import { useModuleOn } from "../_shell/modules-context";
|
|
17
17
|
import list from "../_ui/list-card.module.css";
|
|
@@ -81,7 +81,7 @@ function AccountListRow({
|
|
|
81
81
|
visible.has("stage") && /* @__PURE__ */ jsx("td", { className: list.nowrap, children: a.stage ? /* @__PURE__ */ jsx(
|
|
82
82
|
StatusPill,
|
|
83
83
|
{
|
|
84
|
-
status: a.stage
|
|
84
|
+
status: stageStatus(a.stage),
|
|
85
85
|
label: STAGE_LABELS[a.stage]
|
|
86
86
|
}
|
|
87
87
|
) : /* @__PURE__ */ jsx("span", { className: list.muted, children: EMPTY }) }),
|
|
@@ -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 { 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
|
|
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, stageStatus } 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={stageStatus(a.stage)}\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":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import { Button, EmptyNote } from "@withpotter/ui-kit";
|
|
4
5
|
import { AdmLoading } from "../_ui";
|
|
5
6
|
import { useAccountSessions } from "../hooks";
|
|
@@ -17,11 +18,14 @@ const when = (iso) => iso ? new Date(iso).toLocaleString(void 0, {
|
|
|
17
18
|
dateStyle: "medium",
|
|
18
19
|
timeStyle: "short"
|
|
19
20
|
}) : null;
|
|
21
|
+
const FIRST_PAGE = 5;
|
|
22
|
+
const NEXT_PAGE = 10;
|
|
20
23
|
function deviceOf(s) {
|
|
21
24
|
return s.name || s.kind || s.userAgent || "Unknown device";
|
|
22
25
|
}
|
|
23
26
|
function AccountSessions({ accountId }) {
|
|
24
27
|
const { data, isLoading, isError, refetch } = useAccountSessions(accountId);
|
|
28
|
+
const [shown, setShown] = useState(FIRST_PAGE);
|
|
25
29
|
if (isLoading) return /* @__PURE__ */ jsx(AdmLoading, { label: "Loading sessions\u2026", size: "sm" });
|
|
26
30
|
if (isError) {
|
|
27
31
|
return /* @__PURE__ */ jsxs(EmptyNote, { children: [
|
|
@@ -36,18 +40,29 @@ function AccountSessions({ accountId }) {
|
|
|
36
40
|
const ordered = [...data].sort(
|
|
37
41
|
(a, b) => b.startedAt.localeCompare(a.startedAt)
|
|
38
42
|
);
|
|
39
|
-
return /* @__PURE__ */
|
|
40
|
-
/* @__PURE__ */ jsxs("
|
|
41
|
-
/* @__PURE__ */
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
44
|
+
/* @__PURE__ */ jsx("ul", { className: styles.list, children: ordered.slice(0, shown).map((s) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
|
|
45
|
+
/* @__PURE__ */ jsxs("span", { className: styles.top, children: [
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: styles.device, title: s.userAgent ?? void 0, children: deviceOf(s) }),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: styles.how, "data-live": inUse(s), children: stateOf(s) })
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsx("span", { className: styles.meta, children: [s.ipAddress, s.location].filter(Boolean).join(" \xB7 ") || "No address recorded" }),
|
|
50
|
+
/* @__PURE__ */ jsxs("span", { className: styles.meta, children: [
|
|
51
|
+
"Started ",
|
|
52
|
+
when(s.startedAt),
|
|
53
|
+
s.endedAt ? ` \xB7 Ended ${when(s.endedAt)}` : ` \xB7 Last seen ${relativeTime(s.lastSeenAt)}`
|
|
54
|
+
] })
|
|
55
|
+
] }, s.id)) }),
|
|
56
|
+
ordered.length > shown && /* @__PURE__ */ jsx("div", { className: styles.more, children: /* @__PURE__ */ jsx(
|
|
57
|
+
Button,
|
|
58
|
+
{
|
|
59
|
+
variant: "secondary",
|
|
60
|
+
size: "sm",
|
|
61
|
+
onClick: () => setShown((n) => n + NEXT_PAGE),
|
|
62
|
+
children: "Show older"
|
|
63
|
+
}
|
|
64
|
+
) })
|
|
65
|
+
] });
|
|
51
66
|
}
|
|
52
67
|
export {
|
|
53
68
|
AccountSessions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-sessions.tsx"],"sourcesContent":["'use client';\n\nimport { Button, EmptyNote } from '@withpotter/ui-kit';\nimport type { AccountSession } from '@withpotter/lib/admin-api';\nimport { AdmLoading } from '../_ui';\nimport { useAccountSessions } from '../hooks';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-sessions.module.css';\n\nconst ENDED: Record<AccountSession['endedHow'], string> = {\n active: 'Signed in',\n revoked: 'Signed out',\n expired: 'Expired',\n};\n\n/** A device seen in the last few minutes is in use; a session not ended yet is only signed in. */\nconst IN_USE_SECONDS = 300;\nconst inUse = (s: AccountSession) =>\n s.endedHow === 'active' && seenWithin(s.lastSeenAt, IN_USE_SECONDS);\nconst stateOf = (s: AccountSession) =>\n inUse(s) ? 'Active now' : ENDED[s.endedHow];\n\nconst when = (iso: string | null) =>\n iso\n ? new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n : null;\n\n/** A short device name from the session, falling back to the raw agent. */\nfunction deviceOf(s: AccountSession): string {\n return s.name || s.kind || s.userAgent || 'Unknown device';\n}\n\n/** Where and when this person signed in, newest first. */\nexport function AccountSessions({ accountId }: { accountId: string }) {\n const { data, isLoading, isError, refetch } = useAccountSessions(accountId);\n\n if (isLoading) return <AdmLoading label=\"Loading sessions…\" size=\"sm\" />;\n\n if (isError) {\n return (\n <EmptyNote>\n We could not load their sessions just now.{' '}\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void refetch()}>\n Try again\n </Button>\n </EmptyNote>\n );\n }\n\n if (!data?.length) {\n return <EmptyNote>No sign-ins recorded for this person yet.</EmptyNote>;\n }\n\n const ordered = [...data].sort((a: AccountSession, b: AccountSession) =>\n b.startedAt.localeCompare(a.startedAt),\n );\n\n return (\n <ul className={styles.list}>\n
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-sessions.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, EmptyNote } from '@withpotter/ui-kit';\nimport type { AccountSession } from '@withpotter/lib/admin-api';\nimport { AdmLoading } from '../_ui';\nimport { useAccountSessions } from '../hooks';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-sessions.module.css';\n\nconst ENDED: Record<AccountSession['endedHow'], string> = {\n active: 'Signed in',\n revoked: 'Signed out',\n expired: 'Expired',\n};\n\n/** A device seen in the last few minutes is in use; a session not ended yet is only signed in. */\nconst IN_USE_SECONDS = 300;\nconst inUse = (s: AccountSession) =>\n s.endedHow === 'active' && seenWithin(s.lastSeenAt, IN_USE_SECONDS);\nconst stateOf = (s: AccountSession) =>\n inUse(s) ? 'Active now' : ENDED[s.endedHow];\n\nconst when = (iso: string | null) =>\n iso\n ? new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n : null;\n\n/** Sessions shown first, and how many more each \"Show older\" adds. */\nconst FIRST_PAGE = 5;\nconst NEXT_PAGE = 10;\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 const [shown, setShown] = useState(FIRST_PAGE);\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 <>\n <ul className={styles.list}>\n {ordered.slice(0, shown).map((s: AccountSession) => (\n <li key={s.id} className={styles.item}>\n <span className={styles.top}>\n <span className={styles.device} title={s.userAgent ?? undefined}>\n {deviceOf(s)}\n </span>\n <span className={styles.how} data-live={inUse(s)}>\n {stateOf(s)}\n </span>\n </span>\n <span className={styles.meta}>\n {[s.ipAddress, s.location].filter(Boolean).join(' · ') ||\n 'No address recorded'}\n </span>\n <span className={styles.meta}>\n Started {when(s.startedAt)}\n {s.endedAt\n ? ` · Ended ${when(s.endedAt)}`\n : ` · Last seen ${relativeTime(s.lastSeenAt)}`}\n </span>\n </li>\n ))}\n </ul>\n {ordered.length > shown && (\n <div className={styles.more}>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => setShown((n: number) => n + NEXT_PAGE)}\n >\n Show older\n </Button>\n </div>\n )}\n </>\n );\n}\n"],"mappings":";AA6CwB,SAsBpB,UAtBoB,KAIlB,YAJkB;AA3CxB,SAAS,gBAAgB;AACzB,SAAS,QAAQ,iBAAiB;AAElC,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,cAAc,kBAAkB;AACzC,OAAO,YAAY;AAEnB,MAAM,QAAoD;AAAA,EACxD,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAGA,MAAM,iBAAiB;AACvB,MAAM,QAAQ,CAAC,MACb,EAAE,aAAa,YAAY,WAAW,EAAE,YAAY,cAAc;AACpE,MAAM,UAAU,CAAC,MACf,MAAM,CAAC,IAAI,eAAe,MAAM,EAAE,QAAQ;AAE5C,MAAM,OAAO,CAAC,QACZ,MACI,IAAI,KAAK,GAAG,EAAE,eAAe,QAAW;AAAA,EACtC,WAAW;AAAA,EACX,WAAW;AACb,CAAC,IACD;AAGN,MAAM,aAAa;AACnB,MAAM,YAAY;AAGlB,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;AAC1E,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,UAAU;AAE7C,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,iCACE;AAAA,wBAAC,QAAG,WAAW,OAAO,MACnB,kBAAQ,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAC5B,qBAAC,QAAc,WAAW,OAAO,MAC/B;AAAA,2BAAC,UAAK,WAAW,OAAO,KACtB;AAAA,4BAAC,UAAK,WAAW,OAAO,QAAQ,OAAO,EAAE,aAAa,QACnD,mBAAS,CAAC,GACb;AAAA,QACA,oBAAC,UAAK,WAAW,OAAO,KAAK,aAAW,MAAM,CAAC,GAC5C,kBAAQ,CAAC,GACZ;AAAA,SACF;AAAA,MACA,oBAAC,UAAK,WAAW,OAAO,MACrB,WAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK,KACnD,uBACJ;AAAA,MACA,qBAAC,UAAK,WAAW,OAAO,MAAM;AAAA;AAAA,QACnB,KAAK,EAAE,SAAS;AAAA,QACxB,EAAE,UACC,eAAY,KAAK,EAAE,OAAO,CAAC,KAC3B,mBAAgB,aAAa,EAAE,UAAU,CAAC;AAAA,SAChD;AAAA,SAlBO,EAAE,EAmBX,CACD,GACH;AAAA,IACC,QAAQ,SAAS,SAChB,oBAAC,SAAI,WAAW,OAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS,MAAM,SAAS,CAAC,MAAc,IAAI,SAAS;AAAA,QACrD;AAAA;AAAA,IAED,GACF;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
.list {
|
|
2
2
|
display: grid;
|
|
3
3
|
gap: 8px;
|
|
4
|
-
max-height: 320px;
|
|
5
4
|
margin: 0;
|
|
6
5
|
padding: 0;
|
|
7
|
-
overflow-y: auto;
|
|
8
6
|
list-style: none;
|
|
9
7
|
}
|
|
10
8
|
|
|
@@ -51,3 +49,7 @@
|
|
|
51
49
|
line-height: 16px;
|
|
52
50
|
color: var(--text-tertiary);
|
|
53
51
|
}
|
|
52
|
+
|
|
53
|
+
.more {
|
|
54
|
+
display: flex;
|
|
55
|
+
}
|
|
@@ -21,6 +21,17 @@ const KYC_LABELS = {
|
|
|
21
21
|
DENIED: "Denied"
|
|
22
22
|
};
|
|
23
23
|
const ACTIVE_HINT = "Active means the email is verified, KYC or KYB is approved, and at least one sale was paid.";
|
|
24
|
+
const stageStatus = (stage) => `stage_${stage}`;
|
|
25
|
+
const PROVIDERS = {
|
|
26
|
+
local: "Email",
|
|
27
|
+
email: "Email",
|
|
28
|
+
google: "Google",
|
|
29
|
+
apple: "Apple"
|
|
30
|
+
};
|
|
31
|
+
function providerLabel(provider) {
|
|
32
|
+
const key = provider.toLowerCase();
|
|
33
|
+
return PROVIDERS[key] ?? key.charAt(0).toUpperCase() + key.slice(1);
|
|
34
|
+
}
|
|
24
35
|
function kycLabel(status) {
|
|
25
36
|
if (!status) return null;
|
|
26
37
|
return KYC_LABELS[status] ?? status;
|
|
@@ -36,12 +47,35 @@ function stoppedAt(a) {
|
|
|
36
47
|
if (!a.firstSaleAt) return "Make a first sale";
|
|
37
48
|
return null;
|
|
38
49
|
}
|
|
50
|
+
function stepStates(a) {
|
|
51
|
+
const all = a.stage === "active";
|
|
52
|
+
const done = [
|
|
53
|
+
all || a.emailVerified === true,
|
|
54
|
+
all || (a.workspaceCount ?? 0) > 0,
|
|
55
|
+
all || a.kycStatus === "APPROVED",
|
|
56
|
+
all || Boolean(a.firstSaleAt)
|
|
57
|
+
];
|
|
58
|
+
const labels = [
|
|
59
|
+
"Email verified",
|
|
60
|
+
"Business added",
|
|
61
|
+
"KYC or KYB approved",
|
|
62
|
+
"First sale"
|
|
63
|
+
];
|
|
64
|
+
const current = done.indexOf(false);
|
|
65
|
+
return labels.map((label, i) => ({
|
|
66
|
+
label,
|
|
67
|
+
state: done[i] ? "done" : i === current ? "current" : "todo"
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
39
70
|
export {
|
|
40
71
|
ACTIVE_HINT,
|
|
41
72
|
KYC_LABELS,
|
|
42
73
|
STAGE_LABELS,
|
|
43
74
|
STAGE_ORDER,
|
|
44
75
|
kycLabel,
|
|
76
|
+
providerLabel,
|
|
77
|
+
stageStatus,
|
|
78
|
+
stepStates,
|
|
45
79
|
stoppedAt
|
|
46
80
|
};
|
|
47
81
|
//# sourceMappingURL=account-stage.js.map
|
|
@@ -1 +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":[]}
|
|
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/** The StatusPill key for a stage, so each stage gets its own tone. */\nexport const stageStatus = (stage: AccountStage): string => `stage_${stage}`;\n\nconst PROVIDERS: Record<string, string> = {\n local: 'Email',\n email: 'Email',\n google: 'Google',\n apple: 'Apple',\n};\n\n/** Plain words for a sign-in provider; the capitalised value for unknown ones. */\nexport function providerLabel(provider: string): string {\n const key = provider.toLowerCase();\n\n return PROVIDERS[key] ?? key.charAt(0).toUpperCase() + key.slice(1);\n}\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\nexport type StepState = 'done' | 'current' | 'todo';\n\n/**\n * The four steps to an active account, each done, current (the first not\n * done) or todo, from the same fields as stoppedAt.\n */\nexport function stepStates(\n a: Partial<Progress>,\n): { label: string; state: StepState }[] {\n const all = a.stage === 'active';\n const done = [\n all || a.emailVerified === true,\n all || (a.workspaceCount ?? 0) > 0,\n all || a.kycStatus === 'APPROVED',\n all || Boolean(a.firstSaleAt),\n ];\n const labels = [\n 'Email verified',\n 'Business added',\n 'KYC or KYB approved',\n 'First sale',\n ];\n const current = done.indexOf(false);\n\n return labels.map((label: string, i: number) => ({\n label,\n state: done[i] ? 'done' : i === current ? 'current' : 'todo',\n }));\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,MAAM,cAAc,CAAC,UAAgC,SAAS,KAAK;AAE1E,MAAM,YAAoC;AAAA,EACxC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAGO,SAAS,cAAc,UAA0B;AACtD,QAAM,MAAM,SAAS,YAAY;AAEjC,SAAO,UAAU,GAAG,KAAK,IAAI,OAAO,CAAC,EAAE,YAAY,IAAI,IAAI,MAAM,CAAC;AACpE;AAGO,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;AAQO,SAAS,WACd,GACuC;AACvC,QAAM,MAAM,EAAE,UAAU;AACxB,QAAM,OAAO;AAAA,IACX,OAAO,EAAE,kBAAkB;AAAA,IAC3B,QAAQ,EAAE,kBAAkB,KAAK;AAAA,IACjC,OAAO,EAAE,cAAc;AAAA,IACvB,OAAO,QAAQ,EAAE,WAAW;AAAA,EAC9B;AACA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU,KAAK,QAAQ,KAAK;AAElC,SAAO,OAAO,IAAI,CAAC,OAAe,OAAe;AAAA,IAC/C;AAAA,IACA,OAAO,KAAK,CAAC,IAAI,SAAS,MAAM,UAAU,YAAY;AAAA,EACxD,EAAE;AACJ;","names":[]}
|
|
@@ -1,35 +1,37 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { relativeTime, seenWithin } from "./relative-time";
|
|
3
3
|
import styles from "./account-panel.module.css";
|
|
4
|
+
const SEEN_GAP_MS = 5 * 60 * 1e3;
|
|
4
5
|
function AccountVitals({
|
|
5
6
|
account,
|
|
6
7
|
workspaces
|
|
7
8
|
}) {
|
|
8
|
-
const online = seenWithin(account.lastSeenAt, 15 * 60);
|
|
9
|
+
const online = Boolean(seenWithin(account.lastSeenAt, 15 * 60));
|
|
10
|
+
const laterSeen = account.lastSeenAt && (!account.lastLoginAt || new Date(account.lastSeenAt).getTime() - new Date(account.lastLoginAt).getTime() > SEEN_GAP_MS);
|
|
9
11
|
const tiles = [
|
|
10
|
-
{
|
|
11
|
-
label: "Last seen",
|
|
12
|
-
value: relativeTime(account.lastSeenAt ?? account.lastLoginAt),
|
|
13
|
-
hint: account.activeSessions !== void 0 ? `${account.activeSessions} signed-in device${account.activeSessions === 1 ? "" : "s"}` : void 0,
|
|
14
|
-
live: Boolean(online)
|
|
15
|
-
},
|
|
16
12
|
{
|
|
17
13
|
label: "Last login",
|
|
18
14
|
value: relativeTime(account.lastLoginAt),
|
|
19
|
-
hint: account.lastLoginAt ? new Date(account.lastLoginAt).toLocaleDateString() : void 0
|
|
15
|
+
hint: account.lastLoginAt ? new Date(account.lastLoginAt).toLocaleDateString() : void 0,
|
|
16
|
+
live: online && !laterSeen
|
|
20
17
|
},
|
|
18
|
+
...laterSeen ? [
|
|
19
|
+
{
|
|
20
|
+
label: "Last seen",
|
|
21
|
+
value: relativeTime(account.lastSeenAt),
|
|
22
|
+
live: online
|
|
23
|
+
}
|
|
24
|
+
] : [],
|
|
21
25
|
{
|
|
22
26
|
label: "Workspaces",
|
|
23
|
-
value: workspaces === void 0 ? "\u2026" : String(workspaces)
|
|
24
|
-
hint: "Active memberships"
|
|
27
|
+
value: workspaces === void 0 ? "\u2026" : String(workspaces)
|
|
25
28
|
},
|
|
26
29
|
{
|
|
27
30
|
label: "Joined",
|
|
28
|
-
value: account.createdAt ?
|
|
29
|
-
hint: account.createdAt ? new Date(account.createdAt).toLocaleDateString() : void 0
|
|
31
|
+
value: account.createdAt ? new Date(account.createdAt).toLocaleDateString() : "\u2014"
|
|
30
32
|
}
|
|
31
33
|
];
|
|
32
|
-
return /* @__PURE__ */ jsx("div", { className: styles.vitals, children: tiles.map((tile) => /* @__PURE__ */ jsxs("div", { className: styles.vital, children: [
|
|
34
|
+
return /* @__PURE__ */ jsx("div", { className: styles.vitals, "data-count": tiles.length, children: tiles.map((tile) => /* @__PURE__ */ jsxs("div", { className: styles.vital, children: [
|
|
33
35
|
/* @__PURE__ */ jsxs("span", { className: styles.vitalLabel, children: [
|
|
34
36
|
tile.live && /* @__PURE__ */ jsx("span", { className: styles.live, "aria-label": "Online" }),
|
|
35
37
|
tile.label
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/accounts/account-vitals.tsx"],"sourcesContent":["import type { AccountRow } from '@withpotter/lib/admin-api';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-panel.module.css';\n\n/**
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/accounts/account-vitals.tsx"],"sourcesContent":["import type { AccountRow } from '@withpotter/lib/admin-api';\nimport { relativeTime, seenWithin } from './relative-time';\nimport styles from './account-panel.module.css';\n\n/** Last seen only earns a tile when it is meaningfully after the last login. */\nconst SEEN_GAP_MS = 5 * 60 * 1000;\n\ntype Tile = { label: string; value: string; hint?: string; live?: boolean };\n\n/** Three numbers (a fourth when last seen differs) that say how deeply this person is here. */\nexport function AccountVitals({\n account,\n workspaces,\n}: {\n account: Pick<AccountRow, 'id'> & Partial<AccountRow>;\n workspaces: number | undefined;\n}) {\n const online = Boolean(seenWithin(account.lastSeenAt, 15 * 60));\n const laterSeen =\n account.lastSeenAt &&\n (!account.lastLoginAt ||\n new Date(account.lastSeenAt).getTime() -\n new Date(account.lastLoginAt).getTime() >\n SEEN_GAP_MS);\n const tiles: Tile[] = [\n {\n label: 'Last login',\n value: relativeTime(account.lastLoginAt),\n hint: account.lastLoginAt\n ? new Date(account.lastLoginAt).toLocaleDateString()\n : undefined,\n live: online && !laterSeen,\n },\n ...(laterSeen\n ? [\n {\n label: 'Last seen',\n value: relativeTime(account.lastSeenAt),\n live: online,\n },\n ]\n : []),\n {\n label: 'Workspaces',\n value: workspaces === undefined ? '…' : String(workspaces),\n },\n {\n label: 'Joined',\n value: account.createdAt\n ? new Date(account.createdAt).toLocaleDateString()\n : '—',\n },\n ];\n\n return (\n <div className={styles.vitals} data-count={tiles.length}>\n {tiles.map((tile: Tile) => (\n <div key={tile.label} className={styles.vital}>\n <span className={styles.vitalLabel}>\n {tile.live && <span className={styles.live} aria-label=\"Online\" />}\n {tile.label}\n </span>\n <span className={styles.vitalValue}>{tile.value}</span>\n {tile.hint && <span className={styles.vitalHint}>{tile.hint}</span>}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"AA0DU,SACgB,KADhB;AAzDV,SAAS,cAAc,kBAAkB;AACzC,OAAO,YAAY;AAGnB,MAAM,cAAc,IAAI,KAAK;AAKtB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,QAAM,SAAS,QAAQ,WAAW,QAAQ,YAAY,KAAK,EAAE,CAAC;AAC9D,QAAM,YACJ,QAAQ,eACP,CAAC,QAAQ,eACR,IAAI,KAAK,QAAQ,UAAU,EAAE,QAAQ,IACnC,IAAI,KAAK,QAAQ,WAAW,EAAE,QAAQ,IACtC;AACN,QAAM,QAAgB;AAAA,IACpB;AAAA,MACE,OAAO;AAAA,MACP,OAAO,aAAa,QAAQ,WAAW;AAAA,MACvC,MAAM,QAAQ,cACV,IAAI,KAAK,QAAQ,WAAW,EAAE,mBAAmB,IACjD;AAAA,MACJ,MAAM,UAAU,CAAC;AAAA,IACnB;AAAA,IACA,GAAI,YACA;AAAA,MACE;AAAA,QACE,OAAO;AAAA,QACP,OAAO,aAAa,QAAQ,UAAU;AAAA,QACtC,MAAM;AAAA,MACR;AAAA,IACF,IACA,CAAC;AAAA,IACL;AAAA,MACE,OAAO;AAAA,MACP,OAAO,eAAe,SAAY,WAAM,OAAO,UAAU;AAAA,IAC3D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,OAAO,QAAQ,YACX,IAAI,KAAK,QAAQ,SAAS,EAAE,mBAAmB,IAC/C;AAAA,IACN;AAAA,EACF;AAEA,SACE,oBAAC,SAAI,WAAW,OAAO,QAAQ,cAAY,MAAM,QAC9C,gBAAM,IAAI,CAAC,SACV,qBAAC,SAAqB,WAAW,OAAO,OACtC;AAAA,yBAAC,UAAK,WAAW,OAAO,YACrB;AAAA,WAAK,QAAQ,oBAAC,UAAK,WAAW,OAAO,MAAM,cAAW,UAAS;AAAA,MAC/D,KAAK;AAAA,OACR;AAAA,IACA,oBAAC,UAAK,WAAW,OAAO,YAAa,eAAK,OAAM;AAAA,IAC/C,KAAK,QAAQ,oBAAC,UAAK,WAAW,OAAO,WAAY,eAAK,MAAK;AAAA,OANpD,KAAK,KAOf,CACD,GACH;AAEJ;","names":[]}
|
|
@@ -23,6 +23,12 @@ const TONE_OF = {
|
|
|
23
23
|
completed: "complete",
|
|
24
24
|
closed: "complete",
|
|
25
25
|
draft: "draft",
|
|
26
|
+
// Account stages read as progress: grey, blue, amber, teal, green.
|
|
27
|
+
stage_signed_up: "neutral",
|
|
28
|
+
stage_verified: "processing",
|
|
29
|
+
stage_onboarding: "pending",
|
|
30
|
+
stage_kyc_approved: "ahead",
|
|
31
|
+
stage_active: "success",
|
|
26
32
|
deleted: "neutral",
|
|
27
33
|
rejected: "neutral"
|
|
28
34
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/audit/status-pill.tsx"],"sourcesContent":["import styles from './status-pill.module.css';\n\ntype StatusTone =\n | 'success'\n | 'pending'\n | 'failed'\n | 'processing'\n | 'complete'\n | 'draft'\n | 'neutral';\n\nconst TONE_OF: Record<string, StatusTone> = {\n active: 'success',\n verified: 'success',\n approved: 'success',\n resolved: 'success',\n pending: 'pending',\n pending_verification: 'pending',\n unverified: 'pending',\n invited: 'pending',\n paused: 'pending',\n open: 'pending',\n non_renewing: 'pending',\n trialing: 'processing',\n processing: 'processing',\n scheduled: 'processing',\n acknowledged: 'processing',\n suspended: 'failed',\n failed: 'failed',\n flagged: 'failed',\n past_due: 'failed',\n completed: 'complete',\n closed: 'complete',\n draft: 'draft',\n deleted: 'neutral',\n rejected: 'neutral',\n};\n\n/** Words for a status key: `pending_verification` → \"Pending verification\". */\nconst words = (s: string) => {\n const w = s.replace(/[_-]+/g, ' ');\n return w.charAt(0).toUpperCase() + w.slice(1);\n};\n\n/**\n * A status as a 6px dot plus text. Each state reads as itself: live ok,\n * waiting amber, in flight brand blue, stopped red, finished solid grey,\n * draft hollow. Static: nothing pulses.\n */\nexport function StatusPill({\n status,\n label,\n}: {\n status: string;\n label?: string;\n}) {\n const tone = TONE_OF[status.toLowerCase()] ?? 'neutral';\n return (\n <span className={styles.pill} data-tone={tone}>\n <span className={`${styles.dot} adm-status-dot`} aria-hidden />\n {label ?? words(status)}\n </span>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/audit/status-pill.tsx"],"sourcesContent":["import styles from './status-pill.module.css';\n\ntype StatusTone =\n | 'success'\n | 'pending'\n | 'failed'\n | 'processing'\n | 'complete'\n | 'draft'\n | 'ahead'\n | 'neutral';\n\nconst TONE_OF: Record<string, StatusTone> = {\n active: 'success',\n verified: 'success',\n approved: 'success',\n resolved: 'success',\n pending: 'pending',\n pending_verification: 'pending',\n unverified: 'pending',\n invited: 'pending',\n paused: 'pending',\n open: 'pending',\n non_renewing: 'pending',\n trialing: 'processing',\n processing: 'processing',\n scheduled: 'processing',\n acknowledged: 'processing',\n suspended: 'failed',\n failed: 'failed',\n flagged: 'failed',\n past_due: 'failed',\n completed: 'complete',\n closed: 'complete',\n draft: 'draft',\n // Account stages read as progress: grey, blue, amber, teal, green.\n stage_signed_up: 'neutral',\n stage_verified: 'processing',\n stage_onboarding: 'pending',\n stage_kyc_approved: 'ahead',\n stage_active: 'success',\n deleted: 'neutral',\n rejected: 'neutral',\n};\n\n/** Words for a status key: `pending_verification` → \"Pending verification\". */\nconst words = (s: string) => {\n const w = s.replace(/[_-]+/g, ' ');\n return w.charAt(0).toUpperCase() + w.slice(1);\n};\n\n/**\n * A status as a 6px dot plus text. Each state reads as itself: live ok,\n * waiting amber, in flight brand blue, stopped red, finished solid grey,\n * draft hollow. Static: nothing pulses.\n */\nexport function StatusPill({\n status,\n label,\n}: {\n status: string;\n label?: string;\n}) {\n const tone = TONE_OF[status.toLowerCase()] ?? 'neutral';\n return (\n <span className={styles.pill} data-tone={tone}>\n <span className={`${styles.dot} adm-status-dot`} aria-hidden />\n {label ?? words(status)}\n </span>\n );\n}\n"],"mappings":"AAiEI,SACE,KADF;AAjEJ,OAAO,YAAY;AAYnB,MAAM,UAAsC;AAAA,EAC1C,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,cAAc;AAAA,EACd,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,OAAO;AAAA;AAAA,EAEP,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,SAAS;AAAA,EACT,UAAU;AACZ;AAGA,MAAM,QAAQ,CAAC,MAAc;AAC3B,QAAM,IAAI,EAAE,QAAQ,UAAU,GAAG;AACjC,SAAO,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAC9C;AAOO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AACF,GAGG;AACD,QAAM,OAAO,QAAQ,OAAO,YAAY,CAAC,KAAK;AAC9C,SACE,qBAAC,UAAK,WAAW,OAAO,MAAM,aAAW,MACvC;AAAA,wBAAC,UAAK,WAAW,GAAG,OAAO,GAAG,mBAAmB,eAAW,MAAC;AAAA,IAC5D,SAAS,MAAM,MAAM;AAAA,KACxB;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withpotter/pages-admin",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.86",
|
|
4
4
|
"description": "Box console platform-admin routes: overview, revenue, accounts, tenants, admins, roles, audit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@radix-ui/react-popover": "^1.1.23",
|
|
33
33
|
"@tanstack/react-query": "^5.90.20",
|
|
34
|
-
"@withpotter/lib": "^0.13.0-next.
|
|
35
|
-
"@withpotter/platform-admin": "^0.13.0-next.
|
|
36
|
-
"@withpotter/ui-kit": "^0.13.0-next.
|
|
34
|
+
"@withpotter/lib": "^0.13.0-next.86",
|
|
35
|
+
"@withpotter/platform-admin": "^0.13.0-next.86",
|
|
36
|
+
"@withpotter/ui-kit": "^0.13.0-next.86",
|
|
37
37
|
"lucide-react": "^0.562.0",
|
|
38
38
|
"motion": "^12.42.2"
|
|
39
39
|
},
|