From 3d7b84f37640a8031e4f3ee9a091fe6ce7b1f906 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Tue, 18 Nov 2025 18:22:03 +0000 Subject: [PATCH] feat: Add email field to profile edit form - Add email input field to profile edit form - Display current email or 'No email address set' message - Show helpful hint when adding email for the first time - Update backend to accept email in profile updates - Add email uniqueness validation - Allow users to add/update/remove email address - Support empty string to remove email (converts to null) Users can now add or update their email address from the profile page, enabling them to receive email notifications. --- server/src/controllers/userController.ts | 3 ++- server/src/services/userService.ts | 19 +++++++++++++++++ src/pages/UserProfilePage.tsx | 27 ++++++++++++++++++++++++ src/services/userService.ts | 1 + 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/server/src/controllers/userController.ts b/server/src/controllers/userController.ts index 1fed7e4..561ebbb 100644 --- a/server/src/controllers/userController.ts +++ b/server/src/controllers/userController.ts @@ -41,7 +41,7 @@ export class UserController { async updateUserProfile(req: AuthRequest, res: Response) { try { const { id } = req.params; - const { firstName, lastName, username } = req.body; + const { firstName, lastName, username, email } = req.body; // Authorization check - users can only update their own profile if (!req.userId) { @@ -91,6 +91,7 @@ export class UserController { firstName, lastName, username, + email, }); res.json(updatedProfile); diff --git a/server/src/services/userService.ts b/server/src/services/userService.ts index a3c7d57..5b89425 100644 --- a/server/src/services/userService.ts +++ b/server/src/services/userService.ts @@ -5,6 +5,7 @@ export interface UpdateUserProfileData { firstName?: string; lastName?: string; username?: string; + email?: string; } export interface UpdateNotificationPreferencesData { @@ -146,12 +147,30 @@ export class UserService { } } + // Check if email is being changed and if it's already taken + if (data.email !== undefined) { + // Allow setting email to null or empty string + if (data.email && data.email.trim() !== '') { + const existingUser = await prisma.user.findFirst({ + where: { + email: data.email, + id: { not: userId }, + }, + }); + + if (existingUser) { + throw new Error('Email already in use'); + } + } + } + const updatedUser = await prisma.user.update({ where: { id: userId }, data: { firstName: data.firstName, lastName: data.lastName, username: data.username, + email: data.email !== undefined ? (data.email.trim() === '' ? null : data.email) : undefined, }, select: { id: true, diff --git a/src/pages/UserProfilePage.tsx b/src/pages/UserProfilePage.tsx index 354329c..e4a45b2 100644 --- a/src/pages/UserProfilePage.tsx +++ b/src/pages/UserProfilePage.tsx @@ -39,6 +39,7 @@ export function UserProfilePage() { firstName: '', lastName: '', username: '', + email: '', }); const [notificationPreferences, setNotificationPreferences] = useState({ notificationEmail: true, @@ -86,6 +87,7 @@ export function UserProfilePage() { firstName: profileData.firstName || '', lastName: profileData.lastName || '', username: profileData.username || '', + email: profileData.email || '', }); // Set notification preferences from profile @@ -112,6 +114,7 @@ export function UserProfilePage() { firstName: profile.firstName || '', lastName: profile.lastName || '', username: profile.username || '', + email: profile.email || '', }); } }; @@ -235,6 +238,20 @@ export function UserProfilePage() { placeholder="Username" className="px-3 py-1 border border-gray-300 rounded-md w-full" /> +
+ setEditForm({ ...editForm, email: e.target.value })} + placeholder="Email address (optional)" + className="px-3 py-1 border border-gray-300 rounded-md w-full" + /> + {!profile.email && ( +

+ 💡 Add an email to receive email notifications +

+ )} +
) : ( <> @@ -244,6 +261,16 @@ export function UserProfilePage() { : profile.username}

@{profile.username}

+ {!profile.email && ( +

+ 📧 No email address set +

+ )} + {profile.email && ( +

+ 📧 {profile.email} +

+ )} )} diff --git a/src/services/userService.ts b/src/services/userService.ts index 9934d7a..8ef1ea2 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -31,6 +31,7 @@ export interface UpdateProfileDTO { firstName?: string; lastName?: string; username?: string; + email?: string; } export interface NotificationPreferences {