Skip to content

SANC-62: Review Transport Request Modal Frontend#60

Draft
jason-duong4509 wants to merge 3 commits intomainfrom
SANC-62-review-transport-request-modal-frontend
Draft

SANC-62: Review Transport Request Modal Frontend#60
jason-duong4509 wants to merge 3 commits intomainfrom
SANC-62-review-transport-request-modal-frontend

Conversation

@jason-duong4509
Copy link
Copy Markdown
Contributor

@jason-duong4509 jason-duong4509 commented Feb 19, 2026

Added the review transport request modal

Modal Image

A video of the modal

Video.mp4

The current PR has changed the admin dashboard to include a sample usage of the modal. The modal appears with prefilled information when pressing the "temp review transport modal button"

Summary by CodeRabbit

  • New Features

    • Added a Transport Request modal in the admin dashboard to review, approve, or decline transport requests with resident details, logistics, and optional past history.
  • Style

    • Updated rating icon colors for clearer visual distinction.
  • Refactor

    • Standardized passenger field naming from "Passenger Info" to "Passenger Name" across UI, APIs, surveys, schedules, calendar events, seed data, and database schema.

- Added the review transport request modal
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
salvationarmy Ready Ready Preview, Comment Feb 20, 2026 2:38am

- Fixed the coloring of the rating faces
- Changed the Contact Info field to Phone Number
- Made the Contact Info field optional
- Renamed the bookings.passenger_info column to bookings.passenger_name
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 19, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Renamed booking/survey field passengerInfopassengerName across DB schemas, API routers, types, seeds, and frontend consumers; added an admin TransportRequestModal component with styles and wiring; and updated rating icon SVG fill/stroke colors.

Changes

Cohort / File(s) Summary
Database schemas
src/server/db/booking-schema.ts, src/server/db/post-trip-schema.ts
Renamed DB columns and schema fields from passenger_info/passengerInfopassenger_name/passengerName.
API routers
src/server/api/routers/bookings.ts, src/server/api/routers/surveys.ts, src/server/api/routers/trip.ts
Input schemas, create/update payloads, and insert/update mappings switched to passengerName.
Types & Seeds
src/types/types.ts, drizzle/seed.ts
Types and seed data updated to use passengerName instead of passengerInfo.
Frontend — Admin UI
src/app/_components/admincomponents/admin-dashboard.tsx, src/app/_components/admincomponents/transport-request-modal.tsx, src/app/_components/admincomponents/transport-request-modal.module.scss
Added TransportRequestModal component, SCSS module, and dashboard wiring (open/approve/decline handlers with demo props).
Frontend — Calendar / Events
src/app/_components/common/calendar/calendar-view.tsx, src/app/_components/common/calendar/event-details.tsx
Event extendedProps and UI now expose/display passengerName.
Frontend — Schedule & Driver flows
src/app/_components/agencypage/table-view.tsx, src/app/_components/drivercomponents/surveyButtons/surveyButtons.tsx, src/app/_components/drivercomponents/surveyNotification/surveyNotification.tsx
Schedule, survey payloads, and UI references switched to passengerName.
Frontend — Debug & Icons
src/app/debug/bookings/page.tsx, src/assets/icons/rating1.tsx, src/assets/icons/rating2.tsx, src/assets/icons/rating3.tsx, src/assets/icons/rating4.tsx, src/assets/icons/rating5.tsx
Debug form fields/labels updated to passengerName; rating SVG fills set to white and stroke colors adjusted.
Misc
package.json
Minor manifest updates (lines changed).

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • themaxboucher
  • JustinTan-1
  • burtonjong
  • promatty
  • Yemyam
  • wesleylui
  • Lujarios

Poem

🐰 I hopped through schemas, APIs, and name,
From passengerInfo to passengerName I came,
A modal unfurled for admins to see,
Bright icons gleam — a small jubilee,
The rabbit stamps the change with a happy name!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes adding a transport request modal to the frontend, but the changeset also includes a comprehensive refactor renaming passengerInfo to passengerName across the entire codebase—a significant structural change not reflected in the title. Update the title to reflect both major changes, such as: 'SANC-62: Add transport request modal and rename passengerInfo to passengerName' or split the refactor into a separate PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SANC-62-review-transport-request-modal-frontend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
src/assets/icons/rating4.tsx (1)

9-35: LGTM — color updates look correct; pre-existing duplicate <path> can be cleaned up optionally.

Lines 22–28 have an identical d attribute to lines 15–21 (both draw the same circle outline). With matching stroke and fill, there is no visible effect, but the redundant element is unnecessary.

