Repository navigation
feat(users): ✨ add user identity, notifications, profile, preferences, and security panels - #846
Conversation
…, and security panels
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c4f134cd3b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await db | ||
| .update(core_users) | ||
| .set({ password: hashedPassword }) | ||
| .where(eq(core_users.id, user.id)); |
There was a problem hiding this comment.
Emit an event for administrator password resets
When staff successfully use this new endpoint, the password is changed and all sessions are revoked without emitting a domain event. Unlike the passkey and SSO mutations added here, plugins therefore cannot audit the reset or notify the account owner; emit and document an event after the write succeeds.
AGENTS.md reference: AGENTS.md:L57-L57
Useful? React with 👍 / 👎.
| <EditSheetContent description={<>{description}</>} title={<>{title}</>}> | ||
| {children} | ||
| </EditSheetContent> |
There was a problem hiding this comment.
Lazy-load the new form dialogs
These ordinary dialog children keep both PersonalForm and PreferencesForm, including their AutoForm and combobox dependencies, in the initial user-detail bundle even when neither dialog is opened. Split the form contents into lazy imports and render them through Suspense so opening the detail page does not eagerly download every editor.
AGENTS.md reference: AGENTS.md:L14-L14
Useful? React with 👍 / 👎.
| const hashedPassword = await new PasswordModel().encryptPassword(password); | ||
| await db | ||
| .update(core_users) | ||
| .set({ password: hashedPassword }) | ||
| .where(eq(core_users.id, user.id)); |
There was a problem hiding this comment.
Reject resets when password sign-in is disabled
When authorization.password.enabled is false, this route still stores a password and immediately revokes every target session, although that credential cannot be used to sign back in and the SSO overview reports password sign-in as disabled. The member-facing reset route guards this state with assertPasswordSignInEnabled; apply the same guard before hashing or mutating here to avoid a destructive, unusable reset.
Useful? React with 👍 / 👎.
| const options = | ||
| languages.length > 0 | ||
| ? languages | ||
| : [{ code: user.language, name: user.language }]; | ||
| const codes = options.map(language => language.code); | ||
| const [firstCode = user.language, ...restCodes] = codes; | ||
|
|
||
| const formSchema = z.object({ | ||
| language: z.enum([firstCode, ...restCodes]).default(user.language), |
There was a problem hiding this comment.
Preserve the user's disabled locale in the form enum
If the target user still has a locale that is now configured with enabled: false, useLanguages() omits it, but the schema still defaults to user.language. On submission that defined value is not a member of the enum, so saving an unrelated time-zone or newsletter change fails unless staff first replace the locale. Include the current locale in options even when it is disabled.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 77d406640f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return c.json({ error: "Device not found" }, 404); | ||
| } | ||
|
|
||
| await revokeSessions(c, { deviceId, userId }); |
There was a problem hiding this comment.
Emit events for administrator session revocations
When staff use this endpoint, both user and AdminCP sessions are deleted without emitting a domain event, so plugins cannot audit or react to the forced sign-out; the bulk DELETE /{id}/devices handler has the same omission. Emit and document a session-revocation event after the deletion succeeds.
AGENTS.md reference: AGENTS.md:L57-L57
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| await assertCanEditAdminTarget(c, userId); | ||
| await updateNotificationPreferences(c, userId, c.req.valid("json")); |
There was a problem hiding this comment.
Emit an event for notification preference updates
When an administrator changes a member's notification channels, the write completes without an event, leaving plugins and audit integrations unable to observe who changed the delivery behavior. Emit and document a preference-update event after the mutation succeeds.
AGENTS.md reference: AGENTS.md:L57-L57
Useful? React with 👍 / 👎.
| await new SsoConnectionModel(c).savePreferences({ | ||
| sources, | ||
| sync, | ||
| userId, | ||
| }); |
There was a problem hiding this comment.
Emit an event for SSO preference updates
Saving profile-source or sync-on-sign-in settings currently changes persistent SSO behavior without emitting an event, even though link, unlink, and profile-sync mutations are observable. Emit and document an event after savePreferences succeeds so plugins can audit and react to this administrator action.
AGENTS.md reference: AGENTS.md:L57-L57
Useful? React with 👍 / 👎.
| const passkeys = useQuery({ | ||
| ...adminUserPasskeysQueryOptions(key), | ||
| enabled: passkeysEnabled, |
There was a problem hiding this comment.
Stop leaving passkeys pending after an SSO query error
If the SSO overview request ends in an error, passkeysEnabled remains false and this passkey query stays disabled; however, PasskeysSection receives enabled={!sso.isSuccess || passkeysEnabled}, which is true in that state, so it renders the disabled query's permanent isPending skeleton instead of an error. Handle sso.isError explicitly or keep the section disabled/error-aware when the capability lookup fails.
Useful? React with 👍 / 👎.
- Emit `user.password.updated` event after password change. - Add new translations for password status and loading errors. - Refactor user security forms into separate components for better organization. - Implement lazy loading for user profile forms and security forms. - Enhance user experience with loading indicators and error handling in forms.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33fc8540b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .enum([AUTOMATIC_TIME_ZONE, ...zones]) | ||
| .default(user.timeZone ?? AUTOMATIC_TIME_ZONE), |
There was a problem hiding this comment.
Preserve stored time-zone aliases in the form enum
When the user has a valid IANA alias such as US/Eastern or Etc/UTC, the API accepts and stores it because isValidTimeZone uses Intl.DateTimeFormat, but supportedTimeZones() omits aliases because it relies on Intl.supportedValuesOf. The schema therefore defaults to a value outside its enum, so submitting an unrelated language or newsletter change fails unless staff first replace the time zone. Include the current stored zone in the enum and labels, as this form already does for the current language.
Useful? React with 👍 / 👎.
Improving Documentation
pnpm lint:fixto fix formatting issues before opening the PR.Description
What?
Why?