Account

The account module owns the signed-in user's profile, avatar, notification preferences, security form, and deletion flow.

On this page

Where to look

AreaMaintainer reference
Route
Implementation referencesrc/app/(dashboard)/account/page.tsx
Actions
Implementation referencesrc/features/account/server/actions/profile.ts, security.ts, notifications.ts, delete.ts
Queries
Implementation referencesrc/features/account/server/queries/account.ts
Persistence
Implementation referencesrc/features/account/server/persistence/profile.ts, notification-preferences.ts
UI
Implementation referencesrc/features/account/ui/profile-settings-form.tsx, avatar-uploader.tsx, notification-preferences-form.tsx, security-settings-form.tsx, delete-account-form.tsx
Shared
Implementation referencesrc/features/account/shared/form.ts
Related infrastructure
Implementation referencesrc/lib/auth/session, src/lib/auth/guards, src/lib/auth/rbac
Implementation reference3 areas

These Product code locations explain how the documented behavior is implemented. Expand them when you are ready to customize or maintain this area.

Account page and forms

The route renders account settings while feature UI owns profile, avatar, notifications, security, and deletion forms.

  • src/app/(dashboard)/account/page.tsx
  • src/features/account/ui/profile-settings-form.tsx
  • src/features/account/ui/security-settings-form.tsx

Server actions

Profile, notification preference, password, session, and deletion mutations stay behind authenticated server actions.

  • src/features/account/server/actions/profile.ts
  • src/features/account/server/actions/notifications.ts
  • src/features/account/server/actions/security.ts
  • src/features/account/server/actions/delete.ts

Reads and persistence

Account reads and writes go through query and persistence helpers instead of trusting client-supplied identity.

  • src/features/account/server/queries/account.ts
  • src/features/account/server/persistence/profile.ts
  • src/features/account/server/persistence/notification-preferences.ts

Reference paths are relative to the Shipflash-Product checkout.

Account update flow

  1. 1

    Resolve the signed-in profile

    The page and actions use server-side identity helpers, not browser-provided profile ids.

    Relevant Product code
    • src/app/(dashboard)/account/page.tsx
    • src/lib/auth/session/profile.ts
  2. 2

    Validate form input

    Shared form helpers and action utilities normalize fields before profile, security, or notification mutations run.

    Relevant Product code
    • src/features/account/shared/form.ts
    • src/features/account/server/actions/utils.ts
  3. 3

    Write through persistence helpers

    Profile and preference writes stay behind account-owned persistence modules.

    Relevant Product code
    • src/features/account/server/persistence/profile.ts
    • src/features/account/server/persistence/notification-preferences.ts
  4. 4

    Treat deletion as high risk

    Account deletion is isolated in its own action and should be tested directly before customization.

    Relevant Product code
    • src/features/account/server/actions/delete.ts
    • src/features/account/server/actions/__tests__/delete.test.ts

Profile update boundary

Use this example as a starting point, then adapt it to your product's rules and configuration.

The signed-in server profile owns the update target; the browser never supplies the profile id.

ts
export async function updateMyProfile(prevState: ActionResult, formData: FormData): Promise<ActionResult> {
  const profile = await getActiveProfile();
  if (!profile) {
    return { success: false, message: "Not authenticated" };
  }
  if (hasReadOnlyAccess(profile)) {
    return { success: false, message: "This account has read-only access and cannot make changes." };
  }

  const raw = {
    name: formData.get("name")?.toString() ?? "",
    phone: formData.get("phone")?.toString() ?? "",
    locale: formData.get("locale")?.toString() ?? "",
    timezone: formData.get("timezone")?.toString() ?? "",
    avatar_url: formData.get("avatar_url")?.toString() ?? "",
    job_title: formData.get("job_title")?.toString() ?? "",
    bio: formData.get("bio")?.toString() ?? "",
    github_url: formData.get("github_url")?.toString() ?? "",
    linkedin_url: formData.get("linkedin_url")?.toString() ?? "",
    x_url: formData.get("x_url")?.toString() ?? "",
  };

  const parsed = ProfileUpdateSchema.safeParse(raw);
  if (!parsed.success) {
    return { success: false, message: "Validation failed", errors: zodErrors(parsed.error) };
  }
Source reference
  • src/features/account/server/actions/profile.ts

Change rules

  • Use the server session as the source of identity.
  • Keep profile and preference writes behind server actions.
  • Treat account deletion as a high-risk path and test it directly.
  • Do not trust browser-supplied profile ids when the server already knows the signed-in profile.