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) {
|
async function updateProfile(req, res, next) {
|
||||||
try {
|
try {
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
const { firstName, lastName, email } = req.body;
|
const { firstName, lastName, email, wsdcId } = req.body;
|
||||||
|
|
||||||
// Build update data
|
// Build update data
|
||||||
const updateData = {};
|
const updateData = {};
|
||||||
if (firstName !== undefined) updateData.firstName = firstName;
|
if (firstName !== undefined) updateData.firstName = firstName;
|
||||||
if (lastName !== undefined) updateData.lastName = lastName;
|
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
|
// Check if email is being changed
|
||||||
const currentUser = await prisma.user.findUnique({
|
const currentUser = await prisma.user.findUnique({
|
||||||
|
|||||||
@@ -131,6 +131,11 @@ const updateProfileValidation = [
|
|||||||
.isEmail()
|
.isEmail()
|
||||||
.withMessage('Must be a valid email address')
|
.withMessage('Must be a valid email address')
|
||||||
.normalizeEmail(),
|
.normalizeEmail(),
|
||||||
|
body('wsdcId')
|
||||||
|
.optional()
|
||||||
|
.trim()
|
||||||
|
.matches(/^\d{0,10}$/)
|
||||||
|
.withMessage('WSDC ID must be numeric and up to 10 digits'),
|
||||||
handleValidationErrors,
|
handleValidationErrors,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ router.get('/me', authenticate, async (req, res, next) => {
|
|||||||
username: true,
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
wsdcId: true,
|
||||||
avatar: true,
|
avatar: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { authAPI } from '../services/api';
|
import { authAPI } from '../services/api';
|
||||||
import Layout from '../components/layout/Layout';
|
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 ProfilePage = () => {
|
||||||
const { user, updateUser } = useAuth();
|
const { user, updateUser } = useAuth();
|
||||||
@@ -10,10 +10,23 @@ const ProfilePage = () => {
|
|||||||
|
|
||||||
// Profile edit state
|
// Profile edit state
|
||||||
const [profileData, setProfileData] = useState({
|
const [profileData, setProfileData] = useState({
|
||||||
firstName: user?.firstName || '',
|
firstName: '',
|
||||||
lastName: user?.lastName || '',
|
lastName: '',
|
||||||
email: user?.email || '',
|
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 [profileLoading, setProfileLoading] = useState(false);
|
||||||
const [profileMessage, setProfileMessage] = useState('');
|
const [profileMessage, setProfileMessage] = useState('');
|
||||||
const [profileError, setProfileError] = useState('');
|
const [profileError, setProfileError] = useState('');
|
||||||
@@ -210,6 +223,33 @@ const ProfilePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</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 */}
|
{/* Email */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user