From 6148b60418bda523180e308e24a0fa4cef2ec66e Mon Sep 17 00:00:00 2001 From: Dwain-Anderson Date: Sun, 9 Mar 2025 14:35:58 -0400 Subject: [PATCH] Finished fixing WorkingHours, Sidebar, normalizing the types in EmployeeModal,and uploading photos. --- .../EmployeeCards/EmployeeCards.tsx | 2 +- .../EmployeeModal/EmployeeModal.tsx | 576 +++++++++--------- .../src/components/EmployeeModal/Upload.tsx | 2 +- .../components/EmployeeModal/WorkingHours.tsx | 36 +- frontend/src/components/Sidebar/Sidebar.tsx | 23 +- .../src/components/UserDetail/UserDetail.tsx | 67 +- frontend/src/pages/Admin/Employees.tsx | 27 +- server/src/router/admin.ts | 13 +- server/src/router/common.ts | 4 +- server/src/router/driver.ts | 21 +- 10 files changed, 435 insertions(+), 336 deletions(-) diff --git a/frontend/src/components/EmployeeCards/EmployeeCards.tsx b/frontend/src/components/EmployeeCards/EmployeeCards.tsx index b836d7a08..8c67816a3 100644 --- a/frontend/src/components/EmployeeCards/EmployeeCards.tsx +++ b/frontend/src/components/EmployeeCards/EmployeeCards.tsx @@ -74,7 +74,7 @@ const EmployeeCard = ({ firstName={firstName} lastName={lastName} netId={netId} - photoLink={photoLink} + photoLink={photoLink ? `${photoLink}?t=${new Date().getTime()}` : ''} >

{fmtPhone}

diff --git a/frontend/src/components/EmployeeModal/EmployeeModal.tsx b/frontend/src/components/EmployeeModal/EmployeeModal.tsx index 7ffda6e24..071266900 100644 --- a/frontend/src/components/EmployeeModal/EmployeeModal.tsx +++ b/frontend/src/components/EmployeeModal/EmployeeModal.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import Modal from '../Modal/Modal'; import { Button } from '../FormElements/FormElements'; -import { ObjectType } from '../../types/index'; +import { Employee, ObjectType } from '../../types/index'; import EmployeeInfo from './EmployeeInfo'; import RoleSelector from './RoleSelector'; import StartDate from './StartDate'; @@ -13,41 +13,58 @@ import { useEmployees } from '../../context/EmployeesContext'; import { useToast, ToastStatus } from '../../context/toastContext'; import axios from '../../util/axios'; -type EmployeeModalProps = { - existingEmployee?: { - id?: string; - firstName?: string; - lastName?: string; - type?: string[]; - isDriver?: boolean; - netId?: string; - email?: string; - phone?: string; - availability?: string[][]; - startDate?: string; - photoLink?: string; - }; - isOpen: boolean; - setIsOpen: any; -}; - type AdminData = { - id?: string; - firstName: any; - lastName: any; type: string[]; isDriver: boolean; - email: any; - phoneNumber: any; }; type DriverData = { - id?: string; - firstName: any; - lastName: any; - email: any; - phoneNumber: any; - availability: ObjectType; + availability: any; + startDate: string; +}; + +//normalized type +type EmployeeEntity = { + id: string; + firstName: string; + lastName: string; + phoneNumber: string; + netId: string; + email: string; + driver?: DriverData; + admin?: AdminData; + photoLink?: string; +}; + +// both for formating to current api data expections +function extractAdminData(employeeData: EmployeeEntity) { + return { + firstName: employeeData.firstName, + lastName: employeeData.lastName, + type: employeeData.admin?.type, + isDriver: employeeData.admin?.isDriver, + phoneNumber: employeeData.phoneNumber, + email: employeeData.email, + photoLink: employeeData.photoLink, + }; +} + +function extractDriverData(employeeData: EmployeeEntity) { + return { + firstName: employeeData.firstName, + lastName: employeeData.lastName, + availability: employeeData.driver?.availability, + phoneNumber: employeeData.phoneNumber, + startDate: employeeData.driver?.startDate, + email: employeeData.email, + photoLink: employeeData.photoLink, + }; +} + +type EmployeeModalProps = { + existingEmployee: EmployeeEntity | null; + isOpen: boolean; + setIsOpen: (open: boolean) => void; }; const EmployeeModal = ({ @@ -56,20 +73,9 @@ const EmployeeModal = ({ setIsOpen, }: EmployeeModalProps) => { const { showToast } = useToast(); - - if (existingEmployee?.isDriver !== undefined) { - if (existingEmployee.isDriver) { - existingEmployee?.type?.push('driver'); - } - } else if (existingEmployee) { - existingEmployee.type = existingEmployee?.type || ['driver']; - } - - const [selectedRole, setSelectedRole] = useState( - existingEmployee?.type || [] - ); - const [imageBase64, setImageBase64] = useState(''); const { refreshAdmins, refreshDrivers } = useEmployees(); + const [selectedRoles, setSelectedRole] = useState([]); + const [imageBase64, setImageBase64] = useState(''); const methods = useForm(); const closeModal = () => { @@ -77,305 +83,286 @@ const EmployeeModal = ({ setIsOpen(false); }; - /** - * Converts availabilities expressed as an array of {starTime, endTime, days} - * objects into an object mapping the day to the start and end time of each - * availability period - * - * @param availability the availibity array to convert - * @returns the availibity array expressed as an object mapping the day to - * the start and end time of each availibility period - */ - const parseAvailability = (availability: ObjectType[]) => { + function parseAvailability(availability: ObjectType[]) { + const result: ObjectType = {}; if (availability === null || availability === undefined) { - console.error('Null ptr: Availablity'); - return []; // placeholder - } else { - const result: ObjectType = {}; - availability.forEach(({ startTime, endTime, days }) => { - days.forEach((day: string) => { - result[day] = { startTime, endTime }; - }); - }); return result; } - }; + availability.forEach(({ startTime, endTime, days }) => { + days.forEach((day: string) => { + result[day] = { startTime, endTime }; + }); + }); + return result; + } + + /** + * Uploads an employee's photo (in base64 format) to the backend. + * + * @param employeeId - The ID of the employee whose photo is being uploaded. + * @param table - The name of the table (used for determining where to store the photo). + * @param imageBase64 - The photo image data in base64 format. + * @returns A promise that resolves when the photo is successfully uploaded. + */ async function uploadEmployeePhoto( employeeId: string, table: string, - refresh: () => Promise, imageBase64: string ): Promise { - const photoData = { - id: employeeId, - tableName: table, - fileBuffer: imageBase64, - }; try { - await axios.post('/api/upload/', photoData); + await axios.post('/api/upload/', { + id: employeeId, + tableName: table, + fileBuffer: imageBase64, + }); } catch (error) { console.error('Error uploading photo:', error); } - refresh(); } /** - * Creates a new employee using the provided data and endpoint. Optionally uploads a photo if - * [iteration] is 0 and [imageBase64] is non-empty. + * Sends the employee role data to the backend for creation and returns the new employee ID. * - * @param id - The unique identifier of the employee to create, or an empty string if not provided. - * @param employeeData - The data to create the new employee with. This should be a valid object. - * @param endpoint - The API endpoint to which the employee data will be sent. - * @param refresh - A function that refreshes the employee data or table after the creation. - * @param iteration - A non-negative integer used to conditionally upload a photo. - * @param imageBase64 - A base64 encoded string representing the employee's photo (optional). - * - * @returns A promise that resolves to the created employee data. - * - * @throws {Error} Throws an error if any of the parameters are invalid or if the creation fails. + * @param employeeData - The employee entity containing the details to be created. + * @param endpoint - The API endpoint, either `/api/admins` or `/api/drivers`. + * @returns A promise resolving to the newly created employee ID. */ + async function createEmployee( - id: string, - employeeData: AdminData | DriverData, - endpoint: string, - refresh: () => Promise, - table: string, - iteration: number - ): Promise { - if (Boolean(id) && id !== '') { - (employeeData as any).id = id; - } - const { - data: { data: createdEmployee }, - } = await axios.post(endpoint, employeeData); - if (iteration === 0 && imageBase64 !== '') { - await uploadEmployeePhoto( - createdEmployee.id, - table, - refresh, - imageBase64 - ); + employeeData: EmployeeEntity, + endpoint: '/api/admins' | '/api/drivers' + ): Promise { + let res: any; + console.log(employeeData); + switch (endpoint) { + case '/api/drivers': + const { + data: { data: createdDriver }, + } = await axios.post(endpoint, { + ...extractDriverData(employeeData), + eid: employeeData.id || '', + }); + res = createdDriver; + break; + case '/api/admins': + const { + data: { data: createdAdmin }, + } = await axios.post(endpoint, { + ...extractAdminData(employeeData), + eid: employeeData.id || '', + }); + res = createdAdmin; + break; + default: + break; } - await refresh(); - return createdEmployee; + return res; } /** - * Updates an existing employee's data using the specified endpoint. Optionally uploads a photo - * if [iteration] is 0 and [imageBase64] is non-empty. - * - * @param id - The unique identifier of the employee to update. - * @param employeeData - The data to update the employee with. This should be a valid object. - * @param endpoint - The API endpoint to which the employee data will be sent. - * @param refresh - A function that refreshes the employee data or table after the update. - * @param iteration - A non-negative integer used to conditionally upload a photo. - * @param imageBase64 - A base64 encoded string representing the employee's photo (optional). + * Sends the updated employee role data to the backend and returns the updated employee. * - * @returns A promise that resolves after successfully updating the employee and refreshing - * the data. - * - * @throws {Error} Throws an error if any of the parameters are invalid or if the update fails. + * @param employeeData - The employee entity containing updated data. + * @param endpoint - The API endpoint, either `/api/admins` or `/api/drivers`. + * @returns A promise resolving to the updated employee entity. */ - async function updateEmployee( - id: string, - employeeData: AdminData | DriverData, - endpoint: string, - refresh: () => Promise, - table: string, - iteration: number - ): Promise { - await axios.put(`${endpoint}/${id}`, employeeData); - // iteration count prevents a second write to S3 - if (iteration === 0 && imageBase64 !== '') { - uploadEmployeePhoto(id, table, refresh, imageBase64); + employeeData: EmployeeEntity, + endpoint: '/api/admins' | '/api/drivers' + ): Promise { + let res: any; + console.log(employeeData); + switch (endpoint) { + case '/api/drivers': + const { + data: { data: updatedDriver }, + } = await axios.put( + `${endpoint}/${employeeData.id}`, + extractDriverData(employeeData) + ); + res = updatedDriver; + break; + case '/api/admins': + const { + data: { data: updatedAdmin }, + } = await axios.put( + `${endpoint}/${employeeData.id}`, + extractDriverData(employeeData) + ); + res = updatedAdmin; + break; + default: + break; } - refresh(); + console.log(res); + return res; // Return the updated employee data, remove double nested later } /** - * [deleteEmployee id emptype] removes an employee with the specified [id] from the backend, - * using the employee type [emptype] ('drivers' or 'admins') to determine the endpoint. - * - * @param id - The unique identifier of the employee to delete. - * @param emptype - The type of employee, either 'drivers' or 'admins'. + * Deletes an employee role entry from the backend. * - * @returns A promise that resolves after successfully deleting the employee. - * - * @throws {Error} Throws an error if the id is not a valid string or the emptype is not 'drivers' or 'admins'. + * @param id - The ID of the employee to be deleted. + * @param endpoint - The API endpoint to delete from. + * @returns A promise that resolves when the deletion is complete. */ - async function deleteEmployee(id: string, emptype: 'drivers' | 'admins') { - await axios.delete(`/api/${emptype}/${id}`); + async function deleteEmployee(id: string, endpoint: string): Promise { + await axios.delete(`${endpoint}/${id}`); } /** - * Processes and assigns roles ('driver', 'admin') for an employee, handling creation, - * updating, or deletion of their information as needed. - * - * @param selectedRole - Valid array of roles to assign. - * @param existingEmployee - Existing employee data object or `null` (if new). - * @param admin - Valid employee data object for the 'admin' role. - * @param driver - Valid employee data object for the 'driver' role. - * @returns A promise that resolves when all role processing is complete. - * @throws {Error} If input parameters fail validation (invalid types or structure). + * Processes the selected roles: + * - If no employee exists, creates one using the primary selected role. + * - If the employee exists, updates or adds the appropriate roles. + * - Deletes roles if they are no longer selected. * + * @param selectedRoles - Array of roles from the form. + * @param existingEmployee - Existing employee data or null. + * @returns The updated or newly created employee entity. */ async function processRoles( - selectedRole: any, - existingEmployee: any, - admin: any, - driver: any - ) { - const containsDriver = selectedRole.includes('driver'); - const containsAdmin = - selectedRole.includes('sds-admin') || - selectedRole.includes('redrunner-admin'); - - const rolesToProcess = []; - if (containsAdmin) rolesToProcess.push('admins'); - if (containsDriver) rolesToProcess.push('drivers'); - - let newEmployee = null; // To track new employee creation - let iteration = 0; - - for (const role of rolesToProcess) { - const apiEndpoint = role === 'admins' ? '/api/admins' : '/api/drivers'; - const refreshFunction = - role === 'admins' ? refreshAdmins : refreshDrivers; - const entityType = role === 'admins' ? 'Admins' : 'Drivers'; - - if (Boolean(existingEmployee)) { - switch (role) { - case 'admins': - if (existingEmployee.isDriver && !containsDriver) { - // Transition from driver to admin - await deleteEmployee( - newEmployee?.id || existingEmployee.id, - 'drivers' - ); - } - - if (!existingEmployee.isAdmin) { - // Create admin - await createEmployee( - newEmployee?.id || existingEmployee.id, - admin, - apiEndpoint, - refreshFunction, - entityType, - iteration - ); - } else { - // Update admin - await updateEmployee( - newEmployee?.id || existingEmployee.id, - admin, - apiEndpoint, - refreshFunction, - entityType, - iteration - ); - } - break; - - case 'drivers': - if (existingEmployee.isAdmin && !containsAdmin) { - // Transition from admin to driver - await deleteEmployee( - newEmployee?.id || existingEmployee.id, - 'admins' - ); - } - - if (!existingEmployee.isDriver) { - // Create driver - await createEmployee( - newEmployee?.id || existingEmployee.id, - driver, - apiEndpoint, - refreshFunction, - entityType, - iteration - ); - } else { - // Update driver - await updateEmployee( - newEmployee?.id || existingEmployee.id, - driver, - apiEndpoint, - refreshFunction, - entityType, - iteration - ); - } - break; - } - } else if (!newEmployee) { - // Create a new employee if no existing employee is present - newEmployee = await createEmployee( - '', - role === 'admins' ? admin : driver, - apiEndpoint, - refreshFunction, - entityType, - iteration + selectedRoles: string[], + employeeData: EmployeeEntity + ): Promise { + const hasAdmin = + selectedRoles.includes('sds-admin') || + selectedRoles.includes('redrunner-admin'); + const hasDriver = selectedRoles.includes('driver'); + + let currentId = employeeData.id; + // If no employee exists, create one using a primary role. + if (!currentId || currentId === '') { + if (hasAdmin) { + employeeData.id = ( + await createEmployee(employeeData, '/api/admins') + ).id; + showToast( + `Created a new employee with the admin role`, + ToastStatus.SUCCESS ); - existingEmployee = newEmployee; + } + if (hasDriver) { + employeeData.id = ( + await createEmployee(employeeData, '/api/drivers') + ).id; showToast( - `Created a new employee with the role of ${role} based on your provided data`, + `Created a new employee with the driver role`, ToastStatus.SUCCESS ); } - iteration += 1; + } else { + if (hasAdmin) { + if (employeeData.admin) { + await updateEmployee(employeeData, '/api/admins'); + } else { + await createEmployee(employeeData, '/api/admins'); + } + } else if (employeeData.admin) { + await deleteEmployee(employeeData.id, '/api/admins'); + } + + if (hasDriver) { + if (employeeData.driver) { + await updateEmployee(employeeData, '/api/drivers'); + } else { + await createEmployee(employeeData, '/api/drivers'); + } + } else if (employeeData.driver) { + await deleteEmployee(employeeData.id, '/api/drivers'); + } } + let id = employeeData.id; + if (!hasAdmin && !hasDriver) { + return ''; + } + return id; } - async function onSubmit(data: ObjectType) { - const { firstName, lastName, netid, phoneNumber, startDate, availability } = - data; + /** + * Handles form submission. + * + * - Constructs payloads for both driver and admin roles. + * - Sends the data to the backend via processRoles(). + * - If a photo was uploaded, sends the photo to the backend afterward. + * + * @param formData - The data received from the form/modal. + * @param photo - The optional photo file uploaded by the user. + */ + async function onSubmit(formData: any) { + // Data entered from the form/modal. + console.log(formData); + const hasAdmin = + selectedRoles.includes('sds-admin') || + selectedRoles.includes('redrunner-admin'); + const hasDriver = selectedRoles.includes('driver'); + + let admin_data: AdminData | null = null; + let driver_data: DriverData | null = null; + + if (hasAdmin) { + admin_data = { + type: selectedRoles.filter((role) => role !== 'driver'), + isDriver: hasDriver, + }; + } - const driver = { - firstName, - lastName, - email: `${netid}@cornell.edu`, - phoneNumber, - startDate, - availability: parseAvailability(availability), - }; + if (hasDriver) { + driver_data = { + availability: parseAvailability(formData.availability), + startDate: formData.startDate, + }; + } - const admin = { - firstName, - lastName, - email: `${netid}@cornell.edu`, - type: selectedRole.filter((role) => role !== 'driver'), - phoneNumber, - availability: parseAvailability(availability), - isDriver: selectedRole.includes('driver'), + const employeeData: EmployeeEntity = { + id: existingEmployee?.id || '', + firstName: formData.firstName, + lastName: formData.lastName, + phoneNumber: formData.phoneNumber, + netId: formData.netid, + email: `${formData.netid}@cornell.edu`, + driver: driver_data || undefined, + admin: admin_data || undefined, }; - try { - await processRoles(selectedRole, existingEmployee, admin, driver); - showToast(`Employee information proccessed`, ToastStatus.SUCCESS); + // Process roles: data is sent to the backend endpoints here. + const id = await processRoles(selectedRoles, employeeData); + // If a photo was uploaded, send it to the backend. + if (imageBase64 && id !== '') { + // Decide which table to update based on role selection (to be changed at a later date.) + const targetTable = + selectedRoles.includes('sds-admin') || + selectedRoles.includes('redrunner-admin') + ? 'Admins' + : 'Drivers'; + await uploadEmployeePhoto(id, targetTable, imageBase64); + } + + // Refresh both admin and driver data once after processing. + await refreshAdmins(); + await refreshDrivers(); + + showToast(`Employee information processed`, ToastStatus.SUCCESS); } catch (error) { - showToast('An error occured: ', ToastStatus.ERROR); + showToast('An error occurred: ', ToastStatus.ERROR); } finally { closeModal(); } } - + // ------------------------------- + // PHOTO FILE INPUT HANDLER + // ------------------------------- + // This function handles the photo file input change event. async function updateBase64(e: React.ChangeEvent) { e.preventDefault(); - const { files } = e.target; if (files && files[0]) { const file = files[0]; const reader = new FileReader(); reader.readAsDataURL(file); - reader.onload = async () => { - const base64 = reader.result?.toString().split(',')[1]; // Extract base64 + reader.onload = () => { + const base64 = reader.result?.toString().split(',')[1]; if (base64) { - setImageBase64(base64); // Save the base64 string + setImageBase64(base64); } }; } @@ -389,35 +376,44 @@ const EmployeeModal = ({ onClose={closeModal} id="employee-modal" > + {/* Photo upload component – data enters here */}
{ - methods.handleSubmit(onSubmit)(e); - }} + onSubmit={(e) => methods.handleSubmit(onSubmit)(e)} aria-labelledby="employee-modal" > - + {(selectedRoles.includes('driver') || + existingEmployee?.driver?.availability) && ( + <> + + + + )} - + diff --git a/frontend/src/components/EmployeeModal/Upload.tsx b/frontend/src/components/EmployeeModal/Upload.tsx index 9c5ec7869..d4b325060 100644 --- a/frontend/src/components/EmployeeModal/Upload.tsx +++ b/frontend/src/components/EmployeeModal/Upload.tsx @@ -11,7 +11,7 @@ type UploadProps = { const Upload = ({ imageChange, existingPhoto }: UploadProps) => { const [imageURL, setImageURL] = useState( - existingPhoto ? `${existingPhoto}` : '' + existingPhoto ? `${existingPhoto}?${new Date().getTime()}` : '' ); const [errorMessage, setErrorMessage] = useState(null); const inputRef = createRef(); diff --git a/frontend/src/components/EmployeeModal/WorkingHours.tsx b/frontend/src/components/EmployeeModal/WorkingHours.tsx index 23989b1b8..4a11e14bc 100644 --- a/frontend/src/components/EmployeeModal/WorkingHours.tsx +++ b/frontend/src/components/EmployeeModal/WorkingHours.tsx @@ -150,23 +150,37 @@ type WorkingHoursProps = { existingAvailability?: string[][]; hide: boolean; }; - const WorkingHours: React.FC = ({ existingAvailability, hide, }) => { + // Determine if we have existing availability (a non-empty array) + const hasExisting = existingAvailability && existingAvailability.length > 0; + + // If there is existing availability, use its length; otherwise, start at 1. const [numAvailability, setNumAvailability] = useState( - existingAvailability ? 0 : 1 + hasExisting ? existingAvailability!.length : 1 ); + + // This array will be built from the existingAvailability data (if any) const [availabilityArray, setAvailabilityArray] = useState< [string, string[]][] >([]); - const addAvailabilityInput = () => setNumAvailability((n) => n + 1); + // When the "Add more" button is clicked, increment the number of availability inputs. + const addAvailabilityInput = () => { + setNumAvailability((n) => { + const newValue = n + 1; + console.log('New numAvailability:', newValue); + return newValue; + }); + }; + // Map the existingAvailability (an array of [day, timeRange] pairs) + // into a Map where the key is the timeRange and the value is an array of days. const getAvailabilityMap = useCallback((): Map => { - const availabilityMap = new Map(); - existingAvailability?.forEach((availability) => { + const availabilityMap = new Map(); + (existingAvailability || []).forEach((availability) => { const [day, timeRange] = availability; const dayArray = availabilityMap.get(timeRange) || []; dayArray.push(day); @@ -175,6 +189,7 @@ const WorkingHours: React.FC = ({ return availabilityMap; }, [existingAvailability]); + // Convert the Map into an array for easier mapping. const availabilityMapToArray = useCallback((map: Map) => { const newAvailabilityArray: [string, string[]][] = Array.from( map, @@ -184,14 +199,16 @@ const WorkingHours: React.FC = ({ }, []); useEffect(() => { - availabilityMapToArray(getAvailabilityMap()); - }, [getAvailabilityMap, availabilityMapToArray]); + if (hasExisting) { + availabilityMapToArray(getAvailabilityMap()); + } + }, [hasExisting, getAvailabilityMap, availabilityMapToArray]); return (

Working Hours

- {existingAvailability + {hasExisting ? availabilityArray.map(([timeRange, dayArray], index) => ( = ({ hide={hide} /> )) - : [...Array(numAvailability)].map((_, index) => ( + : // If no existing availability, use fallback: create an array based on numAvailability. + [...Array(numAvailability)].map((_, index) => ( ))} diff --git a/frontend/src/components/Sidebar/Sidebar.tsx b/frontend/src/components/Sidebar/Sidebar.tsx index ce08e0525..f0b65aa19 100644 --- a/frontend/src/components/Sidebar/Sidebar.tsx +++ b/frontend/src/components/Sidebar/Sidebar.tsx @@ -31,7 +31,7 @@ const Sidebar = ({ type, children }: SidebarProps) => { const componentMounted = useRef(true); const { pathname } = useLocation(); const [selected, setSelected] = useState(pathname); - const [profile, setProfile] = useState(''); + const [photoLink, setPhotoLink] = useState(''); const authContext = useContext(AuthContext); const localUserType = localStorage.getItem('userType'); const isAdmin = localUserType === 'Admin'; @@ -41,21 +41,26 @@ const Sidebar = ({ type, children }: SidebarProps) => { if (isAdmin) { axios .get(`/api/admins/${id}`) - .then((res) => res.data) - .then( - (data) => componentMounted.current && setProfile(data.data.photoLink) - ); + .then((res: any) => res.data) + .then((data: any) => { + if (componentMounted) { + setPhotoLink(data.data.photoLink); + } + }); return () => { componentMounted.current = false; }; } else { axios + //riders dont have photo upload, only admin and drivers but maybe the intention was for all three? .get(`/api/riders/${id}`) .then((res) => res.data) - .then( - (data) => componentMounted.current && setProfile(data.data.photoLink) - ); + .then((data) => { + if (componentMounted.current) { + setPhotoLink(data.data.photoLink); + } + }); return () => { componentMounted.current = false; @@ -112,7 +117,7 @@ const Sidebar = ({ type, children }: SidebarProps) => { profile_picture )} {/* Remove the profile condition */} diff --git a/frontend/src/components/UserDetail/UserDetail.tsx b/frontend/src/components/UserDetail/UserDetail.tsx index f84ac2f4b..e5492ccd5 100644 --- a/frontend/src/components/UserDetail/UserDetail.tsx +++ b/frontend/src/components/UserDetail/UserDetail.tsx @@ -88,6 +88,45 @@ const UserDetail = ({ setConfirmationModalisOpen(false); }; + function createEmployeeEntity( + id: string, + firstName: string, + lastName: string, + netId: string, + phoneNumber: string, + availability?: string[][], + startDate?: string, + isDriver?: boolean, + type?: string[], + photoLink?: string + ): any { + return { + id, + firstName, + lastName, + netId, + email: `${netId}@cornell.edu`, + phoneNumber: phoneNumber.replaceAll('-', ''), + ...(availability || startDate + ? { + driver: { + availability: availability || [[]], + startDate: startDate || '', + }, + } + : {}), + ...(isDriver !== undefined || type + ? { + admin: { + isDriver: isDriver || false, + type: type || [], + }, + } + : {}), + photoLink, + }; + } + const toggleActive = (): void => { if (rider) { const { id, active } = rider; @@ -97,6 +136,7 @@ const UserDetail = ({ }); } }; + return (
{isShowing && rider ? ( @@ -109,7 +149,7 @@ const UserDetail = ({ {photoLink && photoLink !== '' ? ( profile ) : null} @@ -128,19 +168,18 @@ const UserDetail = ({ ) : null} {employee ? ( diff --git a/frontend/src/pages/Admin/Employees.tsx b/frontend/src/pages/Admin/Employees.tsx index 8f4680ace..ec251b41d 100644 --- a/frontend/src/pages/Admin/Employees.tsx +++ b/frontend/src/pages/Admin/Employees.tsx @@ -17,6 +17,10 @@ const Employees = () => { const [filteredEmployees, setFilteredEmployees] = useState< (AdminType | DriverType)[] >([]); + const [selectedEmployee, setSelectedEmployee] = useState< + AdminType | DriverType | null + >(null); + const [page, setPage] = useState(1); const pageSize = 8; @@ -42,6 +46,19 @@ const Employees = () => { setFilteredEmployees(displayEmployees); }, [displayEmployees]); + function convertToEmployeeEntity(employee: AdminType | DriverType): any { + const data = { + id: employee.id, + firstName: employee.firstName, + lastName: employee.lastName, + email: employee.email, + admin: {}, + driver: {}, + photoLink: employee.photoLink, + }; + return data; + } + const handleFilterApply = (filteredItems: (AdminType | DriverType)[]) => { setFilteredEmployees(filteredItems); setPage(1); @@ -84,7 +101,15 @@ const Employees = () => {

Employees

- +
diff --git a/server/src/router/admin.ts b/server/src/router/admin.ts index dfa1fe082..1db207dbc 100644 --- a/server/src/router/admin.ts +++ b/server/src/router/admin.ts @@ -24,11 +24,17 @@ router.get('/', validateUser('Admin'), (req, res) => { // Put a driver in Admins table router.post('/', validateUser('Admin'), (req, res) => { const { body } = req; - const id = body.id || uuid(); + const admin = new Admin({ - id: id, - ...body, + id: !body.eid || body.eid === '' ? uuid() : body.eid, + firstName: body.firstName, + lastName: body.lastName, + type: body.type, + isDriver: body.isDriver, + phoneNumber: body.phoneNumber, + email: body.email, }); + db.create(res, admin); }); @@ -38,7 +44,6 @@ router.put('/:id', validateUser('Admin'), (req, res) => { params: { id }, body, } = req; - db.update(res, Admin, { id }, body, tableName); }); diff --git a/server/src/router/common.ts b/server/src/router/common.ts index 86f8e16d6..f8fd7629f 100644 --- a/server/src/router/common.ts +++ b/server/src/router/common.ts @@ -71,9 +71,11 @@ export function create( document: Item, callback?: (value: any) => void ) { + //here document.save(async (err, data) => { if (err) { - res.status(err.statusCode || 500).send({ err: err.message }); + console.log(err); + res.status(err.statusCode || 450).send({ err: err.message }); } else if (!data) { res.status(400).send({ err: 'error when saving document' }); } else if (callback) { diff --git a/server/src/router/driver.ts b/server/src/router/driver.ts index 86961b50f..2f9988178 100644 --- a/server/src/router/driver.ts +++ b/server/src/router/driver.ts @@ -80,7 +80,7 @@ router.get('/:id/profile', validateUser('User'), (req, res) => { }); }); -// Get a driver's weekly stats +// Get a driver's weekly router.get('/:id/stats', validateUser('Admin'), (req, res) => { const { params: { id }, @@ -122,12 +122,21 @@ router.get('/:id/stats', validateUser('Admin'), (req, res) => { // Put a driver in Drivers table router.post('/', validateUser('Admin'), (req, res) => { const { body } = req; - const id = body.id || uuid(); - const driver = new Driver({ - id: id, - ...body, + + const admin = new Driver({ + id: !body.eid || body.eid === '' ? uuid() : body.eid, + firstName: body.firstName, + lastName: body.lastName, + availability: body.availability, + phoneNumber: body.phoneNumber, + startDate: body.startDate, + email: body.email, }); - db.create(res, driver); + if (typeof body.availability !== 'object') { + res.status(469).send({ err: 'availability must be an object' }); + } else { + db.create(res, admin); + } }); // Update an existing driver