feat(profile-A): users.first_name/last_name/phone columns + Settings UI (BUNYIP-139) #169

Merged
YousifShkara merged 1 commit from feat/bunyip-139-user-profile-fields into main 2026-06-18 08:25:56 +02:00
Owner

First slice of BUNYIP-103. Storage + UI only - no OIDC plumbing yet (Slice B
in BUNYIP-140). After this lands: users see new fields in Settings; first
save flips off the dashboard "fill in your profile" banner; the DB carries
the data but no RP can read it.

  • Migration 20260618061153_add_user_profile_fields.sql: nullable
    first_name / last_name / phone columns with per-column CHECK (length <= 64).
  • bunyip-domain User + UserResponse + test fixtures grow the three Option
    fields; UserRepository::update_profile UPSERT helper.
  • bunyip-api: PUT /v1/users/me/profile (UpdateProfileRequest, normalize_profile_field
    trims + length-bounds + sentinel-mapping; over-length returns 400 not 500;
    empty input clears to NULL; absent key leaves the column unchanged).
  • bunyip-web: Settings page Profile panel above Change Email; ProfileForm
    • settings_profile handler; api::auth::update_profile client helper.
  • Dashboard banner: renders when first_name OR last_name is empty/whitespace;
    disappears after either is set (no dismiss state needed).
  • LoginOutcome enum: User grew 72 bytes (3x Option) so the size
    diff between SignedIn(User) and TwoFactorRequired { challenge_token }
    tripped clippy's large_enum_variant. Local allow rather than boxing the
    variant (changes the public API for a one-per-login allocation).

Tests round-trip the new fields; just check-container green.

#BUNYIP-139
#BUNYIP-103

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

First slice of BUNYIP-103. Storage + UI only - no OIDC plumbing yet (Slice B in BUNYIP-140). After this lands: users see new fields in Settings; first save flips off the dashboard "fill in your profile" banner; the DB carries the data but no RP can read it. - Migration 20260618061153_add_user_profile_fields.sql: nullable first_name / last_name / phone columns with per-column CHECK (length <= 64). - bunyip-domain User + UserResponse + test fixtures grow the three Option<String> fields; UserRepository::update_profile UPSERT helper. - bunyip-api: PUT /v1/users/me/profile (UpdateProfileRequest, normalize_profile_field trims + length-bounds + sentinel-mapping; over-length returns 400 not 500; empty input clears to NULL; absent key leaves the column unchanged). - bunyip-web: Settings page Profile panel above Change Email; ProfileForm + settings_profile handler; api::auth::update_profile client helper. - Dashboard banner: renders when first_name OR last_name is empty/whitespace; disappears after either is set (no dismiss state needed). - LoginOutcome enum: User grew 72 bytes (3x Option<String>) so the size diff between SignedIn(User) and TwoFactorRequired { challenge_token } tripped clippy's large_enum_variant. Local allow rather than boxing the variant (changes the public API for a one-per-login allocation). Tests round-trip the new fields; just check-container green. #BUNYIP-139 #BUNYIP-103 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat(profile-A): users.first_name/last_name/phone columns + Settings UI (BUNYIP-139)
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 2m0s
c9b86eb3fd
First slice of BUNYIP-103. Storage + UI only - no OIDC plumbing yet (Slice B
in BUNYIP-140). After this lands: users see new fields in Settings; first
save flips off the dashboard "fill in your profile" banner; the DB carries
the data but no RP can read it.

- Migration 20260618061153_add_user_profile_fields.sql: nullable
  first_name / last_name / phone columns with per-column CHECK (length <= 64).
- bunyip-domain User + UserResponse + test fixtures grow the three Option<String>
  fields; UserRepository::update_profile UPSERT helper.
- bunyip-api: PUT /v1/users/me/profile (UpdateProfileRequest, normalize_profile_field
  trims + length-bounds + sentinel-mapping; over-length returns 400 not 500;
  empty input clears to NULL; absent key leaves the column unchanged).
- bunyip-web: Settings page Profile panel above Change Email; ProfileForm
  + settings_profile handler; api::auth::update_profile client helper.
- Dashboard banner: renders when first_name OR last_name is empty/whitespace;
  disappears after either is set (no dismiss state needed).
- LoginOutcome enum: User grew 72 bytes (3x Option<String>) so the size
  diff between SignedIn(User) and TwoFactorRequired { challenge_token }
  tripped clippy's large_enum_variant. Local allow rather than boxing the
  variant (changes the public API for a one-per-login allocation).

Tests round-trip the new fields; just check-container green.

#BUNYIP-139
#BUNYIP-103

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
YousifShkara deleted branch feat/bunyip-139-user-profile-fields 2026-06-18 08:25:56 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!169
No description provided.