Skip to content

Enhance create user and manage user forms#1339

Merged
joshunrau merged 17 commits intoDouglasNeuroInformatics:mainfrom
david-roper:enhance-create-user
Apr 23, 2026
Merged

Enhance create user and manage user forms#1339
joshunrau merged 17 commits intoDouglasNeuroInformatics:mainfrom
david-roper:enhance-create-user

Conversation

@david-roper
Copy link
Copy Markdown
Collaborator

@david-roper david-roper commented Apr 22, 2026

The pr adds the following:

Added contact information section to create user form:
phone number
email

Added phone number and email to manage user form.

Added confirm password field and check into manage user form

Added initial values for phone, email and groups into the manage user form

refactored phone number regex to a variable exported from utils directory.

works on issue #1294

code reviewed with claude code

model: Claude Sonnet 4.6

Summary by CodeRabbit

  • New Features

    • Added email and phone number fields to user creation and editing
    • Added password confirmation field to user profile updates
    • Implemented phone number format validation
  • Documentation

    • Added English and French translations for new contact information fields

@david-roper david-roper requested a review from joshunrau as a code owner April 22, 2026 17:27
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

Warning

Rate limit exceeded

@david-roper has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 44 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 58 minutes and 44 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ee5c5664-3538-4e78-ba86-e915c1d29b2f

📥 Commits

Reviewing files that changed from the base of the PR and between a4d0140 and be07dff.

📒 Files selected for processing (3)
  • apps/api/src/users/dto/create-user.dto.ts
  • apps/web/src/translations/common.json
  • apps/web/src/utils/validation.ts

Walkthrough

This PR adds email and phoneNumber support to user management across the API and web applications. Changes include API DTOs and services to accept and persist these fields, web forms updated with new input fields and validation rules, a shared phone number regex utility, and corresponding translation strings.

Changes

Cohort / File(s) Summary
API DTO & Service
apps/api/src/users/dto/create-user.dto.ts, apps/api/src/users/users.service.ts
Added optional email and phoneNumber properties to CreateUserDto with API schema documentation. Updated service to destructure and persist these fields to user records.
Web User Creation Form
apps/web/src/routes/_app/admin/users/create.tsx
Introduced "Contact information" section with email and phoneNumber inputs. Added conditional phone format validation using imported PHONE_REGEX with localized error messaging.
Web User Update Form
apps/web/src/routes/_app/admin/users/index.tsx
Extended form to include email, phoneNumber, and password confirmation fields. Updated validation schema to enforce email format, phone format (when provided), and password confirmation match. Modified form submission to exclude confirmPassword from API payload.
Validation & Profile
apps/web/src/utils/validation.ts, apps/web/src/routes/_app/user.tsx
Extracted phone regex pattern to shared PHONE_REGEX constant. Updated profile form to use centralized validation rule instead of locally defined regex.
Translations
apps/web/src/translations/common.json
Added email and phoneNumber translation keys with English and French labels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • joshunrau
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main changes: adding contact information fields (email and phone number) to both the create-user and manage-user forms across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/api/src/users/dto/create-user.dto.ts (1)

57-59: Duplicate @ApiProperty() decorator on sex.

Line 58 re-applies @ApiProperty() with no args, which overrides / conflicts with the descriptive one on line 57. Not introduced by this PR but adjacent to your changes — worth removing while you're here.

Proposed fix
   `@ApiProperty`({ description: 'Sex at Birth' })
