- Remove OtpGateGuard from transactions controller (OTP verified at login) - Fix categories controller to use authenticated user instead of TEMP_USER_ID - Add comprehensive implementation plan document - Update .env.example with WEB_APP_URL - Prepare for admin dashboard development
314 lines
7.6 KiB
Markdown
314 lines
7.6 KiB
Markdown
# ✅ Profile UI Improvements - COMPLETE
|
|
|
|
## 🎉 **ALL FEATURES IMPLEMENTED:**
|
|
|
|
### **1. Avatar Upload** ✅
|
|
- **For non-Google users**: Upload button on avatar
|
|
- **For Google users**: Avatar synced from Google (no upload)
|
|
- **Features**:
|
|
- File type validation (images only)
|
|
- File size validation (max 5MB)
|
|
- Upload icon with loading state
|
|
- Error messages
|
|
- Automatic page reload after upload
|
|
|
|
### **2. Editable Name** ✅
|
|
- **For non-Google users**: Edit button with Save/Cancel
|
|
- **For Google users**: Readonly, synced from Google
|
|
- **Features**:
|
|
- Inline editing
|
|
- Validation (name cannot be empty)
|
|
- Loading states
|
|
- Success/error messages
|
|
- Automatic page reload after save
|
|
|
|
### **3. Email Field** ✅
|
|
- **Always readonly** (best practice)
|
|
- **Reason**: Email is primary identifier, changing it causes security risks
|
|
- **Helper text**: "Email cannot be changed"
|
|
|
|
### **4. Danger Zone** ✅
|
|
- **Location**: Security tab (not Edit Profile)
|
|
- **Features**:
|
|
- Red border card
|
|
- Warning message
|
|
- Password confirmation required
|
|
- Two-step confirmation (button → password input)
|
|
- Delete button with trash icon
|
|
- Loading states
|
|
- Error messages
|
|
- Automatic logout and redirect after deletion
|
|
|
|
---
|
|
|
|
## 📊 **Email Editability - Best Practices Explained:**
|
|
|
|
### **❌ Why Email Should NOT Be Editable:**
|
|
|
|
1. **Security Risk**:
|
|
- Email is the primary identifier
|
|
- Changing it can enable account takeover
|
|
- Requires complex verification flow
|
|
|
|
2. **OAuth Complications**:
|
|
- Breaks Google OAuth connection
|
|
- User loses access to "Continue with Google"
|
|
- Requires re-linking accounts
|
|
|
|
3. **Verification Complexity**:
|
|
- Need to verify NEW email
|
|
- Keep OLD email active until verified
|
|
- Send notifications to both emails
|
|
- Add cooldown period
|
|
|
|
4. **User Confusion**:
|
|
- Login with old email won't work
|
|
- Password reset goes to wrong email
|
|
- Support tickets increase
|
|
|
|
### **✅ Recommended Approach:**
|
|
- **Keep email readonly**
|
|
- **If user wants different email**: Create new account
|
|
- **Alternative**: Add secondary email for notifications (not for login)
|
|
|
|
---
|
|
|
|
## 🎨 **UI Features:**
|
|
|
|
### **Avatar Section**:
|
|
- ✅ Larger avatar (20x20)
|
|
- ✅ Upload button (bottom-right corner)
|
|
- ✅ Conditional display (Google vs non-Google)
|
|
- ✅ Loading spinner during upload
|
|
- ✅ Helper text explaining sync status
|
|
- ✅ Error messages below avatar
|
|
|
|
### **Name Field**:
|
|
- ✅ Conditional editing (Google vs non-Google)
|
|
- ✅ Edit/Save/Cancel buttons
|
|
- ✅ Inline editing (no modal)
|
|
- ✅ Validation messages
|
|
- ✅ Loading states
|
|
- ✅ Disabled state when not editing
|
|
|
|
### **Danger Zone**:
|
|
- ✅ Red border card
|
|
- ✅ Warning icon
|
|
- ✅ Clear warning message
|
|
- ✅ Two-step confirmation
|
|
- ✅ Password input
|
|
- ✅ Delete/Cancel buttons
|
|
- ✅ Loading states
|
|
- ✅ Error handling
|
|
|
|
---
|
|
|
|
## 🔧 **Backend Requirements:**
|
|
|
|
### **New Endpoints Needed**:
|
|
|
|
1. **`GET /api/auth/accounts`** - Check if user has Google OAuth
|
|
```typescript
|
|
// Returns array of auth accounts
|
|
[{ provider: 'google', ... }]
|
|
```
|
|
|
|
2. **`POST /api/users/avatar`** - Upload avatar
|
|
```typescript
|
|
// Accepts multipart/form-data
|
|
// Field name: 'avatar'
|
|
// Returns updated user with new avatarUrl
|
|
```
|
|
|
|
3. **`PUT /api/users/profile`** - Update name (already exists for phone)
|
|
```typescript
|
|
// Add support for 'name' field
|
|
{ name: string }
|
|
```
|
|
|
|
4. **`DELETE /api/users/account`** - Delete account
|
|
```typescript
|
|
// Requires password confirmation
|
|
{ password: string }
|
|
// Deletes all user data
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 **Implementation Details:**
|
|
|
|
### **Google Auth Detection**:
|
|
```typescript
|
|
const checkGoogleAuth = async () => {
|
|
const response = await axios.get(`${API}/auth/accounts`)
|
|
const hasGoogle = response.data.some(acc => acc.provider === 'google')
|
|
setHasGoogleAuth(hasGoogle)
|
|
}
|
|
```
|
|
|
|
### **Avatar Upload**:
|
|
```typescript
|
|
const handleAvatarUpload = async (event) => {
|
|
const file = event.target.files?.[0]
|
|
|
|
// Validate type
|
|
if (!file.type.startsWith('image/')) {
|
|
setAvatarError("Please select an image file")
|
|
return
|
|
}
|
|
|
|
// Validate size (5MB)
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
setAvatarError("Image size must be less than 5MB")
|
|
return
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append('avatar', file)
|
|
|
|
await axios.post(`${API}/users/avatar`, formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' }
|
|
})
|
|
|
|
window.location.reload()
|
|
}
|
|
```
|
|
|
|
### **Name Update**:
|
|
```typescript
|
|
const handleUpdateName = async () => {
|
|
if (!editedName || editedName.trim().length === 0) {
|
|
setNameError("Name cannot be empty")
|
|
return
|
|
}
|
|
|
|
await axios.put(`${API}/users/profile`, { name: editedName })
|
|
setNameSuccess("Name updated successfully!")
|
|
setIsEditingName(false)
|
|
window.location.reload()
|
|
}
|
|
```
|
|
|
|
### **Account Deletion**:
|
|
```typescript
|
|
const handleDeleteAccount = async () => {
|
|
if (!deletePassword) {
|
|
setDeleteError("Please enter your password")
|
|
return
|
|
}
|
|
|
|
await axios.delete(`${API}/users/account`, {
|
|
data: { password: deletePassword }
|
|
})
|
|
|
|
localStorage.removeItem('token')
|
|
window.location.href = '/auth/login'
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 **Testing Checklist:**
|
|
|
|
### **For Google Users**:
|
|
- [ ] Avatar shows Google profile picture
|
|
- [ ] No upload button on avatar
|
|
- [ ] Name field is disabled (gray)
|
|
- [ ] Helper text says "synced from Google"
|
|
- [ ] Email is readonly
|
|
- [ ] Phone is editable
|
|
- [ ] Danger Zone works
|
|
|
|
### **For Email/Password Users**:
|
|
- [ ] Avatar shows default icon or uploaded image
|
|
- [ ] Upload button appears on avatar
|
|
- [ ] Click upload → file picker opens
|
|
- [ ] Upload image → avatar updates
|
|
- [ ] Name field has Edit button
|
|
- [ ] Click Edit → input becomes editable
|
|
- [ ] Change name → click Save → name updates
|
|
- [ ] Click Cancel → changes discarded
|
|
- [ ] Email is readonly
|
|
- [ ] Phone is editable
|
|
- [ ] Danger Zone works
|
|
|
|
### **Danger Zone**:
|
|
- [ ] Located in Security tab
|
|
- [ ] Red border card
|
|
- [ ] Click "Delete Account" → password input appears
|
|
- [ ] Enter wrong password → error message
|
|
- [ ] Enter correct password → account deleted
|
|
- [ ] Redirects to login page
|
|
- [ ] Cannot login with deleted account
|
|
|
|
---
|
|
|
|
## ✅ **ESLint:**
|
|
```bash
|
|
npm run lint
|
|
# ✓ 0 errors, 0 warnings
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **Files Modified:**
|
|
|
|
1. **`apps/web/src/components/pages/Profile.tsx`**
|
|
- Added Google auth detection
|
|
- Added avatar upload
|
|
- Added name editing
|
|
- Added danger zone
|
|
- Added all handlers and states
|
|
|
|
---
|
|
|
|
## 🎯 **User Experience:**
|
|
|
|
### **Before**:
|
|
- All users see same UI
|
|
- No way to upload avatar
|
|
- No way to edit name
|
|
- No way to delete account
|
|
- Confusing for non-Google users
|
|
|
|
### **After**:
|
|
- ✅ Conditional UI based on auth method
|
|
- ✅ Avatar upload for non-Google users
|
|
- ✅ Name editing for non-Google users
|
|
- ✅ Clear helper text explaining restrictions
|
|
- ✅ Danger zone for account deletion
|
|
- ✅ Professional, intuitive interface
|
|
|
|
---
|
|
|
|
## 🚀 **Next Steps:**
|
|
|
|
### **Backend Implementation Required**:
|
|
1. Create `GET /api/auth/accounts` endpoint
|
|
2. Create `POST /api/users/avatar` endpoint with multer
|
|
3. Update `PUT /api/users/profile` to support name
|
|
4. Create `DELETE /api/users/account` endpoint
|
|
|
|
### **Optional Enhancements**:
|
|
- Avatar cropping before upload
|
|
- Image compression
|
|
- Multiple avatar options
|
|
- Account export before deletion
|
|
- Deletion cooldown period (30 days)
|
|
|
|
---
|
|
|
|
## 🎉 **COMPLETE!**
|
|
|
|
**All UI improvements implemented:**
|
|
- ✅ Avatar upload (non-Google users)
|
|
- ✅ Editable name (non-Google users)
|
|
- ✅ Email readonly (best practice)
|
|
- ✅ Danger zone (Security tab)
|
|
- ✅ Conditional UI (Google vs non-Google)
|
|
- ✅ All validations
|
|
- ✅ All error handling
|
|
- ✅ ESLint clean
|
|
|
|
**Ready for backend implementation!** 🚀
|