@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.
- package/CHANGELOG.md +25 -0
- package/README.md +7 -0
- package/dist/chat/kitSxRegistry.js +9 -0
- package/dist/components/charts/kitSxRegistry.js +6 -0
- package/dist/entries/charts.js +6 -0
- package/dist/entries/chat.js +8 -0
- package/dist/entries/markdown.js +1 -0
- package/dist/index.js +0 -14
- package/dist/pages/AccountPage.js +1 -2
- package/dist/pages/LoginPage.js +1 -2
- package/dist/pages/PasswordChangePage.js +1 -2
- package/dist/pages/PasswordInvitePage.js +1 -2
- package/dist/pages/PasswordResetRequestPage.js +1 -2
- package/dist/pages/SignUpPage.js +1 -2
- package/dist/pages/SignupConfirmPage.js +1 -2
- package/dist/theme/kitSxEntry.js +11 -0
- package/dist/theme/kitSxRegistry.js +2 -15
- package/package.json +32 -7
- package/.github/workflows/ci.yml +0 -35
- package/.github/workflows/publish.yml +0 -126
- package/DESIGN.md +0 -60
- package/WORK_ORDERS.md +0 -150
- package/dev/entries.jsx +0 -721
- package/dev/fixtures.js +0 -26
- package/dev/main.jsx +0 -75
- package/dev/mockTransport.jsx +0 -128
- package/docs/CHART-LAYOUT.md +0 -157
- package/docs/DARK-TOKENS.md +0 -140
- package/docs/TYPE-SCALE.md +0 -111
- package/docs/messaging-deviations.md +0 -118
- package/index.html +0 -12
- package/renovate.json +0 -10
- package/scripts/chart-api-census.mjs +0 -415
- package/src/auth/AuthContext.jsx +0 -158
- package/src/auth/ReauthModal.jsx +0 -82
- package/src/auth/UserMenu.jsx +0 -121
- package/src/auth/apiClient.jsx +0 -232
- package/src/auth/authApi.jsx +0 -539
- package/src/auth/authConfig.jsx +0 -34
- package/src/auth/reauth.js +0 -47
- package/src/chat/ChatComposer.jsx +0 -36
- package/src/chat/ChatList.jsx +0 -87
- package/src/chat/ChatThread.jsx +0 -128
- package/src/chat/ChatWorkspace.jsx +0 -244
- package/src/chat/MarkdownReply.jsx +0 -22
- package/src/chat/ModelPicker.jsx +0 -85
- package/src/chat/api.js +0 -18
- package/src/chat/useChatSocket.js +0 -32
- package/src/chat/wire.js +0 -7
- package/src/components/AccessCodeManager.jsx +0 -288
- package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
- package/src/components/AllowedEmailDomainsManager.jsx +0 -92
- package/src/components/AuthFactorRequirementCard.jsx +0 -95
- package/src/components/BulkInviteCsvTab.jsx +0 -198
- package/src/components/EmptyState.jsx +0 -38
- package/src/components/LoginForm.jsx +0 -159
- package/src/components/MFAComponent.jsx +0 -350
- package/src/components/MfaLoginComponent.jsx +0 -252
- package/src/components/MobileBottomNav.jsx +0 -82
- package/src/components/PasskeysComponent.jsx +0 -252
- package/src/components/PasswordChangeForm.jsx +0 -87
- package/src/components/PasswordResetRequestForm.jsx +0 -43
- package/src/components/PasswordSetForm.jsx +0 -97
- package/src/components/ProfileComponent.jsx +0 -348
- package/src/components/QrSignupManager.jsx +0 -404
- package/src/components/QrSignupValidityManager.jsx +0 -106
- package/src/components/RegistrationMethodsManager.jsx +0 -150
- package/src/components/SecurityComponent.jsx +0 -141
- package/src/components/SocialLoginButtons.jsx +0 -83
- package/src/components/SoftChip.jsx +0 -93
- package/src/components/StatTile.jsx +0 -39
- package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
- package/src/components/TurnstileWidget.jsx +0 -99
- package/src/components/UserInviteComponent.jsx +0 -76
- package/src/components/UserListComponent.jsx +0 -618
- package/src/components/charts/BarChart.jsx +0 -102
- package/src/components/charts/ChartFrame.jsx +0 -214
- package/src/components/charts/LineChart.jsx +0 -126
- package/src/components/charts/ScatterChart.jsx +0 -299
- package/src/components/charts/TimeSeriesChart.jsx +0 -300
- package/src/components/charts/chartDefaults.js +0 -644
- package/src/components/charts/chartLabels.js +0 -36
- package/src/components/charts/exportChart.js +0 -258
- package/src/components/charts/formatters.js +0 -39
- package/src/components/charts/palette.js +0 -33
- package/src/components/charts/yearTickInterval.js +0 -7
- package/src/i18n/authTranslations.ts +0 -2651
- package/src/i18n/chartsTranslations.ts +0 -91
- package/src/i18n/chatTranslations.ts +0 -55
- package/src/i18n/messagingTranslations.ts +0 -120
- package/src/i18n/notificationsTranslations.ts +0 -32
- package/src/i18n/onboardingTranslations.ts +0 -29
- package/src/i18n/sectionNavTranslations.ts +0 -20
- package/src/i18n/uiCoreTranslations.js +0 -29
- package/src/i18n/userMenuTranslations.ts +0 -5
- package/src/index.js +0 -158
- package/src/layout/PageLayout.jsx +0 -36
- package/src/layout/SectionNav.jsx +0 -320
- package/src/messaging/AttachmentList.jsx +0 -145
- package/src/messaging/Composer.jsx +0 -197
- package/src/messaging/ConversationLaunchers.jsx +0 -51
- package/src/messaging/ConversationList.jsx +0 -109
- package/src/messaging/DirectMessageLauncher.jsx +0 -112
- package/src/messaging/MessageBubble.jsx +0 -183
- package/src/messaging/MessagingProvider.jsx +0 -0
- package/src/messaging/MessagingScopeConfig.jsx +0 -24
- package/src/messaging/PollCard.jsx +0 -163
- package/src/messaging/ReactionBar.jsx +0 -43
- package/src/messaging/ReadTicks.jsx +0 -82
- package/src/messaging/Thread.jsx +0 -195
- package/src/messaging/api.js +0 -48
- package/src/messaging/composerImageCompression.js +0 -35
- package/src/messaging/linkifyText.jsx +0 -37
- package/src/notifications/NotificationBell.jsx +0 -84
- package/src/notifications/NotificationSettings.jsx +0 -330
- package/src/notifications/NotificationsProvider.jsx +0 -224
- package/src/notifications/PopupSurface.jsx +0 -105
- package/src/notifications/api.js +0 -55
- package/src/notifications/feedApi.js +0 -18
- package/src/notifications/realtime.jsx +0 -138
- package/src/notifications/serviceWorker/sw.js +0 -50
- package/src/onboarding/OnboardingProvider.jsx +0 -326
- package/src/onboarding/OnboardingWizard.jsx +0 -61
- package/src/onboarding/WizardDialogShell.jsx +0 -51
- package/src/onboarding/api.js +0 -13
- package/src/onboarding/stepSelection.js +0 -16
- package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
- package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
- package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
- package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
- package/src/pages/AccountPage.jsx +0 -394
- package/src/pages/LoginPage.jsx +0 -343
- package/src/pages/PasswordChangePage.jsx +0 -62
- package/src/pages/PasswordInvitePage.jsx +0 -177
- package/src/pages/PasswordResetRequestPage.jsx +0 -68
- package/src/pages/SignUpPage.jsx +0 -268
- package/src/pages/SignupConfirmPage.jsx +0 -155
- package/src/testing/checkKitIntegration.js +0 -233
- package/src/theme/createAppTheme.js +0 -423
- package/src/theme/fonts.js +0 -3
- package/src/theme/index.js +0 -21
- package/src/theme/kitSxRegistry.js +0 -285
- package/src/theme/themeCompleteness.js +0 -1045
- package/src/theme/tokens.js +0 -478
- package/src/utils/auth-errors.js +0 -76
- package/src/utils/authService.js +0 -223
- package/src/utils/resolveErrorText.js +0 -28
- package/src/utils/webauthn.js +0 -51
- package/tests/AccountPage.profileStatements.test.jsx +0 -162
- package/tests/AccountPage.test.jsx +0 -200
- package/tests/AuthContext.test.jsx +0 -129
- package/tests/BarChart.test.jsx +0 -112
- package/tests/BrowserPushStep.test.jsx +0 -110
- package/tests/ChartFrame.test.jsx +0 -336
- package/tests/ConversationList.test.jsx +0 -144
- package/tests/CookieConsentStep.test.jsx +0 -69
- package/tests/EmptyState.test.jsx +0 -62
- package/tests/LineChart.test.jsx +0 -80
- package/tests/MessagingProvider.test.jsx +0 -50
- package/tests/MobileBottomNav.test.jsx +0 -319
- package/tests/NotificationBell.test.jsx +0 -60
- package/tests/NotificationSettings.test.jsx +0 -276
- package/tests/NotificationsProvider.test.jsx +0 -240
- package/tests/OnboardingProvider.test.jsx +0 -294
- package/tests/OnboardingWizard.test.jsx +0 -143
- package/tests/PasswordChangeForm.test.jsx +0 -70
- package/tests/PasswordInvitePage.test.jsx +0 -142
- package/tests/PasswordSetForm.test.jsx +0 -70
- package/tests/PopupSurface.test.jsx +0 -142
- package/tests/ProfileComponent.refresh.test.jsx +0 -194
- package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
- package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
- package/tests/ScatterChart.test.jsx +0 -270
- package/tests/SectionNav.test.jsx +0 -363
- package/tests/SignUpPage.test.jsx +0 -208
- package/tests/SoftChip.test.jsx +0 -105
- package/tests/StatTile.test.jsx +0 -57
- package/tests/Thread.test.jsx +0 -151
- package/tests/TimeSeriesChart.test.jsx +0 -367
- package/tests/UserListComponent.test.jsx +0 -192
- package/tests/UserMenu.test.jsx +0 -171
- package/tests/apiClientLanguage.test.js +0 -33
- package/tests/apiClientPublicPaths.test.js +0 -178
- package/tests/authApiLogin.test.js +0 -42
- package/tests/authApiRegistration.test.js +0 -31
- package/tests/authErrorCodeRendering.test.jsx +0 -77
- package/tests/authErrors.test.js +0 -74
- package/tests/authTranslations.test.js +0 -74
- package/tests/chart-api-census.test.js +0 -366
- package/tests/chartAxisTickBand.test.jsx +0 -95
- package/tests/chartDefaults.test.js +0 -902
- package/tests/chartLabels.test.js +0 -50
- package/tests/chartsPalette.test.js +0 -99
- package/tests/chatApi.test.js +0 -48
- package/tests/chatDriverNames.test.jsx +0 -71
- package/tests/chatExports.test.js +0 -3
- package/tests/chatList.test.jsx +0 -84
- package/tests/chatPicker.test.jsx +0 -82
- package/tests/chatSocket.test.jsx +0 -26
- package/tests/chatThread.test.jsx +0 -116
- package/tests/chatTranslations.test.js +0 -47
- package/tests/chatWorkspace.test.jsx +0 -234
- package/tests/checkKitIntegration.test.jsx +0 -219
- package/tests/createAppTheme.test.js +0 -132
- package/tests/createAppThemeDark.test.js +0 -127
- package/tests/createAppThemeLightGuard.test.js +0 -40
- package/tests/createAppThemePageContrast.test.js +0 -108
- package/tests/createAppThemeTypography.test.js +0 -53
- package/tests/errorCallSiteGuard.test.js +0 -32
- package/tests/exportChart.test.js +0 -313
- package/tests/feedApi.test.js +0 -46
- package/tests/fixtures/light-theme-baseline.json +0 -2234
- package/tests/i18nAggregate.test.js +0 -120
- package/tests/i18nGermanVariant.test.js +0 -65
- package/tests/loginPageErrorResolution.test.jsx +0 -102
- package/tests/messagingApi.test.js +0 -32
- package/tests/messagingChunk6.test.jsx +0 -116
- package/tests/messagingCompactBubble.test.jsx +0 -64
- package/tests/messagingComposer.test.jsx +0 -136
- package/tests/messagingComposerChunk5.test.jsx +0 -113
- package/tests/messagingContractConformance.test.js +0 -69
- package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
- package/tests/messagingExports.test.js +0 -21
- package/tests/messagingInteractions.test.jsx +0 -136
- package/tests/messagingLastMessagePreview.test.jsx +0 -93
- package/tests/messagingLinkify.test.jsx +0 -51
- package/tests/messagingMessageActions.test.jsx +0 -163
- package/tests/messagingMsg6.test.jsx +0 -105
- package/tests/messagingMsg6f.test.jsx +0 -155
- package/tests/messagingMsg6g.test.jsx +0 -166
- package/tests/messagingMsg6h.test.jsx +0 -136
- package/tests/messagingMsg6i.test.jsx +0 -129
- package/tests/messagingPollRendering.test.jsx +0 -142
- package/tests/messagingReactionMerge.test.jsx +0 -77
- package/tests/messagingReadTicksTooltip.test.jsx +0 -36
- package/tests/messagingThreadReceipts.test.jsx +0 -178
- package/tests/messagingThreadingQuoting.test.jsx +0 -87
- package/tests/messagingUnreadLifecycle.test.jsx +0 -78
- package/tests/notificationsApi.test.js +0 -41
- package/tests/notificationsExports.test.js +0 -34
- package/tests/onboardingDescriptors.test.js +0 -132
- package/tests/packageTreeShaking.test.js +0 -126
- package/tests/realtime.test.jsx +0 -219
- package/tests/resolveErrorText.test.js +0 -72
- package/tests/stepSelection.test.js +0 -53
- package/tests/themeCompleteness.test.js +0 -626
- package/tests/timeSeriesLegend.test.js +0 -99
- package/tests/userScreenTranslations.test.jsx +0 -218
- package/tsconfig.build.json +0 -18
- package/vite.config.mjs +0 -6
- package/work-orders/AUTH-1.md +0 -74
- package/work-orders/AUTH-2.md +0 -74
- package/work-orders/AUTH-4.md +0 -183
- package/work-orders/AUTH-5.md +0 -154
- package/work-orders/AUTH-7.md +0 -181
- package/work-orders/AUTH-8.md +0 -184
- package/work-orders/CHART-1.md +0 -285
- package/work-orders/CHART-2.md +0 -222
- package/work-orders/CHART-3.md +0 -163
- package/work-orders/CHART-4.md +0 -203
- package/work-orders/CHART-5.md +0 -226
- package/work-orders/CHART-6.md +0 -428
- package/work-orders/CHART-7.md +0 -175
- package/work-orders/DX-1.md +0 -169
- package/work-orders/DX-2.md +0 -188
- package/work-orders/DX-3.md +0 -195
- package/work-orders/I18N-1.md +0 -254
- package/work-orders/I18N-3.md +0 -207
- package/work-orders/MSG-3.md +0 -375
- package/work-orders/MSG-3b.md +0 -1002
- package/work-orders/MSG-3c.md +0 -378
- package/work-orders/MSG-3d.md +0 -268
- package/work-orders/MSG-3e.md +0 -164
- package/work-orders/MSG-3f.md +0 -391
- package/work-orders/MSG-6e.md +0 -308
- package/work-orders/MSG-6f.md +0 -214
- package/work-orders/MSG-6g.md +0 -244
- package/work-orders/MSG-6h.md +0 -238
- package/work-orders/MSG-6i.md +0 -271
- package/work-orders/MSG-6j.md +0 -117
- package/work-orders/NOTIF-12.md +0 -214
- package/work-orders/NOTIF-13.md +0 -142
- package/work-orders/NOTIF-14.md +0 -151
- package/work-orders/PRIM-1.md +0 -177
- package/work-orders/SHELL-1.md +0 -255
- package/work-orders/SHELL-2.md +0 -197
- package/work-orders/SHELL-3.md +0 -359
- package/work-orders/SHELL-4.md +0 -351
- package/work-orders/SHELL-5.md +0 -345
- package/work-orders/SHELL-6.md +0 -210
- package/work-orders/THEME-1.md +0 -587
- package/work-orders/THEME-10.md +0 -303
- package/work-orders/THEME-11.md +0 -208
- package/work-orders/THEME-2.md +0 -339
- package/work-orders/THEME-3.md +0 -210
- package/work-orders/THEME-4.md +0 -271
- package/work-orders/THEME-5.md +0 -341
- package/work-orders/THEME-6.md +0 -247
- package/work-orders/THEME-7.md +0 -82
- package/work-orders/THEME-8.md +0 -211
- package/work-orders/THEME-9.md +0 -194
- package/work-orders/UCM-AUTH-10.md +0 -490
- package/work-orders/UCM-AUTH-11.md +0 -185
- package/work-orders/UCM-AUTH-12.md +0 -98
- package/work-orders/UCM-AUTH-13.md +0 -47
- package/work-orders/UCM-AUTH-9.md +0 -207
- package/work-orders/UCM-CHART-10.md +0 -150
- package/work-orders/UCM-CHART-11.md +0 -132
- package/work-orders/UCM-CHART-12.md +0 -348
- package/work-orders/UCM-CHART-13.md +0 -122
- package/work-orders/UCM-CHART-14.md +0 -198
- package/work-orders/UCM-CHART-15.md +0 -187
- package/work-orders/UCM-CHART-16.md +0 -169
- package/work-orders/UCM-CHART-17.md +0 -195
- package/work-orders/UCM-CHART-18.md +0 -200
- package/work-orders/UCM-CHART-8.md +0 -147
- package/work-orders/UCM-CHART-9.md +0 -265
- package/work-orders/UCM-CHAT-1.md +0 -190
- package/work-orders/UCM-CHAT-2.md +0 -75
- package/work-orders/UCM-CI-1.md +0 -65
- package/work-orders/UCM-DEP-1.md +0 -63
- package/work-orders/UCM-I18N-4.md +0 -226
- package/work-orders/UCM-I18N-5.md +0 -211
- package/work-orders/UCM-MSG-20.md +0 -192
- package/work-orders/UCM-PRIM-2.md +0 -114
- package/work-orders/UCM-SHELL-7.md +0 -134
- package/work-orders/UCM-SHELL-8.md +0 -125
- package/work-orders/UCM-TEST-1.md +0 -304
- package/work-orders/UCM-THEME-13.md +0 -69
- package/work-orders/UCM-THEME-14.md +0 -312
- package/work-orders/UCM-THEME-15.md +0 -356
- package/work-orders/UCM-THEME-16.md +0 -80
- package/work-orders/UCM-THEME-17.md +0 -111
- package/work-orders/UCM-THEME-18.md +0 -184
- package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
- package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
- package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
- package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
- package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
- package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
- 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`).)*
|