@micha.bigler/ui-core-micha 3.14.3 → 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 (367) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +7 -0
  3. package/dist/auth/UserMenu.js +1 -1
  4. package/dist/auth/apiClient.js +7 -6
  5. package/dist/chat/ChatList.js +1 -1
  6. package/dist/chat/ChatThread.js +2 -1
  7. package/dist/chat/ChatWorkspace.js +1 -1
  8. package/dist/chat/ModelPicker.js +2 -5
  9. package/dist/chat/kitSxRegistry.js +9 -0
  10. package/dist/chat/useChatSocket.js +1 -1
  11. package/dist/components/LoginForm.js +1 -1
  12. package/dist/components/SecurityComponent.js +1 -1
  13. package/dist/components/UserListComponent.js +2 -8
  14. package/dist/components/charts/ChartFrame.js +1 -1
  15. package/dist/components/charts/ScatterChart.js +2 -1
  16. package/dist/components/charts/kitSxRegistry.js +6 -0
  17. package/dist/entries/charts.js +6 -0
  18. package/dist/entries/chat.js +8 -0
  19. package/dist/entries/markdown.js +1 -0
  20. package/dist/index.js +0 -14
  21. package/dist/layout/SectionNav.js +1 -1
  22. package/dist/messaging/AttachmentList.js +2 -2
  23. package/dist/messaging/Composer.js +2 -1
  24. package/dist/messaging/ConversationLaunchers.js +1 -4
  25. package/dist/messaging/ConversationList.js +1 -1
  26. package/dist/messaging/DirectMessageLauncher.js +1 -1
  27. package/dist/messaging/MessagingProvider.js +0 -0
  28. package/dist/messaging/PollCard.js +7 -5
  29. package/dist/messaging/ReadTicks.js +2 -2
  30. package/dist/messaging/Thread.js +5 -3
  31. package/dist/notifications/NotificationBell.js +4 -7
  32. package/dist/notifications/NotificationSettings.js +1 -1
  33. package/dist/onboarding/OnboardingProvider.js +3 -6
  34. package/dist/pages/AccountPage.js +1 -2
  35. package/dist/pages/LoginPage.js +2 -3
  36. package/dist/pages/PasswordChangePage.js +2 -3
  37. package/dist/pages/PasswordInvitePage.js +2 -3
  38. package/dist/pages/PasswordResetRequestPage.js +2 -3
  39. package/dist/pages/SignUpPage.js +2 -3
  40. package/dist/pages/SignupConfirmPage.js +2 -3
  41. package/dist/theme/createAppTheme.js +5 -4
  42. package/dist/theme/kitSxEntry.js +11 -0
  43. package/dist/theme/kitSxRegistry.js +2 -15
  44. package/dist/theme/themeCompleteness.js +3 -12
  45. package/dist/utils/auth-errors.js +4 -3
  46. package/package.json +41 -16
  47. package/.github/workflows/publish.yml +0 -126
  48. package/DESIGN.md +0 -60
  49. package/WORK_ORDERS.md +0 -148
  50. package/dev/entries.jsx +0 -721
  51. package/dev/fixtures.js +0 -26
  52. package/dev/main.jsx +0 -75
  53. package/dev/mockTransport.jsx +0 -128
  54. package/docs/CHART-LAYOUT.md +0 -157
  55. package/docs/DARK-TOKENS.md +0 -140
  56. package/docs/TYPE-SCALE.md +0 -111
  57. package/docs/messaging-deviations.md +0 -118
  58. package/index.html +0 -12
  59. package/renovate.json +0 -10
  60. package/scripts/chart-api-census.mjs +0 -415
  61. package/src/auth/AuthContext.jsx +0 -158
  62. package/src/auth/ReauthModal.jsx +0 -82
  63. package/src/auth/UserMenu.jsx +0 -121
  64. package/src/auth/apiClient.jsx +0 -232
  65. package/src/auth/authApi.jsx +0 -539
  66. package/src/auth/authConfig.jsx +0 -34
  67. package/src/auth/reauth.js +0 -47
  68. package/src/chat/ChatComposer.jsx +0 -36
  69. package/src/chat/ChatList.jsx +0 -87
  70. package/src/chat/ChatThread.jsx +0 -128
  71. package/src/chat/ChatWorkspace.jsx +0 -244
  72. package/src/chat/MarkdownReply.jsx +0 -22
  73. package/src/chat/ModelPicker.jsx +0 -85
  74. package/src/chat/api.js +0 -18
  75. package/src/chat/useChatSocket.js +0 -32
  76. package/src/chat/wire.js +0 -7
  77. package/src/components/AccessCodeManager.jsx +0 -288
  78. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  79. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  80. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  81. package/src/components/BulkInviteCsvTab.jsx +0 -198
  82. package/src/components/EmptyState.jsx +0 -38
  83. package/src/components/LoginForm.jsx +0 -159
  84. package/src/components/MFAComponent.jsx +0 -350
  85. package/src/components/MfaLoginComponent.jsx +0 -252
  86. package/src/components/MobileBottomNav.jsx +0 -82
  87. package/src/components/PasskeysComponent.jsx +0 -252
  88. package/src/components/PasswordChangeForm.jsx +0 -87
  89. package/src/components/PasswordResetRequestForm.jsx +0 -43
  90. package/src/components/PasswordSetForm.jsx +0 -97
  91. package/src/components/ProfileComponent.jsx +0 -348
  92. package/src/components/QrSignupManager.jsx +0 -404
  93. package/src/components/QrSignupValidityManager.jsx +0 -106
  94. package/src/components/RegistrationMethodsManager.jsx +0 -150
  95. package/src/components/SecurityComponent.jsx +0 -141
  96. package/src/components/SocialLoginButtons.jsx +0 -83
  97. package/src/components/SoftChip.jsx +0 -93
  98. package/src/components/StatTile.jsx +0 -39
  99. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  100. package/src/components/TurnstileWidget.jsx +0 -99
  101. package/src/components/UserInviteComponent.jsx +0 -76
  102. package/src/components/UserListComponent.jsx +0 -618
  103. package/src/components/charts/BarChart.jsx +0 -102
  104. package/src/components/charts/ChartFrame.jsx +0 -214
  105. package/src/components/charts/LineChart.jsx +0 -126
  106. package/src/components/charts/ScatterChart.jsx +0 -299
  107. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  108. package/src/components/charts/chartDefaults.js +0 -644
  109. package/src/components/charts/chartLabels.js +0 -36
  110. package/src/components/charts/exportChart.js +0 -258
  111. package/src/components/charts/formatters.js +0 -39
  112. package/src/components/charts/palette.js +0 -33
  113. package/src/components/charts/yearTickInterval.js +0 -7
  114. package/src/i18n/authTranslations.ts +0 -2651
  115. package/src/i18n/chartsTranslations.ts +0 -91
  116. package/src/i18n/chatTranslations.ts +0 -55
  117. package/src/i18n/messagingTranslations.ts +0 -120
  118. package/src/i18n/notificationsTranslations.ts +0 -32
  119. package/src/i18n/onboardingTranslations.ts +0 -29
  120. package/src/i18n/sectionNavTranslations.ts +0 -20
  121. package/src/i18n/uiCoreTranslations.js +0 -29
  122. package/src/i18n/userMenuTranslations.ts +0 -5
  123. package/src/index.js +0 -158
  124. package/src/layout/PageLayout.jsx +0 -36
  125. package/src/layout/SectionNav.jsx +0 -320
  126. package/src/messaging/AttachmentList.jsx +0 -145
  127. package/src/messaging/Composer.jsx +0 -197
  128. package/src/messaging/ConversationLaunchers.jsx +0 -51
  129. package/src/messaging/ConversationList.jsx +0 -109
  130. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  131. package/src/messaging/MessageBubble.jsx +0 -183
  132. package/src/messaging/MessagingProvider.jsx +0 -0
  133. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  134. package/src/messaging/PollCard.jsx +0 -163
  135. package/src/messaging/ReactionBar.jsx +0 -43
  136. package/src/messaging/ReadTicks.jsx +0 -82
  137. package/src/messaging/Thread.jsx +0 -195
  138. package/src/messaging/api.js +0 -48
  139. package/src/messaging/composerImageCompression.js +0 -35
  140. package/src/messaging/linkifyText.jsx +0 -37
  141. package/src/notifications/NotificationBell.jsx +0 -84
  142. package/src/notifications/NotificationSettings.jsx +0 -330
  143. package/src/notifications/NotificationsProvider.jsx +0 -224
  144. package/src/notifications/PopupSurface.jsx +0 -105
  145. package/src/notifications/api.js +0 -55
  146. package/src/notifications/feedApi.js +0 -18
  147. package/src/notifications/realtime.jsx +0 -138
  148. package/src/notifications/serviceWorker/sw.js +0 -50
  149. package/src/onboarding/OnboardingProvider.jsx +0 -326
  150. package/src/onboarding/OnboardingWizard.jsx +0 -61
  151. package/src/onboarding/WizardDialogShell.jsx +0 -51
  152. package/src/onboarding/api.js +0 -13
  153. package/src/onboarding/stepSelection.js +0 -16
  154. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  155. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  156. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  157. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  158. package/src/pages/AccountPage.jsx +0 -394
  159. package/src/pages/LoginPage.jsx +0 -343
  160. package/src/pages/PasswordChangePage.jsx +0 -62
  161. package/src/pages/PasswordInvitePage.jsx +0 -177
  162. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  163. package/src/pages/SignUpPage.jsx +0 -268
  164. package/src/pages/SignupConfirmPage.jsx +0 -155
  165. package/src/testing/checkKitIntegration.js +0 -233
  166. package/src/theme/createAppTheme.js +0 -423
  167. package/src/theme/fonts.js +0 -3
  168. package/src/theme/index.js +0 -21
  169. package/src/theme/kitSxRegistry.js +0 -285
  170. package/src/theme/themeCompleteness.js +0 -1045
  171. package/src/theme/tokens.js +0 -478
  172. package/src/utils/auth-errors.js +0 -76
  173. package/src/utils/authService.js +0 -223
  174. package/src/utils/resolveErrorText.js +0 -28
  175. package/src/utils/webauthn.js +0 -51
  176. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  177. package/tests/AccountPage.test.jsx +0 -200
  178. package/tests/AuthContext.test.jsx +0 -129
  179. package/tests/BarChart.test.jsx +0 -112
  180. package/tests/BrowserPushStep.test.jsx +0 -110
  181. package/tests/ChartFrame.test.jsx +0 -336
  182. package/tests/ConversationList.test.jsx +0 -144
  183. package/tests/CookieConsentStep.test.jsx +0 -69
  184. package/tests/EmptyState.test.jsx +0 -62
  185. package/tests/LineChart.test.jsx +0 -80
  186. package/tests/MessagingProvider.test.jsx +0 -50
  187. package/tests/MobileBottomNav.test.jsx +0 -319
  188. package/tests/NotificationBell.test.jsx +0 -60
  189. package/tests/NotificationSettings.test.jsx +0 -276
  190. package/tests/NotificationsProvider.test.jsx +0 -240
  191. package/tests/OnboardingProvider.test.jsx +0 -294
  192. package/tests/OnboardingWizard.test.jsx +0 -143
  193. package/tests/PasswordChangeForm.test.jsx +0 -70
  194. package/tests/PasswordInvitePage.test.jsx +0 -142
  195. package/tests/PasswordSetForm.test.jsx +0 -70
  196. package/tests/PopupSurface.test.jsx +0 -142
  197. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  198. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  199. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  200. package/tests/ScatterChart.test.jsx +0 -270
  201. package/tests/SectionNav.test.jsx +0 -363
  202. package/tests/SignUpPage.test.jsx +0 -208
  203. package/tests/SoftChip.test.jsx +0 -105
  204. package/tests/StatTile.test.jsx +0 -57
  205. package/tests/Thread.test.jsx +0 -151
  206. package/tests/TimeSeriesChart.test.jsx +0 -367
  207. package/tests/UserListComponent.test.jsx +0 -192
  208. package/tests/UserMenu.test.jsx +0 -171
  209. package/tests/apiClientLanguage.test.js +0 -33
  210. package/tests/apiClientPublicPaths.test.js +0 -178
  211. package/tests/authApiLogin.test.js +0 -42
  212. package/tests/authApiRegistration.test.js +0 -31
  213. package/tests/authErrorCodeRendering.test.jsx +0 -77
  214. package/tests/authErrors.test.js +0 -74
  215. package/tests/authTranslations.test.js +0 -74
  216. package/tests/chart-api-census.test.js +0 -366
  217. package/tests/chartAxisTickBand.test.jsx +0 -95
  218. package/tests/chartDefaults.test.js +0 -902
  219. package/tests/chartLabels.test.js +0 -50
  220. package/tests/chartsPalette.test.js +0 -99
  221. package/tests/chatApi.test.js +0 -48
  222. package/tests/chatDriverNames.test.jsx +0 -71
  223. package/tests/chatExports.test.js +0 -3
  224. package/tests/chatList.test.jsx +0 -84
  225. package/tests/chatPicker.test.jsx +0 -82
  226. package/tests/chatSocket.test.jsx +0 -26
  227. package/tests/chatThread.test.jsx +0 -116
  228. package/tests/chatTranslations.test.js +0 -47
  229. package/tests/chatWorkspace.test.jsx +0 -234
  230. package/tests/checkKitIntegration.test.jsx +0 -219
  231. package/tests/createAppTheme.test.js +0 -132
  232. package/tests/createAppThemeDark.test.js +0 -127
  233. package/tests/createAppThemeLightGuard.test.js +0 -40
  234. package/tests/createAppThemePageContrast.test.js +0 -108
  235. package/tests/createAppThemeTypography.test.js +0 -53
  236. package/tests/errorCallSiteGuard.test.js +0 -32
  237. package/tests/exportChart.test.js +0 -313
  238. package/tests/feedApi.test.js +0 -46
  239. package/tests/fixtures/light-theme-baseline.json +0 -2234
  240. package/tests/i18nAggregate.test.js +0 -120
  241. package/tests/i18nGermanVariant.test.js +0 -65
  242. package/tests/loginPageErrorResolution.test.jsx +0 -102
  243. package/tests/messagingApi.test.js +0 -32
  244. package/tests/messagingChunk6.test.jsx +0 -116
  245. package/tests/messagingCompactBubble.test.jsx +0 -64
  246. package/tests/messagingComposer.test.jsx +0 -136
  247. package/tests/messagingComposerChunk5.test.jsx +0 -113
  248. package/tests/messagingContractConformance.test.js +0 -69
  249. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  250. package/tests/messagingExports.test.js +0 -21
  251. package/tests/messagingInteractions.test.jsx +0 -136
  252. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  253. package/tests/messagingLinkify.test.jsx +0 -51
  254. package/tests/messagingMessageActions.test.jsx +0 -163
  255. package/tests/messagingMsg6.test.jsx +0 -105
  256. package/tests/messagingMsg6f.test.jsx +0 -155
  257. package/tests/messagingMsg6g.test.jsx +0 -166
  258. package/tests/messagingMsg6h.test.jsx +0 -136
  259. package/tests/messagingMsg6i.test.jsx +0 -129
  260. package/tests/messagingPollRendering.test.jsx +0 -142
  261. package/tests/messagingReactionMerge.test.jsx +0 -77
  262. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  263. package/tests/messagingThreadReceipts.test.jsx +0 -178
  264. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  265. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  266. package/tests/notificationsApi.test.js +0 -41
  267. package/tests/notificationsExports.test.js +0 -34
  268. package/tests/onboardingDescriptors.test.js +0 -132
  269. package/tests/packageTreeShaking.test.js +0 -126
  270. package/tests/realtime.test.jsx +0 -219
  271. package/tests/resolveErrorText.test.js +0 -72
  272. package/tests/stepSelection.test.js +0 -53
  273. package/tests/themeCompleteness.test.js +0 -626
  274. package/tests/timeSeriesLegend.test.js +0 -99
  275. package/tests/userScreenTranslations.test.jsx +0 -218
  276. package/tsconfig.build.json +0 -18
  277. package/vite.config.mjs +0 -6
  278. package/work-orders/AUTH-1.md +0 -74
  279. package/work-orders/AUTH-2.md +0 -74
  280. package/work-orders/AUTH-4.md +0 -183
  281. package/work-orders/AUTH-5.md +0 -154
  282. package/work-orders/AUTH-7.md +0 -181
  283. package/work-orders/AUTH-8.md +0 -184
  284. package/work-orders/CHART-1.md +0 -285
  285. package/work-orders/CHART-2.md +0 -222
  286. package/work-orders/CHART-3.md +0 -163
  287. package/work-orders/CHART-4.md +0 -203
  288. package/work-orders/CHART-5.md +0 -226
  289. package/work-orders/CHART-6.md +0 -428
  290. package/work-orders/CHART-7.md +0 -175
  291. package/work-orders/DX-1.md +0 -169
  292. package/work-orders/DX-2.md +0 -188
  293. package/work-orders/DX-3.md +0 -195
  294. package/work-orders/I18N-1.md +0 -254
  295. package/work-orders/I18N-3.md +0 -207
  296. package/work-orders/MSG-3.md +0 -375
  297. package/work-orders/MSG-3b.md +0 -1002
  298. package/work-orders/MSG-3c.md +0 -378
  299. package/work-orders/MSG-3d.md +0 -268
  300. package/work-orders/MSG-3e.md +0 -164
  301. package/work-orders/MSG-3f.md +0 -391
  302. package/work-orders/MSG-6e.md +0 -308
  303. package/work-orders/MSG-6f.md +0 -214
  304. package/work-orders/MSG-6g.md +0 -244
  305. package/work-orders/MSG-6h.md +0 -238
  306. package/work-orders/MSG-6i.md +0 -271
  307. package/work-orders/MSG-6j.md +0 -117
  308. package/work-orders/NOTIF-12.md +0 -214
  309. package/work-orders/NOTIF-13.md +0 -142
  310. package/work-orders/NOTIF-14.md +0 -151
  311. package/work-orders/PRIM-1.md +0 -177
  312. package/work-orders/SHELL-1.md +0 -255
  313. package/work-orders/SHELL-2.md +0 -197
  314. package/work-orders/SHELL-3.md +0 -359
  315. package/work-orders/SHELL-4.md +0 -351
  316. package/work-orders/SHELL-5.md +0 -345
  317. package/work-orders/SHELL-6.md +0 -210
  318. package/work-orders/THEME-1.md +0 -587
  319. package/work-orders/THEME-10.md +0 -303
  320. package/work-orders/THEME-11.md +0 -208
  321. package/work-orders/THEME-2.md +0 -339
  322. package/work-orders/THEME-3.md +0 -210
  323. package/work-orders/THEME-4.md +0 -271
  324. package/work-orders/THEME-5.md +0 -341
  325. package/work-orders/THEME-6.md +0 -247
  326. package/work-orders/THEME-7.md +0 -82
  327. package/work-orders/THEME-8.md +0 -211
  328. package/work-orders/THEME-9.md +0 -194
  329. package/work-orders/UCM-AUTH-10.md +0 -490
  330. package/work-orders/UCM-AUTH-11.md +0 -185
  331. package/work-orders/UCM-AUTH-12.md +0 -98
  332. package/work-orders/UCM-AUTH-13.md +0 -47
  333. package/work-orders/UCM-AUTH-9.md +0 -207
  334. package/work-orders/UCM-CHART-10.md +0 -150
  335. package/work-orders/UCM-CHART-11.md +0 -132
  336. package/work-orders/UCM-CHART-12.md +0 -348
  337. package/work-orders/UCM-CHART-13.md +0 -122
  338. package/work-orders/UCM-CHART-14.md +0 -198
  339. package/work-orders/UCM-CHART-15.md +0 -187
  340. package/work-orders/UCM-CHART-16.md +0 -169
  341. package/work-orders/UCM-CHART-17.md +0 -195
  342. package/work-orders/UCM-CHART-18.md +0 -200
  343. package/work-orders/UCM-CHART-8.md +0 -147
  344. package/work-orders/UCM-CHART-9.md +0 -265
  345. package/work-orders/UCM-CHAT-1.md +0 -190
  346. package/work-orders/UCM-CHAT-2.md +0 -75
  347. package/work-orders/UCM-DEP-1.md +0 -63
  348. package/work-orders/UCM-I18N-4.md +0 -226
  349. package/work-orders/UCM-I18N-5.md +0 -211
  350. package/work-orders/UCM-MSG-20.md +0 -192
  351. package/work-orders/UCM-PRIM-2.md +0 -114
  352. package/work-orders/UCM-SHELL-7.md +0 -134
  353. package/work-orders/UCM-SHELL-8.md +0 -125
  354. package/work-orders/UCM-TEST-1.md +0 -304
  355. package/work-orders/UCM-THEME-13.md +0 -69
  356. package/work-orders/UCM-THEME-14.md +0 -312
  357. package/work-orders/UCM-THEME-15.md +0 -356
  358. package/work-orders/UCM-THEME-16.md +0 -80
  359. package/work-orders/UCM-THEME-17.md +0 -111
  360. package/work-orders/UCM-THEME-18.md +0 -184
  361. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  362. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  363. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  364. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  365. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  366. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  367. 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`).)*