-  `@ApiProperty`()
   sex?: Sex;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/users/dto/create-user.dto.ts` around lines 57 - 59, The sex
property in CreateUserDto has a duplicate decorator; remove the redundant
`@ApiProperty`() so only the descriptive `@ApiProperty`({ description: 'Sex at
Birth' }) remains on the sex?: Sex; field to avoid overriding/conflicting
decorators.
apps/web/src/routes/_app/admin/users/index.tsx (2)

92-100: Password/confirmPassword mismatch flagged even when both are empty-but-differ edge cases — consider gating on password presence.

As written, the check fires whenever confirmPassword !== password. If a user fills in only confirmPassword (without password), they'll still get a mismatch — arguably correct, but consider also skipping when both are undefined/empty (they already match via !== → false, so fine). The real gap: if the user sets password but leaves confirmPassword blank, the mismatch error attaches to confirmPassword only — good. Just confirm UX by gating explicitly on ctx.value.password to avoid showing the error when neither field is touched after an accidental interaction:

-        if (ctx.value.confirmPassword !== ctx.value.password) {
+        if ((ctx.value.password || ctx.value.confirmPassword) && ctx.value.confirmPassword !== ctx.value.password) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/_app/admin/users/index.tsx` around lines 92 - 100, The
custom check on the Zod schema (.check callback) currently flags a mismatch
whenever ctx.value.confirmPassword !== ctx.value.password; update the logic to
only validate equality when a password is present (e.g., gate on
ctx.value.password truthiness) so the mismatch error (pushed to path
['confirmPassword']) is not raised when both fields are untouched/empty — locate
the .check block that inspects ctx.value.confirmPassword and ctx.value.password
and add the conditional guard around the equality check.

321-332: Collapse duplicated initialValues branches.

The two branches differ only in whether additionalPermissions is included. You can build a single object and add additionalPermissions conditionally.

