From 22726550a63c77d9f635f9cdfd4f0a26d68061cd Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Sun, 10 May 2026 17:26:14 -0700 Subject: [PATCH] Refactor user profile to comprehensive settings page (#403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add account deletion and multi-section settings page - Rename /user-profile → /settings with 301 redirect from old URL - Add multi-section settings nav (Profile, Account, API placeholder, Data & Privacy) reusing existing SettingsDesktopNav/SettingsMobileGridNav components - Implement account deletion via anonymization: wipes all PII from users row, releases team ownerships, removes commissioner records, deletes sessions/accounts - Add data export request form that emails privacy@brackt.com via Resend - Add deletedAt timestamp column to users table (migration 0100) - Add anonymizeUserAccount() to user model - Add removeAllCommissionersByUserId() to commissioner model - Tests for both new model functions - Update UserMenu "Profile" link → "Settings" at /settings https://claude.ai/code/session_017Hvmof82Xr3UwKFc3pnC4X * Address code review feedback on settings/account deletion 1. Wrap anonymizeUserAccount in a DB transaction so partial failures (e.g. session delete succeeds but user update fails) can't leave accounts in an inconsistent state 2. Escape user email and notes with escapeHtml() before interpolating into the data request email body 3. Reset AlertDialog confirmed state when dialog is dismissed via backdrop click or Escape key (onOpenChange handler) 4. Extract accounts DB query to app/models/account.ts (findLinkedAccountsByUserId) to comply with the "always query through app/models/" convention 5. Replace dynamic imports() in action handlers with static top-level imports 6. Remove the unused hard-delete deleteUser() function 7. Add lastDataRequestAt timestamp to users (migration 0101) and enforce a 30-day server-side cooldown on data export requests 8. Replace fragile actionData type casts with a proper ActionData discriminated union; narrowing now works without `as` assertions 9. Strengthen tests: verify which schema tables are passed to delete() and that operations run inside the transaction https://claude.ai/code/session_017Hvmof82Xr3UwKFc3pnC4X * Fix no-non-null-assertion lint error in PrivacySection Replace non-null assertion with optional chaining on dataRequestCooldownUntil to satisfy oxlint no-non-null-assertion rule. https://claude.ai/code/session_017Hvmof82Xr3UwKFc3pnC4X --------- Co-authored-by: Claude --- app/components/UserMenu.tsx | 4 +- .../user/settings/AccountSection.tsx | 79 + app/components/user/settings/ApiSection.tsx | 24 + .../user/settings/PrivacySection.tsx | 161 + .../user/settings/ProfileSection.tsx | 105 + app/models/__tests__/commissioner.test.ts | 18 +- app/models/__tests__/user.test.ts | 72 + app/models/account.ts | 16 + app/models/commissioner.ts | 5 + app/models/user.ts | 22 +- app/routes.ts | 3 +- .../leagues/__tests__/team-management.test.ts | 6 + app/routes/settings.tsx | 249 + app/routes/user-profile-redirect.tsx | 5 + app/routes/user-profile.tsx | 194 - database/schema.ts | 2 + drizzle/0100_mighty_weapon_omega.sql | 1 + drizzle/0101_simple_black_bolt.sql | 1 + drizzle/meta/0100_snapshot.json | 5775 ++++++++++++++++ drizzle/meta/0101_snapshot.json | 5781 +++++++++++++++++ drizzle/meta/_journal.json | 14 + 21 files changed, 12337 insertions(+), 200 deletions(-) create mode 100644 app/components/user/settings/AccountSection.tsx create mode 100644 app/components/user/settings/ApiSection.tsx create mode 100644 app/components/user/settings/PrivacySection.tsx create mode 100644 app/components/user/settings/ProfileSection.tsx create mode 100644 app/models/__tests__/user.test.ts create mode 100644 app/models/account.ts create mode 100644 app/routes/settings.tsx create mode 100644 app/routes/user-profile-redirect.tsx delete mode 100644 app/routes/user-profile.tsx create mode 100644 drizzle/0100_mighty_weapon_omega.sql create mode 100644 drizzle/0101_simple_black_bolt.sql create mode 100644 drizzle/meta/0100_snapshot.json create mode 100644 drizzle/meta/0101_snapshot.json diff --git a/app/components/UserMenu.tsx b/app/components/UserMenu.tsx index 4c74e52..76763d6 100644 --- a/app/components/UserMenu.tsx +++ b/app/components/UserMenu.tsx @@ -50,10 +50,10 @@ export function UserMenu({
- Profile + Settings