@micha.bigler/ui-core-micha 3.14.4 → 4.0.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 (341) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +7 -0
  3. package/dist/chat/kitSxRegistry.js +9 -0
  4. package/dist/components/charts/kitSxRegistry.js +6 -0
  5. package/dist/entries/charts.js +6 -0
  6. package/dist/entries/chat.js +8 -0
  7. package/dist/entries/markdown.js +1 -0
  8. package/dist/index.js +0 -14
  9. package/dist/pages/AccountPage.js +1 -2
  10. package/dist/pages/LoginPage.js +1 -2
  11. package/dist/pages/PasswordChangePage.js +1 -2
  12. package/dist/pages/PasswordInvitePage.js +1 -2
  13. package/dist/pages/PasswordResetRequestPage.js +1 -2
  14. package/dist/pages/SignUpPage.js +1 -2
  15. package/dist/pages/SignupConfirmPage.js +1 -2
  16. package/dist/theme/kitSxEntry.js +11 -0
  17. package/dist/theme/kitSxRegistry.js +2 -15
  18. package/package.json +32 -7
  19. package/.github/workflows/ci.yml +0 -35
  20. package/.github/workflows/publish.yml +0 -126
  21. package/DESIGN.md +0 -60
  22. package/WORK_ORDERS.md +0 -150
  23. package/dev/entries.jsx +0 -721
  24. package/dev/fixtures.js +0 -26
  25. package/dev/main.jsx +0 -75
  26. package/dev/mockTransport.jsx +0 -128
  27. package/docs/CHART-LAYOUT.md +0 -157
  28. package/docs/DARK-TOKENS.md +0 -140
  29. package/docs/TYPE-SCALE.md +0 -111
  30. package/docs/messaging-deviations.md +0 -118
  31. package/index.html +0 -12
  32. package/renovate.json +0 -10
  33. package/scripts/chart-api-census.mjs +0 -415
  34. package/src/auth/AuthContext.jsx +0 -158
  35. package/src/auth/ReauthModal.jsx +0 -82
  36. package/src/auth/UserMenu.jsx +0 -121
  37. package/src/auth/apiClient.jsx +0 -232
  38. package/src/auth/authApi.jsx +0 -539
  39. package/src/auth/authConfig.jsx +0 -34
  40. package/src/auth/reauth.js +0 -47
  41. package/src/chat/ChatComposer.jsx +0 -36
  42. package/src/chat/ChatList.jsx +0 -87
  43. package/src/chat/ChatThread.jsx +0 -128
  44. package/src/chat/ChatWorkspace.jsx +0 -244
  45. package/src/chat/MarkdownReply.jsx +0 -22
  46. package/src/chat/ModelPicker.jsx +0 -85
  47. package/src/chat/api.js +0 -18
  48. package/src/chat/useChatSocket.js +0 -32
  49. package/src/chat/wire.js +0 -7
  50. package/src/components/AccessCodeManager.jsx +0 -288
  51. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  52. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  53. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  54. package/src/components/BulkInviteCsvTab.jsx +0 -198
  55. package/src/components/EmptyState.jsx +0 -38
  56. package/src/components/LoginForm.jsx +0 -159
  57. package/src/components/MFAComponent.jsx +0 -350
  58. package/src/components/MfaLoginComponent.jsx +0 -252
  59. package/src/components/MobileBottomNav.jsx +0 -82
  60. package/src/components/PasskeysComponent.jsx +0 -252
  61. package/src/components/PasswordChangeForm.jsx +0 -87
  62. package/src/components/PasswordResetRequestForm.jsx +0 -43
  63. package/src/components/PasswordSetForm.jsx +0 -97
  64. package/src/components/ProfileComponent.jsx +0 -348
  65. package/src/components/QrSignupManager.jsx +0 -404
  66. package/src/components/QrSignupValidityManager.jsx +0 -106
  67. package/src/components/RegistrationMethodsManager.jsx +0 -150
  68. package/src/components/SecurityComponent.jsx +0 -141
  69. package/src/components/SocialLoginButtons.jsx +0 -83
  70. package/src/components/SoftChip.jsx +0 -93
  71. package/src/components/StatTile.jsx +0 -39
  72. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  73. package/src/components/TurnstileWidget.jsx +0 -99
  74. package/src/components/UserInviteComponent.jsx +0 -76
  75. package/src/components/UserListComponent.jsx +0 -618
  76. package/src/components/charts/BarChart.jsx +0 -102
  77. package/src/components/charts/ChartFrame.jsx +0 -214
  78. package/src/components/charts/LineChart.jsx +0 -126
  79. package/src/components/charts/ScatterChart.jsx +0 -299
  80. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  81. package/src/components/charts/chartDefaults.js +0 -644
  82. package/src/components/charts/chartLabels.js +0 -36
  83. package/src/components/charts/exportChart.js +0 -258
  84. package/src/components/charts/formatters.js +0 -39
  85. package/src/components/charts/palette.js +0 -33
  86. package/src/components/charts/yearTickInterval.js +0 -7
  87. package/src/i18n/authTranslations.ts +0 -2651
  88. package/src/i18n/chartsTranslations.ts +0 -91
  89. package/src/i18n/chatTranslations.ts +0 -55
  90. package/src/i18n/messagingTranslations.ts +0 -120
  91. package/src/i18n/notificationsTranslations.ts +0 -32
  92. package/src/i18n/onboardingTranslations.ts +0 -29
  93. package/src/i18n/sectionNavTranslations.ts +0 -20
  94. package/src/i18n/uiCoreTranslations.js +0 -29
  95. package/src/i18n/userMenuTranslations.ts +0 -5
  96. package/src/index.js +0 -158
  97. package/src/layout/PageLayout.jsx +0 -36
  98. package/src/layout/SectionNav.jsx +0 -320
  99. package/src/messaging/AttachmentList.jsx +0 -145
  100. package/src/messaging/Composer.jsx +0 -197
  101. package/src/messaging/ConversationLaunchers.jsx +0 -51
  102. package/src/messaging/ConversationList.jsx +0 -109
  103. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  104. package/src/messaging/MessageBubble.jsx +0 -183
  105. package/src/messaging/MessagingProvider.jsx +0 -0
  106. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  107. package/src/messaging/PollCard.jsx +0 -163
  108. package/src/messaging/ReactionBar.jsx +0 -43
  109. package/src/messaging/ReadTicks.jsx +0 -82
  110. package/src/messaging/Thread.jsx +0 -195
  111. package/src/messaging/api.js +0 -48
  112. package/src/messaging/composerImageCompression.js +0 -35
  113. package/src/messaging/linkifyText.jsx +0 -37
  114. package/src/notifications/NotificationBell.jsx +0 -84
  115. package/src/notifications/NotificationSettings.jsx +0 -330
  116. package/src/notifications/NotificationsProvider.jsx +0 -224
  117. package/src/notifications/PopupSurface.jsx +0 -105
  118. package/src/notifications/api.js +0 -55
  119. package/src/notifications/feedApi.js +0 -18
  120. package/src/notifications/realtime.jsx +0 -138
  121. package/src/notifications/serviceWorker/sw.js +0 -50
  122. package/src/onboarding/OnboardingProvider.jsx +0 -326
  123. package/src/onboarding/OnboardingWizard.jsx +0 -61
  124. package/src/onboarding/WizardDialogShell.jsx +0 -51
  125. package/src/onboarding/api.js +0 -13
  126. package/src/onboarding/stepSelection.js +0 -16
  127. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  128. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  129. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  130. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  131. package/src/pages/AccountPage.jsx +0 -394
  132. package/src/pages/LoginPage.jsx +0 -343
  133. package/src/pages/PasswordChangePage.jsx +0 -62
  134. package/src/pages/PasswordInvitePage.jsx +0 -177
  135. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  136. package/src/pages/SignUpPage.jsx +0 -268
  137. package/src/pages/SignupConfirmPage.jsx +0 -155
  138. package/src/testing/checkKitIntegration.js +0 -233
  139. package/src/theme/createAppTheme.js +0 -423
  140. package/src/theme/fonts.js +0 -3
  141. package/src/theme/index.js +0 -21
  142. package/src/theme/kitSxRegistry.js +0 -285
  143. package/src/theme/themeCompleteness.js +0 -1045
  144. package/src/theme/tokens.js +0 -478
  145. package/src/utils/auth-errors.js +0 -76
  146. package/src/utils/authService.js +0 -223
  147. package/src/utils/resolveErrorText.js +0 -28
  148. package/src/utils/webauthn.js +0 -51
  149. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  150. package/tests/AccountPage.test.jsx +0 -200
  151. package/tests/AuthContext.test.jsx +0 -129
  152. package/tests/BarChart.test.jsx +0 -112
  153. package/tests/BrowserPushStep.test.jsx +0 -110
  154. package/tests/ChartFrame.test.jsx +0 -336
  155. package/tests/ConversationList.test.jsx +0 -144
  156. package/tests/CookieConsentStep.test.jsx +0 -69
  157. package/tests/EmptyState.test.jsx +0 -62
  158. package/tests/LineChart.test.jsx +0 -80
  159. package/tests/MessagingProvider.test.jsx +0 -50
  160. package/tests/MobileBottomNav.test.jsx +0 -319
  161. package/tests/NotificationBell.test.jsx +0 -60
  162. package/tests/NotificationSettings.test.jsx +0 -276
  163. package/tests/NotificationsProvider.test.jsx +0 -240
  164. package/tests/OnboardingProvider.test.jsx +0 -294
  165. package/tests/OnboardingWizard.test.jsx +0 -143
  166. package/tests/PasswordChangeForm.test.jsx +0 -70
  167. package/tests/PasswordInvitePage.test.jsx +0 -142
  168. package/tests/PasswordSetForm.test.jsx +0 -70
  169. package/tests/PopupSurface.test.jsx +0 -142
  170. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  171. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  172. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  173. package/tests/ScatterChart.test.jsx +0 -270
  174. package/tests/SectionNav.test.jsx +0 -363
  175. package/tests/SignUpPage.test.jsx +0 -208
  176. package/tests/SoftChip.test.jsx +0 -105
  177. package/tests/StatTile.test.jsx +0 -57
  178. package/tests/Thread.test.jsx +0 -151
  179. package/tests/TimeSeriesChart.test.jsx +0 -367
  180. package/tests/UserListComponent.test.jsx +0 -192
  181. package/tests/UserMenu.test.jsx +0 -171
  182. package/tests/apiClientLanguage.test.js +0 -33
  183. package/tests/apiClientPublicPaths.test.js +0 -178
  184. package/tests/authApiLogin.test.js +0 -42
  185. package/tests/authApiRegistration.test.js +0 -31
  186. package/tests/authErrorCodeRendering.test.jsx +0 -77
  187. package/tests/authErrors.test.js +0 -74
  188. package/tests/authTranslations.test.js +0 -74
  189. package/tests/chart-api-census.test.js +0 -366
  190. package/tests/chartAxisTickBand.test.jsx +0 -95
  191. package/tests/chartDefaults.test.js +0 -902
  192. package/tests/chartLabels.test.js +0 -50
  193. package/tests/chartsPalette.test.js +0 -99
  194. package/tests/chatApi.test.js +0 -48
  195. package/tests/chatDriverNames.test.jsx +0 -71
  196. package/tests/chatExports.test.js +0 -3
  197. package/tests/chatList.test.jsx +0 -84
  198. package/tests/chatPicker.test.jsx +0 -82
  199. package/tests/chatSocket.test.jsx +0 -26
  200. package/tests/chatThread.test.jsx +0 -116
  201. package/tests/chatTranslations.test.js +0 -47
  202. package/tests/chatWorkspace.test.jsx +0 -234
  203. package/tests/checkKitIntegration.test.jsx +0 -219
  204. package/tests/createAppTheme.test.js +0 -132
  205. package/tests/createAppThemeDark.test.js +0 -127
  206. package/tests/createAppThemeLightGuard.test.js +0 -40
  207. package/tests/createAppThemePageContrast.test.js +0 -108
  208. package/tests/createAppThemeTypography.test.js +0 -53
  209. package/tests/errorCallSiteGuard.test.js +0 -32
  210. package/tests/exportChart.test.js +0 -313
  211. package/tests/feedApi.test.js +0 -46
  212. package/tests/fixtures/light-theme-baseline.json +0 -2234
  213. package/tests/i18nAggregate.test.js +0 -120
  214. package/tests/i18nGermanVariant.test.js +0 -65
  215. package/tests/loginPageErrorResolution.test.jsx +0 -102
  216. package/tests/messagingApi.test.js +0 -32
  217. package/tests/messagingChunk6.test.jsx +0 -116
  218. package/tests/messagingCompactBubble.test.jsx +0 -64
  219. package/tests/messagingComposer.test.jsx +0 -136
  220. package/tests/messagingComposerChunk5.test.jsx +0 -113
  221. package/tests/messagingContractConformance.test.js +0 -69
  222. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  223. package/tests/messagingExports.test.js +0 -21
  224. package/tests/messagingInteractions.test.jsx +0 -136
  225. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  226. package/tests/messagingLinkify.test.jsx +0 -51
  227. package/tests/messagingMessageActions.test.jsx +0 -163
  228. package/tests/messagingMsg6.test.jsx +0 -105
  229. package/tests/messagingMsg6f.test.jsx +0 -155
  230. package/tests/messagingMsg6g.test.jsx +0 -166
  231. package/tests/messagingMsg6h.test.jsx +0 -136
  232. package/tests/messagingMsg6i.test.jsx +0 -129
  233. package/tests/messagingPollRendering.test.jsx +0 -142
  234. package/tests/messagingReactionMerge.test.jsx +0 -77
  235. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  236. package/tests/messagingThreadReceipts.test.jsx +0 -178
  237. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  238. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  239. package/tests/notificationsApi.test.js +0 -41
  240. package/tests/notificationsExports.test.js +0 -34
  241. package/tests/onboardingDescriptors.test.js +0 -132
  242. package/tests/packageTreeShaking.test.js +0 -126
  243. package/tests/realtime.test.jsx +0 -219
  244. package/tests/resolveErrorText.test.js +0 -72
  245. package/tests/stepSelection.test.js +0 -53
  246. package/tests/themeCompleteness.test.js +0 -626
  247. package/tests/timeSeriesLegend.test.js +0 -99
  248. package/tests/userScreenTranslations.test.jsx +0 -218
  249. package/tsconfig.build.json +0 -18
  250. package/vite.config.mjs +0 -6
  251. package/work-orders/AUTH-1.md +0 -74
  252. package/work-orders/AUTH-2.md +0 -74
  253. package/work-orders/AUTH-4.md +0 -183
  254. package/work-orders/AUTH-5.md +0 -154
  255. package/work-orders/AUTH-7.md +0 -181
  256. package/work-orders/AUTH-8.md +0 -184
  257. package/work-orders/CHART-1.md +0 -285
  258. package/work-orders/CHART-2.md +0 -222
  259. package/work-orders/CHART-3.md +0 -163
  260. package/work-orders/CHART-4.md +0 -203
  261. package/work-orders/CHART-5.md +0 -226
  262. package/work-orders/CHART-6.md +0 -428
  263. package/work-orders/CHART-7.md +0 -175
  264. package/work-orders/DX-1.md +0 -169
  265. package/work-orders/DX-2.md +0 -188
  266. package/work-orders/DX-3.md +0 -195
  267. package/work-orders/I18N-1.md +0 -254
  268. package/work-orders/I18N-3.md +0 -207
  269. package/work-orders/MSG-3.md +0 -375
  270. package/work-orders/MSG-3b.md +0 -1002
  271. package/work-orders/MSG-3c.md +0 -378
  272. package/work-orders/MSG-3d.md +0 -268
  273. package/work-orders/MSG-3e.md +0 -164
  274. package/work-orders/MSG-3f.md +0 -391
  275. package/work-orders/MSG-6e.md +0 -308
  276. package/work-orders/MSG-6f.md +0 -214
  277. package/work-orders/MSG-6g.md +0 -244
  278. package/work-orders/MSG-6h.md +0 -238
  279. package/work-orders/MSG-6i.md +0 -271
  280. package/work-orders/MSG-6j.md +0 -117
  281. package/work-orders/NOTIF-12.md +0 -214
  282. package/work-orders/NOTIF-13.md +0 -142
  283. package/work-orders/NOTIF-14.md +0 -151
  284. package/work-orders/PRIM-1.md +0 -177
  285. package/work-orders/SHELL-1.md +0 -255
  286. package/work-orders/SHELL-2.md +0 -197
  287. package/work-orders/SHELL-3.md +0 -359
  288. package/work-orders/SHELL-4.md +0 -351
  289. package/work-orders/SHELL-5.md +0 -345
  290. package/work-orders/SHELL-6.md +0 -210
  291. package/work-orders/THEME-1.md +0 -587
  292. package/work-orders/THEME-10.md +0 -303
  293. package/work-orders/THEME-11.md +0 -208
  294. package/work-orders/THEME-2.md +0 -339
  295. package/work-orders/THEME-3.md +0 -210
  296. package/work-orders/THEME-4.md +0 -271
  297. package/work-orders/THEME-5.md +0 -341
  298. package/work-orders/THEME-6.md +0 -247
  299. package/work-orders/THEME-7.md +0 -82
  300. package/work-orders/THEME-8.md +0 -211
  301. package/work-orders/THEME-9.md +0 -194
  302. package/work-orders/UCM-AUTH-10.md +0 -490
  303. package/work-orders/UCM-AUTH-11.md +0 -185
  304. package/work-orders/UCM-AUTH-12.md +0 -98
  305. package/work-orders/UCM-AUTH-13.md +0 -47
  306. package/work-orders/UCM-AUTH-9.md +0 -207
  307. package/work-orders/UCM-CHART-10.md +0 -150
  308. package/work-orders/UCM-CHART-11.md +0 -132
  309. package/work-orders/UCM-CHART-12.md +0 -348
  310. package/work-orders/UCM-CHART-13.md +0 -122
  311. package/work-orders/UCM-CHART-14.md +0 -198
  312. package/work-orders/UCM-CHART-15.md +0 -187
  313. package/work-orders/UCM-CHART-16.md +0 -169
  314. package/work-orders/UCM-CHART-17.md +0 -195
  315. package/work-orders/UCM-CHART-18.md +0 -200
  316. package/work-orders/UCM-CHART-8.md +0 -147
  317. package/work-orders/UCM-CHART-9.md +0 -265
  318. package/work-orders/UCM-CHAT-1.md +0 -190
  319. package/work-orders/UCM-CHAT-2.md +0 -75
  320. package/work-orders/UCM-CI-1.md +0 -65
  321. package/work-orders/UCM-DEP-1.md +0 -63
  322. package/work-orders/UCM-I18N-4.md +0 -226
  323. package/work-orders/UCM-I18N-5.md +0 -211
  324. package/work-orders/UCM-MSG-20.md +0 -192
  325. package/work-orders/UCM-PRIM-2.md +0 -114
  326. package/work-orders/UCM-SHELL-7.md +0 -134
  327. package/work-orders/UCM-SHELL-8.md +0 -125
  328. package/work-orders/UCM-TEST-1.md +0 -304
  329. package/work-orders/UCM-THEME-13.md +0 -69
  330. package/work-orders/UCM-THEME-14.md +0 -312
  331. package/work-orders/UCM-THEME-15.md +0 -356
  332. package/work-orders/UCM-THEME-16.md +0 -80
  333. package/work-orders/UCM-THEME-17.md +0 -111
  334. package/work-orders/UCM-THEME-18.md +0 -184
  335. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  336. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  337. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  338. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  339. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  340. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  341. package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
@@ -1,185 +0,0 @@
1
- # UCM-AUTH-11 — `UserListComponent` offers an admin the delete action on their own row
2
-
3
- # A. Envelope — authored by the Expertenchat
4
-
5
- ## Goal & expected outcome
6
-
7
- - Goal: the user list never offers anyone the deletion of their own account.
8
- - Expected outcome:
9
- - In `UserListComponent`, the row of the signed-in user renders **no** delete button — not a
10
- disabled one, none at all. Every other row behaves exactly as today (enabled or disabled per
11
- `canDeleteUser` / the default).
12
- - This holds for every consumer, **including one that passes its own `canDeleteUser`**
13
- (`AccountPage` forwards it as `userListCanDeleteUser`): a prop that returns true for the own row
14
- does not bring the button back.
15
- - Released; the Envelope's done-state is the release (consumer bumps are separate, see Risks).
16
-
17
- ## Context — measured 2026-09-30 against `main` at 3.9.0
18
-
19
- - **Operator decision 2026-09-30 (from django-core-micha `DCM-AUTH-4`, dropped there): UI-only.**
20
- The backend keeps allowing an admin's generic self-delete (`BaseUserViewSet.destroy()` refuses
21
- only a non-admin's self-delete since dcm 2.44.1); the list simply stops offering it.
22
- - `src/components/UserListComponent.jsx`:
23
- - `defaultCanEdit` (~l.128) returns true for `currentUser.is_superuser` and for role `admin` on
24
- every row, the own row included (only the `teacher` branch excludes self).
25
- - `canDelete` (~l.151) returns `canDeleteUser(...)` when that prop is a function, otherwise
26
- `canEdit(targetUser)`.
27
- - The delete button (~l.438-452) renders whenever `showDeleteAction` is true and is only
28
- `disabled={!canDelete(row)}` — so a row that may not be deleted still shows a disabled button.
29
- - `src/pages/AccountPage.jsx` forwards `userListShowDeleteAction` (default true) and
30
- `userListCanDeleteUser` (default null).
31
- - Consumers measured: kerzenziehen and reimbursements render `AccountPage` without these props, so
32
- an admin there sees an enabled delete button on their own row today. jg-ferien passes its own
33
- `userListCanDeleteUser`, which already returns false for the own row (the button shows disabled).
34
-
35
- ## Scope + non-goals
36
-
37
- - In scope: the own-row rule in `UserListComponent` (identity by `id` of the row vs. the current
38
- user), tests, CHANGELOG entry, release.
39
- - Explicit non-goals / do-not-touch:
40
- - No backend change (dcm untouched).
41
- - Rows other than the own row keep today's disabled-vs-enabled behaviour; no change to
42
- `defaultCanEdit`.
43
- - No other action on the own row changes (edit, extra row actions).
44
- - Consumer pin bumps are not part of this WO.
45
-
46
- ## Tier · gates
47
-
48
- - **Tier 3** — change inside shared-core, permission-adjacent UI.
49
- - Precondition: none.
50
-
51
- ## Risks
52
-
53
- - **Reaching the two affected apps needs a major bump:** kerzenziehen and reimbursements pin
54
- `@micha.bigler/ui-core-micha` **2.41.3**; 3.0.0 removed chart layout props (`UCM-CHART-12`). The
55
- fix only reaches them with that bump, which is its own work per app and is classified there by
56
- the dependency-bump rule.
57
- - The current user may be unknown while loading; with no current user the own-row rule must not
58
- hide buttons on other rows or throw.
59
- - `id` types: compare the row id and the current user id so that `3` and `"3"` match.
60
-
61
- ## Required tests to WRITE (you write them and run YOUR OWN new ones; the orchestrator's run is the gate)
62
-
63
- In `tests/UserListComponent.test.jsx`:
64
-
65
- 1. Superuser, no `canDeleteUser`: own row has no delete button; another row has an enabled one.
66
- 2. Role `admin`, no `canDeleteUser`: same as 1.
67
- 3. `canDeleteUser` returning true for every row: own row still has no delete button.
68
- 4. Current user not yet loaded: other rows unaffected, no error.
69
- 5. **Mutation proof:** remove the own-row rule → tests 1 and 3 fail.
70
-
71
- ---
72
-
73
- # B. Implementation map — filled by the Orchestrator — ADDRESSED TO THE IMPLEMENTER
74
-
75
- ## Context package
76
-
77
- - File: `src/components/UserListComponent.jsx` (current state, verified against `main` at `3.9.0`
78
- post-`UCM-AUTH-10`, line numbers below are exact).
79
- - **The fix must gate RENDERING, not just the disabled state.** `canDelete(row)` (line 151-156)
80
- already decides enabled-vs-disabled; the delete `Button` (line 441-453) already renders
81
- `disabled={!canDelete(row)}`. If the own-row rule were added INSIDE `canDelete`, the button would
82
- still render, just permanently disabled — that is explicitly NOT what the Envelope asks for ("no
83
- delete button — not a disabled one, none at all"). The rule must wrap the render, so a consumer's
84
- `canDeleteUser` returning `true` for the own row cannot bring the button back (Required Test #3).
85
- - Add a small helper next to `canDelete` (around line 156):
86
- ```js
87
- const isOwnRow = (targetUser) => {
88
- if (currentUser?.id == null || targetUser?.id == null) return false;
89
- return String(targetUser.id) === String(currentUser.id);
90
- };
91
- ```
92
- - `currentUser?.id == null` (loose check, catches both `undefined` and `null`) covers the "current
93
- user not yet loaded" risk: when there is no current user at all, every row's `isOwnRow` is
94
- `false`, so nothing is hidden and nothing throws (Required Test #4) — this function must NOT be
95
- the place that decides "hide everything while loading"; it only ever answers "is THIS row me",
96
- and answers `false` when that question is unanswerable.
97
- - `String(...) === String(...)` is the fix for the Risk note's `id` type mismatch (`3` vs `"3"`).
98
- - Wire it into the existing render condition at line 438 — change
99
- `{showDeleteAction && (` to `{showDeleteAction && !isOwnRow(row) && (` (the `Tooltip`/`Button`
100
- JSX inside is otherwise byte-identical — do not touch its internals, only the gating condition
101
- that decides whether that whole block renders at all).
102
- - Do not touch `canDelete`/`canEdit`/`defaultCanEdit` (line 128-156) — the non-goal is explicit that
103
- every OTHER row's enabled-vs-disabled behaviour stays exactly as today; this fix only removes the
104
- own row's button, it does not change what `canDelete` returns for anyone (a consumer's
105
- `canDeleteUser({ targetUser, currentUser, extraContext })` for the own row may still return
106
- `true` internally — that return value is simply never consulted for the own row anymore, because
107
- the render is gated before `canDelete(row)` is ever reached for that row's button).
108
-
109
- ## Test file — `tests/UserListComponent.test.jsx` (existing — read it first for the render harness,
110
- ## the `react-i18next`/`i18n` mock shape `UCM-AUTH-10` already fixed there, and `authApi` mocking
111
- ## convention already established; extend it, do not replace its structure)
112
-
113
- Five cases, matching the Required Tests 1-5 exactly:
114
- 1. `currentUser = { id: 99, is_superuser: true, ... }`, `fetchUsersList` resolves a list including a
115
- row with `id: 99` (the signed-in user) and at least one other row (e.g. `id: 1`); no
116
- `canDeleteUser` prop passed. Assert: querying delete buttons by accessible name/role yields exactly
117
- one (for the OTHER row), and it is not disabled; no delete button exists for the row whose `id`
118
- is `99` (e.g. scope the query to that row's `TableRow` — check how existing tests in this file
119
- locate a specific row, or use `within(row)` from `@testing-library/react` if not already
120
- imported).
121
- 2. Same shape, `currentUser.role = 'admin'` instead of `is_superuser` — same assertions.
122
- 3. Pass `canDeleteUser={() => true}` (unconditionally true for every row, including the own one) —
123
- assert the own row STILL has no delete button, while the other row's is enabled (proves the
124
- render-level gate wins over a consumer prop, not just over the default `canEdit`-based logic).
125
- 4. Render with `currentUser = null` (or omit the prop) while `fetchUsersList` resolves a list
126
- including a row that happens to share no id with anyone — assert the component does not throw
127
- (no unhandled rejection/render error) and renders without crashing; per the Envelope's own
128
- framing, unauthenticated/loading state does not by itself grant any row a delete button (this
129
- test is about NOT crashing and NOT hiding unrelated rows, not about granting new permissions —
130
- keep whatever the existing default-canEdit-based enabled/disabled state would be for other rows
131
- in this configuration, do not assert a specific enabled/disabled state you have not verified from
132
- `defaultCanEdit`'s own logic for a null `currentUser`, which already returns `false` unconditionally
133
- at line 129 — so with the default `canDeleteUser`, every row is disabled-but-present in this
134
- configuration; assert exactly that: buttons present and disabled, none of them thrown away).
135
- 5. **Mutation proof required by the Envelope itself:** after writing tests 1 and 3, temporarily
136
- comment out or revert the `!isOwnRow(row)` addition in your own working copy, re-run tests 1 and
137
- 3 ONLY, confirm both fail, then restore the fix and confirm the full focused file is green again.
138
- Narrate this in `PROGRESS` (which two tests failed, then the restore) rather than leaving a
139
- permanently-broken toggle in the committed test file — same pattern already used successfully in
140
- this repo's own `UCM-THEME-15`/`UCM-PRIM-2` work orders for an identical "prove it would fail"
141
- requirement.
142
-
143
- ## Do-not-touch / invariants
144
-
145
- - No backend change (dcm is a separate repo, untouched, and out of scope regardless).
146
- - No change to `defaultCanEdit`/`canEdit`/`canDelete`'s return values for any OTHER row.
147
- - No change to any other action on the own row (role select, extra row actions) — only the delete
148
- button's presence.
149
- - No consumer (`AccountPage`, etc.) touched — this fix is entirely inside `UserListComponent.jsx`.
150
-
151
- ## Target repo working directory (absolute)
152
-
153
- `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`)
154
-
155
- ## Preamble — a REQUIRED block IN this file, not something appended at invocation
156
-
157
- > The text above is the COMPLETE spec — the committed WO file's content, not a plan to refine; there
158
- > is no separate plan file. Read the nearest `AGENTS.md`, the relevant `.codex/skills/<role>/SKILL.md`, and the
159
- > app `MEMORY.md` ONLY for conventions. Stay in scope; do not touch auth/permissions/deps/schema/CI
160
- > unless the spec says so; do not update `MEMORY.md`. **Do NOT edit `WORK_ORDERS.md` — the register
161
- > row and the review verdicts are the orchestrator's alone.** **Your tools are for editing source
162
- > and test files and for running the tests you wrote — nothing else.** Do NOT install dependencies,
163
- > touch a lockfile, run a package manager, or tidy up stray files; if something in the repo state
164
- > blocks you, stop and report it as `RESULT: BLOCKED <reason>` instead of fixing it. Do NOT
165
- > `git add`/`commit`/`push` — leave every
166
- > change uncommitted in the working tree for the orchestrator's independent review. WRITE the tests
167
- > the `Required tests` section calls for AND **RUN the tests you just wrote** to confirm they execute
168
- > and pass — that is the ONLY test run you do (NOT the app's affected/full suite, NOT any review).
169
- > The orchestrator re-runs the authoritative set + does the independent review after you finish —
170
- > those are the gate; your own run does not count as the gate.
171
- >
172
- > Narrate continuously: a `PLAN: <step1> | <step2> | …` line up front, then a single-line
173
- > `PROGRESS: [<n>/<total>] <present-tense action>` before every relevant action (and `… done` on
174
- > completion), spaced so no gap exceeds ~2 min, stdout unbuffered, plus exactly one final
175
- > `RESULT: DONE|BLOCKED <reason>`.
176
-
177
- ---
178
-
179
- # C. Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
180
-
181
- > **If you are the implementer reading this work order as your own specification: STOP at this line.
182
- > Everything below describes what the Orchestrator does AFTER you finish. You do none of it — no
183
- > reviewers, no verification run, no register edit, no commit.**
184
-
185
- *(to be filled by the Orchestrator: execution directive; `reviewer` (all lenses) + `ui_reviewer` + `sec_reviewer`; release (patch: behaviour fix of an existing component); register row stays `released` until consumers bump; register the kerzenziehen and reimbursements bumps in their own registers; commit.)*
@@ -1,98 +0,0 @@
1
- # UCM-AUTH-12: Optional profile statement text on the account page
2
-
3
- # A. Envelope — operator-approved scope
4
-
5
- ## Goal and expected outcome
6
-
7
- Expose `AccountPage.showProfileStatements` so an app without privacy/cookie statement
8
- endpoints can suppress the explanatory statement panels and their requests. The default
9
- is `true`, preserving every existing consumer's behavior. With `false`, the account profile
10
- still renders and submits both existing consent fields.
11
-
12
- The operator explicitly approved this switch, its release and the innoservice pin bump on
13
- 2026-10-13. innoservice CI38387014900 records privacy/cookie 404s; auth policy returns 200.
14
-
15
- ## Scope and non-goals
16
-
17
- - Add the optional prop and pass it to the existing ProfileComponent statement switch.
18
- - Publish a minor release 3.13.0; innoservice consumes it under INNO-FIX-13.
19
- - Do not change consent defaults, payloads, validation, permissions, authentication,
20
- account navigation, statement fetching implementation, theme tokens or other consumers.
21
- - Do not fix UCM-SHELL-8 or invent statement endpoints/content.
22
- - No new visual design or prototype; this config uses the existing ProfileComponent surface.
23
-
24
- ## Tier, risk and required tests
25
-
26
- Tier 3: source inside shared-core on an account surface. Compatibility rests on default true.
27
- Tests to write: render the real AccountPage and ProfileComponent; omission of the new prop
28
- fetches both statements and presents the existing panels; false performs neither request and
29
- hides the panels, while both consent controls remain editable and their values reach the
30
- existing updateUserProfile submission. Keep the existing account-navigation tests.
31
-
32
- # B. Implementation map — ADDRESSED TO THE IMPLEMENTER
33
-
34
- ## Target and ownership
35
-
36
- Target: `C:/Users/Micha Bigler/Documents/webapps/ui-core-micha`.
37
- Native bootstrap retains workspace-root cwd; use explicit absolute paths and workdirs.
38
- You are not alone in the workspace. Own only:
39
- - `src/pages/AccountPage.jsx`
40
- - new `tests/AccountPage.profileStatements.test.jsx`
41
-
42
- ## Context package
43
-
44
- `src/pages/AccountPage.jsx:39` exports named AccountPage with destructured optional props.
45
- Its profile branch near line228 renders ProfileComponent with showName/showPrivacy/showCookies.
46
- Its existing handleProfileSubmit calls updateUserProfile(payload), then AuthContext.login(updated).
47
- `src/components/ProfileComponent.jsx:23` exports named ProfileComponent; showStatements defaults
48
- true, independently of showPrivacy/showCookies. Its statement-loading effect at83 returns before
49
- either fetch when showStatements is false. Panels at255 are also controlled by showStatements.
50
- Consent controls at230/242 and payload at130/133 depend only on showPrivacy/showCookies.
51
- Use that established switch; leave this component untouched.
52
-
53
- Read only these targets plus `tests/AccountPage.test.jsx` for theme/router/AuthContext setup,
54
- and `src/auth/authApi.jsx` for the four API export names if necessary. Do not explore broadly.
55
- The existing navigation test mocks ProfileComponent, so it cannot prove the no-request and
56
- consent invariant: write a separate test file rendering both real components. Stub network API
57
- functions and unrelated account children only. Relevant real accessible labels are translation
58
- keys Profile.ACCEPT_PRIVACY_LABEL, Profile.ACCEPT_COOKIES_LABEL, Profile.PRIVACY_STATEMENT_TITLE,
59
- Profile.COOKIES_STATEMENT_TITLE and Profile.SAVE_BUTTON.
60
- Use the suite's established translation seam; never mock AccountPage/ProfileComponent under test.
61
-
62
- ## Preamble
63
-
64
- > This committed WO is the COMPLETE spec. You are the initial implementer, not the reviewer.
65
- > Preserve other edits. Edit only the two owned source/test paths using explicit UTF-8 I/O.
66
- > Do not edit dependencies, lockfiles, package/version/changelog/register/README/governance,
67
- > settings, environment, other repos or memory. Never read .env or dump settings/environment.
68
- > No installs, package-manager commands, containers, git operations, self-review or subagents.
69
- > Write the required tests and run your own new file ONCE with the existing Node/Vitest runtime,
70
- > maxWorkers=2; no affected/full suite, build or app startup. If unavailable, report the obstacle.
71
- > The orchestrator's tests and separate reviews are the acceptance gate. Leave uncommitted.
72
- > Start PLAN, emit PROGRESS before relevant actions without silence over two minutes, then
73
- > exactly one final RESULT: DONE or RESULT: BLOCKED.
74
-
75
- # C. Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
76
-
77
- > IMPLEMENTER STOP HERE. You ARE the initial implementation instance. No steps below belong to you.
78
-
79
- Gate #1: operator explicitly approved the exact API/default, retained consents, release and pin
80
- bump in the innoservice orchestration chat on 2026-10-13. Native plain worker requested
81
- codex/gpt-6-luna, fresh context, full committed WO inline. Record accepted ID and actual metadata;
82
- effective provider/model stays unverified when the API does not expose it. No protection-parity claim.
83
-
84
- Parent owns version/changelog/README/register and pnpm state. Independent four lenses plus UI
85
- and security concurrently, full scoped context, CLI read-only without bypass. Parent fixes findings.
86
- Scoped gate: new real-component tests, existing AccountPage navigation and affected auth tests.
87
- Publish workflow on main already runs build/full tests; shared-core ripple justifies that existing
88
- release suite. Do not dispatch publish manually; normal reviewed main push triggers it. Check
89
- publish job success before app pin. Minor 3.13.0 is additive; read exact release diff before app bump.
90
- Final core row includes deduplicated review totals and test results; second register commit before
91
- one main push. innoservice AccountPage/WelcomePage statements false and capture evidence remain
92
- INNO-FIX-13; keep existing consents, request diagnostics and capture checks intact.
93
-
94
- Accepted native implementer: `/root/kit_account_statements`, explicit requested gpt-6-luna,
95
- high reasoning, fresh context, complete WO inline. Effective native provider/model metadata
96
- was not exposed and remains unverified. Source/test edits only; the one permitted author test
97
- attempt failed before collection with temporary-file EPERM (zero tests). Parent runs the
98
- authoritative scoped gate through the normal runtime approval path. No author review or git.
@@ -1,47 +0,0 @@
1
- # UCM-AUTH-13 — Preserve profile edits during a same-person refresh
2
-
3
- ## A. Envelope — operator-approved correction
4
-
5
- The Spesix first-steps browser trace proves that a background current-user response can arrive while the privacy checkbox is being edited. `ProfileComponent` currently resets every form field whenever the context user object changes, losing the person's input.
6
-
7
- Expected outcome: preserve unsaved name and consent edits across a background refresh of the same person. Continue to refresh untouched fields from server data. A change of person, including logout followed by login, must initialize the form from the new person's data and never transfer consent or edits between people. Initial asynchronous user hydration must still work.
8
-
9
- Scope: the existing shared ProfileComponent form state, targeted regression tests, a patch release, and subsequent Spesix dependency consumption. No change to authentication, permissions, consent policy, API payload shape, statement requests, layout or copy. No prototype; existing components and labels remain the visual specification. Token delta: EMPTY. Copy list: EMPTY.
10
-
11
- Tier 3: shared-core and profile/consent surface. Operator explicitly approved correction, checking, publication and Spesix pin update in the Spesix orchestration chat on 2026-10-14.
12
-
13
- Risks: stale pristine fields, failure to hydrate an initially absent user, or carrying unsaved consent into a different account. Tests to write: same-person object replacement preserves typed first/last names and both edited consent values in the submitted payload; untouched fields update; different identity and logout/login reset all fields; initial hydration works. Use real form controls and context rerenders.
14
-
15
- ## B. Implementation map — addressed to the implementer
16
-
17
- Target: `C:/Users/Micha Bigler/Documents/webapps/ui-core-micha`.
18
-
19
- Own only `src/components/ProfileComponent.jsx` and new `tests/ProfileComponent.refresh.test.jsx`. Read `tests/AccountPage.profileStatements.test.jsx` for the existing Vitest/jsdom, MUI and AuthContext conventions; `src/auth/AuthContext.jsx` for user identity shape; and `src/pages/AccountPage.jsx` for the caller contract. The library is not a joined application: one author owns component and focused unit tests.
20
-
21
- The synchronization effect at ProfileComponent lines 56–65 currently depends on `[user]` and unconditionally assigns username, email, first/last name and both consent flags. The editable controls are near lines 205–250. `handleSubmit` near line 117 constructs the existing optional-fields payload; preserve it. Username/email remain read-only. Statement loading effects and visibility props are unchanged. AccountPage passes its profile update callback into this component. Work from these seams; do not broadly explore or change AuthContext or callers. Verify the identity contract before choosing synchronization behavior; do not use object reference as person identity.
22
-
23
- ### Required preamble and progress contract
24
-
25
- The text above is the COMPLETE spec. Read root AGENTS.md and `.codex/skills/frontend-engineering/SKILL.md` only for conventions. Stay in scope. Do not edit WORK_ORDERS.md, MEMORY.md, package files, lockfiles, changelog, CI or unrelated files. Tools are only for reading the named context and editing assigned source/tests, plus one run of the new tests using the existing direct Vitest runner. No installs, package-manager commands, git, self-review or reviewer dispatch. Leave edits uncommitted. Your run is not the gate; the Orchestrator runs the affected set and independent reviews.
26
-
27
- Use explicit UTF-8 file I/O. You share this checkout: preserve all other edits, including the foreign scratchpad directory. Native cwd bootstrap is the workspace root; assigned paths and test cwd must be absolute, without changing the shared process cwd. Start with `PLAN: ...`, report `PROGRESS: [n/total] ...` at meaningful actions with gaps below two minutes, and finish with exactly one `RESULT: DONE|BLOCKED <reason>`.
28
-
29
- ## C. Orchestrator only — NOT addressed to the implementer
30
-
31
- > STOP here if you are the implementer. Everything below belongs to the Orchestrator after implementation. Do not execute it or dispatch anyone.
32
-
33
- Dispatch a fresh native worker, explicit codex/gpt-6-luna, fresh context. Record requested and exposed effective metadata honestly; native provider/cwd/sandbox are unverified if not exposed.
34
-
35
- Gate #1 is the explicit operator approval recorded above. Run independent envelope, regression, duplication and tests lenses plus UI and security concurrently on the corrected diff, read-only, configured Luna. Parent checks profile payload/identity boundaries in addition. Run focused ProfileComponent and existing AccountPage tests and the build. Root owns patch metadata and publication through the existing main-push workflow; no workflow dispatch is authorized by this file. Verify published version before changing Spesix pin, and inspect the intervening 3.12.0-to-current changes. No screenshots are needed for this state correction.
36
-
37
- Foreign scratchpad is excluded. Commit only owned explicit paths. Update register with actual author, reviews, checks, release and consumption evidence; never claim completion before those checks.
38
-
39
- ### Execution and gate evidence
40
-
41
- Fresh native `/root/ucm_auth13_author`, requested codex/gpt-6-luna/high, completed only the two assigned files; no git or package operations. Native provider/cwd/sandbox are not exposed. Author's one test attempt failed before collection on Windows Vitest cache EPERM. Parent authoritative elevated run passed: ProfileComponent.refresh (5) and existing AccountPage suites (6), 11 tests in 3 files. Parent added the independently edited-versus-pristine editable-fields case. Existing-dependency TypeScript build passed. Diff whitespace check passed. Root checked unchanged payload construction, statement effects, visibility flags and server-side authority; local edit tracking resets at identity/logout boundaries.
42
-
43
- Six fresh CLI codex/gpt-6-luna read-only lanes ran concurrently against the same frozen diff, including package/changelog and complete component/caller context. Each exited 0 with requested model, openai provider and library cwd confirmed in banner. Envelope, duplication, UI and security reported No findings. Regression raised mutable fallback identity for consumers without IDs: not accepted as a defect in the supported AuthContext contract, which maps the immutable API id; without an immutable identity the component cannot establish that two changed fallback identities are the same person, and preserving consent across them would weaken account isolation. Tests raised that identity/logout/hydration guards also pass against the previous code: not accepted; these are explicitly required preservation cases and catch a naive freeze-after-first-hydration fix, while two same-person tests discriminate the actual defect and submitted consent values. Review: codex/gpt-6-luna; lenses envelope, regression, duplication, tests, ui, security; 2 raised, 0 accepted, worst accepted none. Some optional narrow reads failed process setup; reviewers completed from the full inline diff/context. UI attempted a second blocked narrow read; no command actually executed. No screenshots or image reviews.
44
-
45
- npm version verified as 3.13.1 before reserving patch 3.13.2. Existing publish.yml builds and runs the library suite on main push before Trusted Publishing; no manual dispatch. Root inspected the consumer's intervening 3.12.0-to-3.13.1 code: AccountPage adds optional showProfileStatements default true, preserving existing controls/payload; SectionNav adds ListItem wrappers with existing selection/callbacks/labels. No changed exports or dependency requirements. Published patch and Spesix installed-package proof remain pending.
46
-
47
- Release completion: main f29d1dd, publish workflow38640512864 succeeded (build,90 test files and npm Trusted Publishing). Public registry confirms3.13.2, published2026-10-14T21:33:03Z after propagation. Spesix consumed exact3.13.2 in a30de18; real installed Welcome/ProfileComponent, AuthRoutesIntegration and AccountHub tests8/8 passed, shared i18next resolution confirmed. Spesix's separate SPX-DEP-5 owns its pending real-browser/capture CI38641555651@7eaefcc. The library correction/release is complete; no production app promotion occurred.
@@ -1,207 +0,0 @@
1
- # UCM-AUTH-9 — Auth flows that dead-end: a rejected password removes the invite form, a closed signup looks open
2
-
3
- # A. Envelope — authored by the Expertenchat
4
-
5
- ## Goal & expected outcome
6
-
7
- - Goal: every auth page leaves the person a way forward and says why something failed.
8
- - Expected outcome:
9
- - Invite/reset page: a server-side rejection of the password keeps the form, shows the reason
10
- (e.g. "too common") at the field, and lets the person retry. After success the person sees a
11
- confirmation, not a silent jump to the login page.
12
- - Signup page: when self-registration is off (`signup: false`, `signup_modes: []`), the page says
13
- so instead of rendering a form whose submit can never enable.
14
- - User list: a role change confirms that it was saved, or shows why not.
15
-
16
- ## Context — measured 2026-09-29 in jg-ferien's local browser test (ucm 3.5.0 installed there)
17
-
18
- - `src/pages/PasswordInvitePage.jsx`: the form renders only when `!successKey && !errorKey`
19
- (~l.142); any error — including the server's `Auth.RESET_PASSWORD_INVALID` with
20
- `messages: ["This password is too common."]` — removes both password fields and the button;
21
- only a reload recovers. The reason is not shown. Client-side validation keeps the form.
22
- - After a successful set the page navigates to login (~l.102) with no confirmation.
23
- - `src/pages/SignUpPage.jsx`: submit is `disabled: submitting || signupModes.length === 0 ||
24
- (turnstileRequired && !turnstileToken)`; with no signup modes the page still shows e-mail and
25
- access-code fields and a permanently grey button, no explanation.
26
- - `src/components/UserListComponent.jsx`: role selects save without any feedback.
27
-
28
- ## Scope + non-goals
29
-
30
- - In scope: the three behaviours above.
31
- - Explicit non-goals / do-not-touch:
32
- - Password rules and the backend responses (dcm) stay as they are.
33
- - Strings and the formal/informal choice are `UCM-I18N-4`.
34
-
35
- ## Tier · gates
36
-
37
- - **Tier 3** — shared-core auth screens.
38
- - Reviewer: `reviewer` (all lenses) · `sec_reviewer` (auth flow) · `ui_reviewer`.
39
- - **Done means released and jg-ferien's pin bumped.**
40
-
41
- ## Risks
42
-
43
- - An invalid/expired token must still end in the "link invalid" state without a form — only a
44
- password rejection keeps the form. The two error classes must be told apart by code, not by text.
45
-
46
- ## Required tests to WRITE (you write them and run YOUR OWN new ones; the orchestrator's run is the gate)
47
-
48
- 1. Server rejects the password → form still present, reason shown, retry possible.
49
- 2. Invalid token → invalid-link state, no form.
50
- 3. Success → confirmation visible.
51
- 4. Signup with no modes → explanatory message, no form.
52
- 5. Role change → success or error feedback.
53
-
54
- ---
55
-
56
- # B. Implementation map — filled by the Orchestrator — ADDRESSED TO THE IMPLEMENTER
57
-
58
- ## Context package — three independent fixes
59
-
60
- ### 1. `src/pages/PasswordInvitePage.jsx` — the dead-end form + silent navigate
61
-
62
- - **Root cause of both bugs: ONE shared `errorKey` state variable covers two structurally
63
- different failures**, and the render condition `{!successKey && !errorKey && <PasswordSetForm
64
- .../>}` (line 142) hides the form for EITHER. The link-validity check (`verifyResetToken`, the
65
- `useEffect` at line 36) and the password submission (`setNewPassword`, `handleSubmit` at line 56)
66
- are two separate API calls that already fail independently — the fix is to stop collapsing their
67
- results into one state variable, not to inspect error codes at render time.
68
- - **Fix — split the state:**
69
- 1. Keep `errorKey` (rename mentally to "link check result") for the `useEffect` at line 36–54
70
- only — this is the existing "invalid/expired link" path and must keep hiding the form (the
71
- Risk note: "an invalid/expired token must still end in the 'link invalid' state without a
72
- form"). No change needed to this effect's logic.
73
- 2. Add a new `submitErrorKey` state, set ONLY inside `handleSubmit`'s `catch` block (line
74
- 103–105) instead of reusing `errorKey`. Read the human-readable reason from the response body
75
- the backend actually sends: `normaliseApiError` (see `src/utils/auth-errors.js`) puts the
76
- whole response body on `err.raw`; a password-policy rejection carries
77
- `err.raw?.messages` (an array of strings, e.g. `["This password is too common."]"`) alongside
78
- `err.code`. Prefer joining `err.raw.messages` when it's a non-empty array; otherwise fall back
79
- to `t(err.code || 'Auth.RESET_PASSWORD_FAILED')`, same as today.
80
- 3. Change the render condition so the form shows whenever the LINK itself is valid, regardless
81
- of `submitErrorKey`: `{!errorKey && !successKey && <PasswordSetForm .../>}` (drop the old
82
- shared `errorKey` check that also hid it on submit errors — `errorKey` from here on only ever
83
- means "link invalid", never "password rejected"). Display `submitErrorKey` in its own
84
- `<Typography color="error">` above or below the form, distinct from the existing `errorKey`
85
- alert.
86
- 4. Pass `submitErrorKey` down or keep it page-local and render it directly — `PasswordSetForm`
87
- itself does not need to change (it already renders its OWN `localErrorKey` for client-side
88
- validation; the server-rejection message is a page-level concern rendered alongside the form,
89
- not inside `PasswordSetForm`).
90
- - **Fix — success confirmation instead of a silent jump:** in `handleSubmit`'s try block (line
91
- 66–102), the code currently computes `target` and calls `navigate(target)` immediately after
92
- `setSuccessKey(...)` — the navigation happens before the success message can ever be seen. Stop
93
- calling `navigate` automatically. Instead:
94
- 1. Keep the existing `successKey` set exactly as today (line 68–72) and the existing
95
- `safeNextPath`/`target` computation (line 73–101) — that same-origin validation logic is
96
- correct and must not change.
97
- 2. Store the computed `target` in a new state variable (e.g. `continueTarget`) instead of
98
- navigating with it immediately.
99
- 3. Where `{successKey && <Typography color="primary" ...>}` already renders (line 136–140), add
100
- a `<Button variant="contained" onClick={() => navigate(continueTarget)}>` reading
101
- `t('Auth.SIGNUP_GO_TO_LOGIN')` (existing key, reused — this button always leads to the login
102
- page, same destination the automatic navigate used to go to) right below the confirmation
103
- text. The person now sees the confirmation and clicks through themselves.
104
- - Test file: extend whatever test already covers `PasswordInvitePage` (grep `tests/` for
105
- `PasswordInvitePage` — if none exists, create `tests/PasswordInvitePage.test.jsx` following
106
- `tests/AccountPage.test.jsx`'s page-level render pattern: mock `../src/auth/authApi`'s
107
- `verifyResetToken`/`setNewPassword` with `vi.mock`, wrap in a router since the page uses
108
- `useNavigate`/`useParams`/`useLocation`).
109
- 1. `verifyResetToken` resolves (valid link), `setNewPassword` rejects with
110
- `{ response: { data: { code: 'Auth.RESET_PASSWORD_INVALID', messages: ['This password is too
111
- common.'] } } }` (the shape `normaliseApiError` reads) → after submit, the `PasswordSetForm`
112
- (its password fields) is still present, "This password is too common." is visible, and the
113
- user can submit again.
114
- 2. `verifyResetToken` rejects → the invalid-link message is shown and `PasswordSetForm` is
115
- ABSENT (`screen.queryByLabelText`/`queryByRole` for a password field returns null).
116
- 3. `verifyResetToken` resolves, `setNewPassword` resolves → after submit, the success message is
117
- visible AND a "go to login" button is visible (and the test can assert `navigate` was NOT
118
- called automatically — only after clicking the button, if you choose to test the click too).
119
-
120
- ### 2. `src/pages/SignUpPage.jsx` — signup closed but form still renders
121
-
122
- - `signupModes` (line 51–59) is already computed as an empty array when self-signup is off. The
123
- page currently still renders the mode-picker (only if `signupModes.length > 1`, so with zero
124
- modes that block is already skipped) AND the full email/access-code form regardless, with the
125
- submit button just permanently disabled (line 234: `disabled={submitting || signupModes.length
126
- === 0 || ...}`) and no explanation anywhere.
127
- - **Fix:** immediately after the `NarrowPage`'s `<Helmet>` block (after line 165), add:
128
- `{signupModes.length === 0 ? (<Alert severity="info" sx={SIGN_UP_PAGE_ALERT_SX}>{t('Auth.SIGNUP_CLOSED', 'Sign-up is currently closed for this project.')}</Alert>) : (<>...the existing success/error
129
- alerts + mode-picker + form + "already have an account" block, unchanged...</>)}` — i.e. wrap
130
- everything from the existing `successKey` alert (line 166) through the "already have an account"
131
- block (line 254) in this conditional, so a zero-modes project shows ONLY the explanatory message,
132
- no form at all. New translation key `Auth.SIGNUP_CLOSED` needs an entry in `authTranslations.ts`
133
- (all locales the file already carries: de/fr/en/sw) — do not leave it English-only via the
134
- `t(key, fallback)` default-only pattern, add the real bundle entry.
135
- - Test file: extend whatever exists for `SignUpPage` (grep `tests/` for `SignUpPage`).
136
- 1. `authMethods.signup_modes = []` and `authMethods.signup = false` → the explanatory message is
137
- shown, no email/access-code `TextField` and no submit button are present.
138
- 2. Existing signup-open behaviour (at least one mode) is unchanged — re-run/extend whatever
139
- assertions already cover the working case so this fix can't be a silent regression.
140
-
141
- ### 3. `src/components/UserListComponent.jsx` — role change has no feedback
142
-
143
- - `handleChangeRole` (line 106–124) already has a `catch` that shows `alert(...)` on failure — that
144
- half already satisfies "or shows why not". The missing half is SUCCESS feedback: today a
145
- successful role change just silently re-fetches (`await loadUsers()`, line 120) with nothing
146
- telling the person it worked.
147
- - **Fix:** add a lightweight success signal after the `await loadUsers()` call inside the `try`
148
- block (line 106–120) — the simplest, lowest-risk option consistent with this component's existing
149
- style (it already uses the browser's own `alert()`/`window.confirm()` for feedback elsewhere in
150
- this same file, e.g. line 93, 102, 122 — match that existing convention rather than introducing a
151
- new Snackbar/Toast pattern this component doesn't otherwise use):
152
- `alert(t('UserList.ROLE_UPDATE_SUCCESS', 'Role updated.'));` right after `await loadUsers();`.
153
- New key `UserList.ROLE_UPDATE_SUCCESS` needs a real bundle entry (all locales) in
154
- `authTranslations.ts` — this key is new to `I18N-4`'s hardcoded-English-string list too; if
155
- `I18N-4` lands first in the register it may already exist, check before adding a duplicate.
156
- - Test file: extend the existing `UserListComponent` test (grep `tests/` for
157
- `UserListComponent`) — assert that a successful `onChangeRole`/`updateUserRole` call triggers the
158
- new success signal (mock `window.alert` with `vi.spyOn(window, 'alert')` and assert it was called
159
- with the translated success text), and that the existing failure path is unaffected (still calls
160
- `alert` with the error path, exactly as today — do not weaken that assertion).
161
-
162
- ## Do-not-touch / invariants
163
-
164
- - Password rules and the backend responses (dcm) stay as they are — this WO only changes how the
165
- frontend reads/displays what the backend already returns.
166
- - Strings and the formal/informal address choice belong to `UCM-I18N-4`, not here — new keys added
167
- here (`Auth.SIGNUP_CLOSED`, `UserList.ROLE_UPDATE_SUCCESS`) get plain, correct de/fr/en/sw values,
168
- not the informal-German variant mechanism (that mechanism is `I18N-4`'s own scope).
169
- - Do not touch `SignupConfirmPage.jsx` — out of scope (not named in the Envelope's Context), even
170
- though it has a structurally similar immediate-navigate-after-success pattern; note it in the
171
- register Notiz as a possible follow-up, do not fix it here.
172
-
173
- ## Target repo working directory (absolute)
174
-
175
- `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`)
176
-
177
- ## Preamble — a REQUIRED block IN this file, not something appended at invocation
178
-
179
- > The text above is the COMPLETE spec — the committed WO file's content, not a plan to refine; there
180
- > is no separate plan file. Read the nearest `AGENTS.md`, the relevant `.codex/skills/<role>/SKILL.md`, and the
181
- > app `MEMORY.md` ONLY for conventions. Stay in scope; do not touch auth/permissions/deps/schema/CI
182
- > unless the spec says so; do not update `MEMORY.md`. **Do NOT edit `WORK_ORDERS.md` — the register
183
- > row and the review verdicts are the orchestrator's alone.** **Your tools are for editing source
184
- > and test files and for running the tests you wrote — nothing else.** Do NOT install dependencies,
185
- > touch a lockfile, run a package manager, or tidy up stray files; if something in the repo state
186
- > blocks you, stop and report it as `RESULT: BLOCKED <reason>` instead of fixing it. Do NOT
187
- > `git add`/`commit`/`push` — leave every
188
- > change uncommitted in the working tree for the orchestrator's independent review. WRITE the tests
189
- > the `Required tests` section calls for AND **RUN the tests you just wrote** to confirm they execute
190
- > and pass — that is the ONLY test run you do (NOT the app's affected/full suite, NOT any review).
191
- > The orchestrator re-runs the authoritative set + does the independent review after you finish —
192
- > those are the gate; your own run does not count as the gate.
193
- >
194
- > Narrate continuously: a `PLAN: <step1> | <step2> | …` line up front, then a single-line
195
- > `PROGRESS: [<n>/<total>] <present-tense action>` before every relevant action (and `… done` on
196
- > completion), spaced so no gap exceeds ~2 min, stdout unbuffered, plus exactly one final
197
- > `RESULT: DONE|BLOCKED <reason>`.
198
-
199
- ---
200
-
201
- # C. Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
202
-
203
- > **If you are the implementer reading this work order as your own specification: STOP at this line.
204
- > Everything below describes what the Orchestrator does AFTER you finish. You do none of it — no
205
- > reviewers, no verification run, no register edit, no commit.**
206
-
207
- *(to be filled by the Orchestrator: execution directive; `reviewer` (all lenses) + `sec_reviewer` + `ui_reviewer`; release; jg-ferien pin bump; register rows here and in jg (`JG-UX-19`).)*