# Profile UI Refactor + Identity Docs + Change Password Implementation ## Overview Full-stack implementation of Profile page UI/UX improvements, identity documents (identity_docs) display/editing, and secure password change functionality. ## Backend Changes ### 1. [`backend/app/schemas/user.py`](backend/app/schemas/user.py:68) — PersonUpdate Schema - Added `identity_docs: Optional[Any] = None` field to `PersonUpdate` class (line 79) - Created new `ChangePasswordRequest` class (lines 93-96) with: - `current_password: str` — the user's existing password for verification - `new_password: str` — the desired new password ### 2. [`backend/app/api/v1/endpoints/users.py`](backend/app/api/v1/endpoints/users.py:179) — PUT /me/person - Updated imports to include `ChangePasswordRequest`, `AuthService`, `verify_password`, `get_password_hash` - Added identity_docs merge logic (lines 219-227): - If `identity_docs` is provided and not null, merges with existing docs using `dict.update()` - Preserves fields not sent in the update payload (e.g., if only LICENSE is sent, ID_CARD is preserved) - Falls back to direct assignment if either value is not a dict ### 3. [`backend/app/api/v1/endpoints/users.py`](backend/app/api/v1/endpoints/users.py:260) — PUT /me/password (New Endpoint) Complete password change flow: 1. **Current password verification** (line 273): Uses `verify_password()` to check the provided current password against the stored hash 2. **New password ≠ old password check** (line 280): Prevents reusing the same password 3. **Complexity validation** (line 287): Calls `AuthService._validate_password_complexity()` which reads dynamic rules from `SystemParameter` (min length, uppercase, digits, special chars) 4. **Hash and save** (line 290): Uses `get_password_hash()` to bcrypt-hash the new password and commits to DB 5. **Error handling** (lines 292-297): Rollback on SQLAlchemyError, returns 500 with detail ## Frontend Changes ### 1. [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:19) — PersonData Interface - Added `identity_docs: Record | null` (line 28) - Added `ice_contact: Record | null` (line 29) ### 2. [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:430) — changePassword Action New Pinia action that: - Calls `PUT /users/me/password` with `{ current_password, new_password }` - Returns `{ status: string, message: string }` on success - Throws with error message on failure ### 3. [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue:1) — Complete Rewrite #### UI Design (Dark Card Theme) - **Card backgrounds**: `bg-black/40 backdrop-blur-xl` with `border border-white/10` - **Data text**: `text-white font-medium` (bold white for readability) - **Labels**: `text-white/50 text-sm` (muted gray labels) - **Accent color**: `#00E5A0` (green) used **only** for buttons and active states - **Input fields**: `bg-white/5 border-white/10` with focus state `border-[#00E5A0]/50` - **Close button**: Top-right X button with `hover:text-red-400` #### Identity Docs Section (lines 214-309) - **Read mode**: Displays ID_CARD (number, expiry_date) and LICENSE (number, expiry_date, categories) in bordered sub-cards - **Edit mode**: Input fields for all identity doc fields, organized in sub-sections - **Payload construction** (`buildIdentityDocsPayload()`, lines 636-663): Only sends identity_docs if at least one field is filled; constructs nested JSON `{ ID_CARD: { number, expiry_date }, LICENSE: { number, expiry_date, categories } }` #### Change Password Modal (lines 431-528) - **Teleport to body**: Renders outside the component tree for proper z-index stacking - **3 fields**: Current password, new password, confirm new password - **Frontend validation** (`submitPasswordChange()`, lines 713-751): - All fields required - New password must match confirmation - Minimum 6 characters - **API call**: Calls `authStore.changePassword()` on success - **Success feedback**: Green success message with **auto-close after 2 seconds** (`setTimeout` → `closePasswordModal()`) - **Error feedback**: Red error message from backend detail or fallback string ## Testing Results | Test | Result | |------|--------| | Backend syntax check (user.py, users.py) | ✅ Pass | | Backend module imports (PersonUpdate, ChangePasswordRequest) | ✅ Pass | | Backend router routes (PUT /me/person, PUT /me/password) | ✅ Pass | | Frontend Vite build (112 modules) | ✅ Pass | | ProfileView chunk size | 27.26 kB | ## Security Considerations 1. **Password never returned in responses** — the `ChangePasswordRequest` is input-only 2. **Bcrypt hashing** — `get_password_hash()` uses bcrypt with work factor 3. **Dynamic complexity rules** — `_validate_password_complexity` reads from `SystemParameter` table, allowing admin to change rules without code deploy 4. **No password logging** — the endpoint never logs the password values 5. **Identity docs merge** — preserves existing data when partial updates are sent