♻️ Optional cleanup
  <path
    stroke="#a2e101"
    strokeLinecap="round"
    strokeLinejoin="round"
    strokeWidth={3}
    d="M16 29.333c7.364 0 13.333-5.97 13.333-13.333 0-7.364-5.97-13.333-13.333-13.333C8.636 2.667 2.667 8.637 2.667 16c0 7.364 5.97 13.333 13.333 13.333Z"
  />
- <path
-   stroke="#a2e101"
-   strokeLinecap="round"
-   strokeLinejoin="round"
-   strokeWidth={3}
-   d="M16 29.333c7.364 0 13.333-5.97 13.333-13.333 0-7.364-5.97-13.333-13.333-13.333C8.636 2.667 2.667 8.637 2.667 16c0 7.364 5.97 13.333 13.333 13.333Z"
- />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/assets/icons/rating4.tsx` around lines 9 - 35, There is a duplicated SVG
circle path in the Rating4 component—two <path> elements with identical
stroke/strokeWidth/strokeLinecap/strokeLinejoin and the same d="M16 29.333...".
Remove the redundant <path> (either the first or second occurrence around the
circle outline) so only one circle outline path remains, leaving the other
unique paths (the mouth/eyes path) untouched.
src/app/_components/drivercomponents/surveyNotification/surveyNotification.tsx (1)

141-141: Prefer || undefined over || "" to avoid storing an empty string when passengerName is absent.

z.string().optional() on the API accepts empty strings, so || "" would persist "" to the DB rather than leaving the column null/unset. Using undefined causes the field to be omitted from the payload entirely, which is the expected behaviour for an optional field.

✏️ Proposed fix
- passengerName: survey.passengerName || "",
+ passengerName: survey.passengerName || undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/_components/drivercomponents/surveyNotification/surveyNotification.tsx`
at line 141, The object construction in surveyNotification (the field
passengerName: survey.passengerName || "") should use undefined instead of an
empty string so optional z.string().optional() fields are omitted rather than
persisted as ""; change the assignment to passengerName: survey.passengerName ||
undefined so the payload omits the key when absent and avoids storing an empty
string in the DB.
src/app/_components/admincomponents/transport-request-modal.tsx (1)

62-68: Remove redundant props that are already MantineModal defaults.

withCloseButton, closeOnClickOutside, and closeOnEscape all default to true in Mantine, and size={"lg"} can be written as size="lg". Explicitly setting them to their defaults adds noise without value.

♻️ Proposed cleanup
-      size={"lg"}
+      size="lg"
       fullScreen={isMobile}
       padding="xl"
