4.9 KiB
4.9 KiB
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 — PersonUpdate Schema
- Added
identity_docs: Optional[Any] = Nonefield toPersonUpdateclass (line 79) - Created new
ChangePasswordRequestclass (lines 93-96) with:current_password: str— the user's existing password for verificationnew_password: str— the desired new password
2. backend/app/api/v1/endpoints/users.py — PUT /me/person
- Updated imports to include
ChangePasswordRequest,AuthService,verify_password,get_password_hash - Added identity_docs merge logic (lines 219-227):
- If
identity_docsis provided and not null, merges with existing docs usingdict.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
- If
3. backend/app/api/v1/endpoints/users.py — PUT /me/password (New Endpoint)
Complete password change flow:
- Current password verification (line 273): Uses
verify_password()to check the provided current password against the stored hash - New password ≠ old password check (line 280): Prevents reusing the same password
- Complexity validation (line 287): Calls
AuthService._validate_password_complexity()which reads dynamic rules fromSystemParameter(min length, uppercase, digits, special chars) - Hash and save (line 290): Uses
get_password_hash()to bcrypt-hash the new password and commits to DB - Error handling (lines 292-297): Rollback on SQLAlchemyError, returns 500 with detail
Frontend Changes
1. frontend/src/stores/auth.ts — PersonData Interface
- Added
identity_docs: Record<string, any> | null(line 28) - Added
ice_contact: Record<string, any> | null(line 29)
2. frontend/src/stores/auth.ts — changePassword Action
New Pinia action that:
- Calls
PUT /users/me/passwordwith{ current_password, new_password } - Returns
{ status: string, message: string }on success - Throws with error message on failure
3. frontend/src/views/ProfileView.vue — Complete Rewrite
UI Design (Dark Card Theme)
- Card backgrounds:
bg-black/40 backdrop-blur-xlwithborder 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/10with focus stateborder-[#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
- Password never returned in responses — the
ChangePasswordRequestis input-only - Bcrypt hashing —
get_password_hash()uses bcrypt with work factor - Dynamic complexity rules —
_validate_password_complexityreads fromSystemParametertable, allowing admin to change rules without code deploy - No password logging — the endpoint never logs the password values
- Identity docs merge — preserves existing data when partial updates are sent