From 712c62975f44fc0b752fd5c636bb47b7f5d470a9 Mon Sep 17 00:00:00 2001 From: MQian21 Date: Wed, 20 Nov 2024 20:18:55 -0500 Subject: [PATCH 1/2] add db check for emails --- .../typescript/rest/serviceRequestRoutes.ts | 23 ++++++++ .../src/APIClients/ServiceRequestAPIClient.ts | 25 ++++++++- .../src/components/common/NavBarVolunteer.tsx | 3 - .../pages/CreateShift/CreateShiftMain.tsx | 56 ++++++++++++++----- 4 files changed, 88 insertions(+), 19 deletions(-) diff --git a/backend/typescript/rest/serviceRequestRoutes.ts b/backend/typescript/rest/serviceRequestRoutes.ts index 20a5278..b328cf5 100644 --- a/backend/typescript/rest/serviceRequestRoutes.ts +++ b/backend/typescript/rest/serviceRequestRoutes.ts @@ -160,4 +160,27 @@ serviceRequestRouter.delete("/delete/:id", isAuthorizedByRole(new Set(["ADMIN", } }); +/* Get user by email - this is for displaying first/last name for email invitation*/ +serviceRequestRouter.get("/user-by-email", isAuthorizedByRole(new Set(["ADMIN", "VOLUNTEER"])), async (req, res) => { + console.log("HIHIHI") + const { email } = req.query; + + if (!email || typeof email !== "string") { + return res.status(400).json({ error: "A valid email query parameter is required." }); + } + + try { + const user = await userService.getUserByEmail(email.trim()); + if (user) { + res.status(200).json({ firstName: user.firstName, lastName: user.lastName }); + } else { + res.status(404).json({ message: "User not found" }); + } + } catch (error: unknown) { + res.status(500).json({ error: getErrorMessage(error) }); + } +}); + + + export default serviceRequestRouter; diff --git a/frontend/src/APIClients/ServiceRequestAPIClient.ts b/frontend/src/APIClients/ServiceRequestAPIClient.ts index 7a29179..dddca84 100644 --- a/frontend/src/APIClients/ServiceRequestAPIClient.ts +++ b/frontend/src/APIClients/ServiceRequestAPIClient.ts @@ -77,4 +77,27 @@ const post = async ({ } }; -export default { get, getPlatformSignups, post }; \ No newline at end of file +const getUserByEmail = async (email: string): Promise<{ firstName: string; lastName: string } | null> => { + const bearerToken = `Bearer ${getLocalStorageObjProperty( + AUTHENTICATED_USER_KEY, + "accessToken", + )}`; + try { + console.log(`/user-by-email?email=${encodeURIComponent(email)}`) + const { data } = await baseAPIClient.get(`/serviceRequests/user-by-email?email=testpost@gmail.com}`, { + headers: { Authorization: bearerToken }, + }); + console.log("check") + return data; // { firstName: string; lastName: string } + } catch (error: any) { + if (error.response?.status === 404) { + console.warn("User not found for email:", email); + return null; + } + console.error("Error fetching user by email:", error); + throw error; + } +}; + + +export default { get, getPlatformSignups, post, getUserByEmail }; \ No newline at end of file diff --git a/frontend/src/components/common/NavBarVolunteer.tsx b/frontend/src/components/common/NavBarVolunteer.tsx index 1bef582..64af0ed 100644 --- a/frontend/src/components/common/NavBarVolunteer.tsx +++ b/frontend/src/components/common/NavBarVolunteer.tsx @@ -102,9 +102,6 @@ const NavBarVolunteer: React.FC = ({firstName, lastName, r Contact Us - {/* */} {/* TODO: add logic behind notification button */} { const { requestName, shiftTime, shiftEndTime, frequency, currentEmail, inviteEmails, requestType } = data; @@ -87,24 +88,49 @@ const CreateShiftMain: CreateShiftFormStepComponentType = ({ onSubmit, updateFie } }; - const handleAddEmail = () => { + const handleAddEmail = async () => { if (currentEmail && currentEmail.trim()) { - const emailError = validateEmail(currentEmail); - if (emailError) { - toast({ - title: "Invalid Email", - description: "Please enter a valid email address.", - status: "error", - position: 'top-right', - duration: 5000, - isClosable: true, - }); - return; // Stop execution if email is invalid + const emailError = validateEmail(currentEmail); + if (emailError) { + toast({ + title: "Invalid Email", + description: "Please enter a valid email address.", + status: "error", + position: 'top-right', + duration: 5000, + isClosable: true, + }); + return; + } + + try { + // Use the API client to get user by email + const user = await ServiceRequestAPIClient.getUserByEmail(currentEmail.trim()); + + if (user) { + // If user exists, display their name + const displayName = `${user.firstName} ${user.lastName}`; + const updatedEmails = [...(inviteEmails || []), displayName]; + updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); + } else { + // If user doesn't exist, add the email as-is + const updatedEmails = [...(inviteEmails || []), currentEmail.trim()]; + updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); } - const updatedEmails = [...(inviteEmails || []), currentEmail.trim()]; - updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); + } catch (error) { + console.error("Error fetching user by email:", error); + toast({ + title: "Error", + description: "Unable to verify email at this time. Please try again later.", + status: "error", + position: 'top-right', + duration: 5000, + isClosable: true, + }); + } } - }; + }; + const handleRemoveEmail = (index: number) => { const updatedEmails = (inviteEmails || []).filter((_, i) => i !== index); From bd2125648c2a43f9467961c36503bf594c03b49e Mon Sep 17 00:00:00 2001 From: MQian21 Date: Sat, 23 Nov 2024 23:04:54 -0500 Subject: [PATCH 2/2] Added DB name search in inviteEmails functionality and error handling for emails not in database --- .../typescript/rest/serviceRequestRoutes.ts | 3 +- .../services/implementations/userService.ts | 1 + .../src/APIClients/ServiceRequestAPIClient.ts | 5 +- .../pages/CreateShift/CreateShiftMain.tsx | 71 +++++++++---------- frontend/src/utils/FormatUtils.ts | 9 +++ 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/backend/typescript/rest/serviceRequestRoutes.ts b/backend/typescript/rest/serviceRequestRoutes.ts index b328cf5..293b440 100644 --- a/backend/typescript/rest/serviceRequestRoutes.ts +++ b/backend/typescript/rest/serviceRequestRoutes.ts @@ -162,7 +162,6 @@ serviceRequestRouter.delete("/delete/:id", isAuthorizedByRole(new Set(["ADMIN", /* Get user by email - this is for displaying first/last name for email invitation*/ serviceRequestRouter.get("/user-by-email", isAuthorizedByRole(new Set(["ADMIN", "VOLUNTEER"])), async (req, res) => { - console.log("HIHIHI") const { email } = req.query; if (!email || typeof email !== "string") { @@ -177,7 +176,7 @@ serviceRequestRouter.get("/user-by-email", isAuthorizedByRole(new Set(["ADMIN", res.status(404).json({ message: "User not found" }); } } catch (error: unknown) { - res.status(500).json({ error: getErrorMessage(error) }); + res.status(404).json({ error: getErrorMessage(error) }); } }); diff --git a/backend/typescript/services/implementations/userService.ts b/backend/typescript/services/implementations/userService.ts index 8e0523c..1190132 100644 --- a/backend/typescript/services/implementations/userService.ts +++ b/backend/typescript/services/implementations/userService.ts @@ -72,6 +72,7 @@ class UserService implements IUserService { throw new Error(`userId with authId ${firebaseUser.uid} not found.`); } } catch (error: unknown) { + console.log("CATCH ERROR") Logger.error(`Failed to get user. Reason = ${getErrorMessage(error)}`); throw error; } diff --git a/frontend/src/APIClients/ServiceRequestAPIClient.ts b/frontend/src/APIClients/ServiceRequestAPIClient.ts index dddca84..320863a 100644 --- a/frontend/src/APIClients/ServiceRequestAPIClient.ts +++ b/frontend/src/APIClients/ServiceRequestAPIClient.ts @@ -84,17 +84,16 @@ const getUserByEmail = async (email: string): Promise<{ firstName: string; lastN )}`; try { console.log(`/user-by-email?email=${encodeURIComponent(email)}`) - const { data } = await baseAPIClient.get(`/serviceRequests/user-by-email?email=testpost@gmail.com}`, { + const { data } = await baseAPIClient.get(`/serviceRequests/user-by-email?email=${encodeURIComponent(String(email))}`, { headers: { Authorization: bearerToken }, }); - console.log("check") return data; // { firstName: string; lastName: string } } catch (error: any) { if (error.response?.status === 404) { console.warn("User not found for email:", email); return null; } - console.error("Error fetching user by email:", error); + // console.error("Error fetching user by email:", error); throw error; } }; diff --git a/frontend/src/components/pages/CreateShift/CreateShiftMain.tsx b/frontend/src/components/pages/CreateShift/CreateShiftMain.tsx index a262359..66b649b 100644 --- a/frontend/src/components/pages/CreateShift/CreateShiftMain.tsx +++ b/frontend/src/components/pages/CreateShift/CreateShiftMain.tsx @@ -3,7 +3,7 @@ import moment from "moment"; import { Button, ModalBody, ModalFooter, Box, FormControl, FormLabel, Input, Flex, Select, NumberDecrementStepper, NumberIncrementStepper, NumberInput, NumberInputField, NumberInputStepper, InputRightElement, InputGroup, FormHelperText, Tag, TagLabel, TagCloseButton, Wrap, WrapItem, useToast} from "@chakra-ui/react"; import { CreateShiftFormStepComponentType, CreateShiftFormStepProps, Frequency, ServiceRequestType } from '../../../types/ServiceRequestTypes'; -import { titleCase } from '../../../utils/FormatUtils'; +import { capitalizeName, titleCase } from '../../../utils/FormatUtils'; import EARLIEST_SHIFT_TIME from '../../../constants/ServiceRequestConstants'; import { validateEmail } from '../../../utils/ValidationUtils' import ServiceRequestAPIClient from "../../../APIClients/ServiceRequestAPIClient"; @@ -90,46 +90,43 @@ const CreateShiftMain: CreateShiftFormStepComponentType = ({ onSubmit, updateFie const handleAddEmail = async () => { if (currentEmail && currentEmail.trim()) { - const emailError = validateEmail(currentEmail); - if (emailError) { - toast({ - title: "Invalid Email", - description: "Please enter a valid email address.", - status: "error", - position: 'top-right', - duration: 5000, - isClosable: true, - }); - return; - } + const emailError = validateEmail(currentEmail); + if (emailError) { + toast({ + title: "Invalid Email", + description: "Please enter a valid email address.", + status: "error", + position: 'top-right', + duration: 5000, + isClosable: true, + }); + return; + } - try { - // Use the API client to get user by email - const user = await ServiceRequestAPIClient.getUserByEmail(currentEmail.trim()); + try { + const user = await ServiceRequestAPIClient.getUserByEmail(currentEmail.trim()); - if (user) { - // If user exists, display their name - const displayName = `${user.firstName} ${user.lastName}`; - const updatedEmails = [...(inviteEmails || []), displayName]; - updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); - } else { - // If user doesn't exist, add the email as-is - const updatedEmails = [...(inviteEmails || []), currentEmail.trim()]; - updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); + if (user) { + // If user exists in the database, add their full name + const displayName = capitalizeName(`${user.firstName} ${user.lastName}`); + const updatedEmails = [...(inviteEmails || []), displayName]; + updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); + } else { + // If user doesn't exist, add the email as-is (this part doesn't work right now so I put the logic in the catch block) + const updatedEmails = [...(inviteEmails || []), currentEmail.trim()]; + updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); + } + } catch (error) { + console.error("User not Found:", error); + // Add the email if there's an error + const updatedEmails = [...(inviteEmails || []), currentEmail.trim()]; + updateFields({ inviteEmails: updatedEmails, currentEmail: "" }); } - } catch (error) { - console.error("Error fetching user by email:", error); - toast({ - title: "Error", - description: "Unable to verify email at this time. Please try again later.", - status: "error", - position: 'top-right', - duration: 5000, - isClosable: true, - }); - } + console.log(inviteEmails); } - }; + }; + + const handleRemoveEmail = (index: number) => { diff --git a/frontend/src/utils/FormatUtils.ts b/frontend/src/utils/FormatUtils.ts index ce7e6dd..b481caa 100644 --- a/frontend/src/utils/FormatUtils.ts +++ b/frontend/src/utils/FormatUtils.ts @@ -2,3 +2,12 @@ export const titleCase = (value: string): string => { return value[0].toUpperCase() + value.slice(1).toLowerCase(); }; + + +export const capitalizeName = (name: string) => { + if (!name) return ''; + return name + .split(' ') + .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()) + .join(' '); +}; \ No newline at end of file