-      withCloseButton={true}
-      closeOnClickOutside={true}
-      closeOnEscape={true}
       transitionProps={{ transition: "slide-left" }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/_components/admincomponents/transport-request-modal.tsx` around lines
62 - 68, In the TransportRequestModal component, remove redundant Mantine Modal
props: drop withCloseButton, closeOnClickOutside, and closeOnEscape since they
default to true, and simplify size={"lg"} to size="lg"; update the JSX where the
Modal (or the component rendering it) sets these props so only non-default props
like fullScreen={isMobile}, padding="xl", and transitionProps remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/_components/admincomponents/admin-dashboard.tsx`:
- Around line 84-110: Remove the temporary Button labeled "temp review transport
modal button" and delete all console.log calls inside the TransportRequestModal
handlers; update the onApprove prop handler to call
setShowTransportRequestModal(false) (matching onDecline/onClose) so approving
also closes the modal — locate the Button element that triggers
setShowTransportRequestModal, and the TransportRequestModal props onClose,
onDecline, and onApprove in admin-dashboard.tsx to apply these changes.

In `@src/app/_components/admincomponents/transport-request-modal.module.scss`:
- Around line 1-37: Update the SCSS to satisfy Stylelint: change the top comment
to "/* Variables */" (add inner spaces), set $headings-font-weight from 1000 to
900, replace "word-break: break-word" in .agencyName with "overflow-wrap:
break-word", convert .fieldHeadings to use a %placeholder (e.g. %fieldHeadings)
and update .pastHistoryHeading to "@extend %fieldHeadings;" instead of extending
the class, add a blank line before the padding-bottom override in
.pastHistoryHeading and change "padding-bottom: 0rem" to "padding-bottom: 0".

In `@src/app/_components/admincomponents/transport-request-modal.tsx`:
- Around line 13-15: The props for TransportRequestModal use overly permissive
callback types; update the prop type signatures for onClose, onDecline, and
onApprove in transport-request-modal.tsx from (...args: any[]) => void to () =>
void so callers must pass no-arg functions and TypeScript can catch accidental
argument usage; locate the props/interface or type where these three symbols are
declared and replace their types accordingly, then run type-check to ensure all
call sites (e.g., admin-dashboard.tsx and MantineModal usage) remain valid.
- Around line 45-50: The Past History visibility logic incorrectly treats
requestedDiffLocation=false as missing; update the guard that sets
showPastHistory to only consider requestedDiffLocation undefined (not falsy). In
the block that uses transportRating, prevDriverNotes, and requestedDiffLocation
(where showPastHistory is set), change the requestedDiffLocation check to an
explicit undefined check (e.g., requestedDiffLocation === undefined) so that a
legitimate false value does not hide the Past History section; leave the other
checks for transportRating and prevDriverNotes as-is.
- Around line 70-250: The left-column static labels are rendered with <Textarea>
which is focusable and not semantically linked to their value fields; replace
those label Textarea usages with non-focusable semantics (e.g., <Text> or
<label>) and ensure each value Textarea (e.g., those showing residentName,
phoneNumber, additionalInfo, dateAndTime, purpose, destinationAddress,
prevDriverNotes, requestedDiffLocation) has either an id and the label uses
htmlFor or the value Textarea has aria-labelledby referencing the label element;
update all rows (including the Past History block controlled by showPastHistory
and the transportRating display) to follow this pattern so labels are not
tabbable and screen readers correctly associate label→value.

In `@src/server/api/routers/trip.ts`:
- Line 27: The passengerName template currently adds an unconditional space
before the conditional additionalInfo which yields a trailing space when
additionalInfo is absent; update the passengerName expression (the assignment
using input.residentName and input.additionalInfo) so the space is included only
when input.additionalInfo is present (e.g., append `' ' + input.additionalInfo`
or include the space inside the truthy branch) to avoid creating "John Doe "
when additionalInfo is undefined.

In `@src/server/db/booking-schema.ts`:
- Line 14: You renamed the column passenger_info to passenger_name in the schema
(passengerName: text("passenger_name").notNull()), so create and commit a
migration that renames the column on the database to avoid data loss: add a SQL
migration containing ALTER TABLE bookings RENAME COLUMN passenger_info TO
passenger_name; and ALTER TABLE post_trip_surveys RENAME COLUMN passenger_info
TO passenger_name; then run your migration tool (e.g., drizzle-kit push or
migrate) to apply it and verify both tables use passenger_name.

---

Duplicate comments:
In `@src/app/_components/common/calendar/calendar-view.tsx`:
- Line 70: The assignment passengerName: booking.passengerName can produce the
same null/coercion mismatch as in table-view.tsx; fix it by normalizing the
value to the expected type (e.g. string) using a safe fallback like
passengerName: booking?.passengerName ?? '' or passengerName:
String(booking?.passengerName ?? '') so the field never becomes null/undefined
and matches the expected type used elsewhere (mirror the fix applied in
table-view.tsx).

In `@src/server/db/post-trip-schema.ts`:
- Line 44: The schema change renames passenger_info → passenger_name for the
post_trip_surveys table (field passengerName: text("passenger_name")), but
there’s no migration to preserve existing data; add a proper migration that
renames the column on post_trip_surveys from passenger_info to passenger_name
(or copies data then drops old column) so data isn’t lost, and update the schema
reference (passengerName) to match; mirror the same migration pattern used for
bookings (see booking-schema.ts) to ensure safe rollout and rollback.

---

Nitpick comments:
In `@src/app/_components/admincomponents/transport-request-modal.tsx`:
- Around line 62-68: In the TransportRequestModal component, remove redundant
Mantine Modal props: drop withCloseButton, closeOnClickOutside, and
closeOnEscape since they default to true, and simplify size={"lg"} to size="lg";
update the JSX where the Modal (or the component rendering it) sets these props
so only non-default props like fullScreen={isMobile}, padding="xl", and
transitionProps remain.

In
`@src/app/_components/drivercomponents/surveyNotification/surveyNotification.tsx`:
- Line 141: The object construction in surveyNotification (the field
passengerName: survey.passengerName || "") should use undefined instead of an
empty string so optional z.string().optional() fields are omitted rather than
persisted as ""; change the assignment to passengerName: survey.passengerName ||
undefined so the payload omits the key when absent and avoids storing an empty
string in the DB.

In `@src/assets/icons/rating4.tsx`:
- Around line 9-35: There is a duplicated SVG circle path in the Rating4
component—two <path> elements with identical
stroke/strokeWidth/strokeLinecap/strokeLinejoin and the same d="M16 29.333...".
Remove the redundant <path> (either the first or second occurrence around the
circle outline) so only one circle outline path remains, leaving the other
unique paths (the mouth/eyes path) untouched.

Comment on lines +1 to +37
@use "@/styles/_variables" as *;

/*Variables in this file*/
$headings-font-weight: 1000;
$padding-bottom-amount: 1rem;

/*Styles*/
.title {
font-weight: $headings-font-weight;
font-size: x-large;
}

.agencyName {
word-break: break-word;
font-size: larger;
font-weight: $headings-font-weight;
}

.fieldHeadings {
color: $color-cherry-red;
font-weight: $headings-font-weight;
font-size: large;
padding-bottom: $padding-bottom-amount;
}

.pastHistoryHeading {
@extend .fieldHeadings;
padding-bottom: 0rem;
}

.grid {
padding-bottom: $padding-bottom-amount;
}

.ratingText {
font-size: small;
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Address all Stylelint errors before merge.

Several issues flagged by Stylelint (reported as errors):

Line Rule Fix
3, 7 comment-whitespace-inside /* Variables */ → add inner spaces
14 declaration-property-value-keyword-no-deprecated word-break: break-word is deprecated — replace with overflow-wrap: break-word
27 scss/at-extend-no-missing-placeholder Extend a %placeholder, not a class selector, to avoid unintended CSS coupling
28 declaration-empty-line-before Add a blank line before the padding-bottom override

Additionally, padding-bottom: 0rem (line 28) should be padding-bottom: 0 (units are meaningless on zero), and $headings-font-weight: 1000 (line 4) exceeds what most fonts expose — 900 is the practical maximum for font-weight.

♻️ Proposed fixes
-/*Variables in this file*/
+/* Variables in this file */
 $headings-font-weight: 1000;
+/* ^^^ consider 900 — 1000 is spec-valid but rarely distinguishable */
 $padding-bottom-amount: 1rem;

-/*Styles*/
+/* Styles */

-.fieldHeadings {
+%fieldHeadings {
   color: $color-cherry-red;
   font-weight: $headings-font-weight;
   font-size: large;
   padding-bottom: $padding-bottom-amount;
 }

+.fieldHeadings {
+  `@extend` %fieldHeadings;
+}

 .pastHistoryHeading {
-  `@extend` .fieldHeadings;
-  padding-bottom: 0rem;
+  `@extend` %fieldHeadings;
+
+  padding-bottom: 0;
 }

 .agencyName {
   word-break: break-word;   /* deprecated */
+  overflow-wrap: break-word;
-  word-break: break-word;
   font-size: larger;
   font-weight: $headings-font-weight;
 }
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 27-27: Expected a placeholder selector (e.g. %placeholder) to be used in @extend (scss/at-extend-no-missing-placeholder)

(scss/at-extend-no-missing-placeholder)


[error] 1-1: Unexpected leading underscore in imported partial name (scss/load-no-partial-leading-underscore)

(scss/load-no-partial-leading-underscore)


[error] 3-3: Expected whitespace after "/*" (comment-whitespace-inside)

(comment-whitespace-inside)


[error] 3-3: Expected whitespace before "*/" (comment-whitespace-inside)

(comment-whitespace-inside)


[error] 7-7: Expected whitespace after "/*" (comment-whitespace-inside)

(comment-whitespace-inside)


[error] 7-7: Expected whitespace before "*/" (comment-whitespace-inside)

(comment-whitespace-inside)


[error] 28-28: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 14-14: Unexpected deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)

(declaration-property-value-keyword-no-deprecated)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/_components/admincomponents/transport-request-modal.module.scss`
around lines 1 - 37, Update the SCSS to satisfy Stylelint: change the top
comment to "/* Variables */" (add inner spaces), set $headings-font-weight from
1000 to 900, replace "word-break: break-word" in .agencyName with
"overflow-wrap: break-word", convert .fieldHeadings to use a %placeholder (e.g.
%fieldHeadings) and update .pastHistoryHeading to "@extend %fieldHeadings;"
instead of extending the class, add a blank line before the padding-bottom
override in .pastHistoryHeading and change "padding-bottom: 0rem" to
"padding-bottom: 0".

Comment on lines +70 to +250
<Text className={ui.fieldHeadings}>Personal Information</Text>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Resident Name"} />
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={residentName} />
</GridCol>
</Grid>

{phoneNumber && (
<>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Phone Number"} />
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={phoneNumber} />
</GridCol>
</Grid>
</>
)}

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
rows={5}
value={
"Additional Information (pets, personal belongings, support required entering / exiting vehicle, etc.)"
}
/>
</GridCol>

<GridCol span={8}>
<Textarea readOnly rows={5} value={additionalInfo} />
</GridCol>
</Grid>

<Text className={ui.fieldHeadings}>Logistics</Text>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={3}
minRows={2}
value={"Date and Time of Transport"}
/>
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={3} value={dateAndTime} />
</GridCol>
</Grid>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Purpose"} />
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={5} value={purpose} />
</GridCol>
</Grid>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={2}
value={"Destination Address"}
/>
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={destinationAddress} />
</GridCol>
</Grid>

{showPastHistory && (
<>
<Text className={ui.pastHistoryHeading}>Past History</Text>
<Textarea
variant="unstyled"
readOnly
autosize
className={ui.grid}
value={
"This resident has previously used the Navigation Centre's services.\nView the details from their previous ride below."
}
/>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={2}
value={"Transport fitness rating"}
/>
</GridCol>

<GridCol span={8}>
<Group gap={"xs"}>
{transportRating === 1 && (
<>
<Rating1 />
<Text className={ui.ratingText}>Poor</Text>
</>
)}
{transportRating === 2 && (
<>
<Rating2 />
<Text className={ui.ratingText}>Okay</Text>
</>
)}
{transportRating === 3 && (
<>
<Rating3 />
<Text className={ui.ratingText}>Standard</Text>
</>
)}
{transportRating === 4 && (
<>
<Rating4 />
<Text className={ui.ratingText}>Good</Text>
</>
)}
{transportRating === 5 && (
<>
<Rating5 />
<Text className={ui.ratingText}>Excellent</Text>
</>
)}
</Group>
</GridCol>
</Grid>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={4}
value={"Previous driver notes"}
/>
</GridCol>

<GridCol span={8}>
<Textarea readOnly autosize maxRows={4} value={prevDriverNotes} />
</GridCol>
</Grid>

<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
rows={5}
value={
"Did the passenger request to be dropped off at a different location than originally booked?"
}
/>
</GridCol>

<GridCol span={8}>
<Textarea readOnly rows={1} value={requestedDiffLocation ? "Yes" : "No"} />
</GridCol>
</Grid>
</>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Accessibility: <Textarea readOnly> must not be used for static label text.

Every left-column cell (e.g., "Resident Name", "Date and Time of Transport", "Previous driver notes") is rendered as a <textarea> HTML element. This causes two problems:

  1. Keyboard navigation: Read-only <textarea> elements are still focusable by default; keyboard users must tab through every label before reaching the actual value, creating unnecessary focus noise.
  2. No semantic association: None of the label textareas are linked to their adjacent value fields via htmlFor/id or aria-labelledby, so screen readers cannot announce what data a value field represents.

Replace the static label column with <Text> (or <label>), and add proper aria-label or id/aria-labelledby pairing if keeping value <Textarea> elements.

♻️ Example fix for one grid row (apply pattern to all rows)
-        <GridCol span={4}>
-          <Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Resident Name"} />
-        </GridCol>
-        <GridCol span={8}>
-          <Textarea readOnly autosize maxRows={2} value={residentName} />
-        </GridCol>
+        <GridCol span={4}>
+          <Text id="label-resident-name" fw={500}>Resident Name</Text>
+        </GridCol>
+        <GridCol span={8}>
+          <Textarea
+            readOnly
+            autosize
+            maxRows={2}
+            value={residentName}
+            aria-labelledby="label-resident-name"
+          />
+        </GridCol>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Text className={ui.fieldHeadings}>Personal Information</Text>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Resident Name"} />
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={residentName} />
</GridCol>
</Grid>
{phoneNumber && (
<>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Phone Number"} />
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={phoneNumber} />
</GridCol>
</Grid>
</>
)}
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
rows={5}
value={
"Additional Information (pets, personal belongings, support required entering / exiting vehicle, etc.)"
}
/>
</GridCol>
<GridCol span={8}>
<Textarea readOnly rows={5} value={additionalInfo} />
</GridCol>
</Grid>
<Text className={ui.fieldHeadings}>Logistics</Text>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={3}
minRows={2}
value={"Date and Time of Transport"}
/>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={3} value={dateAndTime} />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea variant="unstyled" readOnly autosize maxRows={2} value={"Purpose"} />
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={5} value={purpose} />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={2}
value={"Destination Address"}
/>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={destinationAddress} />
</GridCol>
</Grid>
{showPastHistory && (
<>
<Text className={ui.pastHistoryHeading}>Past History</Text>
<Textarea
variant="unstyled"
readOnly
autosize
className={ui.grid}
value={
"This resident has previously used the Navigation Centre's services.\nView the details from their previous ride below."
}
/>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={2}
value={"Transport fitness rating"}
/>
</GridCol>
<GridCol span={8}>
<Group gap={"xs"}>
{transportRating === 1 && (
<>
<Rating1 />
<Text className={ui.ratingText}>Poor</Text>
</>
)}
{transportRating === 2 && (
<>
<Rating2 />
<Text className={ui.ratingText}>Okay</Text>
</>
)}
{transportRating === 3 && (
<>
<Rating3 />
<Text className={ui.ratingText}>Standard</Text>
</>
)}
{transportRating === 4 && (
<>
<Rating4 />
<Text className={ui.ratingText}>Good</Text>
</>
)}
{transportRating === 5 && (
<>
<Rating5 />
<Text className={ui.ratingText}>Excellent</Text>
</>
)}
</Group>
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
autosize
maxRows={4}
value={"Previous driver notes"}
/>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={4} value={prevDriverNotes} />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Textarea
variant="unstyled"
readOnly
rows={5}
value={
"Did the passenger request to be dropped off at a different location than originally booked?"
}
/>
</GridCol>
<GridCol span={8}>
<Textarea readOnly rows={1} value={requestedDiffLocation ? "Yes" : "No"} />
</GridCol>
</Grid>
</>
<Text className={ui.fieldHeadings}>Personal Information</Text>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-resident-name" fw={500}>Resident Name</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={residentName} aria-labelledby="label-resident-name" />
</GridCol>
</Grid>
{phoneNumber && (
<>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-phone-number" fw={500}>Phone Number</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={phoneNumber} aria-labelledby="label-phone-number" />
</GridCol>
</Grid>
</>
)}
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-additional-info" fw={500}>Additional Information (pets, personal belongings, support required entering / exiting vehicle, etc.)</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly rows={5} value={additionalInfo} aria-labelledby="label-additional-info" />
</GridCol>
</Grid>
<Text className={ui.fieldHeadings}>Logistics</Text>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-date-time" fw={500}>Date and Time of Transport</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={3} value={dateAndTime} aria-labelledby="label-date-time" />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-purpose" fw={500}>Purpose</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={5} value={purpose} aria-labelledby="label-purpose" />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-destination" fw={500}>Destination Address</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={2} value={destinationAddress} aria-labelledby="label-destination" />
</GridCol>
</Grid>
{showPastHistory && (
<>
<Text className={ui.pastHistoryHeading}>Past History</Text>
<Textarea
variant="unstyled"
readOnly
autosize
className={ui.grid}
value={
"This resident has previously used the Navigation Centre's services.\nView the details from their previous ride below."
}
/>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-transport-rating" fw={500}>Transport fitness rating</Text>
</GridCol>
<GridCol span={8}>
<Group gap={"xs"} aria-labelledby="label-transport-rating">
{transportRating === 1 && (
<>
<Rating1 />
<Text className={ui.ratingText}>Poor</Text>
</>
)}
{transportRating === 2 && (
<>
<Rating2 />
<Text className={ui.ratingText}>Okay</Text>
</>
)}
{transportRating === 3 && (
<>
<Rating3 />
<Text className={ui.ratingText}>Standard</Text>
</>
)}
{transportRating === 4 && (
<>
<Rating4 />
<Text className={ui.ratingText}>Good</Text>
</>
)}
{transportRating === 5 && (
<>
<Rating5 />
<Text className={ui.ratingText}>Excellent</Text>
</>
)}
</Group>
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-prev-driver-notes" fw={500}>Previous driver notes</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly autosize maxRows={4} value={prevDriverNotes} aria-labelledby="label-prev-driver-notes" />
</GridCol>
</Grid>
<Grid gutter={"xl"} className={ui.grid}>
<GridCol span={4}>
<Text id="label-diff-location" fw={500}>Did the passenger request to be dropped off at a different location than originally booked?</Text>
</GridCol>
<GridCol span={8}>
<Textarea readOnly rows={1} value={requestedDiffLocation ? "Yes" : "No"} aria-labelledby="label-diff-location" />
</GridCol>
</Grid>
</>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/_components/admincomponents/transport-request-modal.tsx` around lines
70 - 250, The left-column static labels are rendered with <Textarea> which is
focusable and not semantically linked to their value fields; replace those label
Textarea usages with non-focusable semantics (e.g., <Text> or <label>) and
ensure each value Textarea (e.g., those showing residentName, phoneNumber,
additionalInfo, dateAndTime, purpose, destinationAddress, prevDriverNotes,
requestedDiffLocation) has either an id and the label uses htmlFor or the value
Textarea has aria-labelledby referencing the label element; update all rows
(including the Past History block controlled by showPastHistory and the
transportRating display) to follow this pattern so labels are not tabbable and
screen readers correctly associate label→value.

pickupAddress: input.pickupAddress,
destinationAddress: input.destinationAddress,
passengerInfo: `${input.residentName} ${input.additionalInfo ? `${input.additionalInfo}` : ""}`,
passengerName: `${input.residentName} ${input.additionalInfo ? `${input.additionalInfo}` : ""}`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Trailing space in passengerName when additionalInfo is absent.

The current template produces "John Doe " (with a trailing space) when additionalInfo is undefined, because the space before the conditional is unconditional.

🐛 Proposed fix
-        passengerName: `${input.residentName} ${input.additionalInfo ? `${input.additionalInfo}` : ""}`,
+        passengerName: `${input.residentName}${input.additionalInfo ? ` ${input.additionalInfo}` : ""}`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
passengerName: `${input.residentName} ${input.additionalInfo ? `${input.additionalInfo}` : ""}`,
passengerName: `${input.residentName}${input.additionalInfo ? ` ${input.additionalInfo}` : ""}`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/api/routers/trip.ts` at line 27, The passengerName template
currently adds an unconditional space before the conditional additionalInfo
which yields a trailing space when additionalInfo is absent; update the
passengerName expression (the assignment using input.residentName and
input.additionalInfo) so the space is included only when input.additionalInfo is
present (e.g., append `' ' + input.additionalInfo` or include the space inside
the truthy branch) to avoid creating "John Doe " when additionalInfo is
undefined.

destinationAddress: text("destination_address").notNull(),
purpose: text("purpose"),
passengerInfo: text("passenger_info").notNull(),
passengerName: text("passenger_name").notNull(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check directory structure for migrations
echo "=== Checking for migrations directory ==="
fd . -type d -name migrations

echo -e "\n=== Searching for migration files ==="
fd -e sql -e ts . -path '*/migration*' 2>/dev/null | head -20

echo -e "\n=== Searching for 'passenger' references in migration files ==="
rg "passenger" --type sql --type ts -g '**/{migration*,drizzle}/**' 2>/dev/null || echo "No matches found"

echo -e "\n=== Checking for ALTER TABLE RENAME in any SQL/TS migrations ==="
rg "ALTER TABLE.*RENAME|RENAME COLUMN" --type sql --type ts 2>/dev/null | head -20

Repository: Code-the-Change-YYC/salvationarmy

Length of output: 1235


🏁 Script executed:

#!/bin/bash
echo "=== Directory structure of drizzle folder ==="
fd . drizzle --type f 2>/dev/null | head -30

echo -e "\n=== All .sql files in repository ==="
fd -e sql 2>/dev/null

echo -e "\n=== Checking git status for any migration files ==="
git ls-files | grep -i migration

echo -e "\n=== Check if drizzle.config exists and what it references ==="
fd drizzle.config -e ts -e js
cat drizzle.config.ts 2>/dev/null | head -30

Repository: Code-the-Change-YYC/salvationarmy

Length of output: 884


Migration required for column rename.

No migration files exist in this repository. Renaming passenger_info to passenger_name in both bookings and post_trip_surveys tables requires an explicit migration. Without it, drizzle-kit push may drop and recreate the column (losing existing data), while drizzle-kit generate requires interactive rename confirmation. On deployed databases, the schema mismatch will cause runtime failures.

Generate and commit a migration that includes:

ALTER TABLE bookings RENAME COLUMN passenger_info TO passenger_name;
ALTER TABLE post_trip_surveys RENAME COLUMN passenger_info TO passenger_name;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/db/booking-schema.ts` at line 14, You renamed the column
passenger_info to passenger_name in the schema (passengerName:
text("passenger_name").notNull()), so create and commit a migration that renames
the column on the database to avoid data loss: add a SQL migration containing
ALTER TABLE bookings RENAME COLUMN passenger_info TO passenger_name; and ALTER
TABLE post_trip_surveys RENAME COLUMN passenger_info TO passenger_name; then run
your migration tool (e.g., drizzle-kit push or migrate) to apply it and verify
both tables use passenger_name.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/app/_components/admincomponents/transport-request-modal.tsx (2)

62-65: Remove redundant props that restate Mantine Modal's built-in defaults.

Mantine's Modal defaultProps explicitly sets closeOnClickOutside: true, closeOnEscape: true, and withCloseButton: true, so all three props on lines 63–65 are no-ops and only add noise.

♻️ Proposed cleanup
       size={"lg"}
       fullScreen={isMobile}
       padding="xl"
-      withCloseButton={true}
-      closeOnClickOutside={true}
-      closeOnEscape={true}
       transitionProps={{ transition: "slide-left" }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/_components/admincomponents/transport-request-modal.tsx` around lines
62 - 65, Remove the redundant Mantine Modal props that restate defaults: in the
TransportRequestModal component where Modal is rendered (the JSX with
padding="xl"), delete withCloseButton={true}, closeOnClickOutside={true}, and
closeOnEscape={true} so the Modal relies on Mantine's defaultProps; keep other
props like padding intact.

62-65: Remove redundant Modal props that match Mantine defaults.

withCloseButton={true}, closeOnClickOutside={true}, and closeOnEscape={true} all match Mantine v8 Modal's out-of-the-box defaults. Removing them eliminates visual noise without changing behavior.

♻️ Proposed cleanup
       size={"lg"}
       fullScreen={isMobile}
       padding="xl"
-      withCloseButton={true}
-      closeOnClickOutside={true}
-      closeOnEscape={true}
       transitionProps={{ transition: "slide-left" }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/_components/admincomponents/transport-request-modal.tsx` around lines
62 - 65, The Modal component in transport-request-modal.tsx is passing redundant
props that match Mantine v8 defaults: remove withCloseButton={true},
closeOnClickOutside={true}, and closeOnEscape={true} from the <Modal ... />
usage so the code is cleaner without changing behavior; locate the Modal JSX in
the TransportRequestModal component and delete those three props (leave other
props intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/_components/admincomponents/transport-request-modal.tsx`:
- Around line 45-48: The inline comment on the showPastHistory boolean is
incorrect: the expression const showPastHistory = transportRating !== undefined
&& prevDriverNotes !== undefined && requestedDiffLocation !== undefined;
evaluates to true only when all three optional fields (transportRating,
prevDriverNotes, requestedDiffLocation) are defined, not when one is blank;
update the trailing comment to accurately state "True if all three optional
fields are defined (none are blank)" or remove the stale comment entirely so it
no longer misleads future maintainers.
- Around line 45-48: The inline comment next to the showPastHistory declaration
incorrectly states the opposite meaning; update the comment for showPastHistory
(the const in transport-request-modal.tsx) to reflect that it is true only when
all three optional fields (transportRating, prevDriverNotes,
requestedDiffLocation) are defined (none are blank), e.g., change the comment to
indicate "true when all optional fields are present" or similar so future
maintainers aren't misled.

---

Duplicate comments:
In `@src/app/_components/admincomponents/transport-request-modal.tsx`:
- Around line 68-248: Replace all left-column label Textarea instances with
non-focusable Text components and add aria-labelledby attributes to the
corresponding right-column value Textarea components so assistive tech can
associate labels with values; specifically, change the label Textareas (the ones
with values like "Resident Name", "Phone Number", "Additional Information (…)",
"Date and Time of Transport", "Purpose", "Destination Address", the Past History
intro paragraph, "Transport fitness rating", "Previous driver notes", and "Did
the passenger request…") to use the Text component, give each label Text a
unique id (e.g., label-residentName, label-phoneNumber, etc.), then add
aria-labelledby="that-id" to the matching value Textarea elements rendering
residentName, phoneNumber, additionalInfo, dateAndTime, purpose,
destinationAddress, the past-history paragraph, transportRating display,
prevDriverNotes, and requestedDiffLocation so labels are not keyboard-focusable
and screen readers announce context correctly.
- Around line 68-248: Left-column label fields are rendered as focusable
<Textarea> elements causing tab trapping and no label linkage; replace each
left-column label Textarea (the ones whose value strings are "Resident Name",
"Phone Number", "Additional Information (...)", "Date and Time of Transport",
"Purpose", "Destination Address", the Past History intro paragraph, "Transport
fitness rating", "Previous driver notes", and the "Did the passenger request…"
prompt) with non-focusable <Text> components, and add matching id attributes to
those Text nodes (e.g., residentNameLabel, phoneLabel, additionalInfoLabel,
dateTimeLabel, purposeLabel, destinationLabel, pastHistoryIntroLabel,
transportRatingLabel, prevDriverNotesLabel, requestedDiffLocationLabel) then set
aria-labelledby on the corresponding readOnly value Textarea components (the
ones using residentName, phoneNumber, additionalInfo, dateAndTime, purpose,
destinationAddress, the past-history Textarea, transportRating Group container,
prevDriverNotes, and requestedDiffLocation value) so screen readers announce the
label and keyboard tab order skips the static labels; ensure ids are unique and
only applied to the left-column Text elements and aria-labelledby to the
right-column Textarea elements.

---

Nitpick comments:
In `@src/app/_components/admincomponents/transport-request-modal.tsx`:
- Around line 62-65: Remove the redundant Mantine Modal props that restate
defaults: in the TransportRequestModal component where Modal is rendered (the
JSX with padding="xl"), delete withCloseButton={true},
closeOnClickOutside={true}, and closeOnEscape={true} so the Modal relies on
Mantine's defaultProps; keep other props like padding intact.
- Around line 62-65: The Modal component in transport-request-modal.tsx is
passing redundant props that match Mantine v8 defaults: remove
withCloseButton={true}, closeOnClickOutside={true}, and closeOnEscape={true}
from the <Modal ... /> usage so the code is cleaner without changing behavior;
locate the Modal JSX in the TransportRequestModal component and delete those
three props (leave other props intact).

- Fixed a bug caused when a passenger requested a new location than
  booked. Originally that would hide past history information from the
  modal. This does not happen anymore
- Changed type definitions of 3 props to exclude functions that accept
  parameters
@promatty
Copy link
Copy Markdown
Contributor

waiting to hear back from the Salvation Army on whether we still need this

@promatty promatty marked this pull request as draft February 27, 2026 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants