@eifi1/ui-kit 0.44.0 → 0.46.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 (285) hide show
  1. package/README.md +29 -14
  2. package/dist/admin/admin-action-log-labels.js +9 -0
  3. package/dist/admin/admin-action-log-labels.js.map +1 -1
  4. package/dist/admin/admin-action-log.d.ts +18 -1
  5. package/dist/admin/admin-action-log.js +25 -16
  6. package/dist/admin/admin-action-log.js.map +1 -1
  7. package/dist/admin/admin-area-labels.d.ts +15 -0
  8. package/dist/admin/admin-area-labels.js +6 -1
  9. package/dist/admin/admin-area-labels.js.map +1 -1
  10. package/dist/admin/admin-overview.d.ts +16 -1
  11. package/dist/admin/admin-overview.js +12 -1
  12. package/dist/admin/admin-overview.js.map +1 -1
  13. package/dist/admin/mail-test-card-labels.d.ts +3 -0
  14. package/dist/admin/mail-test-card-labels.js +17 -0
  15. package/dist/admin/mail-test-card-labels.js.map +1 -0
  16. package/dist/admin/mail-test-card.d.ts +75 -0
  17. package/dist/admin/mail-test-card.js +82 -0
  18. package/dist/admin/mail-test-card.js.map +1 -0
  19. package/dist/components/appearance-labels.d.ts +10 -0
  20. package/dist/components/appearance-labels.js +6 -1
  21. package/dist/components/appearance-labels.js.map +1 -1
  22. package/dist/components/data-table-column-settings.js +2 -2
  23. package/dist/components/data-table-column-settings.js.map +1 -1
  24. package/dist/components/data-table-head.js +2 -2
  25. package/dist/components/data-table-head.js.map +1 -1
  26. package/dist/components/data-table-mobile.js +17 -2
  27. package/dist/components/data-table-mobile.js.map +1 -1
  28. package/dist/components/data-table-row-actions.d.ts +18 -2
  29. package/dist/components/data-table-row-actions.js +86 -2
  30. package/dist/components/data-table-row-actions.js.map +1 -1
  31. package/dist/components/data-table-shared.d.ts +2 -0
  32. package/dist/components/data-table-shared.js +17 -2
  33. package/dist/components/data-table-shared.js.map +1 -1
  34. package/dist/components/data-table.d.ts +43 -2
  35. package/dist/components/data-table.js +10 -10
  36. package/dist/components/data-table.js.map +1 -1
  37. package/dist/components/date-format-setting.d.ts +29 -0
  38. package/dist/components/date-format-setting.js +36 -0
  39. package/dist/components/date-format-setting.js.map +1 -0
  40. package/dist/components/swipeable-row.d.ts +8 -0
  41. package/dist/components/swipeable-row.js.map +1 -1
  42. package/dist/components/tabs.d.ts +3 -2
  43. package/dist/components/tabs.js +1 -1
  44. package/dist/components/tabs.js.map +1 -1
  45. package/dist/components/translation-review-editor.js +1 -0
  46. package/dist/components/translation-review-editor.js.map +1 -1
  47. package/dist/components/translation-review-labels.d.ts +5 -0
  48. package/dist/components/translation-review-labels.js +2 -0
  49. package/dist/components/translation-review-labels.js.map +1 -1
  50. package/dist/components/translation-review.d.ts +18 -8
  51. package/dist/components/translation-review.js +64 -7
  52. package/dist/components/translation-review.js.map +1 -1
  53. package/dist/components/write-lock-labels.js +2 -1
  54. package/dist/components/write-lock-labels.js.map +1 -1
  55. package/dist/components/write-lock.d.ts +33 -3
  56. package/dist/components/write-lock.js +9 -0
  57. package/dist/components/write-lock.js.map +1 -1
  58. package/dist/data-table.d.ts +1 -1
  59. package/dist/data-table.js.map +1 -1
  60. package/dist/eslint/index.js +1 -1
  61. package/dist/feedback/feedback-row-detail.d.ts +4 -2
  62. package/dist/feedback/feedback-row-detail.js +17 -26
  63. package/dist/feedback/feedback-row-detail.js.map +1 -1
  64. package/dist/feedback/feedback-swipe.d.ts +18 -0
  65. package/dist/feedback/feedback-swipe.js +10 -1
  66. package/dist/feedback/feedback-swipe.js.map +1 -1
  67. package/dist/i18n/defaults.js +9 -1
  68. package/dist/i18n/defaults.js.map +1 -1
  69. package/dist/i18n/german.js +81 -5
  70. package/dist/i18n/german.js.map +1 -1
  71. package/dist/i18n/kit-labels.d.ts +25 -1
  72. package/dist/i18n/kit-labels.js +8 -2
  73. package/dist/i18n/kit-labels.js.map +1 -1
  74. package/dist/i18n/locales/en.js +97 -7
  75. package/dist/i18n/locales/en.js.map +3 -3
  76. package/dist/i18n/locales/es.js +81 -5
  77. package/dist/i18n/locales/es.js.map +1 -1
  78. package/dist/i18n/locales/fr.js +81 -5
  79. package/dist/i18n/locales/fr.js.map +1 -1
  80. package/dist/i18n/locales/hu.js +81 -5
  81. package/dist/i18n/locales/hu.js.map +1 -1
  82. package/dist/i18n/locales/it.js +81 -5
  83. package/dist/i18n/locales/it.js.map +1 -1
  84. package/dist/i18n/locales/zh.js +81 -5
  85. package/dist/i18n/locales/zh.js.map +1 -1
  86. package/dist/i18n/review.js +16 -2
  87. package/dist/i18n/review.js.map +1 -1
  88. package/dist/index.d.ts +17 -2
  89. package/dist/index.js +40 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/lib/date-format.d.ts +81 -0
  92. package/dist/lib/date-format.js +56 -0
  93. package/dist/lib/date-format.js.map +1 -0
  94. package/dist/lib/format.d.ts +5 -1
  95. package/dist/lib/format.js +11 -5
  96. package/dist/lib/format.js.map +1 -1
  97. package/dist/notifications/device-zone-report.d.ts +27 -0
  98. package/dist/notifications/device-zone-report.js +24 -0
  99. package/dist/notifications/device-zone-report.js.map +1 -0
  100. package/dist/notifications/feedback-notification-types-labels.d.ts +7 -1
  101. package/dist/notifications/feedback-notification-types-labels.js +4 -1
  102. package/dist/notifications/feedback-notification-types-labels.js.map +1 -1
  103. package/dist/notifications/feedback-notification-types.d.ts +11 -5
  104. package/dist/notifications/feedback-notification-types.js +2 -1
  105. package/dist/notifications/feedback-notification-types.js.map +1 -1
  106. package/dist/notifications/notification-bell.d.ts +33 -0
  107. package/dist/notifications/notification-bell.js +74 -0
  108. package/dist/notifications/notification-bell.js.map +1 -0
  109. package/dist/notifications/notification-inbox.d.ts +77 -0
  110. package/dist/notifications/notification-inbox.js +206 -0
  111. package/dist/notifications/notification-inbox.js.map +1 -0
  112. package/dist/notifications/notification-types-setting.d.ts +6 -4
  113. package/dist/notifications/notification-types-setting.js +103 -45
  114. package/dist/notifications/notification-types-setting.js.map +1 -1
  115. package/dist/notifications/notification-types.d.ts +19 -7
  116. package/dist/notifications/notifications-labels.d.ts +41 -7
  117. package/dist/notifications/notifications-labels.js +18 -1
  118. package/dist/notifications/notifications-labels.js.map +1 -1
  119. package/dist/notifications/push-devices-setting.d.ts +20 -3
  120. package/dist/notifications/push-devices-setting.js +93 -33
  121. package/dist/notifications/push-devices-setting.js.map +1 -1
  122. package/dist/notifications/push-worker.d.ts +108 -0
  123. package/dist/notifications/push-worker.js +62 -0
  124. package/dist/notifications/push-worker.js.map +1 -0
  125. package/dist/notifications/quiet-hours-setting.d.ts +29 -17
  126. package/dist/notifications/quiet-hours-setting.js +9 -41
  127. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  128. package/dist/notifications/time-zone-select.d.ts +5 -2
  129. package/dist/notifications/time-zone-select.js.map +1 -1
  130. package/dist/notifications/use-push-device.d.ts +4 -17
  131. package/dist/notifications/use-push-device.js +7 -15
  132. package/dist/notifications/use-push-device.js.map +1 -1
  133. package/dist/offline/local-first.d.ts +33 -0
  134. package/dist/offline/local-first.js +39 -0
  135. package/dist/offline/local-first.js.map +1 -0
  136. package/dist/offline/pwa-update-prompt-labels.d.ts +2 -0
  137. package/dist/offline/pwa-update-prompt-labels.js +9 -0
  138. package/dist/offline/pwa-update-prompt-labels.js.map +1 -0
  139. package/dist/offline/pwa-update-prompt.d.ts +32 -0
  140. package/dist/offline/pwa-update-prompt.js +36 -0
  141. package/dist/offline/pwa-update-prompt.js.map +1 -0
  142. package/dist/offline/query-persistence.d.ts +125 -0
  143. package/dist/offline/query-persistence.js +183 -0
  144. package/dist/offline/query-persistence.js.map +1 -0
  145. package/dist/offline/service-worker-updates.d.ts +46 -0
  146. package/dist/offline/service-worker-updates.js +39 -0
  147. package/dist/offline/service-worker-updates.js.map +1 -0
  148. package/dist/offline/shared-computer-labels.d.ts +2 -0
  149. package/dist/offline/shared-computer-labels.js +9 -0
  150. package/dist/offline/shared-computer-labels.js.map +1 -0
  151. package/dist/offline/shared-computer.d.ts +44 -0
  152. package/dist/offline/shared-computer.js +51 -0
  153. package/dist/offline/shared-computer.js.map +1 -0
  154. package/dist/offline/sign-out-pending-dialog-labels.d.ts +2 -0
  155. package/dist/offline/sign-out-pending-dialog-labels.js +14 -0
  156. package/dist/offline/sign-out-pending-dialog-labels.js.map +1 -0
  157. package/dist/offline/sign-out-pending-dialog.d.ts +58 -0
  158. package/dist/offline/sign-out-pending-dialog.js +66 -0
  159. package/dist/offline/sign-out-pending-dialog.js.map +1 -0
  160. package/dist/offline.d.ts +11 -0
  161. package/dist/offline.js +33 -0
  162. package/dist/offline.js.map +1 -0
  163. package/dist/push-worker.d.ts +2 -0
  164. package/dist/push-worker.js +14 -0
  165. package/dist/push-worker.js.map +1 -0
  166. package/dist/shell/account-menu-text-size.d.ts +33 -0
  167. package/dist/shell/account-menu-text-size.js +51 -0
  168. package/dist/shell/account-menu-text-size.js.map +1 -0
  169. package/dist/shell/admin-menu.d.ts +24 -0
  170. package/dist/shell/admin-menu.js +12 -3
  171. package/dist/shell/admin-menu.js.map +1 -1
  172. package/dist/shell/top-bar-actions.d.ts +6 -2
  173. package/dist/shell/top-bar-actions.js +1 -0
  174. package/dist/shell/top-bar-actions.js.map +1 -1
  175. package/dist/shell.d.ts +4 -0
  176. package/dist/shell.js +4 -0
  177. package/dist/shell.js.map +1 -1
  178. package/dist/sync/db.d.ts +76 -0
  179. package/dist/sync/db.js +24 -0
  180. package/dist/sync/db.js.map +1 -0
  181. package/dist/sync/engine.d.ts +124 -0
  182. package/dist/sync/engine.js +474 -0
  183. package/dist/sync/engine.js.map +1 -0
  184. package/dist/sync/hooks.d.ts +30 -0
  185. package/dist/sync/hooks.js +67 -0
  186. package/dist/sync/hooks.js.map +1 -0
  187. package/dist/sync/testing.d.ts +28 -0
  188. package/dist/sync/testing.js +41 -0
  189. package/dist/sync/testing.js.map +1 -0
  190. package/dist/sync/wire.d.ts +50 -0
  191. package/dist/sync/wire.js +2 -0
  192. package/dist/sync/wire.js.map +1 -0
  193. package/dist/sync.d.ts +7 -0
  194. package/dist/sync.js +22 -0
  195. package/dist/sync.js.map +1 -0
  196. package/dist/tour/tour-catalogue.d.ts +7 -1
  197. package/dist/tour/tour-catalogue.js.map +1 -1
  198. package/dist/tour/tour-labels.js +1 -0
  199. package/dist/tour/tour-labels.js.map +1 -1
  200. package/dist/tour/tour.d.ts +30 -0
  201. package/dist/tour/tour.js +54 -8
  202. package/dist/tour/tour.js.map +1 -1
  203. package/dist/tour/use-tour-progress.d.ts +99 -1
  204. package/dist/tour/use-tour-progress.js +228 -12
  205. package/dist/tour/use-tour-progress.js.map +1 -1
  206. package/dist/tour.d.ts +2 -0
  207. package/dist/tour.js +2 -0
  208. package/dist/tour.js.map +1 -1
  209. package/package.json +43 -1
  210. package/src/admin/admin-action-log-labels.ts +9 -0
  211. package/src/admin/admin-action-log.tsx +41 -5
  212. package/src/admin/admin-area-labels.ts +20 -0
  213. package/src/admin/admin-overview.tsx +37 -2
  214. package/src/admin/mail-test-card-labels.ts +19 -0
  215. package/src/admin/mail-test-card.tsx +190 -0
  216. package/src/components/appearance-labels.ts +16 -0
  217. package/src/components/data-table-column-settings.tsx +2 -2
  218. package/src/components/data-table-head.tsx +2 -2
  219. package/src/components/data-table-mobile.tsx +26 -3
  220. package/src/components/data-table-row-actions.tsx +118 -2
  221. package/src/components/data-table-shared.tsx +39 -3
  222. package/src/components/data-table.tsx +52 -21
  223. package/src/components/date-format-setting.tsx +64 -0
  224. package/src/components/swipeable-row.tsx +8 -0
  225. package/src/components/tabs.tsx +4 -3
  226. package/src/components/translation-review-editor.tsx +2 -0
  227. package/src/components/translation-review-labels.ts +7 -0
  228. package/src/components/translation-review.tsx +102 -14
  229. package/src/components/write-lock-labels.ts +1 -0
  230. package/src/components/write-lock.tsx +36 -3
  231. package/src/data-table.ts +1 -0
  232. package/src/feedback/feedback-row-detail.tsx +18 -23
  233. package/src/feedback/feedback-swipe.tsx +28 -1
  234. package/src/i18n/defaults.ts +8 -0
  235. package/src/i18n/german.ts +90 -4
  236. package/src/i18n/kit-labels.tsx +31 -1
  237. package/src/i18n/locales/en.ts +13 -0
  238. package/src/i18n/locales/es.ts +90 -4
  239. package/src/i18n/locales/fr.ts +90 -4
  240. package/src/i18n/locales/hu.ts +86 -4
  241. package/src/i18n/locales/it.ts +90 -4
  242. package/src/i18n/locales/zh.ts +84 -2
  243. package/src/i18n/review.ts +15 -1
  244. package/src/index.ts +45 -0
  245. package/src/lib/date-format.ts +151 -0
  246. package/src/lib/format.ts +23 -6
  247. package/src/notifications/device-zone-report.ts +54 -0
  248. package/src/notifications/feedback-notification-types-labels.ts +10 -1
  249. package/src/notifications/feedback-notification-types.ts +12 -5
  250. package/src/notifications/notification-bell.tsx +116 -0
  251. package/src/notifications/notification-inbox.tsx +304 -0
  252. package/src/notifications/notification-types-setting.tsx +151 -55
  253. package/src/notifications/notification-types.ts +20 -8
  254. package/src/notifications/notifications-labels.ts +69 -11
  255. package/src/notifications/push-devices-setting.tsx +123 -34
  256. package/src/notifications/push-worker.ts +160 -0
  257. package/src/notifications/quiet-hours-setting.tsx +41 -58
  258. package/src/notifications/time-zone-select.tsx +5 -2
  259. package/src/notifications/use-push-device.ts +20 -33
  260. package/src/offline/local-first.ts +76 -0
  261. package/src/offline/pwa-update-prompt-labels.ts +9 -0
  262. package/src/offline/pwa-update-prompt.tsx +72 -0
  263. package/src/offline/query-persistence.ts +356 -0
  264. package/src/offline/service-worker-updates.ts +81 -0
  265. package/src/offline/shared-computer-labels.ts +8 -0
  266. package/src/offline/shared-computer.tsx +87 -0
  267. package/src/offline/sign-out-pending-dialog-labels.ts +21 -0
  268. package/src/offline/sign-out-pending-dialog.tsx +135 -0
  269. package/src/offline.ts +32 -0
  270. package/src/push-worker.ts +25 -0
  271. package/src/shell/account-menu-text-size.tsx +77 -0
  272. package/src/shell/admin-menu.tsx +40 -3
  273. package/src/shell/top-bar-actions.tsx +6 -1
  274. package/src/shell.ts +7 -0
  275. package/src/sync/db.ts +103 -0
  276. package/src/sync/engine.ts +699 -0
  277. package/src/sync/hooks.ts +93 -0
  278. package/src/sync/testing.ts +73 -0
  279. package/src/sync/wire.ts +61 -0
  280. package/src/sync.ts +29 -0
  281. package/src/tour/tour-catalogue.tsx +7 -1
  282. package/src/tour/tour-labels.ts +1 -0
  283. package/src/tour/tour.tsx +105 -9
  284. package/src/tour/use-tour-progress.ts +378 -12
  285. package/src/tour.ts +3 -0
package/README.md CHANGED
@@ -65,6 +65,9 @@ when it chooses. To work on the design system and an app together, use
65
65
  | `sonner` | peer, `>=2`, **optional** | Under the kit's `toast` and `<Toaster>` (and through them `FileDropzone` and the wizard's "fill in the required fields" toast), reached only through `await import()`. Pass `onValidationFailed` to route the wizard's elsewhere. |
66
66
  | `react-hook-form` | peer, `^7.55.0`, **optional** | Only `@eifi1/ui-kit/rhf` imports it; the main barrel does not (see "Forms / refs" below). |
67
67
  | `modern-screenshot` | peer, `^4.7.0`, **optional** | The feedback dialog's "Capture screenshot" (`captureAppScreenshot`), reached only through `await import()`. Without it the capture rejects and the dialog toasts `feedbackToast.captureFailed`. |
68
+ | `@tanstack/react-query`, `@tanstack/react-query-persist-client`, `@tanstack/query-async-storage-persister` | peer, `^5`, **optional** | Only `@eifi1/ui-kit/offline` imports them (0.45, the persisted query cache, `createQueryPersistence`); the main barrel does not. |
69
+ | `idb-keyval` | peer, `^6`, **optional** | The persisted cache's default IndexedDB store, in `@eifi1/ui-kit/offline` only. |
70
+ | `dexie` | peer, `^4`, **optional** | The sync engine's device copy, in `@eifi1/ui-kit/sync` only (0.46); `/offline` loads without it. |
68
71
  | `clsx`, `tailwind-merge`, `zustand` | dependency | Small, imported directly, and the kit owns the version. Add `zustand` to Vite's `dedupe` (below) — the appearance stores are singletons. |
69
72
  | `flag-icons` | **nothing — you supply the stylesheet** | See below. |
70
73
 
