fix: profile page form pre-population and WSDC ID editing
- Add useEffect to pre-fill profile form with current user data - Add WSDC ID field to profile edit form with numeric validation - Update backend to accept wsdcId in profile updates with null handling - Add wsdcId validation to updateProfileValidation middleware - Include firstName, lastName, wsdcId in GET /api/users/me response Fixes issue where profile inputs were empty on page load and allows users to update their WSDC ID.
This commit is contained in:
@@ -10,12 +10,13 @@ const { sanitizeForEmail } = require('../utils/sanitize');
|
||||
async function updateProfile(req, res, next) {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const { firstName, lastName, email } = req.body;
|
||||
const { firstName, lastName, email, wsdcId } = req.body;
|
||||
|
||||
// Build update data
|
||||
const updateData = {};
|
||||
if (firstName !== undefined) updateData.firstName = firstName;
|
||||
if (lastName !== undefined) updateData.lastName = lastName;
|
||||
if (wsdcId !== undefined) updateData.wsdcId = wsdcId || null; // Allow empty string to clear WSDC ID
|
||||
|
||||
// Check if email is being changed
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
|
||||
@@ -131,6 +131,11 @@ const updateProfileValidation = [
|
||||
.isEmail()
|
||||
.withMessage('Must be a valid email address')
|
||||
.normalizeEmail(),
|
||||
body('wsdcId')
|
||||
.optional()
|
||||
.trim()
|
||||
.matches(/^\d{0,10}$/)
|
||||
.withMessage('WSDC ID must be numeric and up to 10 digits'),
|
||||
handleValidationErrors,
|
||||
];
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ router.get('/me', authenticate, async (req, res, next) => {
|
||||
username: true,
|
||||
email: true,
|
||||
emailVerified: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
wsdcId: true,
|
||||
avatar: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { authAPI } from '../services/api';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import { User, Mail, Lock, Save, AlertCircle, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import { User, Mail, Lock, Save, AlertCircle, CheckCircle, Loader2, Hash } from 'lucide-react';
|
||||
|
||||
const ProfilePage = () => {
|
||||
const { user, updateUser } = useAuth();
|
||||
@@ -10,10 +10,23 @@ const ProfilePage = () => {
|
||||
|
||||
// Profile edit state
|
||||
const [profileData, setProfileData] = useState({
|
||||
firstName: user?.firstName || '',
|
||||
lastName: user?.lastName || '',
|
||||
email: user?.email || '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
wsdcId: '',
|
||||
});
|
||||
|
||||
// Load user data when component mounts or user changes
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
setProfileData({
|
||||
firstName: user.firstName || '',
|
||||
lastName: user.lastName || '',
|
||||
email: user.email || '',
|
||||
wsdcId: user.wsdcId || '',
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
const [profileLoading, setProfileLoading] = useState(false);
|
||||
const [profileMessage, setProfileMessage] = useState('');
|
||||
const [profileError, setProfileError] = useState('');
|
||||
@@ -210,6 +223,33 @@ const ProfilePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* WSDC ID */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
WSDC ID
|
||||
</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<Hash className="h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="wsdcId"
|
||||
value={profileData.wsdcId}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace(/\D/g, '');
|
||||
setProfileData({ ...profileData, wsdcId: value });
|
||||
}}
|
||||
className="pl-10 w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-primary-500 focus:border-primary-500"
|
||||
placeholder="12345"
|
||||
maxLength={10}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Your World Swing Dance Council ID (optional)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
|
||||
Reference in New Issue
Block a user