From b88d00576c6610677e559596c582081011c27664 Mon Sep 17 00:00:00 2001 From: Anika Date: Tue, 5 May 2026 20:56:26 -0400 Subject: [PATCH 1/4] Add edit rider modal to student page --- frontend/src/components/Modal/RiderModal.tsx | 8 +- .../src/components/Modal/RiderModalInfo.tsx | 135 +++++++++--------- frontend/src/pages/Rider/Settings.tsx | 15 +- 3 files changed, 90 insertions(+), 68 deletions(-) diff --git a/frontend/src/components/Modal/RiderModal.tsx b/frontend/src/components/Modal/RiderModal.tsx index 1dac33a73..62144ef1b 100644 --- a/frontend/src/components/Modal/RiderModal.tsx +++ b/frontend/src/components/Modal/RiderModal.tsx @@ -71,7 +71,13 @@ const RiderModal = ({ return ( <> = ({ )} -
- - - {errors.netid &&

Invalid NetID

} -
+ {!isStudentEditing && ( +
+ + + {errors.netid &&

Invalid NetID

} +
+ )}
-
-

Duration

-
-
- - - {errors.joinDate && ( -

Please enter a join date

- )} -
-
-

-
-
- - { - const joinDate = getValues('joinDate'); - return joinDate < endDate; - }, - })} - type="date" - aria-required="true" - disabled={isStudentEditing} - className={styles.riderDate} - /> - {errors.endDate?.type === 'required' && ( -

Please enter an end date

- )} - {errors.endDate?.type === 'validate' && ( -

Invalid end time

- )} + {!isStudentEditing && ( +
+

Duration

+
+
+ + + {errors.joinDate && ( +

Please enter a join date

+ )} +
+
+

+
+
+ + { + const joinDate = getValues('joinDate'); + return joinDate < endDate; + }, + })} + type="date" + aria-required="true" + className={styles.riderDate} + /> + {errors.endDate?.type === 'required' && ( +

Please enter an end date

+ )} + {errors.endDate?.type === 'validate' && ( +

Invalid end time

+ )} +
-
+ )}
@@ -415,7 +416,11 @@ const RiderModalInfo: React.FC = ({ Cancel
diff --git a/frontend/src/pages/Rider/Settings.tsx b/frontend/src/pages/Rider/Settings.tsx index 5cf8936ed..bb90e8ebc 100644 --- a/frontend/src/pages/Rider/Settings.tsx +++ b/frontend/src/pages/Rider/Settings.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react'; +import { useContext, useEffect, useState } from 'react'; import UserDetail, { UserContactInfo, } from '../../components/UserDetail/legacy/UserDetail'; @@ -6,10 +6,13 @@ import { phone, mail } from '../../icons/userInfo/index'; import AuthContext from '../../context/auth'; import pageStyles from '../Admin/page.module.css'; import { RiderType } from '@carriage-web/shared/types/rider'; +import RiderModal from '../../components/Modal/RiderModal'; +import { Button } from '../../components/FormElements/FormElements'; const Settings = () => { const { user } = useContext(AuthContext); const netId = user?.email.split('@')[0] || ''; + const [isEditOpen, setIsEditOpen] = useState(false); useEffect(() => { document.title = 'Settings - Carriage'; @@ -19,7 +22,9 @@ const Settings = () => {

Settings

-
+
+ +
{ /> +
); }; From 5343c3c740f565870166ddf53732dc0c02b1faea Mon Sep 17 00:00:00 2001 From: Anika Date: Tue, 5 May 2026 20:56:45 -0400 Subject: [PATCH 2/4] Forgot to add other files --- frontend/src/components/Modal/RiderModal.tsx | 4 ++-- frontend/src/components/Modal/RiderModalInfo.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/Modal/RiderModal.tsx b/frontend/src/components/Modal/RiderModal.tsx index 62144ef1b..d92e27903 100644 --- a/frontend/src/components/Modal/RiderModal.tsx +++ b/frontend/src/components/Modal/RiderModal.tsx @@ -75,8 +75,8 @@ const RiderModal = ({ !existingRider ? 'Add a Student' : isRiderWeb - ? 'Edit Profile' - : 'Edit a Student' + ? 'Edit Profile' + : 'Edit a Student' } isOpen={isOpen} currentPage={0} diff --git a/frontend/src/components/Modal/RiderModalInfo.tsx b/frontend/src/components/Modal/RiderModalInfo.tsx index b8e00263f..91561813a 100644 --- a/frontend/src/components/Modal/RiderModalInfo.tsx +++ b/frontend/src/components/Modal/RiderModalInfo.tsx @@ -419,8 +419,8 @@ const RiderModalInfo: React.FC = ({ {isStudentEditing ? 'Save Changes' : isEditing - ? 'Edit a Student' - : 'Add a Student'} + ? 'Edit a Student' + : 'Add a Student'}
From 4f96043c211ddeea2be339192cb0af3038c5e48d Mon Sep 17 00:00:00 2001 From: Anika Date: Tue, 5 May 2026 21:11:04 -0400 Subject: [PATCH 3/4] add docs --- frontend/src/components/Modal/RiderModal.tsx | 3 +++ frontend/src/components/Modal/RiderModalInfo.tsx | 8 +++++++- frontend/src/pages/Rider/Settings.tsx | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Modal/RiderModal.tsx b/frontend/src/components/Modal/RiderModal.tsx index d92e27903..18a764a45 100644 --- a/frontend/src/components/Modal/RiderModal.tsx +++ b/frontend/src/components/Modal/RiderModal.tsx @@ -31,6 +31,7 @@ const RiderModal = ({ const closeModal = () => setIsOpen(false); + // Accumulates form data into state before calling the next step (supports multi-page forms) const saveDataThen = (next: () => void) => (data: ObjectType) => { setFormData((prev) => ({ ...prev, ...data })); next(); @@ -41,6 +42,8 @@ const RiderModal = ({ closeModal(); }; + // Modal closes first (submitData sets isSubmitted), then this effect fires the API call so the + // user isn't blocked waiting on the network before modal dismisses useEffect(() => { if (isSubmitted) { const method = existingRider ? axios.put : axios.post; diff --git a/frontend/src/components/Modal/RiderModalInfo.tsx b/frontend/src/components/Modal/RiderModalInfo.tsx index 91561813a..6fda8a589 100644 --- a/frontend/src/components/Modal/RiderModalInfo.tsx +++ b/frontend/src/components/Modal/RiderModalInfo.tsx @@ -39,6 +39,7 @@ const RiderModalInfo: React.FC = ({ const [showCustomInput, setShowCustomInput] = useState(false); const [customNeed, setCustomNeed] = useState(''); + // Returns a validator function so both name fields share the same logic with different labels const makeNameValidator = (fieldLabel: 'First name' | 'Last name') => (value: string) => { const trimmed = value.trim(); @@ -124,6 +125,8 @@ const RiderModalInfo: React.FC = ({ }), }; + // "Add Custom Need" is a sentinel option + // intercept it to show the free-text input instead of adding it as a real need const handleNeedsChange = ( selectedOptions: readonly NeedOption[] | null, { action }: any @@ -145,6 +148,7 @@ const RiderModalInfo: React.FC = ({ if (customNeed.trim()) { const currentNeeds = getValues('needs') || []; const newNeed: NeedOption = { + // Normalize to UPPER_SNAKE_CASE to match the Accessibility enum format on the backend value: customNeed.toUpperCase().replace(/\s+/g, '_'), label: customNeed.trim(), }; @@ -173,6 +177,7 @@ const RiderModalInfo: React.FC = ({ const accessibility = needs.map((option) => option.value.toString()); const normalizedPhoneNumber = normalizePhoneNumber(phoneNumber); + // Derive active status client-side so backend doesn't need to recalculate it on every edit const today = new Date().toISOString().slice(0, 10); const active = joinDate <= today && today <= endDate; @@ -192,12 +197,13 @@ const RiderModalInfo: React.FC = ({ }; const cancel = () => { - setFormData({}); + setFormData({}); // Clear stale data so a reopened modal doesn't submit a partial previous entry setIsOpen(false); }; const localUserType = localStorage.getItem('userType'); const isEditing = rider !== undefined; + // Riders editing their own profile get a restricted form — no NetID or duration fields const isStudentEditing = isEditing && localUserType === 'Rider'; const needsOptions: NeedOption[] = [ diff --git a/frontend/src/pages/Rider/Settings.tsx b/frontend/src/pages/Rider/Settings.tsx index bb90e8ebc..8b62eb615 100644 --- a/frontend/src/pages/Rider/Settings.tsx +++ b/frontend/src/pages/Rider/Settings.tsx @@ -41,6 +41,7 @@ const Settings = () => { /> + {/* isRiderWeb triggers student-edit mode: hides NetID/duration fields and refreshes the auth user on save */} Date: Tue, 5 May 2026 21:11:25 -0400 Subject: [PATCH 4/4] add docs --- frontend/src/components/Modal/RiderModalInfo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Modal/RiderModalInfo.tsx b/frontend/src/components/Modal/RiderModalInfo.tsx index 6fda8a589..0d3ecb0f2 100644 --- a/frontend/src/components/Modal/RiderModalInfo.tsx +++ b/frontend/src/components/Modal/RiderModalInfo.tsx @@ -197,7 +197,7 @@ const RiderModalInfo: React.FC = ({ }; const cancel = () => { - setFormData({}); // Clear stale data so a reopened modal doesn't submit a partial previous entry + setFormData({}); // Clear stale data so a reopened modal doesn't submit a partial previous entry setIsOpen(false); };