@@ -556,20 +559,20 @@ columns, strings and permissions (see the note at the top of `src/feedback/feedb
556
559
 
557
560
  <!-- BEGIN GENERATED: exports — node scripts/gen-export-inventory.mjs -->
558
561
 
559
- **2081 names from 341 modules** — 959 values and 1122 types. _Italic_ is a type-only export.
562
+ **2133 names from 349 modules** — 985 values and 1148 types. _Italic_ is a type-only export.
560
563
 
561
564
  Generated from `dist/index.d.ts` by `node scripts/gen-export-inventory.mjs`; the count
562
565
  is pinned by `src/__tests__/public-surface.test.ts`. Do not edit between the markers.
563
566
 
564
567
  | Entry point | Names |
565
568
  |---|---|
566
- | `@eifi1/ui-kit` | 2081 |
569
+ | `@eifi1/ui-kit` | 2133 |
567
570
  | `@eifi1/ui-kit/chart` | 92 |
568
- | `@eifi1/ui-kit/shell` | 110 |
569
- | `@eifi1/ui-kit/data-table` | 51 |
571
+ | `@eifi1/ui-kit/shell` | 114 |
572
+ | `@eifi1/ui-kit/data-table` | 52 |
570
573
  | `@eifi1/ui-kit/wizard` | 24 |
571
- | `@eifi1/ui-kit/tour` | 33 |
572
- | `@eifi1/ui-kit/feedback` | 205 |
574
+ | `@eifi1/ui-kit/tour` | 37 |
575
+ | `@eifi1/ui-kit/feedback` | 206 |
573
576
  | `@eifi1/ui-kit/search` | 23 |
574
577
  | `@eifi1/ui-kit/dates` | 22 |
575
578
  | `@eifi1/ui-kit/table-text` | 14 |
@@ -577,6 +580,10 @@ is pinned by `src/__tests__/public-surface.test.ts`. Do not edit between the mar
577
580
  | `@eifi1/ui-kit/eslint` | 1 |
578
581
  | `@eifi1/ui-kit/testing` | 11 |
579
582
  | `@eifi1/ui-kit/rhf` | 48 |
583
+ | `@eifi1/ui-kit/offline` | 32 |
584
+ | `@eifi1/ui-kit/sync` | 35 |
585
+ | `@eifi1/ui-kit/sync/testing` | 3 |
586
+ | `@eifi1/ui-kit/push-worker` | 10 |
580
587
 
581
588
  Everything below is reachable from the main `@eifi1/ui-kit` barrel. The subpaths are a
582
589
  re-slicing of it, never a second API.
@@ -602,6 +609,7 @@ re-slicing of it, never a second API.
602
609
  | `lib/isin` | `formatIsin`, `isinCheckDigit`, `isValidIsin` |
603
610
  | `lib/phone` | `formatNationalPhone`, `formatPhone`, `isE164`, `parsePhone`, `PHONE_COUNTRIES`, `PHONE_DIAL_CODES`, _`ParsedPhone`_, _`PhoneCountry`_, _`PhoneCountryCode`_ |
604
611
  | `lib/column-mapping` | `assignColumnRole`, `guessMapping`, `missingRoles`, `readMappedTable`, `readTextFile`, `roleOfColumn`, _`ColumnMapperResult`_, _`ColumnMapping`_, _`ColumnRole`_ |
612
+ | `lib/date-format` | `DATE_FORMAT_PREFERENCES`, `dateFormatSample`, `dateFormatterFor`, `formatDateWithPreference`, `isDateFormatPreference`, `resolveDateFormat`, _`DateFormatOptions`_, _`DateFormatPreference`_, _`ResolvedDateFormat`_ |
605
613
  | `lib/device-settings` | `deviceSettings`, `registerDeviceSetting`, `resetDeviceSettings`, _`DeviceSetting`_, _`DeviceSettingKind`_ |
606
614
  | `lib/clipping` | `CLIPS_ATTRIBUTE` |
607
615
  | `lib/chip-edge-snap` | `refreshChipEdges` |
@@ -649,7 +657,7 @@ re-slicing of it, never a second API.
649
657
 
650
658
  | Module | Exports |
651
659
  |---|---|
652
- | `i18n/kit-labels` | `DEFAULT_APP_SHELL_LABELS`, `DEFAULT_CALCULATOR_LABELS`, `DEFAULT_COMBOBOX_LABELS`, `DEFAULT_COMMON_LABELS`, `DEFAULT_CURRENCY_LABELS`, `DEFAULT_DATE_PICKER_LABELS`, `DEFAULT_FILE_LABELS`, `DEFAULT_MULTI_SELECT_LABELS`, `DEFAULT_PICKER_SHEET_LABELS`, `DEFAULT_SWIPEABLE_ROW_LABELS`, `DEFAULT_TOP_BAR_LABELS`, `formatFileSize`, `missingKitLabels`, `UiKitProvider`, `useKitChartTooltipPlacement`, `useKitDateFormatter`, `useKitFileLabels`, `useKitLabelOverrides`, `useKitLabels`, `useKitLink`, `useKitLocale`, `useKitTableStorageKeyPrefix`, `useKitWeekStart`, _`AppShellLabels`_, _`CalculatorLabels`_, _`ChartTooltipPlacement`_, _`ComboboxLabels`_, _`CommonLabels`_, _`CurrencyLabels`_, _`DatePickerLabels`_, _`FileLabels`_, _`KitDateFormatContext`_, _`KitDateFormatter`_, _`KitLinkComponent`_, _`KitLinkProps`_, _`LabelOverride`_, _`MultiSelectLabels`_, _`PickerSheetLabels`_, _`SwipeableRowLabels`_, _`TopBarLabels`_, _`UiKitLabelOverrides`_, _`UiKitLabels`_, _`UiKitProviderProps`_ |
660
+ | `i18n/kit-labels` | `DEFAULT_APP_SHELL_LABELS`, `DEFAULT_CALCULATOR_LABELS`, `DEFAULT_COMBOBOX_LABELS`, `DEFAULT_COMMON_LABELS`, `DEFAULT_CURRENCY_LABELS`, `DEFAULT_DATE_PICKER_LABELS`, `DEFAULT_FILE_LABELS`, `DEFAULT_MULTI_SELECT_LABELS`, `DEFAULT_PICKER_SHEET_LABELS`, `DEFAULT_SWIPEABLE_ROW_LABELS`, `DEFAULT_TOP_BAR_LABELS`, `formatFileSize`, `missingKitLabels`, `UiKitProvider`, `useKitChartTooltipPlacement`, `useKitDateFormatter`, `useKitFileLabels`, `useKitLabelOverrides`, `useKitLabels`, `useKitLink`, `useKitLocale`, `useKitTableStorageKeyPrefix`, `useKitTimeZone`, `useKitWeekStart`, _`AppShellLabels`_, _`CalculatorLabels`_, _`ChartTooltipPlacement`_, _`ComboboxLabels`_, _`CommonLabels`_, _`CurrencyLabels`_, _`DatePickerLabels`_, _`FileLabels`_, _`KitDateFormatContext`_, _`KitDateFormatter`_, _`KitLinkComponent`_, _`KitLinkProps`_, _`LabelOverride`_, _`MultiSelectLabels`_, _`PickerSheetLabels`_, _`SwipeableRowLabels`_, _`TopBarLabels`_, _`UiKitLabelOverrides`_, _`UiKitLabels`_, _`UiKitProviderProps`_ |
653
661
  | `i18n/defaults` | `DEFAULT_UI_KIT_LABELS` |
654
662
  | `i18n/review` | `kitLabelStrings`, _`KitLabelStringsOptions`_ |
655
663
  | `i18n/languages` | `formatLocaleOf`, `KIT_LANGUAGES`, `languageOptions`, `loadUiKitLabels`, `peekUiKitLabels`, `resolveLanguage`, `useUiKitLabels`, _`KitLanguage`_, _`KitLanguageCode`_ |
@@ -745,7 +753,7 @@ re-slicing of it, never a second API.
745
753
  | `components/tree-view` | `TreeRow`, `TreeView`, _`TreeItemState`_, _`TreeNode`_, _`TreeRowProps`_, _`TreeViewProps`_ |
746
754
  | `components/description-list` | `DEFAULT_DESCRIPTION_LIST_LABELS`, `DescriptionItem`, `DescriptionList`, _`DescriptionItemProps`_, _`DescriptionListColumns`_, _`DescriptionListDensity`_, _`DescriptionListLabels`_, _`DescriptionListLayout`_, _`DescriptionListProps`_ |
747
755
  | `components/form-actions` | `DEFAULT_FORM_ACTIONS_LABELS`, `FormActions`, _`FormActionsAlign`_, _`FormActionsDestructive`_, _`FormActionsLabels`_, _`FormActionsPlacement`_, _`FormActionsProps`_, _`FormActionsStack`_, _`FormActionsStickyWithin`_, _`FormActionsSubmitProps`_, _`FormActionsSubmitShortcut`_, _`ResponsiveFormActionsPlacement`_ |
748
- | `components/write-lock` | `COMMIT_EXCEPT_BILLING`, `DEFAULT_WRITE_LOCK_LABELS`, `useWriteLock`, `writeLockFor`, `WriteLockProvider`, _`CommitScope`_, _`WriteLock`_, _`WriteLockHold`_, _`WriteLockKind`_, _`WriteLockLabels`_, _`WriteLockProviderProps`_ |
756
+ | `components/write-lock` | `COMMIT_EXCEPT_BILLING`, `COMMIT_EXCEPT_OFFLINE`, `DEFAULT_WRITE_LOCK_LABELS`, `OFFLINE_LOCK`, `useOfflineWriteLock`, `useWriteLock`, `writeLockFor`, `WriteLockProvider`, _`CommitScope`_, _`WriteLock`_, _`WriteLockHold`_, _`WriteLockKind`_, _`WriteLockLabels`_, _`WriteLockProviderProps`_ |
749
757
  | `components/account-chips` | `ACCOUNT_STATE_TONES`, `AccountRoleChip`, `AccountStateChip`, `dateColumn`, `DateMark`, `DEFAULT_ACCOUNT_STATE_LABELS`, `RoleChip`, _`AccountRoleChipProps`_, _`AccountState`_, _`AccountStateChipProps`_, _`AccountStateLabels`_, _`DateColumnOptions`_, _`DateMarkProps`_, _`RoleChipProps`_, _`RoleDefinition`_, _`RoleVocabulary`_ |
750
758
  | `components/server-wake` | `DEFAULT_SERVER_WAKE_LABELS`, `ServerWakeNotice`, `useServerWakeStage`, _`ServerWakeLabels`_, _`ServerWakeNoticeProps`_ |
751
759
  | `components/share-card` | `DEFAULT_SHARE_CARD_LABELS`, `ShareCard`, `ShareDialog`, `SharePanel`, _`ShareAddRequest`_, _`ShareCandidate`_, _`ShareCardLabels`_, _`ShareCardProps`_, _`ShareDialogProps`_, _`ShareGrantee`_, _`SharePanelProps`_, _`SharePendingGrant`_, _`ShareRole`_ |
@@ -775,6 +783,7 @@ re-slicing of it, never a second API.
775
783
  | `components/legal` | `DEFAULT_LEGAL_LABELS`, `LEGAL_HREFS`, `LegalFooter`, `LegalLayout`, `LegalLinks`, `legalOperatorText`, `LegalSection`, `useLegalLabels`, _`LegalDisclaimerSectionLabels`_, _`LegalFooterProps`_, _`LegalFramedSectionLabels`_, _`LegalLabels`_, _`LegalLayoutProps`_, _`LegalLink`_, _`LegalLinksProps`_, _`LegalOperator`_, _`LegalOperatorSectionLabels`_, _`LegalOperatorText`_, _`LegalPageKey`_, _`LegalSectionProps`_, _`LegalTextSectionLabels`_ |
776
784
  | `components/legal-page` | `LEGAL_SKELETON`, `LegalAcceptCheckbox`, `LegalKitSection`, `LegalPage`, _`LegalAcceptCheckboxProps`_, _`LegalDisclaimerVariant`_, _`LegalKitSectionKey`_, _`LegalKitSectionProps`_, _`LegalPageProps`_, _`LegalSectionOwner`_, _`LegalSkeletonEntry`_, _`LegalSkeletonPage`_ |
777
785
  | `components/appearance-labels` | `DEFAULT_APPEARANCE_LABELS`, `useAppearanceLabels`, _`AppearanceLabels`_ |
786
+ | `components/date-format-setting` | `DateFormatSetting`, _`DateFormatSettingProps`_ |
778
787
  | `components/row-actions` | `DEFAULT_ROW_ACTIONS_LABELS`, `RowActions`, `rowActionsColumn`, _`RowAction`_, _`RowActionList`_, _`RowActionsCollapse`_, _`RowActionsColumnOptions`_, _`RowActionsLabels`_, _`RowActionsProps`_, _`RowActionsSize`_, _`RowActionTone`_ |
779
788
  | `components/checkbox-group` | `CheckboxGroup`, _`CheckboxGroupLegendVisibility`_, _`CheckboxGroupOption`_, _`CheckboxGroupProps`_ |
780
789
  | `components/one-time-code-input` | `OneTimeCodeInput`, _`OneTimeCodeInputProps`_ |
@@ -792,7 +801,7 @@ re-slicing of it, never a second API.
792
801
  | `components/data-table-filters` | `dateFilter`, `decodeFilterValue`, `decodeFilterValueOfType`, `defaultFilterState`, `encodeFilterValue`, `filterHref`, `isFilterActive`, `numberFilter`, `resolveFilter`, `rowMatches`, `selectFilter`, `textFilter`, _`ColumnFilter`_, _`FilterValue`_ |
793
802
  | `components/data-table-pagination` | `PAGE_SIZE_OPTIONS`, `Pagination` |
794
803
  | `components/data-table-filter-popover` | `FilterPopover` |
795
- | `components/data-table` | `DataTable`, `DEFAULT_DATA_TABLE_SORT_LABELS`, _`DataTableCellProps`_, _`DataTableChrome`_, _`DataTableColumn`_, _`DataTableDensity`_, _`DataTableHeadProps`_, _`DataTableProps`_, _`DataTableRowAction`_, _`DataTableSortLabels`_, _`FilterState`_, _`MobileSwipeActions`_, _`ServerPagination`_ |
804
+ | `components/data-table` | `DataTable`, `DEFAULT_DATA_TABLE_SORT_LABELS`, _`DataTableCellProps`_, _`DataTableChrome`_, _`DataTableColumn`_, _`DataTableDensity`_, _`DataTableDesktopSwipeActions`_, _`DataTableHeadProps`_, _`DataTableProps`_, _`DataTableRowAction`_, _`DataTableSortLabels`_, _`FilterState`_, _`MobileSwipeActions`_, _`ServerPagination`_ |
796
805
  | `components/data-table-cells` | `booleanColumn`, `BooleanMark`, _`BooleanColumnOptions`_, _`BooleanMarkProps`_ |
797
806
  | `components/use-table-state` | `readTableUrlState`, `useTableUrlState`, _`TableUrlState`_, _`TableUrlStateProps`_, _`TableUrlSync`_, _`UseTableUrlStateOptions`_, _`UseTableUrlStateReturn`_ |
798
807
  | `components/translation-review-editor` | `TranslationReviewEditor`, _`TranslationReviewEditorProps`_ |
@@ -819,9 +828,10 @@ re-slicing of it, never a second API.
819
828
  | `shell/top-bar-brand` | `TopBarBrand`, _`TopBarBrandProps`_ |
820
829
  | `shell/auth-layout` | `AuthLayout`, _`AuthLayoutProps`_ |
821
830
  | `shell/fill-height` | `FILL_HEIGHT_CLASS`, `FillHeight`, _`FillHeightProps`_ |
822
- | `shell/admin-menu` | `adminMenuEntry`, `AdminMenuRow`, `useAdminMenuEntry`, _`AdminMenuEntryOptions`_, _`AdminMenuRowProps`_, _`UseAdminMenuEntryOptions`_ |
831
+ | `shell/admin-menu` | `adminMenuEntry`, `AdminMenuRow`, `AdminWaitingBadge`, `useAdminMenuEntry`, _`AdminMenuEntryOptions`_, _`AdminMenuRowProps`_, _`AdminWaitingBadgeProps`_, _`UseAdminMenuEntryOptions`_ |
823
832
  | `shell/top-bar-actions` | `TOP_BAR_ACTION_ORDER`, `TopBarActions`, _`TopBarActionsProps`_ |
824
833
  | `shell/account-menu-language` | `AccountMenuLanguage`, _`AccountMenuLanguageOption`_, _`AccountMenuLanguageProps`_ |
834
+ | `shell/account-menu-text-size` | `AccountMenuTextSize`, _`AccountMenuTextSizeProps`_ |
825
835
  | `shell/tour-menu` | `TourMenu`, _`TourMenuProps`_, _`TourMenuTour`_ |
826
836
  | `shell/sync-status-indicator` | `syncState`, `SyncStatusIndicator`, _`SyncState`_, _`SyncStatus`_, _`SyncStatusIndicatorProps`_, _`SyncStatusScope`_ |
827
837
  | `shell/dev-switchers` | `DEV_ORIGINAL_ROLE_KEY`, `DevSwitchers`, _`DevSwitchersProps`_ |
@@ -867,7 +877,7 @@ re-slicing of it, never a second API.
867
877
  | `feedback/feedback-record` | `appendRework`, `attachmentName`, `FEEDBACK_ATTACHMENT_ACCEPT`, `FEEDBACK_AUTHOR_EDITABLE_STATUSES`, `FEEDBACK_AWAITING_STATUSES`, `FEEDBACK_COMMENT_MAX`, `FEEDBACK_DECIDED_STATUSES`, `FEEDBACK_PICKABLE_CATEGORIES`, `FEEDBACK_QUEUE_STATUSES`, `FEEDBACK_REPLY_MAX`, `FEEDBACK_REWORKABLE_STATUSES`, `feedbackAttachmentLine`, `feedbackReworkCount`, `isImageAttachment`, `replaceDescription`, `reworkCount`, `splitBodyAttachments`, `splitDescription`, _`FeedbackContext`_, _`FeedbackEnvironment`_, _`FeedbackRecord`_ |
868
878
  | `feedback/feedback-labels` | `DEFAULT_FEEDBACK_CATEGORY_LABELS`, `DEFAULT_FEEDBACK_STATUS_LABELS`, `DEFAULT_FEEDBACK_TOAST_LABELS`, `useFeedbackCategoryLabels`, `useFeedbackStatusLabels`, `useFeedbackToastLabels`, _`FeedbackCategoryLabels`_, _`FeedbackStatusLabels`_, _`FeedbackToastLabels`_ |
869
879
  | `feedback/feedback-status-undo` | `FEEDBACK_UNDO_DURATION`, `useFeedbackStatusUndo`, _`FeedbackStatusChange`_, _`FeedbackStatusMutate`_, _`FeedbackStatusPatch`_, _`FeedbackStatusRow`_, _`FeedbackStatusUndoOptions`_ |
870
- | `feedback/feedback-swipe` | `DEFAULT_FEEDBACK_SWIPE`, `FEEDBACK_SWIPE_ACTIONS`, `feedbackSwipePlan`, _`FeedbackSwipeAction`_, _`FeedbackSwipeBinding`_, _`FeedbackSwipeOptions`_ |
880
+ | `feedback/feedback-swipe` | `DEFAULT_FEEDBACK_SWIPE`, `FEEDBACK_SWIPE_ACTIONS`, `feedbackSwipeOmitShown`, `feedbackSwipePlan`, _`FeedbackSwipeAction`_, _`FeedbackSwipeBinding`_, _`FeedbackSwipeOptions`_ |
871
881
  | `feedback/feedback-menu` | `DEFAULT_FEEDBACK_MENU_LABELS`, `FeedbackMenu`, _`FeedbackMenuLabels`_, _`FeedbackMenuProps`_ |
872
882
  | `feedback/feedback-context` | `DEFAULT_FEEDBACK_CONTEXT_LABELS`, `feedbackContext`, `FeedbackContextBox`, _`FeedbackContextBoxProps`_, _`FeedbackContextInput`_, _`FeedbackContextLabels`_, _`FeedbackSubmitter`_ |
873
883
  | `feedback/feedback-capture` | `captureAppScreenshot`, _`CaptureAppScreenshotOptions`_ |
@@ -902,7 +912,7 @@ re-slicing of it, never a second API.
902
912
  | `tour/tour` | `DEFAULT_TOUR_LABELS`, `TourProvider`, `useTour`, `useTourOptional`, _`TourCompletion`_, _`TourLabels`_, _`TourPlacement`_, _`TourStep`_ |
903
913
  | `tour/tour-catalogue` | `DEFAULT_TOUR_CATALOGUE_LABELS`, `TourCatalogue`, `TourCatalogueCard`, _`TourCatalogueCardProps`_, _`TourCatalogueLabels`_, _`TourCatalogueProps`_ |
904
914
  | `tour/tour-info` | `doneCount`, `earnedPoints`, `isTourDone`, `isTourNew`, `isTourReset`, _`TourCompletions`_, _`TourGroup`_, _`TourInfo`_, _`TourUpdate`_ |
905
- | `tour/use-tour-progress` | `completeTour`, `resetTourProgress`, `useTourProgress`, _`TourProgress`_, _`UseTourProgressOptions`_ |
915
+ | `tour/use-tour-progress` | `completeTour`, `resetTourProgress`, `signOutTourProgress`, `useTourProgress`, _`TourProgress`_, _`TourProgressAccount`_, _`TourProgressAdopt`_, _`TourProgressBody`_, _`UseTourProgressOptions`_ |
906
916
  | `tour/use-tour-url-sync` | `useTourCatalogueUrlState`, `useTourUrlSync`, _`TourCatalogueFilter`_, _`TourStateFilter`_, _`UseTourUrlSyncOptions`_ |
907
917
 
908
918
  ### search
@@ -957,8 +967,9 @@ re-slicing of it, never a second API.
957
967
  | `admin/admin-area-labels` | `DEFAULT_ADMIN_AREA_LABELS`, `useAdminAreaLabels`, _`AdminAreaLabels`_, _`AdminSharedSection`_, _`AdminTileLabels`_ |
958
968
  | `admin/admin-sections` | `ADMIN_SECTIONS`, `adminSectionOrder`, `adminSections`, _`AdminKeptSection`_, _`AdminOwnSection`_, _`AdminSectionDefinition`_, _`AdminSectionOrderOptions`_, _`AdminSectionPlace`_, _`AdminSectionsOptions`_ |
959
969
  | `admin/admin-forbidden` | `AdminForbidden`, _`AdminForbiddenProps`_ |
960
- | `admin/admin-overview` | `AdminOverview`, `adminWaitingCount`, `AttentionTile`, `DEFAULT_ADMIN_ATTENTION_HREFS`, _`AdminAttention`_, _`AdminAttentionTile`_, _`AdminExtraTile`_, _`AdminOverviewProps`_, _`AttentionTileProps`_ |
970
+ | `admin/admin-overview` | `AdminOverview`, `adminWaitingCount`, `AttentionTile`, `DEFAULT_ADMIN_ATTENTION_HREFS`, _`AdminAttention`_, _`AdminAttentionTile`_, _`AdminExtraTile`_, _`AdminFeedbackTile`_, _`AdminFeedbackTileKey`_, _`AdminOverviewProps`_, _`AdminOwnTile`_, _`AttentionTileProps`_ |
961
971
  | `admin/admin-tool-links` | `AdminToolLinks`, _`AdminToolLink`_, _`AdminToolLinksProps`_ |
972
+ | `admin/mail-test-card` | `DEFAULT_MAIL_TEST_CARD_LABELS`, `MAIL_TEST_ANCHOR`, `MailTestCard`, `maskMailAddress`, _`MailTestAnswer`_, _`MailTestCardLabels`_, _`MailTestCardProps`_ |
962
973
  | `admin/plan-change` | `DEFAULT_PLAN_CHANGE_LABELS`, `PlanChangeConfirm`, `planColumn`, `usePlanChangeResult`, `usePlanColumn`, _`PlanChange`_, _`PlanChangeConfirmProps`_, _`PlanChangeLabels`_, _`PlanChangeLine`_, _`PlanChangeOutcome`_, _`PlanChangeResultOptions`_, _`PlanChangeTone`_, _`PlanColumnOptions`_, _`PlanColumnUsage`_ |
963
974
 
964
975
  ### billing
@@ -990,11 +1001,15 @@ re-slicing of it, never a second API.
990
1001
  | Module | Exports |
991
1002
  |---|---|
992
1003
  | `notifications/notifications-labels` | `DEFAULT_NOTIFICATIONS_LABELS`, _`NotificationRegionLabels`_, _`NotificationsLabels`_ |
993
- | `notifications/notification-types` | _`DigestCadence`_, _`NotificationSettingsPatch`_, _`NotificationSwitches`_, _`NotificationTypeEntry`_, _`NotificationTypeGroup`_, _`NotificationTypesValue`_, _`PushDeviceItem`_ |
1004
+ | `notifications/notification-types` | _`DigestCadence`_, _`NotificationChannel`_, _`NotificationSettingsPatch`_, _`NotificationSwitches`_, _`NotificationTypeEntry`_, _`NotificationTypeGroup`_, _`NotificationTypesValue`_, _`PushDeviceItem`_ |
994
1005
  | `notifications/notification-types-setting` | `NotificationTypesSetting`, _`NotificationTypesSettingProps`_ |
995
1006
  | `notifications/quiet-hours-setting` | `QuietHoursSetting`, _`QuietHoursSettingProps`_ |
996
1007
  | `notifications/time-zone-select` | `deviceTimeZone`, `timeZoneOptions`, `TimeZoneSelect`, _`TimeZoneSelectProps`_ |
1008
+ | `notifications/notification-inbox` | `INBOX_BADGE_INTERVAL`, `NotificationInbox`, _`NotificationInboxItem`_, _`NotificationInboxProps`_ |
1009
+ | `notifications/notification-bell` | `NotificationBell`, _`NotificationBellProps`_ |
1010
+ | `notifications/device-zone-report` | `useDeviceZoneReport`, _`DeviceZoneReportOptions`_ |
997
1011
  | `notifications/use-push-device` | `notifyPushSubscriptionChanged`, `PUSH_SUBSCRIPTION_CHANGED`, `PushDeviceError`, `pushSupported`, `usePushDevice`, _`PushDevice`_, _`PushDeviceErrorCode`_, _`PushPermission`_, _`PushSubscriptionInput`_, _`UsePushDeviceOptions`_ |
1012
+ | `notifications/push-worker` | `handlePushSubscriptionChange`, `pushSubscriptionInput`, `resubscribeAfterChange`, _`PushReplaceInput`_, _`PushResubscribeOutcome`_, _`PushSubscriptionChangeEventLike`_, _`PushSubscriptionChangeOptions`_, _`PushWorkerScope`_ |
998
1013
  | `notifications/push-device-setting` | `PushDeviceSetting`, _`PushDeviceAction`_, _`PushDeviceSettingProps`_ |
999
1014
  | `notifications/push-devices-setting` | `describeUserAgent`, `PushDevicesSetting`, _`PushDevicesSettingProps`_ |
1000
1015
  | `notifications/unsubscribe-panel` | `UnsubscribePanel`, `unsubscribeTokenType`, _`UnsubscribePanelProps`_ |
@@ -28,11 +28,14 @@ const DEFAULT_ADMIN_ACTION_LOG_LABELS = {
28
28
  erase: "Account erased",
29
29
  plan: "Plan changed"
30
30
  },
31
+ renamedFromTo: (from, to) => `from ${from} to ${to}`,
31
32
  groupActions: {
32
33
  group: {
33
34
  group_create: "Group created",
34
35
  group_rename: "Group renamed",
35
36
  group_delete: "Group deleted",
37
+ group_deactivate: "Group deactivated",
38
+ group_reactivate: "Group reactivated",
36
39
  membership_add: "Added to the group",
37
40
  membership_role: "Role in the group changed",
38
41
  membership_remove: "Removed from the group",
@@ -42,6 +45,8 @@ const DEFAULT_ADMIN_ACTION_LOG_LABELS = {
42
45
  group_create: "Team created",
43
46
  group_rename: "Team renamed",
44
47
  group_delete: "Team deleted",
48
+ group_deactivate: "Team deactivated",
49
+ group_reactivate: "Team reactivated",
45
50
  membership_add: "Added to the team",
46
51
  membership_role: "Role in the team changed",
47
52
  membership_remove: "Removed from the team",
@@ -51,6 +56,8 @@ const DEFAULT_ADMIN_ACTION_LOG_LABELS = {
51
56
  group_create: "Company created",
52
57
  group_rename: "Company renamed",
53
58
  group_delete: "Company deleted",
59
+ group_deactivate: "Company deactivated",
60
+ group_reactivate: "Company reactivated",
54
61
  membership_add: "Added to the company",
55
62
  membership_role: "Role in the company changed",
56
63
  membership_remove: "Removed from the company",
@@ -60,6 +67,8 @@ const DEFAULT_ADMIN_ACTION_LOG_LABELS = {
60
67
  group_create: "Budget created",
61
68
  group_rename: "Budget renamed",
62
69
  group_delete: "Budget deleted",
70
+ group_deactivate: "Budget deactivated",
71
+ group_reactivate: "Budget reactivated",
63
72
  membership_add: "Added to the budget",
64
73
  membership_role: "Role in the budget changed",
65
74
  membership_remove: "Removed from the budget",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/admin-action-log-labels.ts"],"sourcesContent":["// The English words of ./admin-action-log, in a module with no component code: the English\n// catalogue (src/i18n/defaults.ts) imports them without the component, so `en` loads as one\n// chunk like the other languages (0.34, KS U8). ./admin-action-log re-exports them.\n\nimport type { AdminActionLogLabels } from \"./admin-action-log\";\n\nexport const DEFAULT_ADMIN_ACTION_LOG_LABELS: AdminActionLogLabels = {\n title: \"Admin actions\",\n empty: \"No admin actions yet.\",\n loading: \"Loading…\",\n by: (actor) => `by ${actor}`,\n bySelf: \"by the user themselves\",\n automatic: \"automatically\",\n erased: \"an erased account\",\n filteredTo: (target) => `Only ${target}`,\n showAll: \"Show all actions\",\n filterBy: (target) => `Show only the actions on ${target}`,\n actions: {\n deactivate: \"Deactivated\",\n reactivate: \"Reactivated\",\n role: \"Role changed\",\n password_change_require: \"New password required\",\n password_change_withdraw: \"Password requirement withdrawn\",\n mail_verification: \"Verification mail sent\",\n mail_reset: \"Password reset mail sent\",\n reviewer: \"Reviewer scope changed\",\n invite: \"Invited\",\n invite_resend: \"Invitation resent\",\n invite_revoke: \"Invitation revoked\",\n transfer: \"Work transferred\",\n deletion_request: \"Deletion requested\",\n deletion_cancel: \"Deletion cancelled\",\n erase: \"Account erased\",\n plan: \"Plan changed\",\n },\n groupActions: {\n group: {\n group_create: \"Group created\",\n group_rename: \"Group renamed\",\n group_delete: \"Group deleted\",\n membership_add: \"Added to the group\",\n membership_role: \"Role in the group changed\",\n membership_remove: \"Removed from the group\",\n deleted: \"a deleted group\",\n },\n team: {\n group_create: \"Team created\",\n group_rename: \"Team renamed\",\n group_delete: \"Team deleted\",\n membership_add: \"Added to the team\",\n membership_role: \"Role in the team changed\",\n membership_remove: \"Removed from the team\",\n deleted: \"a deleted team\",\n },\n company: {\n group_create: \"Company created\",\n group_rename: \"Company renamed\",\n group_delete: \"Company deleted\",\n membership_add: \"Added to the company\",\n membership_role: \"Role in the company changed\",\n membership_remove: \"Removed from the company\",\n deleted: \"a deleted company\",\n },\n budget: {\n group_create: \"Budget created\",\n group_rename: \"Budget renamed\",\n group_delete: \"Budget deleted\",\n membership_add: \"Added to the budget\",\n membership_role: \"Role in the budget changed\",\n membership_remove: \"Removed from the budget\",\n deleted: \"a deleted budget\",\n },\n },\n};\n"],"mappings":";AAMO,MAAM,kCAAwD;AAAA,EACnE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,IAAI,CAAC,UAAU,MAAM,KAAK;AAAA,EAC1B,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,YAAY,CAAC,WAAW,QAAQ,MAAM;AAAA,EACtC,SAAS;AAAA,EACT,UAAU,CAAC,WAAW,4BAA4B,MAAM;AAAA,EACxD,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,eAAe;AAAA,IACf,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,MACL,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,MAAM;AAAA,MACJ,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,SAAS;AAAA,MACP,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,QAAQ;AAAA,MACN,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/admin/admin-action-log-labels.ts"],"sourcesContent":["// The English words of ./admin-action-log, in a module with no component code: the English\n// catalogue (src/i18n/defaults.ts) imports them without the component, so `en` loads as one\n// chunk like the other languages (0.34, KS U8). ./admin-action-log re-exports them.\n\nimport type { AdminActionLogLabels } from \"./admin-action-log\";\n\nexport const DEFAULT_ADMIN_ACTION_LOG_LABELS: AdminActionLogLabels = {\n title: \"Admin actions\",\n empty: \"No admin actions yet.\",\n loading: \"Loading…\",\n by: (actor) => `by ${actor}`,\n bySelf: \"by the user themselves\",\n automatic: \"automatically\",\n erased: \"an erased account\",\n filteredTo: (target) => `Only ${target}`,\n showAll: \"Show all actions\",\n filterBy: (target) => `Show only the actions on ${target}`,\n actions: {\n deactivate: \"Deactivated\",\n reactivate: \"Reactivated\",\n role: \"Role changed\",\n password_change_require: \"New password required\",\n password_change_withdraw: \"Password requirement withdrawn\",\n mail_verification: \"Verification mail sent\",\n mail_reset: \"Password reset mail sent\",\n reviewer: \"Reviewer scope changed\",\n invite: \"Invited\",\n invite_resend: \"Invitation resent\",\n invite_revoke: \"Invitation revoked\",\n transfer: \"Work transferred\",\n deletion_request: \"Deletion requested\",\n deletion_cancel: \"Deletion cancelled\",\n erase: \"Account erased\",\n plan: \"Plan changed\",\n },\n renamedFromTo: (from, to) => `from ${from} to ${to}`,\n groupActions: {\n group: {\n group_create: \"Group created\",\n group_rename: \"Group renamed\",\n group_delete: \"Group deleted\",\n group_deactivate: \"Group deactivated\",\n group_reactivate: \"Group reactivated\",\n membership_add: \"Added to the group\",\n membership_role: \"Role in the group changed\",\n membership_remove: \"Removed from the group\",\n deleted: \"a deleted group\",\n },\n team: {\n group_create: \"Team created\",\n group_rename: \"Team renamed\",\n group_delete: \"Team deleted\",\n group_deactivate: \"Team deactivated\",\n group_reactivate: \"Team reactivated\",\n membership_add: \"Added to the team\",\n membership_role: \"Role in the team changed\",\n membership_remove: \"Removed from the team\",\n deleted: \"a deleted team\",\n },\n company: {\n group_create: \"Company created\",\n group_rename: \"Company renamed\",\n group_delete: \"Company deleted\",\n group_deactivate: \"Company deactivated\",\n group_reactivate: \"Company reactivated\",\n membership_add: \"Added to the company\",\n membership_role: \"Role in the company changed\",\n membership_remove: \"Removed from the company\",\n deleted: \"a deleted company\",\n },\n budget: {\n group_create: \"Budget created\",\n group_rename: \"Budget renamed\",\n group_delete: \"Budget deleted\",\n group_deactivate: \"Budget deactivated\",\n group_reactivate: \"Budget reactivated\",\n membership_add: \"Added to the budget\",\n membership_role: \"Role in the budget changed\",\n membership_remove: \"Removed from the budget\",\n deleted: \"a deleted budget\",\n },\n },\n};\n"],"mappings":";AAMO,MAAM,kCAAwD;AAAA,EACnE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,IAAI,CAAC,UAAU,MAAM,KAAK;AAAA,EAC1B,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,YAAY,CAAC,WAAW,QAAQ,MAAM;AAAA,EACtC,SAAS;AAAA,EACT,UAAU,CAAC,WAAW,4BAA4B,MAAM;AAAA,EACxD,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,eAAe;AAAA,IACf,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,eAAe,CAAC,MAAM,OAAO,QAAQ,IAAI,OAAO,EAAE;AAAA,EAClD,cAAc;AAAA,IACZ,OAAO;AAAA,MACL,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,MAAM;AAAA,MACJ,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,SAAS;AAAA,MACP,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,IACA,QAAQ;AAAA,MACN,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,SAAS;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
@@ -35,7 +35,7 @@ import { DEFAULT_ADMIN_ACTION_LOG_LABELS } from "./admin-action-log-labels.js";
35
35
  * It never fetches: the page of rows and the paging are the app's.
36
36
  */
37
37
  /** The shared vocabulary of `admin_actions.action` (§4.3). */
38
- export type AdminActionKind = "deactivate" | "reactivate" | "role" | "membership_remove" | "password_change_require" | "password_change_withdraw" | "mail_verification" | "mail_reset" | "reviewer" | "invite" | "invite_resend" | "invite_revoke" | "transfer" | "deletion_request" | "deletion_cancel" | "erase" | "plan" | "group_create" | "group_rename" | "group_delete" | "membership_add" | "membership_role";
38
+ export type AdminActionKind = "deactivate" | "reactivate" | "role" | "membership_remove" | "password_change_require" | "password_change_withdraw" | "mail_verification" | "mail_reset" | "reviewer" | "invite" | "invite_resend" | "invite_revoke" | "transfer" | "deletion_request" | "deletion_cancel" | "erase" | "plan" | "group_create" | "group_rename" | "group_delete" | "membership_add" | "membership_role" | "group_deactivate" | "group_reactivate";
39
39
  /** Every {@link AdminActionKind}, in the contract's order. */
40
40
  export declare const ADMIN_ACTION_KINDS: readonly AdminActionKind[];
41
41
  /** The app's word for its groups (§11.5): Kurvenschmiede's teams, kastlan's companies,
@@ -94,6 +94,16 @@ export interface AdminActionLogEntry {
94
94
  group?: AdminGroupTarget | null;
95
95
  /** The `detail` in the app's words — "Member → Admin", "12 items to Ben Example". */
96
96
  detail?: ReactNode;
97
+ /**
98
+ * 0.45 (admin §9.4, Marcel 2026-10-19): a `group_rename` row's `detail.{from, to}` —
99
+ * kastlan's company rename logs both names. With both, the row reads "Company renamed
100
+ * from A to B" (`renamedFromTo`) in place of the target. A rename that logs `{}` (KS's
101
+ * team rename: never a name) leaves it out and reads "Team renamed" and the group.
102
+ */
103
+ renamed?: {
104
+ from?: string | null;
105
+ to?: string | null;
106
+ } | null;
97
107
  }
98
108
  /** The `adminActionLog` namespace. */
99
109
  export interface AdminActionLogLabels {
@@ -115,6 +125,8 @@ export interface AdminActionLogLabels {
115
125
  showAll: string;
116
126
  /** A target name as a filter button — "Show only the actions on Ada Example". */
117
127
  filterBy: (target: string) => string;
128
+ /** 0.45: after "Company renamed" when the row carries both names — "from A to B". */
129
+ renamedFromTo: (from: string, to: string) => string;
118
130
  /** What each action is called, past tense, keyed by the vocabulary. An app adds its
119
131
  * own here (`labels={{ actions: { plan: "Plan changed" } }}`). A word given here
120
132
  * outranks the noun's in `groupActions` — so no catalogue holds the group kinds
@@ -135,6 +147,11 @@ interface AdminGroupActionLabels {
135
147
  group_rename: string;
136
148
  /** "Team deleted". */
137
149
  group_delete: string;
150
+ /** "Company deactivated" (0.45, admin §9.1.3): the group switched off — its people
151
+ * locked out — not a person's `deactivate`. */
152
+ group_deactivate: string;
153
+ /** "Company reactivated" (0.45). */
154
+ group_reactivate: string;
138
155
  /** "Added to the team" — said of the person, so it agrees with the account where a
139
156
  * language agrees (es "Añadida", fr "Ajouté"). */
140
157
  membership_add: string;
@@ -32,12 +32,16 @@ const ADMIN_ACTION_KINDS = [
32
32
  "group_rename",
33
33
  "group_delete",
34
34
  "membership_add",
35
- "membership_role"
35
+ "membership_role",
36
+ "group_deactivate",
37
+ "group_reactivate"
36
38
  ];
37
39
  const GROUP_ACTION_KINDS = /* @__PURE__ */ new Set([
38
40
  "group_create",
39
41
  "group_rename",
40
42
  "group_delete",
43
+ "group_deactivate",
44
+ "group_reactivate",
41
45
  "membership_add",
42
46
  "membership_role",
43
47
  "membership_remove"
@@ -117,23 +121,28 @@ function AdminActionLog({
117
121
  "aria-label": labels.title,
118
122
  "aria-busy": loading || void 0,
119
123
  className: cn("divide-y divide-subtle", loading && "opacity-60"),
120
- children: entries.map((entry) => /* @__PURE__ */ jsxs("li", { "data-action": entry.action, className: "space-y-0.5 py-2 first:pt-0 last:pb-0", children: [
121
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5", children: [
122
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm", children: [
123
- /* @__PURE__ */ jsx("span", { className: "font-medium text-primary", children: actionText(entry.action) }),
124
- entry.target !== void 0 && /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: targetNode(entry) }),
125
- entry.group && /* @__PURE__ */ jsx("span", { "data-group": true, className: "min-w-0 break-words text-xs text-muted", children: groupText(entry.group) })
124
+ children: entries.map((entry) => {
125
+ const from = (entry.renamed?.from ?? "").trim();
126
+ const to = (entry.renamed?.to ?? "").trim();
127
+ const renamed = entry.action === "group_rename" && from !== "" && to !== "";
128
+ return /* @__PURE__ */ jsxs("li", { "data-action": entry.action, className: "space-y-0.5 py-2 first:pt-0 last:pb-0", children: [
129
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5", children: [
130
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm", children: [
131
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-primary", children: actionText(entry.action) }),
132
+ renamed ? /* @__PURE__ */ jsx("span", { "data-private": true, "data-renamed": true, className: "min-w-0 break-words text-primary", children: labels.renamedFromTo(from, to) }) : entry.target !== void 0 && /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: targetNode(entry) }),
133
+ entry.group && /* @__PURE__ */ jsx("span", { "data-group": true, className: "min-w-0 break-words text-xs text-muted", children: groupText(entry.group) })
134
+ ] }),
135
+ /* @__PURE__ */ jsx(DateMark, { value: entry.at, display: "relative", className: "text-xs text-muted" })
126
136
  ] }),
127
- /* @__PURE__ */ jsx(DateMark, { value: entry.at, display: "relative", className: "text-xs text-muted" })
128
- ] }),
129
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted", children: [
130
- /* @__PURE__ */ jsx("span", { "data-private": entry.actor !== null && entry.actor !== "self" ? true : void 0, children: actorText(entry) }),
131
- entry.detail !== void 0 && entry.detail !== null && entry.detail !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
132
- " \xB7 ",
133
- /* @__PURE__ */ jsx("span", { className: "text-secondary", children: entry.detail })
137
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted", children: [
138
+ /* @__PURE__ */ jsx("span", { "data-private": entry.actor !== null && entry.actor !== "self" ? true : void 0, children: actorText(entry) }),
139
+ entry.detail !== void 0 && entry.detail !== null && entry.detail !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
140
+ " \xB7 ",
141
+ /* @__PURE__ */ jsx("span", { className: "text-secondary", children: entry.detail })
142
+ ] })
134
143
  ] })
135
- ] })
136
- ] }, entry.id))
144
+ ] }, entry.id);
145
+ })
137
146
  }
138
147
  ),
139
148
  paging && paging.total > 0 && /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/admin-action-log.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport type { DateInput } from \"../lib/format\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\nimport { DateMark } from \"../components/account-chips\";\nimport { Chip } from \"../components/chip\";\nimport { Pagination } from \"../components/data-table-pagination\";\nimport { Caption } from \"../components/text\";\nimport { Button } from \"../components/button\";\nimport { Spinner } from \"../components/spinner\";\nimport { usePersonLabel } from \"./admin-parts\";\nimport type { AdminPerson } from \"./admin-parts\";\nimport { DEFAULT_ADMIN_ACTION_LOG_LABELS } from \"./admin-action-log-labels\";\n\n/**\n * The admin actions, as the audit table records them (docs/user-admin-harmonization.md\n * §2.3, §4.3): `GET /admin/actions?limit=&offset=&target=`, newest first, on the admin\n * page and filtered to one account from its row.\n *\n * Every app writes the same small `admin_actions` row in the transaction of the action\n * (keksdose and Kurvenschmiede had log lines; kastlan has an HTTP log with no named\n * action), so the vocabulary is shared — {@link AdminActionKind} — and an app's own\n * actions join it through `labels.actions`.\n *\n * What a row says, and what it never does: who, what, to whom, when, and a summary of the\n * `detail` the APP writes (ids, roles, counts — never content, §4.3). Three kinds of\n * actor: an admin; **the user themselves** (`deletion_request`: the actor is the target);\n * and **nobody** (the scheduled erasure, the hand-over at erasure, §2.7), said as\n * \"automatically\". After an erasure the target's name and address are scrubbed and the\n * row stays: it reads \"an erased account\".\n *\n * GROUPS (round 0.34, §11): Kurvenschmiede's teams, kastlan's companies and keksdose's\n * budgets are groups of accounts, and the vocabulary has words for what is done to one\n * (`group_create`, `group_rename`, `group_delete`) and to who is in it (`membership_add`,\n * `membership_role`, `membership_remove`). A row about the group itself has the group as\n * its `target`; a row about a person in it has the person as its target and the group as\n * its `group`, drawn after the person by its name alone, muted — no preposition, so no\n * language has to inflect the name. The kinds and codes say \"group\"; the labels say the\n * app's own noun, picked with `groupNoun` — whole phrases per noun, since a noun dropped\n * into one phrase breaks the participle's agreement in fr and it and the suffix in hu\n * (decision 4). A group with no name left reads as the noun's \"a deleted team\"; only a\n * missing PERSON is \"an erased account\".\n *\n * It never fetches: the page of rows and the paging are the app's.\n */\n\n/** The shared vocabulary of `admin_actions.action` (§4.3). */\nexport type AdminActionKind =\n | \"deactivate\"\n | \"reactivate\"\n | \"role\"\n | \"membership_remove\"\n | \"password_change_require\"\n | \"password_change_withdraw\"\n | \"mail_verification\"\n | \"mail_reset\"\n | \"reviewer\"\n | \"invite\"\n | \"invite_resend\"\n | \"invite_revoke\"\n | \"transfer\"\n | \"deletion_request\"\n | \"deletion_cancel\"\n | \"erase\"\n // 0.32.1 (keksdose's 0.32 report): server-kit 0.6's `AdminAction.PLAN`, an operator's\n // plan change or grant (docs/billing-harmonization.md §6).\n | \"plan\"\n // 0.34 (§11.3): what is done to a group of accounts, and to who is in it — beside\n // `membership_remove`. server-kit 0.8's `AdminAction` has the same five.\n | \"group_create\"\n | \"group_rename\"\n | \"group_delete\"\n | \"membership_add\"\n | \"membership_role\";\n\n/** Every {@link AdminActionKind}, in the contract's order. */\nexport const ADMIN_ACTION_KINDS: readonly AdminActionKind[] = [\n \"deactivate\",\n \"reactivate\",\n \"role\",\n \"membership_remove\",\n \"password_change_require\",\n \"password_change_withdraw\",\n \"mail_verification\",\n \"mail_reset\",\n \"reviewer\",\n \"invite\",\n \"invite_resend\",\n \"invite_revoke\",\n \"transfer\",\n \"deletion_request\",\n \"deletion_cancel\",\n \"erase\",\n \"plan\",\n \"group_create\",\n \"group_rename\",\n \"group_delete\",\n \"membership_add\",\n \"membership_role\",\n];\n\n/** The app's word for its groups (§11.5): Kurvenschmiede's teams, kastlan's companies,\n * keksdose's budgets; `group` is the neutral word. Picks the set of phrases the group\n * actions are said in. */\nexport type AdminGroupNoun = \"group\" | \"team\" | \"company\" | \"budget\";\n\n/** The kinds said in the noun's words: the five of 0.34, and `membership_remove`. */\ntype AdminGroupActionKind =\n | \"group_create\"\n | \"group_rename\"\n | \"group_delete\"\n | \"membership_add\"\n | \"membership_role\"\n | \"membership_remove\";\n\nconst GROUP_ACTION_KINDS: ReadonlySet<string> = new Set<AdminGroupActionKind>([\n \"group_create\",\n \"group_rename\",\n \"group_delete\",\n \"membership_add\",\n \"membership_role\",\n \"membership_remove\",\n]);\n\n/**\n * A group as a log target (§11.4): a row about the group itself (`group_*`, kastlan's\n * `plan`), or the `group` a person was added to, moved in or taken out of. The app joins\n * its `name` when the log is read — from the live group, else from its `group_delete`\n * row's stamp (`target_group_name`, decision 6) — as it joins a person's. With no name\n * left it reads as the noun's \"a deleted team\".\n */\nexport interface AdminGroupTarget {\n kind: \"group\";\n id?: string | number | null;\n name?: string | null;\n}\n\n/** A target that is neither a person nor a group (§11.4): an app's own action on a demo\n * dataset or a setting, said by its `label`. */\nexport interface AdminOtherTarget {\n kind: \"other\";\n /** What it was done to, in the app's words, as it is drawn. */\n label: string;\n /** The app's id for it, when it has one. Beyond §11.4's shape so that code reading\n * `entry.target.id` on any target — keksdose's filter, the showcase's — still\n * type-checks against the wider union. */\n id?: string | number | null;\n}\n\n/** Whether a target is a group. A person has no `kind`; an object whose `kind` is\n * anything else is read as a person too. */\nexport function isGroupTarget(target: unknown): target is AdminGroupTarget {\n return typeof target === \"object\" && target !== null && (target as { kind?: unknown }).kind === \"group\";\n}\n\nfunction isOtherTarget(target: unknown): target is AdminOtherTarget {\n return typeof target === \"object\" && target !== null && (target as { kind?: unknown }).kind === \"other\";\n}\n\n/** One logged action — the app's `admin_actions` row with the people joined in. */\nexport interface AdminActionLogEntry {\n id: string | number;\n /** When, as the row's `at`. */\n at: DateInput;\n /**\n * Who did it: a person; `\"self\"` — the user themselves (a deletion request); or `null`\n * — nobody, the scheduled job. A person whose `id` (or address) is the target's is\n * read as `\"self\"` too.\n */\n actor: AdminPerson | \"self\" | null;\n /** A {@link AdminActionKind}, or one of the app's own (named in `labels.actions`). */\n action: AdminActionKind | (string & {});\n /**\n * What it was done to (§11.4): a person (no `kind`) — `null`, or a person with nothing\n * left in it, is \"an erased account\", and an invitation's person is its address\n * (`target_email`); a group (`kind: \"group\"`), for a row about the group itself; or\n * something else (`kind: \"other\"`), said by its label. Left out: nothing is drawn\n * (until 0.34 it read as erased).\n */\n target?: AdminPerson | AdminGroupTarget | AdminOtherTarget | null;\n /** The group a `membership_*` or invitation row happened in, drawn after the person\n * by its name (§11.4). */\n group?: AdminGroupTarget | null;\n /** The `detail` in the app's words — \"Member → Admin\", \"12 items to Ben Example\". */\n detail?: ReactNode;\n}\n\n/** The `adminActionLog` namespace. */\nexport interface AdminActionLogLabels {\n /** The list's accessible name. */\n title: string;\n empty: string;\n loading: string;\n /** \"by Ada Example\". */\n by: (actor: string) => string;\n /** The user acted on their own account. */\n bySelf: string;\n /** No actor: a scheduled job. */\n automatic: string;\n /** A target scrubbed by an erasure. */\n erased: string;\n /** The filter chip: only one account's actions are shown. */\n filteredTo: (target: string) => string;\n /** The chip's ×. */\n showAll: string;\n /** A target name as a filter button — \"Show only the actions on Ada Example\". */\n filterBy: (target: string) => string;\n /** What each action is called, past tense, keyed by the vocabulary. An app adds its\n * own here (`labels={{ actions: { plan: \"Plan changed\" } }}`). A word given here\n * outranks the noun's in `groupActions` — so no catalogue holds the group kinds\n * here, `membership_remove` included (0.34). */\n actions: { [K in AdminActionKind]?: string } & { [action: string]: string | undefined };\n /** The group actions in each noun's words (§11.5), picked with `groupNoun`. */\n groupActions: Record<AdminGroupNoun, AdminGroupActionLabels>;\n}\n\n/** One noun's phrases (§11.5): whole phrases, never a noun dropped into one. */\ninterface AdminGroupActionLabels {\n /** \"Team created\". */\n group_create: string;\n /** \"Team renamed\". */\n group_rename: string;\n /** \"Team deleted\". */\n group_delete: string;\n /** \"Added to the team\" — said of the person, so it agrees with the account where a\n * language agrees (es \"Añadida\", fr \"Ajouté\"). */\n membership_add: string;\n /** \"Role in the team changed\" — the role in the group, not the account's (`role`). */\n membership_role: string;\n /** \"Removed from the team\". The `company` set's is the string `actions` held until\n * 0.34, in every language. */\n membership_remove: string;\n /** A group target with no name left: \"a deleted team\". */\n deleted: string;\n}\n\n// Defined in ./admin-action-log-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_ADMIN_ACTION_LOG_LABELS };\n\n/** The paging of `GET /admin/actions` — DataTable's `ServerPagination`, which the\n * footer is. */\nexport interface AdminActionLogPaging {\n /** 0-based. */\n page: number;\n pageSize: number;\n total: number;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n}\n\nexport interface AdminActionLogProps {\n /** One page, newest first, as the server sends it. */\n entries: readonly AdminActionLogEntry[];\n /** Left out: no footer (a short list on a row). */\n paging?: AdminActionLogPaging;\n /** The page is being fetched. */\n loading?: boolean;\n /** The filter in force: only this account's actions are shown (`?target=`). With\n * `onTargetFilter`, a chip says so and its × clears it. */\n target?: AdminPerson | null;\n /** Filter by an account — or clear it with `null`. Given, every person target's name\n * is a button that narrows the list to it; a group or other target never is (0.34:\n * `?target=` stays a person's). Left out, the list is not filterable here. */\n onTargetFilter?: (target: AdminPerson | null) => void;\n /** The app's word for its groups (§11.5): picks the phrases the group actions and \"a\n * deleted …\" are said in. Default `group`; `labels.actions[kind]` still wins. */\n groupNoun?: AdminGroupNoun;\n className?: string;\n /** Any part, at any depth: `{ groupActions: { team: { group_create: \"…\" } } }`. */\n labels?: LabelOverride<AdminActionLogLabels>;\n}\n\n/** Whether a person has nothing left that names them — an erased target. */\nfunction scrubbed(person: AdminPerson | null | undefined): boolean {\n return !person || ![person.first, person.last, person.name, person.email].some((part) => (part ?? \"\").trim());\n}\n\n/** Whether two people are the same account. A group or other target is never a person\n * (§11.4), so it never reads \"by the user themselves\". */\nfunction samePerson(a: AdminPerson, b: AdminActionLogEntry[\"target\"]): boolean {\n if (!b || isGroupTarget(b) || isOtherTarget(b)) return false;\n if (a.id !== undefined && a.id !== null && b.id !== undefined && b.id !== null) return a.id === b.id;\n const ea = (a.email ?? \"\").trim().toLowerCase();\n return ea !== \"\" && ea === (b.email ?? \"\").trim().toLowerCase();\n}\n\nexport function AdminActionLog({\n entries,\n paging,\n loading = false,\n target,\n onTargetFilter,\n groupNoun = \"group\",\n className,\n labels: labelsProp,\n}: AdminActionLogProps) {\n // Merged key by key at every depth, so a deep override is what `useKitLabels` takes.\n const labels = useKitLabels(\"adminActionLog\", DEFAULT_ADMIN_ACTION_LOG_LABELS, labelsProp);\n const nameOf = usePersonLabel();\n const filtered = target !== undefined && target !== null && !scrubbed(target);\n // A noun the type doesn't know (an app in plain JS) says the neutral word.\n const groupWords = labels.groupActions[groupNoun] ?? labels.groupActions.group;\n\n /** An app's own word first, then the noun's phrase for a group kind, else the key. */\n const actionText = (action: string): string =>\n labels.actions[action] ??\n (GROUP_ACTION_KINDS.has(action) ? groupWords[action as AdminGroupActionKind] : undefined) ??\n action;\n\n /** A group by its name, or — muted, as an erased person is — the noun's \"a deleted\n * team\" when none is left. */\n const groupText = (group: AdminGroupTarget, className?: string): ReactNode => {\n const name = (group.name ?? \"\").trim();\n return name ? (\n <span data-private className={className}>\n {name}\n </span>\n ) : (\n <span className=\"italic text-muted\">{groupWords.deleted}</span>\n );\n };\n\n const actorText = (entry: AdminActionLogEntry): string => {\n const { actor } = entry;\n if (actor === null) return labels.automatic;\n if (actor === \"self\" || samePerson(actor, entry.target)) return labels.bySelf;\n return labels.by(nameOf(actor) || labels.erased);\n };\n\n const targetNode = (entry: AdminActionLogEntry): ReactNode => {\n const subject = entry.target;\n // Left out: nothing (§11.4). `null` is a person the erasure took.\n if (subject === undefined) return null;\n if (isGroupTarget(subject)) return groupText(subject, \"font-medium text-primary\");\n if (isOtherTarget(subject)) return <span className=\"font-medium text-primary\">{subject.label}</span>;\n if (scrubbed(subject)) return <span className=\"italic text-muted\">{labels.erased}</span>;\n const person = subject!;\n const name = nameOf(person);\n const address = (person.email ?? \"\").trim();\n const text = (\n <>\n <span data-private className=\"font-medium text-primary\">\n {name}\n </span>\n {address && address !== name && (\n <span data-private className=\"ms-1 text-xs text-muted\">\n {address}\n </span>\n )}\n </>\n );\n if (!onTargetFilter || (filtered && samePerson(person, target))) return text;\n return (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto min-h-0 p-0 text-start\"\n aria-label={labels.filterBy(name)}\n onClick={() => onTargetFilter(person)}\n >\n {text}\n </Button>\n );\n };\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {filtered && onTargetFilter && (\n <Chip size=\"sm\" tone=\"brand\" onRemove={() => onTargetFilter(null)} removeLabel={labels.showAll}>\n <span data-private>{labels.filteredTo(nameOf(target))}</span>\n </Chip>\n )}\n {loading && entries.length === 0 ? (\n <div className=\"flex justify-center py-4\">\n <Spinner label={labels.loading} />\n </div>\n ) : entries.length === 0 ? (\n <Caption>{labels.empty}</Caption>\n ) : (\n <ol\n aria-label={labels.title}\n aria-busy={loading || undefined}\n className={cn(\"divide-y divide-subtle\", loading && \"opacity-60\")}\n >\n {entries.map((entry) => (\n <li key={entry.id} data-action={entry.action} className=\"space-y-0.5 py-2 first:pt-0 last:pb-0\">\n <div className=\"flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5\">\n <div className=\"flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm\">\n <span className=\"font-medium text-primary\">{actionText(entry.action)}</span>\n {entry.target !== undefined && <span className=\"min-w-0 break-words\">{targetNode(entry)}</span>}\n {entry.group && (\n <span data-group className=\"min-w-0 break-words text-xs text-muted\">\n {groupText(entry.group)}\n </span>\n )}\n </div>\n <DateMark value={entry.at} display=\"relative\" className=\"text-xs text-muted\" />\n </div>\n <div className=\"text-xs text-muted\">\n <span data-private={entry.actor !== null && entry.actor !== \"self\" ? true : undefined}>\n {actorText(entry)}\n </span>\n {entry.detail !== undefined && entry.detail !== null && entry.detail !== \"\" && (\n <>\n {\" · \"}\n <span className=\"text-secondary\">{entry.detail}</span>\n </>\n )}\n </div>\n </li>\n ))}\n </ol>\n )}\n {paging && paging.total > 0 && (\n <Pagination\n page={paging.page}\n pageSize={paging.pageSize}\n total={paging.total}\n totalPages={Math.max(1, Math.ceil(paging.total / Math.max(1, paging.pageSize)))}\n onPage={paging.onPageChange}\n onPageSize={paging.onPageSizeChange}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["className"]}
1
+ {"version":3,"sources":["../../src/admin/admin-action-log.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport type { DateInput } from \"../lib/format\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\nimport { DateMark } from \"../components/account-chips\";\nimport { Chip } from \"../components/chip\";\nimport { Pagination } from \"../components/data-table-pagination\";\nimport { Caption } from \"../components/text\";\nimport { Button } from \"../components/button\";\nimport { Spinner } from \"../components/spinner\";\nimport { usePersonLabel } from \"./admin-parts\";\nimport type { AdminPerson } from \"./admin-parts\";\nimport { DEFAULT_ADMIN_ACTION_LOG_LABELS } from \"./admin-action-log-labels\";\n\n/**\n * The admin actions, as the audit table records them (docs/user-admin-harmonization.md\n * §2.3, §4.3): `GET /admin/actions?limit=&offset=&target=`, newest first, on the admin\n * page and filtered to one account from its row.\n *\n * Every app writes the same small `admin_actions` row in the transaction of the action\n * (keksdose and Kurvenschmiede had log lines; kastlan has an HTTP log with no named\n * action), so the vocabulary is shared — {@link AdminActionKind} — and an app's own\n * actions join it through `labels.actions`.\n *\n * What a row says, and what it never does: who, what, to whom, when, and a summary of the\n * `detail` the APP writes (ids, roles, counts — never content, §4.3). Three kinds of\n * actor: an admin; **the user themselves** (`deletion_request`: the actor is the target);\n * and **nobody** (the scheduled erasure, the hand-over at erasure, §2.7), said as\n * \"automatically\". After an erasure the target's name and address are scrubbed and the\n * row stays: it reads \"an erased account\".\n *\n * GROUPS (round 0.34, §11): Kurvenschmiede's teams, kastlan's companies and keksdose's\n * budgets are groups of accounts, and the vocabulary has words for what is done to one\n * (`group_create`, `group_rename`, `group_delete`) and to who is in it (`membership_add`,\n * `membership_role`, `membership_remove`). A row about the group itself has the group as\n * its `target`; a row about a person in it has the person as its target and the group as\n * its `group`, drawn after the person by its name alone, muted — no preposition, so no\n * language has to inflect the name. The kinds and codes say \"group\"; the labels say the\n * app's own noun, picked with `groupNoun` — whole phrases per noun, since a noun dropped\n * into one phrase breaks the participle's agreement in fr and it and the suffix in hu\n * (decision 4). A group with no name left reads as the noun's \"a deleted team\"; only a\n * missing PERSON is \"an erased account\".\n *\n * It never fetches: the page of rows and the paging are the app's.\n */\n\n/** The shared vocabulary of `admin_actions.action` (§4.3). */\nexport type AdminActionKind =\n | \"deactivate\"\n | \"reactivate\"\n | \"role\"\n | \"membership_remove\"\n | \"password_change_require\"\n | \"password_change_withdraw\"\n | \"mail_verification\"\n | \"mail_reset\"\n | \"reviewer\"\n | \"invite\"\n | \"invite_resend\"\n | \"invite_revoke\"\n | \"transfer\"\n | \"deletion_request\"\n | \"deletion_cancel\"\n | \"erase\"\n // 0.32.1 (keksdose's 0.32 report): server-kit 0.6's `AdminAction.PLAN`, an operator's\n // plan change or grant (docs/billing-harmonization.md §6).\n | \"plan\"\n // 0.34 (§11.3): what is done to a group of accounts, and to who is in it — beside\n // `membership_remove`. server-kit 0.8's `AdminAction` has the same five.\n | \"group_create\"\n | \"group_rename\"\n | \"group_delete\"\n | \"membership_add\"\n | \"membership_role\"\n // 0.45 (admin §9.1.3): a group switched off or on again — kastlan's company PATCH\n // (`is_active`). server-kit 0.15's `AdminAction.GROUP_DEACTIVATE` / `GROUP_REACTIVATE`.\n | \"group_deactivate\"\n | \"group_reactivate\";\n\n/** Every {@link AdminActionKind}, in the contract's order. */\nexport const ADMIN_ACTION_KINDS: readonly AdminActionKind[] = [\n \"deactivate\",\n \"reactivate\",\n \"role\",\n \"membership_remove\",\n \"password_change_require\",\n \"password_change_withdraw\",\n \"mail_verification\",\n \"mail_reset\",\n \"reviewer\",\n \"invite\",\n \"invite_resend\",\n \"invite_revoke\",\n \"transfer\",\n \"deletion_request\",\n \"deletion_cancel\",\n \"erase\",\n \"plan\",\n \"group_create\",\n \"group_rename\",\n \"group_delete\",\n \"membership_add\",\n \"membership_role\",\n \"group_deactivate\",\n \"group_reactivate\",\n];\n\n/** The app's word for its groups (§11.5): Kurvenschmiede's teams, kastlan's companies,\n * keksdose's budgets; `group` is the neutral word. Picks the set of phrases the group\n * actions are said in. */\nexport type AdminGroupNoun = \"group\" | \"team\" | \"company\" | \"budget\";\n\n/** The kinds said in the noun's words: the five of 0.34, `membership_remove`, and 0.45's\n * `group_deactivate` / `group_reactivate`. */\ntype AdminGroupActionKind =\n | \"group_create\"\n | \"group_rename\"\n | \"group_delete\"\n | \"group_deactivate\"\n | \"group_reactivate\"\n | \"membership_add\"\n | \"membership_role\"\n | \"membership_remove\";\n\nconst GROUP_ACTION_KINDS: ReadonlySet<string> = new Set<AdminGroupActionKind>([\n \"group_create\",\n \"group_rename\",\n \"group_delete\",\n \"group_deactivate\",\n \"group_reactivate\",\n \"membership_add\",\n \"membership_role\",\n \"membership_remove\",\n]);\n\n/**\n * A group as a log target (§11.4): a row about the group itself (`group_*`, kastlan's\n * `plan`), or the `group` a person was added to, moved in or taken out of. The app joins\n * its `name` when the log is read — from the live group, else from its `group_delete`\n * row's stamp (`target_group_name`, decision 6) — as it joins a person's. With no name\n * left it reads as the noun's \"a deleted team\".\n */\nexport interface AdminGroupTarget {\n kind: \"group\";\n id?: string | number | null;\n name?: string | null;\n}\n\n/** A target that is neither a person nor a group (§11.4): an app's own action on a demo\n * dataset or a setting, said by its `label`. */\nexport interface AdminOtherTarget {\n kind: \"other\";\n /** What it was done to, in the app's words, as it is drawn. */\n label: string;\n /** The app's id for it, when it has one. Beyond §11.4's shape so that code reading\n * `entry.target.id` on any target — keksdose's filter, the showcase's — still\n * type-checks against the wider union. */\n id?: string | number | null;\n}\n\n/** Whether a target is a group. A person has no `kind`; an object whose `kind` is\n * anything else is read as a person too. */\nexport function isGroupTarget(target: unknown): target is AdminGroupTarget {\n return typeof target === \"object\" && target !== null && (target as { kind?: unknown }).kind === \"group\";\n}\n\nfunction isOtherTarget(target: unknown): target is AdminOtherTarget {\n return typeof target === \"object\" && target !== null && (target as { kind?: unknown }).kind === \"other\";\n}\n\n/** One logged action — the app's `admin_actions` row with the people joined in. */\nexport interface AdminActionLogEntry {\n id: string | number;\n /** When, as the row's `at`. */\n at: DateInput;\n /**\n * Who did it: a person; `\"self\"` — the user themselves (a deletion request); or `null`\n * — nobody, the scheduled job. A person whose `id` (or address) is the target's is\n * read as `\"self\"` too.\n */\n actor: AdminPerson | \"self\" | null;\n /** A {@link AdminActionKind}, or one of the app's own (named in `labels.actions`). */\n action: AdminActionKind | (string & {});\n /**\n * What it was done to (§11.4): a person (no `kind`) — `null`, or a person with nothing\n * left in it, is \"an erased account\", and an invitation's person is its address\n * (`target_email`); a group (`kind: \"group\"`), for a row about the group itself; or\n * something else (`kind: \"other\"`), said by its label. Left out: nothing is drawn\n * (until 0.34 it read as erased).\n */\n target?: AdminPerson | AdminGroupTarget | AdminOtherTarget | null;\n /** The group a `membership_*` or invitation row happened in, drawn after the person\n * by its name (§11.4). */\n group?: AdminGroupTarget | null;\n /** The `detail` in the app's words — \"Member → Admin\", \"12 items to Ben Example\". */\n detail?: ReactNode;\n /**\n * 0.45 (admin §9.4, Marcel 2026-10-19): a `group_rename` row's `detail.{from, to}` —\n * kastlan's company rename logs both names. With both, the row reads \"Company renamed\n * from A to B\" (`renamedFromTo`) in place of the target. A rename that logs `{}` (KS's\n * team rename: never a name) leaves it out and reads \"Team renamed\" and the group.\n */\n renamed?: { from?: string | null; to?: string | null } | null;\n}\n\n/** The `adminActionLog` namespace. */\nexport interface AdminActionLogLabels {\n /** The list's accessible name. */\n title: string;\n empty: string;\n loading: string;\n /** \"by Ada Example\". */\n by: (actor: string) => string;\n /** The user acted on their own account. */\n bySelf: string;\n /** No actor: a scheduled job. */\n automatic: string;\n /** A target scrubbed by an erasure. */\n erased: string;\n /** The filter chip: only one account's actions are shown. */\n filteredTo: (target: string) => string;\n /** The chip's ×. */\n showAll: string;\n /** A target name as a filter button — \"Show only the actions on Ada Example\". */\n filterBy: (target: string) => string;\n /** 0.45: after \"Company renamed\" when the row carries both names — \"from A to B\". */\n renamedFromTo: (from: string, to: string) => string;\n /** What each action is called, past tense, keyed by the vocabulary. An app adds its\n * own here (`labels={{ actions: { plan: \"Plan changed\" } }}`). A word given here\n * outranks the noun's in `groupActions` — so no catalogue holds the group kinds\n * here, `membership_remove` included (0.34). */\n actions: { [K in AdminActionKind]?: string } & { [action: string]: string | undefined };\n /** The group actions in each noun's words (§11.5), picked with `groupNoun`. */\n groupActions: Record<AdminGroupNoun, AdminGroupActionLabels>;\n}\n\n/** One noun's phrases (§11.5): whole phrases, never a noun dropped into one. */\ninterface AdminGroupActionLabels {\n /** \"Team created\". */\n group_create: string;\n /** \"Team renamed\". */\n group_rename: string;\n /** \"Team deleted\". */\n group_delete: string;\n /** \"Company deactivated\" (0.45, admin §9.1.3): the group switched off — its people\n * locked out — not a person's `deactivate`. */\n group_deactivate: string;\n /** \"Company reactivated\" (0.45). */\n group_reactivate: string;\n /** \"Added to the team\" — said of the person, so it agrees with the account where a\n * language agrees (es \"Añadida\", fr \"Ajouté\"). */\n membership_add: string;\n /** \"Role in the team changed\" — the role in the group, not the account's (`role`). */\n membership_role: string;\n /** \"Removed from the team\". The `company` set's is the string `actions` held until\n * 0.34, in every language. */\n membership_remove: string;\n /** A group target with no name left: \"a deleted team\". */\n deleted: string;\n}\n\n// Defined in ./admin-action-log-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_ADMIN_ACTION_LOG_LABELS };\n\n/** The paging of `GET /admin/actions` — DataTable's `ServerPagination`, which the\n * footer is. */\nexport interface AdminActionLogPaging {\n /** 0-based. */\n page: number;\n pageSize: number;\n total: number;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n}\n\nexport interface AdminActionLogProps {\n /** One page, newest first, as the server sends it. */\n entries: readonly AdminActionLogEntry[];\n /** Left out: no footer (a short list on a row). */\n paging?: AdminActionLogPaging;\n /** The page is being fetched. */\n loading?: boolean;\n /** The filter in force: only this account's actions are shown (`?target=`). With\n * `onTargetFilter`, a chip says so and its × clears it. */\n target?: AdminPerson | null;\n /** Filter by an account — or clear it with `null`. Given, every person target's name\n * is a button that narrows the list to it; a group or other target never is (0.34:\n * `?target=` stays a person's). Left out, the list is not filterable here. */\n onTargetFilter?: (target: AdminPerson | null) => void;\n /** The app's word for its groups (§11.5): picks the phrases the group actions and \"a\n * deleted …\" are said in. Default `group`; `labels.actions[kind]` still wins. */\n groupNoun?: AdminGroupNoun;\n className?: string;\n /** Any part, at any depth: `{ groupActions: { team: { group_create: \"…\" } } }`. */\n labels?: LabelOverride<AdminActionLogLabels>;\n}\n\n/** Whether a person has nothing left that names them — an erased target. */\nfunction scrubbed(person: AdminPerson | null | undefined): boolean {\n return !person || ![person.first, person.last, person.name, person.email].some((part) => (part ?? \"\").trim());\n}\n\n/** Whether two people are the same account. A group or other target is never a person\n * (§11.4), so it never reads \"by the user themselves\". */\nfunction samePerson(a: AdminPerson, b: AdminActionLogEntry[\"target\"]): boolean {\n if (!b || isGroupTarget(b) || isOtherTarget(b)) return false;\n if (a.id !== undefined && a.id !== null && b.id !== undefined && b.id !== null) return a.id === b.id;\n const ea = (a.email ?? \"\").trim().toLowerCase();\n return ea !== \"\" && ea === (b.email ?? \"\").trim().toLowerCase();\n}\n\nexport function AdminActionLog({\n entries,\n paging,\n loading = false,\n target,\n onTargetFilter,\n groupNoun = \"group\",\n className,\n labels: labelsProp,\n}: AdminActionLogProps) {\n // Merged key by key at every depth, so a deep override is what `useKitLabels` takes.\n const labels = useKitLabels(\"adminActionLog\", DEFAULT_ADMIN_ACTION_LOG_LABELS, labelsProp);\n const nameOf = usePersonLabel();\n const filtered = target !== undefined && target !== null && !scrubbed(target);\n // A noun the type doesn't know (an app in plain JS) says the neutral word.\n const groupWords = labels.groupActions[groupNoun] ?? labels.groupActions.group;\n\n /** An app's own word first, then the noun's phrase for a group kind, else the key. */\n const actionText = (action: string): string =>\n labels.actions[action] ??\n (GROUP_ACTION_KINDS.has(action) ? groupWords[action as AdminGroupActionKind] : undefined) ??\n action;\n\n /** A group by its name, or — muted, as an erased person is — the noun's \"a deleted\n * team\" when none is left. */\n const groupText = (group: AdminGroupTarget, className?: string): ReactNode => {\n const name = (group.name ?? \"\").trim();\n return name ? (\n <span data-private className={className}>\n {name}\n </span>\n ) : (\n <span className=\"italic text-muted\">{groupWords.deleted}</span>\n );\n };\n\n const actorText = (entry: AdminActionLogEntry): string => {\n const { actor } = entry;\n if (actor === null) return labels.automatic;\n if (actor === \"self\" || samePerson(actor, entry.target)) return labels.bySelf;\n return labels.by(nameOf(actor) || labels.erased);\n };\n\n const targetNode = (entry: AdminActionLogEntry): ReactNode => {\n const subject = entry.target;\n // Left out: nothing (§11.4). `null` is a person the erasure took.\n if (subject === undefined) return null;\n if (isGroupTarget(subject)) return groupText(subject, \"font-medium text-primary\");\n if (isOtherTarget(subject)) return <span className=\"font-medium text-primary\">{subject.label}</span>;\n if (scrubbed(subject)) return <span className=\"italic text-muted\">{labels.erased}</span>;\n const person = subject!;\n const name = nameOf(person);\n const address = (person.email ?? \"\").trim();\n const text = (\n <>\n <span data-private className=\"font-medium text-primary\">\n {name}\n </span>\n {address && address !== name && (\n <span data-private className=\"ms-1 text-xs text-muted\">\n {address}\n </span>\n )}\n </>\n );\n if (!onTargetFilter || (filtered && samePerson(person, target))) return text;\n return (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto min-h-0 p-0 text-start\"\n aria-label={labels.filterBy(name)}\n onClick={() => onTargetFilter(person)}\n >\n {text}\n </Button>\n );\n };\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {filtered && onTargetFilter && (\n <Chip size=\"sm\" tone=\"brand\" onRemove={() => onTargetFilter(null)} removeLabel={labels.showAll}>\n <span data-private>{labels.filteredTo(nameOf(target))}</span>\n </Chip>\n )}\n {loading && entries.length === 0 ? (\n <div className=\"flex justify-center py-4\">\n <Spinner label={labels.loading} />\n </div>\n ) : entries.length === 0 ? (\n <Caption>{labels.empty}</Caption>\n ) : (\n <ol\n aria-label={labels.title}\n aria-busy={loading || undefined}\n className={cn(\"divide-y divide-subtle\", loading && \"opacity-60\")}\n >\n {entries.map((entry) => {\n const from = (entry.renamed?.from ?? \"\").trim();\n const to = (entry.renamed?.to ?? \"\").trim();\n const renamed = entry.action === \"group_rename\" && from !== \"\" && to !== \"\";\n return (\n <li key={entry.id} data-action={entry.action} className=\"space-y-0.5 py-2 first:pt-0 last:pb-0\">\n <div className=\"flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5\">\n <div className=\"flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm\">\n <span className=\"font-medium text-primary\">{actionText(entry.action)}</span>\n {renamed ? (\n <span data-private data-renamed className=\"min-w-0 break-words text-primary\">\n {labels.renamedFromTo(from, to)}\n </span>\n ) : (\n entry.target !== undefined && <span className=\"min-w-0 break-words\">{targetNode(entry)}</span>\n )}\n {entry.group && (\n <span data-group className=\"min-w-0 break-words text-xs text-muted\">\n {groupText(entry.group)}\n </span>\n )}\n </div>\n <DateMark value={entry.at} display=\"relative\" className=\"text-xs text-muted\" />\n </div>\n <div className=\"text-xs text-muted\">\n <span data-private={entry.actor !== null && entry.actor !== \"self\" ? true : undefined}>\n {actorText(entry)}\n </span>\n {entry.detail !== undefined && entry.detail !== null && entry.detail !== \"\" && (\n <>\n {\" · \"}\n <span className=\"text-secondary\">{entry.detail}</span>\n </>\n )}\n </div>\n </li>\n );\n })}\n </ol>\n )}\n {paging && paging.total > 0 && (\n <Pagination\n page={paging.page}\n pageSize={paging.pageSize}\n total={paging.total}\n totalPages={Math.max(1, Math.ceil(paging.total / Math.max(1, paging.pageSize)))}\n onPage={paging.onPageChange}\n onPageSize={paging.onPageSizeChange}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["className"]}
@@ -21,6 +21,15 @@ export interface AdminTileLabels {
21
21
  users: string;
22
22
  /** `users_new_7d`, the line under the users tile. */
23
23
  usersNew: (count: number) => string;
24
+ /** 0.46 (Kurvenschmiede 1, admin §9.5): server-kit's `awaiting_verdict` as an `extra`
25
+ * tile — answered reports that wait on a verdict (IN_EVALUATION, NEEDS_LIVE_TEST). */
26
+ awaitingVerdict: string;
27
+ /** Its line under the count. */
28
+ awaitingVerdictHint: string;
29
+ /** 0.46: server-kit's `released_for_work` — the reports released for the work queue. */
30
+ releasedForWork: string;
31
+ /** Its line under the count. */
32
+ releasedForWorkHint: string;
24
33
  }
25
34
  export interface AdminAreaLabels {
26
35
  /** "Administration": the account menu's row, and the admin page's heading. */
@@ -28,6 +37,12 @@ export interface AdminAreaLabels {
28
37
  /** The row's waiting count, for a reader ("Administration, 3 waiting"); the eye sees
29
38
  * the number in the badge. */
30
39
  waiting: (count: number) => string;
40
+ /**
41
+ * 0.45 (admin §9.3.4): a button's whole name with its count — "Platform, 2 waiting" —
42
+ * for an `IconButton` whose `badge` is an `AdminWaitingBadge`: the badge is decorative
43
+ * there, so the name carries the count. `label` is the button's own name.
44
+ */
45
+ waitingLabel: (label: string, count: number) => string;
31
46
  /** The 403 page's heading (§2.2 decision 8). */
32
47
  forbiddenTitle: string;
33
48
  /**
@@ -3,6 +3,7 @@ import { useKitLabels } from "../i18n/kit-labels.js";
3
3
  const DEFAULT_ADMIN_AREA_LABELS = {
4
4
  title: "Administration",
5
5
  waiting: (count) => `${count} waiting`,
6
+ waitingLabel: (label, count) => `${label}, ${count} waiting`,
6
7
  forbiddenTitle: "Only for administrators",
7
8
  forbiddenBody: (appName) => `This is where administrators run ${appName}. Your account cannot open this page.`,
8
9
  forbiddenHome: "Go to the start page",
@@ -19,7 +20,11 @@ const DEFAULT_ADMIN_AREA_LABELS = {
19
20
  deletionRequests: "Deletion requests",
20
21
  invitationsPending: "Pending invitations",
21
22
  users: "Users",
22
- usersNew: (count) => `${count} new in 7 days`
23
+ usersNew: (count) => `${count} new in 7 days`,
24
+ awaitingVerdict: "Awaiting a verdict",
25
+ awaitingVerdictHint: "Answered, and waiting for you or the reporter to confirm the fix",
26
+ releasedForWork: "Released for work",
27
+ releasedForWorkHint: "Ready, rework requested or in progress"
23
28
  },
24
29
  overviewError: "The overview couldn\u2019t be loaded.",
25
30
  retry: "Try again",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin/admin-area-labels.ts"],"sourcesContent":["import { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { SettingsGroupLabels } from \"../settings/settings-labels\";\n\n/**\n * The `adminArea` namespace (0.42, docs/admin-harmonization.md §3.1): the kit's words of\n * the admin area every app shares — the account menu's \"Administration\" row, the 403\n * page, the shared sections' names and help lines, the overview's tiles and the Tools\n * list. A module of words with no component code, like every `*-labels.ts` (0.34, KS U8).\n * An app's own sections and tiles bring their own words.\n */\n\n/** The sections every app's admin area shares, in the template's order (§2.3). */\nexport type AdminSharedSection = \"overview\" | \"users\" | \"invitations\" | \"billing\" | \"activity\";\n\n/** The overview's shared tiles (§2.4, §6.1). */\nexport interface AdminTileLabels {\n /** `feedback_waiting`: OPEN reports, crash reports included (§6.1). */\n feedbackWaiting: string;\n /** `deletion_requests`. */\n deletionRequests: string;\n /** `invitations_pending`. */\n invitationsPending: string;\n /** `users_total`. */\n users: string;\n /** `users_new_7d`, the line under the users tile. */\n usersNew: (count: number) => string;\n}\n\nexport interface AdminAreaLabels {\n /** \"Administration\": the account menu's row, and the admin page's heading. */\n title: string;\n /** The row's waiting count, for a reader (\"Administration, 3 waiting\"); the eye sees\n * the number in the badge. */\n waiting: (count: number) => string;\n /** The 403 page's heading (§2.2 decision 8). */\n forbiddenTitle: string;\n /**\n * Under it. Says only that THIS area is closed to the account — never that the person\n * administers nothing anywhere (§6.7: a kastlan superuser who isn't ADMIN in the acting\n * company gets this page).\n */\n forbiddenBody: (appName: string) => string;\n /** The button back to the app's start, unless the app names its own (`homeLabel`). */\n forbiddenHome: string;\n /** A reviewer's second way out, to the translations review. */\n forbiddenReviewer: string;\n /** The shared sections' names and help lines (§2.3 decision 11). */\n sections: Record<AdminSharedSection, SettingsGroupLabels>;\n /** The overview's tiles. */\n tiles: AdminTileLabels;\n /** 0.43: `AdminOverview`'s `error` — the counts could not be loaded. */\n overviewError: string;\n /** 0.43: its button, which calls `onRetry`. */\n retry: string;\n /** The Tools list's heading and name (§2.5 decision 16). */\n tools: string;\n}\n\nexport const DEFAULT_ADMIN_AREA_LABELS: AdminAreaLabels = {\n title: \"Administration\",\n waiting: (count) => `${count} waiting`,\n forbiddenTitle: \"Only for administrators\",\n forbiddenBody: (appName) => `This is where administrators run ${appName}. Your account cannot open this page.`,\n forbiddenHome: \"Go to the start page\",\n forbiddenReviewer: \"Review translations\",\n sections: {\n overview: { title: \"Overview\", help: \"What is waiting, at a glance.\" },\n users: { title: \"Users\", help: \"Who has an account, and what each may do.\" },\n invitations: { title: \"Invitations\", help: \"Invite people by e-mail in their language; resend or revoke what’s still open.\" },\n billing: { title: \"Billing\", help: \"The plan, the payment and the invoices.\" },\n activity: { title: \"Activity\", help: \"What the admins did, newest first.\" },\n },\n tiles: {\n feedbackWaiting: \"To triage\",\n deletionRequests: \"Deletion requests\",\n invitationsPending: \"Pending invitations\",\n users: \"Users\",\n usersNew: (count) => `${count} new in 7 days`,\n },\n overviewError: \"The overview couldn’t be loaded.\",\n retry: \"Try again\",\n tools: \"Tools\",\n};\n\n/**\n * The `adminArea` namespace, resolved — English, then the provider, then `labels`. What\n * an app reads to hand {@link adminSections} its words from inside a component.\n */\nexport function useAdminAreaLabels(labels?: LabelOverride<AdminAreaLabels>): AdminAreaLabels {\n return useKitLabels(\"adminArea\", DEFAULT_ADMIN_AREA_LABELS, labels);\n}\n"],"mappings":";AAAA,SAAS,oBAAwC;AA0D1C,MAAM,4BAA6C;AAAA,EACxD,OAAO;AAAA,EACP,SAAS,CAAC,UAAU,GAAG,KAAK;AAAA,EAC5B,gBAAgB;AAAA,EAChB,eAAe,CAAC,YAAY,oCAAoC,OAAO;AAAA,EACvE,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,UAAU;AAAA,IACR,UAAU,EAAE,OAAO,YAAY,MAAM,gCAAgC;AAAA,IACrE,OAAO,EAAE,OAAO,SAAS,MAAM,4CAA4C;AAAA,IAC3E,aAAa,EAAE,OAAO,eAAe,MAAM,sFAAiF;AAAA,IAC5H,SAAS,EAAE,OAAO,WAAW,MAAM,0CAA0C;AAAA,IAC7E,UAAU,EAAE,OAAO,YAAY,MAAM,qCAAqC;AAAA,EAC5E;AAAA,EACA,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,OAAO;AAAA,IACP,UAAU,CAAC,UAAU,GAAG,KAAK;AAAA,EAC/B;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,OAAO;AACT;AAMO,SAAS,mBAAmB,QAA0D;AAC3F,SAAO,aAAa,aAAa,2BAA2B,MAAM;AACpE;","names":[]}
1
+ {"version":3,"sources":["../../src/admin/admin-area-labels.ts"],"sourcesContent":["import { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { SettingsGroupLabels } from \"../settings/settings-labels\";\n\n/**\n * The `adminArea` namespace (0.42, docs/admin-harmonization.md §3.1): the kit's words of\n * the admin area every app shares — the account menu's \"Administration\" row, the 403\n * page, the shared sections' names and help lines, the overview's tiles and the Tools\n * list. A module of words with no component code, like every `*-labels.ts` (0.34, KS U8).\n * An app's own sections and tiles bring their own words.\n */\n\n/** The sections every app's admin area shares, in the template's order (§2.3). */\nexport type AdminSharedSection = \"overview\" | \"users\" | \"invitations\" | \"billing\" | \"activity\";\n\n/** The overview's shared tiles (§2.4, §6.1). */\nexport interface AdminTileLabels {\n /** `feedback_waiting`: OPEN reports, crash reports included (§6.1). */\n feedbackWaiting: string;\n /** `deletion_requests`. */\n deletionRequests: string;\n /** `invitations_pending`. */\n invitationsPending: string;\n /** `users_total`. */\n users: string;\n /** `users_new_7d`, the line under the users tile. */\n usersNew: (count: number) => string;\n /** 0.46 (Kurvenschmiede 1, admin §9.5): server-kit's `awaiting_verdict` as an `extra`\n * tile — answered reports that wait on a verdict (IN_EVALUATION, NEEDS_LIVE_TEST). */\n awaitingVerdict: string;\n /** Its line under the count. */\n awaitingVerdictHint: string;\n /** 0.46: server-kit's `released_for_work` — the reports released for the work queue. */\n releasedForWork: string;\n /** Its line under the count. */\n releasedForWorkHint: string;\n}\n\nexport interface AdminAreaLabels {\n /** \"Administration\": the account menu's row, and the admin page's heading. */\n title: string;\n /** The row's waiting count, for a reader (\"Administration, 3 waiting\"); the eye sees\n * the number in the badge. */\n waiting: (count: number) => string;\n /**\n * 0.45 (admin §9.3.4): a button's whole name with its count — \"Platform, 2 waiting\" —\n * for an `IconButton` whose `badge` is an `AdminWaitingBadge`: the badge is decorative\n * there, so the name carries the count. `label` is the button's own name.\n */\n waitingLabel: (label: string, count: number) => string;\n /** The 403 page's heading (§2.2 decision 8). */\n forbiddenTitle: string;\n /**\n * Under it. Says only that THIS area is closed to the account — never that the person\n * administers nothing anywhere (§6.7: a kastlan superuser who isn't ADMIN in the acting\n * company gets this page).\n */\n forbiddenBody: (appName: string) => string;\n /** The button back to the app's start, unless the app names its own (`homeLabel`). */\n forbiddenHome: string;\n /** A reviewer's second way out, to the translations review. */\n forbiddenReviewer: string;\n /** The shared sections' names and help lines (§2.3 decision 11). */\n sections: Record<AdminSharedSection, SettingsGroupLabels>;\n /** The overview's tiles. */\n tiles: AdminTileLabels;\n /** 0.43: `AdminOverview`'s `error` — the counts could not be loaded. */\n overviewError: string;\n /** 0.43: its button, which calls `onRetry`. */\n retry: string;\n /** The Tools list's heading and name (§2.5 decision 16). */\n tools: string;\n}\n\nexport const DEFAULT_ADMIN_AREA_LABELS: AdminAreaLabels = {\n title: \"Administration\",\n waiting: (count) => `${count} waiting`,\n waitingLabel: (label, count) => `${label}, ${count} waiting`,\n forbiddenTitle: \"Only for administrators\",\n forbiddenBody: (appName) => `This is where administrators run ${appName}. Your account cannot open this page.`,\n forbiddenHome: \"Go to the start page\",\n forbiddenReviewer: \"Review translations\",\n sections: {\n overview: { title: \"Overview\", help: \"What is waiting, at a glance.\" },\n users: { title: \"Users\", help: \"Who has an account, and what each may do.\" },\n invitations: { title: \"Invitations\", help: \"Invite people by e-mail in their language; resend or revoke what’s still open.\" },\n billing: { title: \"Billing\", help: \"The plan, the payment and the invoices.\" },\n activity: { title: \"Activity\", help: \"What the admins did, newest first.\" },\n },\n tiles: {\n feedbackWaiting: \"To triage\",\n deletionRequests: \"Deletion requests\",\n invitationsPending: \"Pending invitations\",\n users: \"Users\",\n usersNew: (count) => `${count} new in 7 days`,\n awaitingVerdict: \"Awaiting a verdict\",\n awaitingVerdictHint: \"Answered, and waiting for you or the reporter to confirm the fix\",\n releasedForWork: \"Released for work\",\n releasedForWorkHint: \"Ready, rework requested or in progress\",\n },\n overviewError: \"The overview couldn’t be loaded.\",\n retry: \"Try again\",\n tools: \"Tools\",\n};\n\n/**\n * The `adminArea` namespace, resolved — English, then the provider, then `labels`. What\n * an app reads to hand {@link adminSections} its words from inside a component.\n */\nexport function useAdminAreaLabels(labels?: LabelOverride<AdminAreaLabels>): AdminAreaLabels {\n return useKitLabels(\"adminArea\", DEFAULT_ADMIN_AREA_LABELS, labels);\n}\n"],"mappings":";AAAA,SAAS,oBAAwC;AAyE1C,MAAM,4BAA6C;AAAA,EACxD,OAAO;AAAA,EACP,SAAS,CAAC,UAAU,GAAG,KAAK;AAAA,EAC5B,cAAc,CAAC,OAAO,UAAU,GAAG,KAAK,KAAK,KAAK;AAAA,EAClD,gBAAgB;AAAA,EAChB,eAAe,CAAC,YAAY,oCAAoC,OAAO;AAAA,EACvE,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,UAAU;AAAA,IACR,UAAU,EAAE,OAAO,YAAY,MAAM,gCAAgC;AAAA,IACrE,OAAO,EAAE,OAAO,SAAS,MAAM,4CAA4C;AAAA,IAC3E,aAAa,EAAE,OAAO,eAAe,MAAM,sFAAiF;AAAA,IAC5H,SAAS,EAAE,OAAO,WAAW,MAAM,0CAA0C;AAAA,IAC7E,UAAU,EAAE,OAAO,YAAY,MAAM,qCAAqC;AAAA,EAC5E;AAAA,EACA,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,OAAO;AAAA,IACP,UAAU,CAAC,UAAU,GAAG,KAAK;AAAA,IAC7B,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,EACvB;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,OAAO;AACT;AAMO,SAAS,mBAAmB,QAA0D;AAC3F,SAAO,aAAa,aAAa,2BAA2B,MAAM;AACpE;","names":[]}
@@ -54,8 +54,16 @@ export interface AttentionTileProps extends Omit<StatTileProps, "href" | "onClic
54
54
  * is a quiet count, no link.
55
55
  */
56
56
  export declare function AttentionTile({ count, href, ...tile }: AttentionTileProps): import("react").JSX.Element;
57
+ /**
58
+ * 0.46 (Kurvenschmiede 1, admin §9.5): the `extra` keys server-kit's
59
+ * `feedback_attention` fills, whose tiles have the kit's words
60
+ * (`adminArea.tiles.awaitingVerdict`, `releasedForWork`, with a line each).
61
+ */
62
+ export type AdminFeedbackTileKey = "awaiting_verdict" | "released_for_work";
57
63
  /** One of the app's own tiles (§2.4 decision 13: keksdose's "Support waiting"). */
58
- export interface AdminExtraTile {
64
+ export type AdminExtraTile = AdminOwnTile | AdminFeedbackTile;
65
+ /** A tile of the app's own: it brings its words. */
66
+ export interface AdminOwnTile {
59
67
  /** Unique among the tiles; usually the `extra` key it shows. */
60
68
  key: string;
61
69
  label: ReactNode;
@@ -67,6 +75,13 @@ export interface AdminExtraTile {
67
75
  /** A line under the count. */
68
76
  description?: ReactNode;
69
77
  }
78
+ /** 0.46: a tile of server-kit's feedback counts — `label` and `description` default to
79
+ * the kit's words; an app's own still win. The kit's `description` fills in only when
80
+ * `label` is left out too: an app that names the tile gets no hint line it did not ask for. */
81
+ export interface AdminFeedbackTile extends Omit<AdminOwnTile, "key" | "label"> {
82
+ key: AdminFeedbackTileKey;
83
+ label?: ReactNode;
84
+ }
70
85
  export interface AdminOverviewProps {
71
86
  /** The server's answer; `undefined` while it loads (the grid shows its skeletons). */
72
87
  attention: AdminAttention | null | undefined;
@@ -60,9 +60,20 @@ function AdminOverview({
60
60
  description: attention && typeof attention.users_new_7d === "number" ? labels.tiles.usersNew(attention.users_new_7d) : void 0
61
61
  }
62
62
  ];
63
+ const feedbackWords = {
64
+ awaiting_verdict: { label: labels.tiles.awaitingVerdict, description: labels.tiles.awaitingVerdictHint },
65
+ released_for_work: { label: labels.tiles.releasedForWork, description: labels.tiles.releasedForWorkHint }
66
+ };
63
67
  const tiles = [
64
68
  ...shared.map((t) => ({ ...t, count: attention ? attention[t.key] : void 0, href: href[t.key] })),
65
- ...extraTiles
69
+ ...extraTiles.map((t) => {
70
+ const words = Object.prototype.hasOwnProperty.call(feedbackWords, t.key) ? feedbackWords[t.key] : void 0;
71
+ return words ? {
72
+ ...t,
73
+ label: t.label ?? words.label,
74
+ description: t.description ?? (t.label === void 0 ? words.description : void 0)
75
+ } : t;
76
+ })
66
77
  ].filter((t) => loading || t.count !== null && t.count !== void 0);
67
78
  if (!loading && tiles.length === 0) return error ? errorBox(true) : null;
68
79
  const grid = /* @__PURE__ */ jsx(StatTileGrid, { "data-slot": error ? void 0 : "admin-overview", loading, className: error ? void 0 : className, children: tiles.map((t) => /* @__PURE__ */ jsx(