Proposed refactor
-          initialValues: selectedUser?.additionalPermissions.length
-            ? {
-                additionalPermissions: selectedUser.additionalPermissions,
-                email: selectedUser.email ?? undefined,
-                groupIds: new Set(selectedUser.groupIds),
-                phoneNumber: selectedUser.phoneNumber ?? undefined
-              }
-            : {
-                email: selectedUser.email ?? undefined,
-                groupIds: new Set(selectedUser.groupIds),
-                phoneNumber: selectedUser.phoneNumber ?? undefined
-              }
+          initialValues: {
+            email: selectedUser.email ?? undefined,
+            groupIds: new Set(selectedUser.groupIds),
+            phoneNumber: selectedUser.phoneNumber ?? undefined,
+            ...(selectedUser.additionalPermissions.length
+              ? { additionalPermissions: selectedUser.additionalPermissions }
+              : {})
+          }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/_app/admin/users/index.tsx` around lines 321 - 332,
Collapse the duplicated initialValues branches by constructing a single base
object for initialValues (include email: selectedUser.email ?? undefined,
groupIds: new Set(selectedUser.groupIds), phoneNumber: selectedUser.phoneNumber
?? undefined) and then conditionally add additionalPermissions when
selectedUser?.additionalPermissions.length is truthy (e.g., if
(selectedUser?.additionalPermissions?.length)
base.initialValues.additionalPermissions = selectedUser.additionalPermissions).
Update the initialValues assignment to use this single object; reference the
initialValues assignment, selectedUser, and additionalPermissions to locate the
code.
apps/web/src/utils/validation.ts (1)

1-1: Drop the redundant new RegExp(...) wrapper.

Wrapping a regex literal in new RegExp(...) is a no-op — just export the literal directly.

Proposed simplification
-export const PHONE_REGEX = new RegExp(/^\+?\(?\d{1,4}\)?[\s.-]?\d{1,4}[\s.-]?\d{1,9}$/);
+export const PHONE_REGEX = /^\+?\(?\d{1,4}\)?[\s.-]?\d{1,4}[\s.-]?\d{1,9}$/;

Side note: the pattern accepts fairly loose inputs (e.g., 1 is 3 groups of \d{1,N} all optional-separated, but first group is required; strings as short as 2 digits pass). If you want stricter E.164-ish validation, consider a library like libphonenumber-js.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/utils/validation.ts` at line 1, The PHONE_REGEX export wraps a
regex literal in new RegExp(...) which is redundant; replace the wrapped
expression by exporting the regex literal directly (replace export const
PHONE_REGEX = new RegExp(/^\+?\(?\d{1,4}\)?[\s.-]?\d{1,4}[\s.-]?\d{1,9}$/); with
a direct literal assignment to PHONE_REGEX) and, if desired, consider tightening
the pattern or using libphonenumber-js for stricter E.164-style validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/users/dto/create-user.dto.ts`:
- Around line 35-36: The $CreateUserData schema currently validates email with
z.email() but leaves phoneNumber as z.string(); update the phoneNumber validator
in the $CreateUserData schema (and the other instance noted) to enforce the same
PHONE_REGEX used by the UI (e.g. z.string().regex(PHONE_REGEX, "Invalid phone
number") or a z.string().refine(...) using PHONE_REGEX) and import/reuse the
PHONE_REGEX constant where it’s defined so server-side validation matches the UI
pattern for phone numbers (refer to the phoneNumber field and $CreateUserData
symbol).

In `@apps/web/src/translations/common.json`:
- Around line 50-53: Fix the French translation typos and capitalization in
common.json: change the "email" object's fr value from "Addresse courriel" to
"Adresse courriel" (or "Courriel" if you prefer the shorter label) and update
the French label whose current value is "numéro de téléphone" to use
sentence-case capitalization "Numéro de téléphone" so it matches other labels
like "Mot de passe" and "Nom d'utilisateur".

---

Nitpick comments:
In `@apps/api/src/users/dto/create-user.dto.ts`:
- Around line 57-59: The sex property in CreateUserDto has a duplicate
decorator; remove the redundant `@ApiProperty`() so only the descriptive
`@ApiProperty`({ description: 'Sex at Birth' }) remains on the sex?: Sex; field to
avoid overriding/conflicting decorators.

In `@apps/web/src/routes/_app/admin/users/index.tsx`:
- Around line 92-100: The custom check on the Zod schema (.check callback)
currently flags a mismatch whenever ctx.value.confirmPassword !==
ctx.value.password; update the logic to only validate equality when a password
is present (e.g., gate on ctx.value.password truthiness) so the mismatch error
(pushed to path ['confirmPassword']) is not raised when both fields are
untouched/empty — locate the .check block that inspects
ctx.value.confirmPassword and ctx.value.password and add the conditional guard
around the equality check.
- Around line 321-332: Collapse the duplicated initialValues branches by
constructing a single base object for initialValues (include email:
selectedUser.email ?? undefined, groupIds: new Set(selectedUser.groupIds),
phoneNumber: selectedUser.phoneNumber ?? undefined) and then conditionally add
additionalPermissions when selectedUser?.additionalPermissions.length is truthy
(e.g., if (selectedUser?.additionalPermissions?.length)
base.initialValues.additionalPermissions = selectedUser.additionalPermissions).
Update the initialValues assignment to use this single object; reference the
initialValues assignment, selectedUser, and additionalPermissions to locate the
code.

In `@apps/web/src/utils/validation.ts`:
- Line 1: The PHONE_REGEX export wraps a regex literal in new RegExp(...) which
is redundant; replace the wrapped expression by exporting the regex literal
directly (replace export const PHONE_REGEX = new
RegExp(/^\+?\(?\d{1,4}\)?[\s.-]?\d{1,4}[\s.-]?\d{1,9}$/); with a direct literal
assignment to PHONE_REGEX) and, if desired, consider tightening the pattern or
using libphonenumber-js for stricter E.164-style validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 89f02106-872f-4a6e-9c8a-52d95d392b08

📥 Commits

Reviewing files that changed from the base of the PR and between 7dbc438 and a4d0140.

📒 Files selected for processing (7)
  • apps/api/src/users/dto/create-user.dto.ts
  • apps/api/src/users/users.service.ts
  • apps/web/src/routes/_app/admin/users/create.tsx
  • apps/web/src/routes/_app/admin/users/index.tsx
  • apps/web/src/routes/_app/user.tsx
  • apps/web/src/translations/common.json
  • apps/web/src/utils/validation.ts

Comment thread apps/api/src/users/dto/create-user.dto.ts
Comment thread apps/web/src/translations/common.json
@joshunrau joshunrau merged commit 1c18932 into DouglasNeuroInformatics:main Apr 23, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants