@eifi1/ui-kit 0.29.0 → 0.31.0

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.
Files changed (307) hide show
  1. package/README.md +65 -6
  2. package/dist/account/account-parts.d.ts +21 -0
  3. package/dist/account/account-parts.js +40 -0
  4. package/dist/account/account-parts.js.map +1 -0
  5. package/dist/account/data-export-setting.d.ts +73 -0
  6. package/dist/account/data-export-setting.js +114 -0
  7. package/dist/account/data-export-setting.js.map +1 -0
  8. package/dist/account/delete-account-setting.d.ts +95 -0
  9. package/dist/account/delete-account-setting.js +200 -0
  10. package/dist/account/delete-account-setting.js.map +1 -0
  11. package/dist/account/email-change-setting.d.ts +136 -0
  12. package/dist/account/email-change-setting.js +278 -0
  13. package/dist/account/email-change-setting.js.map +1 -0
  14. package/dist/account/sessions-setting.d.ts +110 -0
  15. package/dist/account/sessions-setting.js +147 -0
  16. package/dist/account/sessions-setting.js.map +1 -0
  17. package/dist/admin/admin-action-confirm.d.ts +95 -0
  18. package/dist/admin/admin-action-confirm.js +276 -0
  19. package/dist/admin/admin-action-confirm.js.map +1 -0
  20. package/dist/admin/admin-action-log.d.ts +105 -0
  21. package/dist/admin/admin-action-log.js +153 -0
  22. package/dist/admin/admin-action-log.js.map +1 -0
  23. package/dist/admin/admin-parts.d.ts +40 -0
  24. package/dist/admin/admin-parts.js +45 -0
  25. package/dist/admin/admin-parts.js.map +1 -0
  26. package/dist/admin/invitations-panel.d.ts +95 -0
  27. package/dist/admin/invitations-panel.js +290 -0
  28. package/dist/admin/invitations-panel.js.map +1 -0
  29. package/dist/admin/reviewer-scope-editor.d.ts +95 -0
  30. package/dist/admin/reviewer-scope-editor.js +134 -0
  31. package/dist/admin/reviewer-scope-editor.js.map +1 -0
  32. package/dist/admin/role-select.d.ts +95 -0
  33. package/dist/admin/role-select.js +128 -0
  34. package/dist/admin/role-select.js.map +1 -0
  35. package/dist/admin/transfer-ownership-dialog.d.ts +95 -0
  36. package/dist/admin/transfer-ownership-dialog.js +83 -0
  37. package/dist/admin/transfer-ownership-dialog.js.map +1 -0
  38. package/dist/admin/user-roster.d.ts +95 -0
  39. package/dist/admin/user-roster.js +285 -0
  40. package/dist/admin/user-roster.js.map +1 -0
  41. package/dist/auth/auth-errors.d.ts +103 -6
  42. package/dist/auth/auth-errors.js +68 -2
  43. package/dist/auth/auth-errors.js.map +1 -1
  44. package/dist/auth/forgot-password-form.d.ts +10 -3
  45. package/dist/auth/forgot-password-form.js +6 -3
  46. package/dist/auth/forgot-password-form.js.map +1 -1
  47. package/dist/auth/register-form.d.ts +21 -12
  48. package/dist/auth/register-form.js +6 -4
  49. package/dist/auth/register-form.js.map +1 -1
  50. package/dist/auth/reset-password-form.d.ts +10 -3
  51. package/dist/auth/reset-password-form.js +6 -3
  52. package/dist/auth/reset-password-form.js.map +1 -1
  53. package/dist/auth/sign-in-form.d.ts +75 -9
  54. package/dist/auth/sign-in-form.js +105 -15
  55. package/dist/auth/sign-in-form.js.map +1 -1
  56. package/dist/auth/verify-email.d.ts +7 -2
  57. package/dist/auth/verify-email.js +44 -8
  58. package/dist/auth/verify-email.js.map +1 -1
  59. package/dist/chart.d.ts +20 -11
  60. package/dist/components/account-chips.d.ts +47 -7
  61. package/dist/components/account-chips.js +19 -3
  62. package/dist/components/account-chips.js.map +1 -1
  63. package/dist/components/account-settings-labels.d.ts +24 -1
  64. package/dist/components/account-settings-labels.js +6 -2
  65. package/dist/components/account-settings-labels.js.map +1 -1
  66. package/dist/components/account-settings.d.ts +69 -8
  67. package/dist/components/account-settings.js +133 -25
  68. package/dist/components/account-settings.js.map +1 -1
  69. package/dist/components/amount-input.d.ts +21 -12
  70. package/dist/components/button-group.d.ts +21 -12
  71. package/dist/components/calculator.d.ts +21 -12
  72. package/dist/components/confirm-dialog.d.ts +21 -12
  73. package/dist/components/copy-button.d.ts +18 -9
  74. package/dist/components/danger-confirm.d.ts +21 -12
  75. package/dist/components/data-table.js +8 -1
  76. package/dist/components/data-table.js.map +1 -1
  77. package/dist/components/date-picker.js +9 -4
  78. package/dist/components/date-picker.js.map +1 -1
  79. package/dist/components/facing-pair.d.ts +21 -12
  80. package/dist/components/file-button.d.ts +21 -12
  81. package/dist/components/file-dropzone.d.ts +21 -12
  82. package/dist/components/form-actions.d.ts +21 -12
  83. package/dist/components/iban-input.d.ts +21 -12
  84. package/dist/components/language-select.d.ts +21 -12
  85. package/dist/components/legal-page.d.ts +21 -12
  86. package/dist/components/legal.d.ts +21 -12
  87. package/dist/components/money-field.d.ts +21 -12
  88. package/dist/components/number-field.d.ts +21 -12
  89. package/dist/components/number-input.d.ts +21 -12
  90. package/dist/components/numpad-sheet.d.ts +21 -12
  91. package/dist/components/passkeys-setting.js +2 -1
  92. package/dist/components/passkeys-setting.js.map +1 -1
  93. package/dist/components/phone-input.d.ts +21 -12
  94. package/dist/components/series-chart.d.ts +21 -12
  95. package/dist/components/settings-fields.d.ts +55 -13
  96. package/dist/components/settings-fields.js +20 -1
  97. package/dist/components/settings-fields.js.map +1 -1
  98. package/dist/components/share-card.d.ts +20 -11
  99. package/dist/components/text-link.d.ts +21 -12
  100. package/dist/components/time-input.d.ts +21 -12
  101. package/dist/components/ui.d.ts +17 -8
  102. package/dist/{register-form-V1OeNlMr.d.ts → delete-account-setting-DCFS5Vof.d.ts} +6612 -5612
  103. package/dist/demo/demo-banner.d.ts +51 -0
  104. package/dist/demo/demo-banner.js +97 -0
  105. package/dist/demo/demo-banner.js.map +1 -0
  106. package/dist/demo/demo-countdown.d.ts +58 -0
  107. package/dist/demo/demo-countdown.js +79 -0
  108. package/dist/demo/demo-countdown.js.map +1 -0
  109. package/dist/demo/demo-ended.d.ts +45 -0
  110. package/dist/demo/demo-ended.js +42 -0
  111. package/dist/demo/demo-ended.js.map +1 -0
  112. package/dist/demo/demo-labels.d.ts +53 -0
  113. package/dist/demo/demo-labels.js +29 -0
  114. package/dist/demo/demo-labels.js.map +1 -0
  115. package/dist/demo/demo-session.d.ts +30 -0
  116. package/dist/demo/demo-session.js +8 -0
  117. package/dist/demo/demo-session.js.map +1 -0
  118. package/dist/demo/demo-start.d.ts +54 -0
  119. package/dist/demo/demo-start.js +68 -0
  120. package/dist/demo/demo-start.js.map +1 -0
  121. package/dist/feedback/feedback-attachment.d.ts +21 -12
  122. package/dist/feedback/feedback-context.d.ts +21 -12
  123. package/dist/feedback/feedback-crash.d.ts +21 -12
  124. package/dist/feedback/feedback-dialog.d.ts +21 -12
  125. package/dist/feedback/feedback-inbox.d.ts +21 -12
  126. package/dist/feedback/feedback-inbox.js +12 -2
  127. package/dist/feedback/feedback-inbox.js.map +1 -1
  128. package/dist/feedback/feedback-labels.d.ts +21 -12
  129. package/dist/feedback/feedback-labels.js +1 -0
  130. package/dist/feedback/feedback-labels.js.map +1 -1
  131. package/dist/feedback/feedback-menu.d.ts +21 -12
  132. package/dist/feedback/feedback-record.d.ts +21 -12
  133. package/dist/feedback/feedback-record.js +2 -0
  134. package/dist/feedback/feedback-record.js.map +1 -1
  135. package/dist/feedback/feedback-row-detail.d.ts +21 -12
  136. package/dist/feedback/feedback-row-detail.js +7 -0
  137. package/dist/feedback/feedback-row-detail.js.map +1 -1
  138. package/dist/feedback/feedback-status-undo.d.ts +21 -12
  139. package/dist/feedback/feedback-submit.d.ts +21 -12
  140. package/dist/feedback/feedback-swipe.d.ts +22 -13
  141. package/dist/feedback/feedback-swipe.js.map +1 -1
  142. package/dist/feedback/feedback-table.d.ts +18 -9
  143. package/dist/feedback/feedback-thread.d.ts +21 -12
  144. package/dist/feedback.d.ts +21 -12
  145. package/dist/hooks/use-anchored-panel.d.ts +6 -2
  146. package/dist/hooks/use-anchored-panel.js +2 -1
  147. package/dist/hooks/use-anchored-panel.js.map +1 -1
  148. package/dist/hooks/use-file-drop.d.ts +21 -12
  149. package/dist/i18n/defaults.d.ts +21 -12
  150. package/dist/i18n/defaults.js +28 -0
  151. package/dist/i18n/defaults.js.map +1 -1
  152. package/dist/i18n/german.d.ts +21 -12
  153. package/dist/i18n/german.js +298 -12
  154. package/dist/i18n/german.js.map +1 -1
  155. package/dist/i18n/kit-labels.d.ts +14 -5
  156. package/dist/i18n/kit-labels.js.map +1 -1
  157. package/dist/i18n/languages.d.ts +15 -6
  158. package/dist/i18n/locales/de-CH.d.ts +21 -12
  159. package/dist/i18n/locales/en.d.ts +21 -12
  160. package/dist/i18n/locales/es.d.ts +21 -12
  161. package/dist/i18n/locales/es.js +287 -10
  162. package/dist/i18n/locales/es.js.map +1 -1
  163. package/dist/i18n/locales/fr.d.ts +21 -12
  164. package/dist/i18n/locales/fr.js +290 -7
  165. package/dist/i18n/locales/fr.js.map +1 -1
  166. package/dist/i18n/locales/hu.d.ts +21 -12
  167. package/dist/i18n/locales/hu.js +288 -12
  168. package/dist/i18n/locales/hu.js.map +1 -1
  169. package/dist/i18n/locales/it.d.ts +21 -12
  170. package/dist/i18n/locales/it.js +289 -7
  171. package/dist/i18n/locales/it.js.map +1 -1
  172. package/dist/i18n/locales/zh.d.ts +21 -12
  173. package/dist/i18n/locales/zh.js +279 -7
  174. package/dist/i18n/locales/zh.js.map +1 -1
  175. package/dist/i18n/review.d.ts +21 -12
  176. package/dist/i18n/review.js +50 -1
  177. package/dist/i18n/review.js.map +1 -1
  178. package/dist/index.d.ts +31 -4
  179. package/dist/index.js +148 -0
  180. package/dist/index.js.map +1 -1
  181. package/dist/landing/access.d.ts +48 -0
  182. package/dist/landing/access.js +38 -0
  183. package/dist/landing/access.js.map +1 -0
  184. package/dist/landing/landing-actions.d.ts +138 -0
  185. package/dist/landing/landing-actions.js +39 -0
  186. package/dist/landing/landing-actions.js.map +1 -0
  187. package/dist/landing/landing-labels.d.ts +43 -0
  188. package/dist/landing/landing-labels.js +23 -0
  189. package/dist/landing/landing-labels.js.map +1 -0
  190. package/dist/landing/landing-sections.d.ts +218 -0
  191. package/dist/landing/landing-sections.js +167 -0
  192. package/dist/landing/landing-sections.js.map +1 -0
  193. package/dist/landing/page-seo.d.ts +99 -0
  194. package/dist/landing/page-seo.js +121 -0
  195. package/dist/landing/page-seo.js.map +1 -0
  196. package/dist/landing/public-header.d.ts +148 -0
  197. package/dist/landing/public-header.js +49 -0
  198. package/dist/landing/public-header.js.map +1 -0
  199. package/dist/landing/routing.d.ts +218 -0
  200. package/dist/landing/routing.js +81 -0
  201. package/dist/landing/routing.js.map +1 -0
  202. package/dist/rhf/fields.d.ts +21 -12
  203. package/dist/rhf/form.d.ts +21 -12
  204. package/dist/rhf.d.ts +21 -12
  205. package/dist/settings/settings-catalogue.d.ts +84 -0
  206. package/dist/settings/settings-catalogue.js +47 -0
  207. package/dist/settings/settings-catalogue.js.map +1 -0
  208. package/dist/settings/settings-context.d.ts +31 -0
  209. package/dist/settings/settings-context.js +11 -0
  210. package/dist/settings/settings-context.js.map +1 -0
  211. package/dist/settings/settings-heading.d.ts +50 -0
  212. package/dist/settings/settings-heading.js +27 -0
  213. package/dist/settings/settings-heading.js.map +1 -0
  214. package/dist/settings/settings-labels.d.ts +41 -0
  215. package/dist/settings/settings-labels.js +38 -0
  216. package/dist/settings/settings-labels.js.map +1 -0
  217. package/dist/settings/settings-layout.d.ts +77 -0
  218. package/dist/settings/settings-layout.js +242 -0
  219. package/dist/settings/settings-layout.js.map +1 -0
  220. package/dist/settings/settings-section.d.ts +133 -0
  221. package/dist/settings/settings-section.js +63 -0
  222. package/dist/settings/settings-section.js.map +1 -0
  223. package/dist/settings/use-account-language.d.ts +83 -0
  224. package/dist/settings/use-account-language.js +62 -0
  225. package/dist/settings/use-account-language.js.map +1 -0
  226. package/dist/settings/use-settings-focus.d.ts +34 -0
  227. package/dist/settings/use-settings-focus.js +77 -0
  228. package/dist/settings/use-settings-focus.js.map +1 -0
  229. package/dist/settings/use-settings-route.d.ts +118 -0
  230. package/dist/settings/use-settings-route.js +120 -0
  231. package/dist/settings/use-settings-route.js.map +1 -0
  232. package/dist/shell/app-shell.d.ts +27 -12
  233. package/dist/shell/app-shell.js +3 -2
  234. package/dist/shell/app-shell.js.map +1 -1
  235. package/dist/shell/top-bar-brand.d.ts +21 -12
  236. package/dist/shell.d.ts +25 -6
  237. package/dist/shell.js +56 -0
  238. package/dist/shell.js.map +1 -1
  239. package/dist/wizard/stepper-nav.d.ts +21 -12
  240. package/dist/wizard.d.ts +21 -12
  241. package/package.json +2 -1
  242. package/src/account/account-parts.ts +67 -0
  243. package/src/account/data-export-setting.tsx +219 -0
  244. package/src/account/delete-account-setting.tsx +363 -0
  245. package/src/account/email-change-setting.tsx +454 -0
  246. package/src/account/sessions-setting.tsx +284 -0
  247. package/src/admin/admin-action-confirm.tsx +495 -0
  248. package/src/admin/admin-action-log.tsx +297 -0
  249. package/src/admin/admin-parts.ts +82 -0
  250. package/src/admin/invitations-panel.tsx +523 -0
  251. package/src/admin/reviewer-scope-editor.tsx +234 -0
  252. package/src/admin/role-select.tsx +286 -0
  253. package/src/admin/transfer-ownership-dialog.tsx +172 -0
  254. package/src/admin/user-roster.tsx +573 -0
  255. package/src/auth/auth-errors.ts +196 -8
  256. package/src/auth/forgot-password-form.tsx +21 -5
  257. package/src/auth/register-form.tsx +24 -6
  258. package/src/auth/reset-password-form.tsx +19 -5
  259. package/src/auth/sign-in-form.tsx +241 -38
  260. package/src/auth/verify-email.tsx +59 -8
  261. package/src/components/account-chips.tsx +64 -7
  262. package/src/components/account-settings-labels.ts +28 -1
  263. package/src/components/account-settings.tsx +231 -34
  264. package/src/components/data-table.tsx +8 -0
  265. package/src/components/date-picker.tsx +9 -4
  266. package/src/components/passkeys-setting.tsx +2 -1
  267. package/src/components/settings-fields.tsx +50 -1
  268. package/src/demo/demo-banner.tsx +159 -0
  269. package/src/demo/demo-countdown.ts +141 -0
  270. package/src/demo/demo-ended.tsx +89 -0
  271. package/src/demo/demo-labels.ts +78 -0
  272. package/src/demo/demo-session.ts +32 -0
  273. package/src/demo/demo-start.tsx +150 -0
  274. package/src/feedback/feedback-inbox.tsx +21 -8
  275. package/src/feedback/feedback-labels.ts +2 -1
  276. package/src/feedback/feedback-record.ts +10 -7
  277. package/src/feedback/feedback-row-detail.tsx +28 -4
  278. package/src/feedback/feedback-swipe.tsx +1 -1
  279. package/src/hooks/use-anchored-panel.ts +14 -3
  280. package/src/i18n/defaults.ts +28 -0
  281. package/src/i18n/german.ts +327 -5
  282. package/src/i18n/kit-labels.tsx +42 -0
  283. package/src/i18n/locales/es.ts +313 -3
  284. package/src/i18n/locales/fr.ts +316 -0
  285. package/src/i18n/locales/hu.ts +308 -5
  286. package/src/i18n/locales/it.ts +313 -0
  287. package/src/i18n/locales/zh.ts +286 -0
  288. package/src/i18n/review.ts +49 -0
  289. package/src/index.ts +184 -0
  290. package/src/landing/access.ts +97 -0
  291. package/src/landing/landing-actions.tsx +92 -0
  292. package/src/landing/landing-labels.ts +59 -0
  293. package/src/landing/landing-sections.tsx +337 -0
  294. package/src/landing/page-seo.ts +251 -0
  295. package/src/landing/public-header.tsx +120 -0
  296. package/src/landing/routing.tsx +209 -0
  297. package/src/settings/settings-catalogue.ts +132 -0
  298. package/src/settings/settings-context.ts +33 -0
  299. package/src/settings/settings-heading.tsx +65 -0
  300. package/src/settings/settings-labels.ts +75 -0
  301. package/src/settings/settings-layout.tsx +379 -0
  302. package/src/settings/settings-section.tsx +108 -0
  303. package/src/settings/use-account-language.ts +146 -0
  304. package/src/settings/use-settings-focus.ts +120 -0
  305. package/src/settings/use-settings-route.ts +258 -0
  306. package/src/shell/app-shell.tsx +10 -2
  307. package/src/shell.ts +45 -0
