Member Settings Page

Context

Members have no way to edit their profile, change their password, manage email, or control privacy. The guild activity feed is blocked behind privacy controls — we need members to opt in before showing their activity publicly. This page unblocks both the live activity feed and basic account management.

Scope

PR 1: Profile, privacy, password, email

  1. Profile fields — edit name, nickname, phone, birthday
  2. Privacy controls — toggle guild activity visibility (unblocks live feed)
  3. Password change — change password while logged in
  4. Email change — change email with re-verification

PR 2: Discord OAuth management (separate)

  1. Discord link/unlink — connect or disconnect Discord from the settings page

Add “Settings” link to the NavUser dropdown in the sidebar footer (alongside theme toggle + logout). Keeps the 5-page RPG nav structure clean.

Data model changes

Members collection — add privacy fields

privacy group:
  showInGuildActivity: boolean (default false — opt-in)

No new fields needed for profile — name, nickname, phone, birthday already exist.

Route structure

src/app/(frontend)/dashboard/settings/
  page.tsx              — server component, fetches member data
  profile-form.tsx      — client component: name, nickname, phone, birthday
  privacy-form.tsx      — client component: guild activity toggle
  password-form.tsx     — client component: current + new password
  email-form.tsx        — client component: new email + password confirmation
  discord-section.tsx   — client component: link/unlink Discord (PR 2)

API routes

POST /api/member/profile — update profile fields

  • Auth: Better Auth session → find member by betterAuthUserId
  • Validates + updates: name, nickname, phone, birthday
  • Uses payload.update() with overrideAccess: true (members can’t update their own records via Payload access control, so the API route handles auth and delegates)

POST /api/member/privacy — update privacy settings

  • Same auth pattern
  • Updates: privacy.showInGuildActivity

Password change — Better Auth client

No custom API route needed. Better Auth’s authClient.changePassword() handles this client-side.

Email change — Better Auth client

authClient.changeEmail() if available, otherwise custom flow: verify current password → update BA user email → send verification → update Payload member email on verification callback.

authClient.linkSocial({ provider: 'discord' }) to link, custom endpoint to unlink.

Page layout

Settings
├── Profile (Card)
│   ├── Name, Nickname, Phone, Birthday inputs
│   └── Save button
├── Privacy (Card)
│   └── "Show my activity in Guild Hall" toggle + description
├── Email (Card)
│   ├── Current email (display)
│   ├── New email input + password confirmation
│   └── Update button
├── Password (Card)
│   ├── Current password, New password, Confirm password
│   └── Update button
└── Connected Accounts (Card) — PR 2
    ├── Discord: Connected as @username [Unlink] / Not connected [Link]
    └── (future: other OAuth providers)

Key decisions

  • Privacy is opt-in (default false) — members must explicitly choose to show activity in the guild feed. Respects privacy by default.
  • Profile updates go through API routes not server actions — matches existing form patterns (signup, login all use API routes).
  • Members can’t update Payload records directly — access control is staff+. API routes authenticate via Better Auth session, then use overrideAccess: true.
  • Better Auth handles password/email/OAuth — we don’t reinvent auth flows.