From 1d39c69fe462c59fac3c56b65a6ebb53022deecf Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Sat, 11 Apr 2026 12:41:27 +0100 Subject: [PATCH] docs: add settings Vue migration design spec --- ...026-04-11-settings-vue-migration-design.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-11-settings-vue-migration-design.md diff --git a/docs/superpowers/specs/2026-04-11-settings-vue-migration-design.md b/docs/superpowers/specs/2026-04-11-settings-vue-migration-design.md new file mode 100644 index 0000000..1dd85f1 --- /dev/null +++ b/docs/superpowers/specs/2026-04-11-settings-vue-migration-design.md @@ -0,0 +1,130 @@ +# Settings Vue Migration — Design Spec + +**Date:** 2026-04-11 +**Status:** Approved + +## Goal + +Migrate the Livewire settings pages (Profile, Security, Appearance) into the Vue SPA so the app is fully unified under one frontend. Auth pages (`/login`, `/register`, Fortify) remain Livewire and are untouched. + +--- + +## Architecture + +### Routing + +New Vue Router routes nested under `/dashboard/settings`: + +``` +/dashboard/settings → redirect to /dashboard/settings/profile +/dashboard/settings/profile +/dashboard/settings/security +/dashboard/settings/appearance +``` + +A `SettingsLayout.vue` wraps all three views, providing a sub-nav sidebar (Profile / Security / Appearance) that mirrors the existing Livewire settings layout structure and uses the same warm colour palette (`#faf6f3`, `#bb5b3e`, `#89726c`, `#4a3f3b`, `#e5ded7`). + +### Top Nav — User Dropdown + +`DashboardLayout.vue` top nav: replace the plain `{{ user?.email }}` text with a user avatar button showing the user's initials (e.g. "OU"). Clicking opens a dropdown showing: + +- Avatar + full name + email +- Settings link → `/dashboard/settings` +- Log out (calls `POST /api/auth/logout`, clears user, redirects to `/`) + +The dropdown is implemented with Alpine.js `x-data` / `x-show` inline, consistent with the project's existing Alpine usage pattern. + +--- + +## Components + +### New files + +| File | Purpose | +|------|---------| +| `resources/js/views/dashboard/settings/SettingsLayout.vue` | Sub-nav wrapper (Profile / Security / Appearance) | +| `resources/js/views/dashboard/settings/Profile.vue` | Name/email form + delete account | +| `resources/js/views/dashboard/settings/Security.vue` | Password change + 2FA management | +| `resources/js/views/dashboard/settings/Appearance.vue` | Light/dark/system theme toggle | + +### Modified files + +| File | Change | +|------|--------| +| `resources/js/router/index.js` | Add `/dashboard/settings` routes | +| `resources/js/views/dashboard/DashboardLayout.vue` | Replace email text with user dropdown | +| `resources/js/composables/useAuth.js` | Expose `updateProfile`, `updatePassword`, `deleteAccount` methods | +| `app/Http/Controllers/Api/UserController.php` | Add `updateProfile`, `updatePassword`, `deleteAccount` actions | +| `routes/api.php` | Register 3 new endpoints | +| `routes/settings.php` | Remove Livewire settings routes | + +--- + +## Data Flow + +### Profile update + +1. Vue calls `PUT /api/user/profile` with `{ name, email }` +2. `UserController@updateProfile` validates, updates `users` table, nulls `email_verified_at` if email changed +3. Returns updated user JSON; `useAuth` updates the `user` ref + +### Password update + +1. Vue calls `PUT /api/user/password` with `{ current_password, password, password_confirmation }` +2. `UserController@updatePassword` verifies current password via `Hash::check`, updates hash +3. Returns `200` on success; Vue shows inline success message + +### Delete account + +1. User clicks "Delete account", enters password in a confirmation modal +2. Vue calls `DELETE /api/user` with `{ password }` +3. `UserController@deleteAccount` verifies password, deletes user, revokes Sanctum tokens +4. Vue calls logout, redirects to `/` + +### 2FA (uses Fortify web routes directly) + +Fortify's web routes accept `application/json` and work with session cookies (axios already sends these via `withCredentials: true`): + +| Action | Endpoint | +|--------|----------| +| Enable | `POST /user/two-factor-authentication` | +| Disable | `DELETE /user/two-factor-authentication` | +| Confirm code | `POST /user/confirmed-two-factor-authentication` | +| Get QR code SVG | `GET /user/two-factor-qr-code` | +| Get secret key | `GET /user/two-factor-secret-key` | +| Get recovery codes | `GET /user/two-factor-recovery-codes` | +| Regenerate recovery codes | `POST /user/two-factor-recovery-codes` | + +A separate axios instance (base URL `/`, same credentials) is used for these Fortify calls to avoid the `/api` prefix. + +### Appearance + +Theme preference stored in `localStorage` under key `appearance` with values `light` / `dark` / `system`. Applied via a class on ``. No backend call needed. + +--- + +## Error Handling + +- Validation errors from API returned as `422` with `errors` object; displayed inline under each field +- Network errors shown as a generic top-level message within the form card +- 2FA setup errors (QR fetch failure, bad confirmation code) shown inline with retry option +- Delete account modal clears and closes on any error; error shown inside the modal + +--- + +## Cleanup (after Vue implementation verified) + +- Remove `routes/settings.php` (all three `Route::livewire` entries) +- Remove `app/Livewire/Settings/` directory (Profile, Security, Appearance, DeleteUserForm, TwoFactor/RecoveryCodes) +- Remove `resources/views/livewire/settings/` directory +- Remove `resources/views/components/settings/layout.blade.php` +- Remove `resources/views/partials/settings-heading.blade.php` if it exists +- Remove `app/Concerns/ProfileValidationRules.php` and `PasswordValidationRules.php` if unused elsewhere + +--- + +## Out of Scope + +- `/login`, `/register`, Fortify password reset flows — remain Livewire, untouched +- Subscription management (separate feature) +- WhatsApp/SMS notification preferences (separate feature)