@@ -0,0 +1,83 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { useKitLabels } from "../i18n/kit-labels.js";
5
+ import { Select } from "../components/ui.js";
6
+ import { Caption } from "../components/text.js";
7
+ import { AdminActionConfirm } from "./admin-action-confirm.js";
8
+ import { usePersonLabel } from "./admin-parts.js";
9
+ const DEFAULT_TRANSFER_OWNERSHIP_LABELS = {
10
+ title: (name) => `Hand on ${name}\u2019s work`,
11
+ body: "Everything this account owns moves to the account you choose, in one step.",
12
+ recipient: "Hand it to",
13
+ choose: "Choose an account\u2026",
14
+ chooseFirst: "Choose who receives the work",
15
+ confirm: "Hand it on",
16
+ customer: "a customer",
17
+ deactivated: "deactivated",
18
+ self: "the same account",
19
+ unavailable: (account, reason) => `${account} (${reason})`,
20
+ noCandidates: "No other account can receive it."
21
+ };
22
+ function TransferOwnershipDialog({
23
+ from,
24
+ candidates,
25
+ summary,
26
+ onTransfer,
27
+ onClose,
28
+ level = "type_email",
29
+ describeError,
30
+ open,
31
+ labels: labelsProp
32
+ }) {
33
+ const labels = useKitLabels("transferOwnership", DEFAULT_TRANSFER_OWNERSHIP_LABELS, labelsProp);
34
+ const nameOf = usePersonLabel();
35
+ const [chosen, setChosen] = useState("");
36
+ const reasonOf = (candidate) => {
37
+ const own = candidate.unavailable;
38
+ const isSource = from.id !== void 0 && from.id !== null && candidate.id === from.id || candidate.email.trim().toLowerCase() === from.email.trim().toLowerCase();
39
+ const code = isSource ? "self" : own;
40
+ if (!code) return void 0;
41
+ if (code === "customer") return labels.customer;
42
+ if (code === "deactivated") return labels.deactivated;
43
+ if (code === "self") return labels.self;
44
+ return code;
45
+ };
46
+ const rows = candidates.map((candidate) => {
47
+ const name = nameOf(candidate);
48
+ const shown = name && name !== candidate.email ? `${name} \u2014 ${candidate.email}` : candidate.email;
49
+ const reason = reasonOf(candidate);
50
+ return { candidate, value: String(candidate.id), text: reason ? labels.unavailable(shown, reason) : shown, reason };
51
+ });
52
+ const available = rows.filter((row) => !row.reason);
53
+ const recipient = available.find((row) => row.value === chosen)?.candidate;
54
+ return /* @__PURE__ */ jsxs(
55
+ AdminActionConfirm,
56
+ {
57
+ open,
58
+ level: level === "none" ? "acknowledge" : level,
59
+ target: from,
60
+ title: labels.title(nameOf(from)),
61
+ consequence: labels.body,
62
+ confirmLabel: labels.confirm,
63
+ tone: "danger",
64
+ confirmDisabledReason: available.length === 0 ? labels.noCandidates : recipient === void 0 ? labels.chooseFirst : void 0,
65
+ onConfirm: (values) => recipient === void 0 ? void 0 : onTransfer({ toUserId: recipient.id, confirmEmail: values.confirmEmail }),
66
+ onClose,
67
+ describeError,
68
+ children: [
69
+ summary && /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--text-secondary)]", children: summary }),
70
+ /* @__PURE__ */ jsxs(Select, { label: labels.recipient, value: chosen, onChange: (event) => setChosen(event.target.value), children: [
71
+ /* @__PURE__ */ jsx("option", { value: "", children: labels.choose }),
72
+ rows.map((row) => /* @__PURE__ */ jsx("option", { value: row.value, disabled: Boolean(row.reason), children: row.text }, row.value))
73
+ ] }),
74
+ available.length === 0 && /* @__PURE__ */ jsx(Caption, { children: labels.noCandidates })
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ export {
80
+ DEFAULT_TRANSFER_OWNERSHIP_LABELS,
81
+ TransferOwnershipDialog
82
+ };
83
+ //# sourceMappingURL=transfer-ownership-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/transfer-ownership-dialog.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { Select } from \"../components/ui\";\nimport { Caption } from \"../components/text\";\nimport { AdminActionConfirm } from \"./admin-action-confirm\";\nimport type { AdminActionTarget, AdminConfirmLevel } from \"./admin-action-confirm\";\nimport { usePersonLabel } from \"./admin-parts\";\nimport type { AdminPerson, MaybePromise } from \"./admin-parts\";\n\n/**\n * Hand an account's owned work to another account — Kurvenschmiede's `TypedConfirm`\n * with its recipient select (features/admin/typed-confirm.tsx, users-panel.tsx;\n * docs/user-admin-harmonization.md §4.1 `POST /admin/users/{id}/transfer {to,\n * confirm_email}`, §4.2: `type_email`).\n *\n * It is {@link AdminActionConfirm} with the recipient as the action's own field: the\n * typed address is the SOURCE account's — the one whose work moves, which is the row the\n * admin must prove they are on — and the confirm waits for a recipient too.\n *\n * WHO MAY RECEIVE IT (§9.6): never a customer, never a deactivated account, never the\n * account itself. The APP decides — it knows its roles and states — and marks each\n * candidate with why it cannot (`unavailable`); the kit shows those accounts, unavailable,\n * with the reason, rather than leaving the admin to wonder where somebody went. The\n * source account is marked `self` here whether or not the app did.\n */\n\n/** Why a candidate cannot receive the work: the kit's three, or the app's own words. */\nexport type TransferUnavailable = \"customer\" | \"deactivated\" | \"self\";\n\n/** An account that might receive the work. */\nexport interface TransferCandidate extends AdminPerson {\n id: string | number;\n email: string;\n /** Why it cannot: `\"customer\"`, `\"deactivated\"`, `\"self\"`, or the app's own words. */\n unavailable?: TransferUnavailable | (string & {});\n}\n\n/** What {@link TransferOwnershipDialogProps.onTransfer} receives — the request body. */\nexport interface TransferValues {\n toUserId: string | number;\n /** The source account's address as typed (at `type_email`). */\n confirmEmail?: string;\n}\n\n/** The `transferOwnership` namespace. The guard and the refusals are `adminAction`'s\n * and `dangerConfirm`'s. */\nexport interface TransferOwnershipLabels {\n /** The heading — \"Hand on Ada Example’s work\". */\n title: (name: string) => string;\n /** What happens. */\n body: string;\n /** The recipient select's label. */\n recipient: string;\n /** Its empty first option. */\n choose: string;\n /** Why the confirm is held while nobody is chosen. */\n chooseFirst: string;\n /** The confirm button. */\n confirm: string;\n /** Reasons, said after the account's name inside its option. */\n customer: string;\n deactivated: string;\n self: string;\n /** An unavailable option's text: the account and why. */\n unavailable: (account: string, reason: string) => string;\n /** No account can receive it. */\n noCandidates: string;\n}\n\nexport const DEFAULT_TRANSFER_OWNERSHIP_LABELS: TransferOwnershipLabels = {\n title: (name) => `Hand on ${name}’s work`,\n body: \"Everything this account owns moves to the account you choose, in one step.\",\n recipient: \"Hand it to\",\n choose: \"Choose an account…\",\n chooseFirst: \"Choose who receives the work\",\n confirm: \"Hand it on\",\n customer: \"a customer\",\n deactivated: \"deactivated\",\n self: \"the same account\",\n unavailable: (account, reason) => `${account} (${reason})`,\n noCandidates: \"No other account can receive it.\",\n};\n\nexport interface TransferOwnershipDialogProps {\n /** The account whose work moves — its address is what the admin types. */\n from: AdminActionTarget;\n /** Who might receive it, each marked with why not where it cannot. */\n candidates: readonly TransferCandidate[];\n /** What moves, in the app's words — \"3 sessions · 1 gear · 2 projects\" (counts, never\n * content). Under the sentence. */\n summary?: ReactNode;\n /** Sends it. Promise handling as {@link AdminActionConfirm}'s `onConfirm`. */\n onTransfer: (values: TransferValues) => MaybePromise;\n onClose: (done: boolean) => void;\n /** The server's level for `transfer` — `type_email` (§4.2), the default. A transfer\n * always has a recipient to choose, so `none` asks as `acknowledge` does. */\n level?: AdminConfirmLevel;\n describeError?: (error: unknown) => ReactNode | undefined;\n /** Kept-mounted mode. */\n open?: boolean;\n labels?: Partial<TransferOwnershipLabels>;\n}\n\nexport function TransferOwnershipDialog({\n from,\n candidates,\n summary,\n onTransfer,\n onClose,\n level = \"type_email\",\n describeError,\n open,\n labels: labelsProp,\n}: TransferOwnershipDialogProps) {\n const labels = useKitLabels(\"transferOwnership\", DEFAULT_TRANSFER_OWNERSHIP_LABELS, labelsProp);\n const nameOf = usePersonLabel();\n const [chosen, setChosen] = useState(\"\");\n\n const reasonOf = (candidate: TransferCandidate): string | undefined => {\n const own = candidate.unavailable;\n const isSource =\n (from.id !== undefined && from.id !== null && candidate.id === from.id) ||\n candidate.email.trim().toLowerCase() === from.email.trim().toLowerCase();\n const code = isSource ? \"self\" : own;\n if (!code) return undefined;\n if (code === \"customer\") return labels.customer;\n if (code === \"deactivated\") return labels.deactivated;\n if (code === \"self\") return labels.self;\n return code;\n };\n\n const rows = candidates.map((candidate) => {\n const name = nameOf(candidate);\n const shown = name && name !== candidate.email ? `${name} — ${candidate.email}` : candidate.email;\n const reason = reasonOf(candidate);\n return { candidate, value: String(candidate.id), text: reason ? labels.unavailable(shown, reason) : shown, reason };\n });\n const available = rows.filter((row) => !row.reason);\n const recipient = available.find((row) => row.value === chosen)?.candidate;\n\n return (\n <AdminActionConfirm\n open={open}\n level={level === \"none\" ? \"acknowledge\" : level}\n target={from}\n title={labels.title(nameOf(from))}\n consequence={labels.body}\n confirmLabel={labels.confirm}\n tone=\"danger\"\n confirmDisabledReason={\n available.length === 0 ? labels.noCandidates : recipient === undefined ? labels.chooseFirst : undefined\n }\n onConfirm={(values) =>\n recipient === undefined ? undefined : onTransfer({ toUserId: recipient.id, confirmEmail: values.confirmEmail })\n }\n onClose={onClose}\n describeError={describeError}\n >\n {summary && <div className=\"text-sm text-[var(--text-secondary)]\">{summary}</div>}\n <Select label={labels.recipient} value={chosen} onChange={(event) => setChosen(event.target.value)}>\n <option value=\"\">{labels.choose}</option>\n {rows.map((row) => (\n <option key={row.value} value={row.value} disabled={Boolean(row.reason)}>\n {row.text}\n </option>\n ))}\n </Select>\n {available.length === 0 && <Caption>{labels.noCandidates}</Caption>}\n </AdminActionConfirm>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,95 @@
1
+ import 'react';
2
+ import 'lucide-react';
3
+ import '../lib/format.js';
4
+ import '../lib/person-name.js';
5
+ import '../components/account-chips.js';
6
+ import '../components/chip.js';
7
+ import '../data-table-labels-DUELeXpa.js';
8
+ import '../components/data-table-sort.js';
9
+ export { bz as AdminUserStateFields, cJ as DEFAULT_USER_ROSTER_LABELS, et as RoleEditing, eB as RosterState, f8 as USER_ROSTER_SORT_KEYS, ff as UserIdentityCell, fg as UserIdentityCellProps, fh as UserRosterColumnsOptions, fi as UserRosterLabels, fj as UserRosterRow, fk as UserRosterSortKey, fl as UserRowAction, fm as UserRowActionList, fn as UserRowActions, fo as UserRowActionsProps, fp as adminUserStates, fO as useUserRosterColumns, fP as userRosterColumns, fQ as userRosterSort } from '../delete-account-setting-DCFS5Vof.js';
10
+ import '../components/swipeable-row.js';
11
+ import '../components/tooltip.js';
12
+ import '../hooks/use-anchored-rect.js';
13
+ import '../lib/clipping.js';
14
+ import '../components/mini-calendar.js';
15
+ import '../components/calendar-heatmap.js';
16
+ import '../components/popover.js';
17
+ import '../components/field-sync.js';
18
+ import '../wizard/types.js';
19
+ import '../tour/tour.js';
20
+ import '../search/command-palette.js';
21
+ import '../search/global-search.js';
22
+ import '../search/search-index.js';
23
+ import '../components/month-picker.js';
24
+ import '../components/page-contents.js';
25
+ import '../components/series-chart-labels.js';
26
+ import '../components/pie-chart-labels.js';
27
+ import '../components/sparkline.js';
28
+ import '../components/stat-tile.js';
29
+ import '../components/signature-pad.js';
30
+ import '../components/password-strength.js';
31
+ import '../components/swatch-picker.js';
32
+ import '../components/tile-radio.js';
33
+ import '../components/icon-picker.js';
34
+ import '../components/dialog-frame.js';
35
+ import '../components/modal.js';
36
+ import '../hooks/use-search-param-state.js';
37
+ import '../components/measured-grid.js';
38
+ import '../components/user-avatar.js';
39
+ import '../components/status-dot.js';
40
+ import '../components/progress-bar.js';
41
+ import '../components/signed-amount.js';
42
+ import '../shell/topbar-action-menu.js';
43
+ import '../components/menu-item.js';
44
+ import '../components/floating-panel.js';
45
+ import '../hooks/use-authed-src.js';
46
+ import '../components/account-settings-labels.js';
47
+ import '../components/bulk-action-bar.js';
48
+ import '../components/list.js';
49
+ import '../components/breadcrumbs.js';
50
+ import '../components/toast.js';
51
+ import '../components/description-list.js';
52
+ import '../components/line-items.js';
53
+ import '../components/error-boundary.js';
54
+ import '../components/authed-image.js';
55
+ import '../components/image-grid.js';
56
+ import '../components/lightbox.js';
57
+ import '../components/write-lock.js';
58
+ import '../auth/sign-in-form.js';
59
+ import '../landing/access.js';
60
+ import '../landing/landing-labels.js';
61
+ import '../shell/topbar-controls.js';
62
+ import '../theme/palette-presets.js';
63
+ import '../theme/chart-palette.js';
64
+ import '../theme/theme-store.js';
65
+ import 'zustand/middleware';
66
+ import 'zustand';
67
+ import '../auth/complete-name-dialog.js';
68
+ import '../auth/forgot-password-form.js';
69
+ import '../auth/status-parts.js';
70
+ import '../auth/reset-password-form.js';
71
+ import '../auth/verify-email.js';
72
+ import '../auth/not-found-page.js';
73
+ import '../auth/accept-invitation.js';
74
+ import '../shell/company-switcher.js';
75
+ import '../account/email-change-setting.js';
76
+ import '../account/sessions-setting.js';
77
+ import '../account/data-export-setting.js';
78
+ import './admin-parts.js';
79
+ import './admin-action-log.js';
80
+ import '../settings/settings-labels.js';
81
+ import '../demo/demo-labels.js';
82
+ import '../components/reauth-dialog.js';
83
+ import '../components/server-wake.js';
84
+ import '../lib/server-wake.js';
85
+ import '../components/translation-review-labels.js';
86
+ import '../lib/translation-review.js';
87
+ import '../components/country-select.js';
88
+ import '../components/inline-edit-field.js';
89
+ import '../lib/iban.js';
90
+ import '../lib/phone.js';
91
+ import '../components/sign-chip.js';
92
+ import '../components/column-mapper.js';
93
+ import '../lib/column-mapping.js';
94
+ import '../lib/table-text.js';
95
+ import '../components/field-parts.js';
@@ -0,0 +1,285 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { MoreHorizontal } from "lucide-react";
5
+ import { cn } from "../lib/cn.js";
6
+ import { useKitLabels, useKitLocale } from "../i18n/kit-labels.js";
7
+ import { AccountStateChip, RoleChip, dateColumn } from "../components/account-chips.js";
8
+ import { encodeSorts } from "../components/data-table-sort.js";
9
+ import { Popover } from "../components/popover.js";
10
+ import { Button, IconButton } from "../components/ui.js";
11
+ import { UserAvatar } from "../components/user-avatar.js";
12
+ import { personLabel } from "./admin-parts.js";
13
+ import { RoleSelect } from "./role-select.js";
14
+ function adminUserStates(row) {
15
+ const out = [];
16
+ if (row.deletion_scheduled_at || row.deletion_requested_at) {
17
+ out.push({ state: "deletion", date: row.deletion_scheduled_at ?? null });
18
+ } else if (row.is_active !== void 0) {
19
+ out.push(row.is_active ? "active" : "inactive");
20
+ }
21
+ if (row.email_verified === false) out.push("unverified");
22
+ if (row.password_change_required_at) out.push("passwordChange");
23
+ return out;
24
+ }
25
+ const USER_ROSTER_SORT_KEYS = ["name", "email", "role", "created", "last_login"];
26
+ function userRosterSort(sorts) {
27
+ const first = sorts.find((s) => USER_ROSTER_SORT_KEYS.includes(s.key));
28
+ return first ? encodeSorts([first]) ?? void 0 : void 0;
29
+ }
30
+ const DEFAULT_USER_ROSTER_LABELS = {
31
+ name: "Name",
32
+ email: "Email",
33
+ role: "Role",
34
+ state: "State",
35
+ created: "Created",
36
+ lastLogin: "Last login",
37
+ never: "Never",
38
+ actions: "Actions",
39
+ actionsFor: (name) => `Actions for ${name}`
40
+ };
41
+ function UserIdentityCell({
42
+ person,
43
+ name,
44
+ email,
45
+ avatar = true,
46
+ badge,
47
+ locale: localeProp,
48
+ className
49
+ }) {
50
+ const locale = useKitLocale(localeProp);
51
+ const named = personLabel({ first: person?.first, last: person?.last, name }, locale);
52
+ const address = (email ?? "").trim();
53
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center gap-2.5", className), children: [
54
+ avatar && /* @__PURE__ */ jsx(UserAvatar, { person, name, email, size: "sm" }),
55
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
56
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
57
+ /* @__PURE__ */ jsx("span", { "data-private": true, className: "truncate font-medium text-[var(--text-primary)]", children: named || address }),
58
+ badge
59
+ ] }),
60
+ named && address && /* @__PURE__ */ jsx("div", { "data-private": true, className: "truncate text-xs text-[var(--text-muted)]", children: address })
61
+ ] })
62
+ ] });
63
+ }
64
+ function UserRowActions({ actions, name, labels: labelsProp }) {
65
+ const labels = useKitLabels("userRoster", DEFAULT_USER_ROSTER_LABELS, labelsProp);
66
+ const entries = actions.filter((a) => Boolean(a) && !a.hidden);
67
+ if (entries.length === 0) return null;
68
+ const title = labels.actionsFor(name);
69
+ return /* @__PURE__ */ jsx(
70
+ Popover,
71
+ {
72
+ width: 240,
73
+ "aria-label": title,
74
+ className: "p-1",
75
+ trigger: ({ open, toggle, ref }) => /* @__PURE__ */ jsx(
76
+ IconButton,
77
+ {
78
+ ref,
79
+ size: "xs",
80
+ label: title,
81
+ "aria-haspopup": "dialog",
82
+ "aria-expanded": open,
83
+ stopPropagation: true,
84
+ onClick: toggle,
85
+ children: /* @__PURE__ */ jsx(MoreHorizontal, {})
86
+ }
87
+ ),
88
+ children: (close) => /* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: entries.map((action) => {
89
+ const Icon = action.icon;
90
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
91
+ Button,
92
+ {
93
+ type: "button",
94
+ variant: "ghost",
95
+ size: "sm",
96
+ stretch: true,
97
+ tone: action.tone === "danger" ? "danger" : void 0,
98
+ disabledReason: action.disabledReason,
99
+ className: "justify-start",
100
+ onClick: () => {
101
+ close();
102
+ action.onSelect();
103
+ },
104
+ children: [
105
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0", "aria-hidden": true }),
106
+ action.label
107
+ ]
108
+ }
109
+ ) }, action.key ?? action.label);
110
+ }) })
111
+ }
112
+ );
113
+ }
114
+ function rolesOf(row, roleOf) {
115
+ const raw = roleOf ? roleOf(row) : row.roles ?? row.role;
116
+ if (raw === null || raw === void 0 || raw === "") return [];
117
+ return typeof raw === "string" ? [raw] : [...raw];
118
+ }
119
+ function RowName({ row, children }) {
120
+ const locale = useKitLocale();
121
+ const name = personLabel(
122
+ { first: row.first_name, last: row.last_name, name: row.display_name, email: row.email },
123
+ locale
124
+ );
125
+ return /* @__PURE__ */ jsx(Fragment, { children: children(name) });
126
+ }
127
+ function StateChips({ states }) {
128
+ if (states.length === 0) return null;
129
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: states.map((entry) => {
130
+ const spec = typeof entry === "string" ? { state: entry } : entry;
131
+ return /* @__PURE__ */ jsx(AccountStateChip, { state: spec.state, date: spec.date, tone: spec.tone, size: "xs", children: spec.label }, spec.state);
132
+ }) });
133
+ }
134
+ function userRosterColumns({
135
+ roles,
136
+ stateOf = adminUserStates,
137
+ extra = [],
138
+ actions,
139
+ editableRole,
140
+ onRoleChange,
141
+ roleOf,
142
+ emailColumn = false,
143
+ labels: labelsProp
144
+ }) {
145
+ const labels = { ...DEFAULT_USER_ROSTER_LABELS, ...labelsProp };
146
+ const roleWords = (row) => rolesOf(row, roleOf).map((role) => roles[role]?.label ?? role).join(", ");
147
+ const columns = [
148
+ {
149
+ key: "name",
150
+ header: labels.name,
151
+ cell: (row) => /* @__PURE__ */ jsx(
152
+ UserIdentityCell,
153
+ {
154
+ person: { first: row.first_name, last: row.last_name },
155
+ name: row.display_name,
156
+ email: row.email
157
+ }
158
+ ),
159
+ // By last name, then first (auth §3.2), as the server sorts `name`.
160
+ sortBy: (row) => `${row.last_name ?? ""} ${row.first_name ?? ""}`.trim().toLocaleLowerCase() || row.email,
161
+ // Either part, in either order, or the address (auth §10.6).
162
+ filterBy: (row) => [row.first_name, row.last_name, row.last_name, row.first_name, row.display_name, row.email].filter(Boolean).join(" "),
163
+ mobilePrimary: true
164
+ }
165
+ ];
166
+ if (emailColumn) {
167
+ columns.push({
168
+ key: "email",
169
+ header: labels.email,
170
+ className: "whitespace-nowrap",
171
+ cell: (row) => /* @__PURE__ */ jsx("span", { "data-private": true, className: "text-[var(--text-secondary)]", children: row.email }),
172
+ sortBy: (row) => row.email.toLowerCase(),
173
+ filterBy: (row) => row.email,
174
+ mobileHidden: true
175
+ });
176
+ }
177
+ columns.push(
178
+ {
179
+ key: "role",
180
+ header: labels.role,
181
+ cell: (row) => {
182
+ const held = rolesOf(row, roleOf);
183
+ const editing = held.length <= 1 ? editableRole?.(row) : false;
184
+ if (editing && onRoleChange) {
185
+ const rules = editing === true ? {} : editing;
186
+ return /* @__PURE__ */ jsx(RowName, { row, children: (name) => /* @__PURE__ */ jsx(
187
+ RoleSelect,
188
+ {
189
+ size: "sm",
190
+ name,
191
+ value: held[0] ?? null,
192
+ roles,
193
+ offered: rules.offered,
194
+ disabledReason: rules.disabledReason,
195
+ optionDisabledReason: rules.optionDisabledReason,
196
+ onChange: (role) => onRoleChange(row, role)
197
+ }
198
+ ) });
199
+ }
200
+ if (held.length === 0) return null;
201
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: held.map((role) => /* @__PURE__ */ jsx(RoleChip, { value: role, roles, size: "xs" }, role)) });
202
+ },
203
+ sortBy: roleWords,
204
+ filter: {
205
+ type: "select",
206
+ getValue: (row) => rolesOf(row, roleOf)[0] ?? "",
207
+ options: Object.keys(roles).map((value) => ({ value, label: roles[value].label }))
208
+ }
209
+ },
210
+ {
211
+ key: "state",
212
+ header: labels.state,
213
+ cell: (row) => /* @__PURE__ */ jsx(StateChips, { states: stateOf(row) })
214
+ },
215
+ dateColumn({
216
+ key: "created",
217
+ header: labels.created,
218
+ value: (row) => row.created_at ?? null,
219
+ column: { mobileHidden: true }
220
+ }),
221
+ // How long ago rather than a date — the question is "recently?" — with the exact
222
+ // time in a tooltip (keksdose's last-seen column, Kurvenschmiede's).
223
+ dateColumn({
224
+ key: "last_login",
225
+ header: labels.lastLogin,
226
+ value: (row) => row.last_login_at ?? null,
227
+ display: "relative",
228
+ empty: labels.never
229
+ }),
230
+ ...extra
231
+ );
232
+ if (actions) {
233
+ columns.push({
234
+ key: "actions",
235
+ header: /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.actions }),
236
+ headerText: labels.actions,
237
+ // The caller's own words only: the menu reads the provider's itself, and the
238
+ // English defaults merged in above must not shadow them.
239
+ cell: (row) => /* @__PURE__ */ jsx(RowName, { row, children: (name) => /* @__PURE__ */ jsx(UserRowActions, { actions: actions(row), name, labels: labelsProp }) }),
240
+ className: "w-px whitespace-nowrap text-end",
241
+ headClassName: "w-px",
242
+ // The menu button keeps its own click: a row link must not wrap it.
243
+ noRowLink: true
244
+ });
245
+ }
246
+ return columns;
247
+ }
248
+ function useUserRosterColumns({
249
+ roles,
250
+ stateOf,
251
+ extra,
252
+ actions,
253
+ editableRole,
254
+ onRoleChange,
255
+ roleOf,
256
+ emailColumn,
257
+ labels: labelsProp
258
+ }) {
259
+ const labels = useKitLabels("userRoster", DEFAULT_USER_ROSTER_LABELS, labelsProp);
260
+ return useMemo(
261
+ () => userRosterColumns({
262
+ roles,
263
+ stateOf,
264
+ extra,
265
+ actions,
266
+ editableRole,
267
+ onRoleChange,
268
+ roleOf,
269
+ emailColumn,
270
+ labels
271
+ }),
272
+ [roles, stateOf, extra, actions, editableRole, onRoleChange, roleOf, emailColumn, labels]
273
+ );
274
+ }
275
+ export {
276
+ DEFAULT_USER_ROSTER_LABELS,
277
+ USER_ROSTER_SORT_KEYS,
278
+ UserIdentityCell,
279
+ UserRowActions,
280
+ adminUserStates,
281
+ useUserRosterColumns,
282
+ userRosterColumns,
283
+ userRosterSort
284
+ };
285
+ //# sourceMappingURL=user-roster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/user-roster.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { MoreHorizontal } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport type { DateInput } from \"../lib/format\";\nimport type { PersonName } from \"../lib/person-name\";\nimport { useKitLabels, useKitLocale } from \"../i18n/kit-labels\";\nimport { AccountStateChip, RoleChip, dateColumn } from \"../components/account-chips\";\nimport type { AccountState, RoleVocabulary } from \"../components/account-chips\";\nimport type { ChipTone } from \"../components/chip\";\nimport type { DataTableColumn } from \"../components/data-table\";\nimport { encodeSorts } from \"../components/data-table-sort\";\nimport type { SortState } from \"../components/data-table-sort\";\nimport { Popover } from \"../components/popover\";\nimport { Button, IconButton } from \"../components/ui\";\nimport { UserAvatar } from \"../components/user-avatar\";\nimport { personLabel } from \"./admin-parts\";\nimport { RoleSelect } from \"./role-select\";\nimport type { RoleLock } from \"./role-select\";\n\n/**\n * The admin's user list (docs/user-admin-harmonization.md §3): a column preset for the\n * kit's `DataTable`, not a table of its own.\n *\n * keksdose built its roster by hand (1150 lines, its own Badge), Kurvenschmiede on the\n * kit `DataTable` with the 0.18 chips, kastlan as a settings page. What the three share\n * is the row — name over address, a role, the account's states, created, last login, the\n * app's own columns, and a handful of actions — so that is what the kit draws, and the\n * table, its paging, its sort and its filters stay the app's `DataTable` wiring:\n *\n * ```tsx\n * const columns = useUserRosterColumns<AdminUserRow, Role>({ roles: ROLES, actions, extra });\n * <DataTable rows={page.users} columns={columns} rowKey={(r) => r.id}\n * serverPagination={{ page, pageSize, total: page.total, onPageChange }}\n * sorts={sorts} onSortsChange={setSorts} />\n * // GET /admin/users?sort=${userRosterSort(sorts)}&…\n * ```\n *\n * The column keys ARE the server's sort keys (§3.1): `name`, `email`, `role`, `created`,\n * `last_login` — so {@link userRosterSort} turns the table's sort state into the query\n * as it is. Phone cards come from the same columns: the identity is the card's headline,\n * the role, states, last login and the actions are its fields, created is left off.\n */\n\n/* ── Rows and states ────────────────────────────────────────────────────────── */\n\n/**\n * The fields of the contract's `AdminUserRow` the preset reads (§3.2, the auth\n * contract's `UserResponse` core). An app's row has more — its `extra` — and the\n * preset passes the whole row to every callback.\n */\nexport interface UserRosterRow extends AdminUserStateFields {\n id: string | number;\n email: string;\n first_name?: string | null;\n last_name?: string | null;\n /** The derived whole name — only the fallback when both parts are blank. */\n display_name?: string | null;\n /** keksdose, Kurvenschmiede: one role. */\n role?: string | null;\n /** kastlan: the account's roles in the current company. Wins over `role`. */\n roles?: readonly string[] | null;\n created_at?: DateInput;\n /** Account-wide; kastlan's comes from its session rows (§3.2). */\n last_login_at?: DateInput;\n}\n\n/** The state fields of the contract's row that {@link adminUserStates} maps (§3.2). */\nexport interface AdminUserStateFields {\n is_active?: boolean;\n email_verified?: boolean;\n password_change_required_at?: string | null;\n /** `after_days` mode: the day the account is erased (§6.4). */\n deletion_scheduled_at?: string | null;\n /** When the user asked. The only deletion field in kastlan's `operator` mode, where\n * nothing is scheduled. */\n deletion_requested_at?: string | null;\n}\n\n/** One state chip of a row: an {@link AccountState}, or one with its `date` (the\n * `deletion` day), a `tone` or the app's own words. */\nexport type RosterState =\n | AccountState\n | { state: AccountState; date?: DateInput; tone?: ChipTone; label?: ReactNode };\n\n/**\n * The contract's row as state chips (§3.2: \"map 1:1 to the kit's AccountStateChip\"):\n *\n * - marked for deletion → `deletion` with the scheduled day (none in `operator` mode),\n * IN PLACE of `inactive` — such an account is always deactivated (§2.1);\n * - else `active` / `inactive` from `is_active`;\n * - `unverified` when `email_verified` is `false` (not when it is missing);\n * - `passwordChange` while `password_change_required_at` is set.\n */\nexport function adminUserStates(row: AdminUserStateFields): RosterState[] {\n const out: RosterState[] = [];\n if (row.deletion_scheduled_at || row.deletion_requested_at) {\n out.push({ state: \"deletion\", date: row.deletion_scheduled_at ?? null });\n } else if (row.is_active !== undefined) {\n out.push(row.is_active ? \"active\" : \"inactive\");\n }\n if (row.email_verified === false) out.push(\"unverified\");\n if (row.password_change_required_at) out.push(\"passwordChange\");\n return out;\n}\n\n/** The server's sort keys, which are the preset's column keys (§3.1). */\nexport const USER_ROSTER_SORT_KEYS = [\"name\", \"email\", \"role\", \"created\", \"last_login\"] as const;\nexport type UserRosterSortKey = (typeof USER_ROSTER_SORT_KEYS)[number];\n\n/**\n * The table's sort state as the `sort` query parameter of `GET /admin/users` (§3.1), in\n * the DataTable's own encoding ({@link encodeSorts}: `\"last_login.desc\"`), which\n * server-kit 0.4 reads beside the `-key` form. Only the first criterion on one of the\n * server's keys: an app's own column (`owned`) is the client's to sort, and the query\n * takes one key — a shift-clicked second criterion is not sent. `undefined` when nothing\n * the server knows is sorted, so its default order applies.\n */\nexport function userRosterSort(sorts: readonly SortState[]): string | undefined {\n const first = sorts.find((s) => (USER_ROSTER_SORT_KEYS as readonly string[]).includes(s.key));\n return first ? (encodeSorts([first]) ?? undefined) : undefined;\n}\n\n/* ── Labels ─────────────────────────────────────────────────────────────────── */\n\n/** The `userRoster` namespace: the preset's headers and the action menu's words. */\nexport interface UserRosterLabels {\n /** The identity column — the name with the address under it. */\n name: string;\n /** The separate address column (`emailColumn`). */\n email: string;\n role: string;\n state: string;\n created: string;\n lastLogin: string;\n /** A last login that never happened. */\n never: string;\n /** The actions column's (hidden) header. */\n actions: string;\n /** The action menu's button and panel — \"Actions for Ada Example\". */\n actionsFor: (name: string) => string;\n}\n\nexport const DEFAULT_USER_ROSTER_LABELS: UserRosterLabels = {\n name: \"Name\",\n email: \"Email\",\n role: \"Role\",\n state: \"State\",\n created: \"Created\",\n lastLogin: \"Last login\",\n never: \"Never\",\n actions: \"Actions\",\n actionsFor: (name) => `Actions for ${name}`,\n};\n\n/* ── UserIdentityCell ───────────────────────────────────────────────────────── */\n\nexport interface UserIdentityCellProps {\n /** First and last name (auth §3.2), shown in the READER's order. */\n person?: PersonName | null;\n /** A whole name, for an API that still sends `display_name` only. */\n name?: string | null;\n email?: string | null;\n /** The initials avatar before the name. Default true. */\n avatar?: boolean;\n /** After the name, on its line — a \"You\" chip, a demo mark. */\n badge?: ReactNode;\n /** Over the provider's locale. */\n locale?: string;\n className?: string;\n}\n\n/**\n * Who an account is, in one cell (§3.3): the initials avatar, the name in the reader's\n * order ({@link formatPersonName} — \"Example Ada\" in Hungarian) and the address under it\n * — the cell both rosters drew by hand. An account with no name yet (a migrated row\n * before `CompleteNameDialog`, an invitee) shows its address once, as the name.\n *\n * Both lines carry `data-private`, so keksdose's demo blur reaches them.\n */\nexport function UserIdentityCell({\n person,\n name,\n email,\n avatar = true,\n badge,\n locale: localeProp,\n className,\n}: UserIdentityCellProps) {\n const locale = useKitLocale(localeProp);\n const named = personLabel({ first: person?.first, last: person?.last, name }, locale);\n const address = (email ?? \"\").trim();\n return (\n <div className={cn(\"flex min-w-0 items-center gap-2.5\", className)}>\n {avatar && <UserAvatar person={person} name={name} email={email} size=\"sm\" />}\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span data-private className=\"truncate font-medium text-[var(--text-primary)]\">\n {named || address}\n </span>\n {badge}\n </div>\n {named && address && (\n <div data-private className=\"truncate text-xs text-[var(--text-muted)]\">\n {address}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/* ── The row's actions ──────────────────────────────────────────────────────── */\n\n/** One entry of a row's action menu (§4). */\nexport interface UserRowAction {\n /** React key. Default: the label. */\n key?: string;\n label: string;\n /** Runs it — usually opens `AdminActionConfirm` with the level the server states. */\n onSelect: () => void;\n icon?: LucideIcon;\n /** `danger` for deactivate, transfer, erase. */\n tone?: \"default\" | \"danger\";\n /** Why it is not available for this row (\"You can’t deactivate yourself\"). The entry\n * stays in the menu, focusable, and says so — an action that silently vanished\n * would leave the admin looking for it. */\n disabledReason?: ReactNode;\n /** Leave it out for this row. */\n hidden?: boolean;\n}\n\n/** What `actions(row)` may return: entries, and `false` / `null` for the ones a\n * condition left out (`row.is_active && {…}`). */\nexport type UserRowActionList = readonly (UserRowAction | false | null | undefined)[];\n\nexport interface UserRowActionsProps {\n actions: UserRowActionList;\n /** The account's name, for the menu's name — \"Actions for Ada Example\". */\n name: string;\n labels?: Partial<UserRosterLabels>;\n}\n\n/**\n * A row's actions as one \"…\" button and a menu of them: keksdose had a strip of icon\n * buttons in the expanded row, Kurvenschmiede five text buttons in a cell — neither fits\n * a phone card, and both grow with every action §2.4 adds. The menu is portalled, so a\n * table's scroller cannot clip it; Escape and an outside press close it, and focus goes\n * back to the button, which is where a confirmation dialog then returns it.\n */\nexport function UserRowActions({ actions, name, labels: labelsProp }: UserRowActionsProps) {\n const labels = useKitLabels(\"userRoster\", DEFAULT_USER_ROSTER_LABELS, labelsProp);\n const entries = actions.filter((a): a is UserRowAction => Boolean(a) && !(a as UserRowAction).hidden);\n if (entries.length === 0) return null;\n const title = labels.actionsFor(name);\n return (\n <Popover\n width={240}\n aria-label={title}\n className=\"p-1\"\n trigger={({ open, toggle, ref }) => (\n <IconButton\n ref={ref}\n size=\"xs\"\n label={title}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n stopPropagation\n onClick={toggle}\n >\n <MoreHorizontal />\n </IconButton>\n )}\n >\n {(close) => (\n <ul className=\"space-y-0.5\">\n {entries.map((action) => {\n const Icon = action.icon;\n return (\n <li key={action.key ?? action.label}>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n stretch\n tone={action.tone === \"danger\" ? \"danger\" : undefined}\n disabledReason={action.disabledReason}\n className=\"justify-start\"\n onClick={() => {\n close();\n action.onSelect();\n }}\n >\n {Icon && <Icon className=\"size-4 shrink-0\" aria-hidden />}\n {action.label}\n </Button>\n </li>\n );\n })}\n </ul>\n )}\n </Popover>\n );\n}\n\n/* ── The preset ─────────────────────────────────────────────────────────────── */\n\n/** How a row's role may be changed inline: `true`, or with reasons. */\nexport interface RoleEditing<R extends string> {\n /** Why the whole select is locked for this row — `\"self\"`, `\"last_admin\"`, or words. */\n disabledReason?: RoleLock;\n /** Why one role cannot be chosen for this row (a string: it goes inside an option). */\n optionDisabledReason?: (role: R) => string | undefined;\n /** The roles that may be chosen. Default: the whole vocabulary. */\n offered?: readonly R[];\n}\n\nexport interface UserRosterColumnsOptions<T extends UserRosterRow, R extends string = string> {\n /** The app's roles, as `RoleChip` reads them. */\n roles: RoleVocabulary<R>;\n /** The row's state chips. Default {@link adminUserStates}, which reads the contract's\n * fields (`is_active`, `email_verified`, `password_change_required_at`,\n * `deletion_scheduled_at`, `deletion_requested_at`). */\n stateOf?: (row: T) => readonly RosterState[];\n /** The app's own columns — keksdose's plan and key custody, Kurvenschmiede's owned\n * counts — after the dates, before the actions. */\n extra?: readonly DataTableColumn<T>[];\n /** The row's action menu (§4). Left out: no actions column. */\n actions?: (row: T) => UserRowActionList;\n /**\n * Whether this row's role is a select rather than a chip, and with which reasons.\n * Left out, or `false`: a chip. kastlan's several roles are never a select here — they\n * are a \"Manage roles\" action that opens `RolesEditor`.\n */\n editableRole?: (row: T) => boolean | RoleEditing<R>;\n /** A role was picked in a row's select. The app confirms (`acknowledge`) and sends it. */\n onRoleChange?: (row: T, role: R) => void;\n /** The row's role(s). Default `row.roles ?? row.role`. */\n roleOf?: (row: T) => R | readonly R[] | null | undefined;\n /** A separate, sortable \"Email\" column (the server's `email` sort key) beside the\n * identity. Default false: the address is under the name already. */\n emailColumn?: boolean;\n /** The words, already resolved. Default English; {@link useUserRosterColumns} reads\n * the provider's `userRoster` namespace. */\n labels?: Partial<UserRosterLabels>;\n}\n\nfunction rolesOf<T extends UserRosterRow, R extends string>(\n row: T,\n roleOf: UserRosterColumnsOptions<T, R>[\"roleOf\"],\n): R[] {\n const raw = roleOf ? roleOf(row) : ((row.roles ?? row.role) as R | readonly R[] | null | undefined);\n if (raw === null || raw === undefined || raw === \"\") return [];\n return typeof raw === \"string\" ? [raw as R] : [...(raw as readonly R[])];\n}\n\n/** The row's name in the reader's order — a component, because the order is the\n * READER's and the locale comes from the provider. */\nfunction RowName<T extends UserRosterRow>({ row, children }: { row: T; children: (name: string) => ReactNode }) {\n const locale = useKitLocale();\n const name = personLabel(\n { first: row.first_name, last: row.last_name, name: row.display_name, email: row.email },\n locale,\n );\n return <>{children(name)}</>;\n}\n\nfunction StateChips({ states }: { states: readonly RosterState[] }) {\n if (states.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {states.map((entry) => {\n const spec = typeof entry === \"string\" ? { state: entry } : entry;\n return (\n <AccountStateChip key={spec.state} state={spec.state} date={spec.date} tone={spec.tone} size=\"xs\">\n {spec.label}\n </AccountStateChip>\n );\n })}\n </div>\n );\n}\n\n/**\n * The roster's columns, in the contract's order (§3.3):\n *\n * | key | cell | sort | phone |\n * |---|---|---|---|\n * | `name` | {@link UserIdentityCell} | last, first | the card's headline |\n * | `email` | the address (`emailColumn` only) | address | hidden |\n * | `role` | `RoleChip`(s), or {@link RoleSelect} where `editableRole` | role | field |\n * | `state` | `AccountStateChip`s from `stateOf` | — | field |\n * | `created` | `dateColumn` | instant | hidden |\n * | `last_login` | `dateColumn`, relative, \"Never\" | instant | field |\n * | …`extra` | the app's | the app's | the app's |\n * | `actions` | {@link UserRowActions} | — | field |\n *\n * Pure: hand it resolved words, or use {@link useUserRosterColumns}. The keys are stable,\n * so an app may drop or move a column (`columns.filter((c) => c.key !== \"created\")`).\n * Under `serverPagination` the `sortBy`s only make the headers clickable; the app sends\n * the sort ({@link userRosterSort}).\n */\nexport function userRosterColumns<T extends UserRosterRow, R extends string = string>({\n roles,\n stateOf = adminUserStates,\n extra = [],\n actions,\n editableRole,\n onRoleChange,\n roleOf,\n emailColumn = false,\n labels: labelsProp,\n}: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[] {\n const labels: UserRosterLabels = { ...DEFAULT_USER_ROSTER_LABELS, ...labelsProp };\n const roleWords = (row: T) =>\n rolesOf(row, roleOf)\n .map((role) => roles[role]?.label ?? role)\n .join(\", \");\n const columns: DataTableColumn<T>[] = [\n {\n key: \"name\",\n header: labels.name,\n cell: (row) => (\n <UserIdentityCell\n person={{ first: row.first_name, last: row.last_name }}\n name={row.display_name}\n email={row.email}\n />\n ),\n // By last name, then first (auth §3.2), as the server sorts `name`.\n sortBy: (row) => `${row.last_name ?? \"\"} ${row.first_name ?? \"\"}`.trim().toLocaleLowerCase() || row.email,\n // Either part, in either order, or the address (auth §10.6).\n filterBy: (row) =>\n [row.first_name, row.last_name, row.last_name, row.first_name, row.display_name, row.email]\n .filter(Boolean)\n .join(\" \"),\n mobilePrimary: true,\n },\n ];\n if (emailColumn) {\n columns.push({\n key: \"email\",\n header: labels.email,\n className: \"whitespace-nowrap\",\n cell: (row) => (\n <span data-private className=\"text-[var(--text-secondary)]\">\n {row.email}\n </span>\n ),\n sortBy: (row) => row.email.toLowerCase(),\n filterBy: (row) => row.email,\n mobileHidden: true,\n });\n }\n columns.push(\n {\n key: \"role\",\n header: labels.role,\n cell: (row) => {\n const held = rolesOf(row, roleOf);\n const editing = held.length <= 1 ? editableRole?.(row) : false;\n if (editing && onRoleChange) {\n const rules: RoleEditing<R> = editing === true ? {} : editing;\n return (\n <RowName row={row}>\n {(name) => (\n <RoleSelect<R>\n size=\"sm\"\n name={name}\n value={held[0] ?? null}\n roles={roles}\n offered={rules.offered}\n disabledReason={rules.disabledReason}\n optionDisabledReason={rules.optionDisabledReason}\n onChange={(role) => onRoleChange(row, role)}\n />\n )}\n </RowName>\n );\n }\n if (held.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {held.map((role) => (\n <RoleChip<R> key={role} value={role} roles={roles} size=\"xs\" />\n ))}\n </div>\n );\n },\n sortBy: roleWords,\n filter: {\n type: \"select\",\n getValue: (row) => rolesOf(row, roleOf)[0] ?? \"\",\n options: (Object.keys(roles) as R[]).map((value) => ({ value, label: roles[value].label })),\n },\n },\n {\n key: \"state\",\n header: labels.state,\n cell: (row) => <StateChips states={stateOf(row)} />,\n },\n dateColumn<T>({\n key: \"created\",\n header: labels.created,\n value: (row) => row.created_at ?? null,\n column: { mobileHidden: true },\n }),\n // How long ago rather than a date — the question is \"recently?\" — with the exact\n // time in a tooltip (keksdose's last-seen column, Kurvenschmiede's).\n dateColumn<T>({\n key: \"last_login\",\n header: labels.lastLogin,\n value: (row) => row.last_login_at ?? null,\n display: \"relative\",\n empty: labels.never,\n }),\n ...extra,\n );\n if (actions) {\n columns.push({\n key: \"actions\",\n header: <span className=\"sr-only\">{labels.actions}</span>,\n headerText: labels.actions,\n // The caller's own words only: the menu reads the provider's itself, and the\n // English defaults merged in above must not shadow them.\n cell: (row) => (\n <RowName row={row}>\n {(name) => <UserRowActions actions={actions(row)} name={name} labels={labelsProp} />}\n </RowName>\n ),\n className: \"w-px whitespace-nowrap text-end\",\n headClassName: \"w-px\",\n // The menu button keeps its own click: a row link must not wrap it.\n noRowLink: true,\n });\n }\n return columns;\n}\n\n/**\n * {@link userRosterColumns} with the `userRoster` words from the provider, memoised on\n * the options — pass stable callbacks (`useCallback`) and a module-level vocabulary, or\n * the columns are rebuilt on every render.\n */\nexport function useUserRosterColumns<T extends UserRosterRow, R extends string = string>({\n roles,\n stateOf,\n extra,\n actions,\n editableRole,\n onRoleChange,\n roleOf,\n emailColumn,\n labels: labelsProp,\n}: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[] {\n const labels = useKitLabels(\"userRoster\", DEFAULT_USER_ROSTER_LABELS, labelsProp);\n return useMemo(\n () =>\n userRosterColumns<T, R>({\n roles,\n stateOf,\n extra,\n actions,\n editableRole,\n onRoleChange,\n roleOf,\n emailColumn,\n labels,\n }),\n [roles, stateOf, extra, actions, editableRole, onRoleChange, roleOf, emailColumn, labels],\n );\n}\n"],"mappings":"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","names":[]}