Skip to content

SANC-80: Driver Trip List API Endpoint#50

Merged
burtonjong merged 3 commits intomainfrom
SANC-80-Driver-Trip-List-API-Endpoint
Feb 13, 2026
Merged

SANC-80: Driver Trip List API Endpoint#50
burtonjong merged 3 commits intomainfrom
SANC-80-Driver-Trip-List-API-Endpoint

Conversation

@jason-duong4509
Copy link
Contributor

@jason-duong4509 jason-duong4509 commented Jan 26, 2026

Created an endpoint that returns all trips for a driver on a given date range. Also connected the endpoint to the front-end, allowing the information to display on the calendar

The video below shows the usage of the new endpoint when connected to the front-end. It also shows filtering by driver ID through assigning a trip to another driver in the DB

Each time a new date is viewed, the endpoint is queried for trips on that date

Video.mp4

Summary by CodeRabbit

  • New Features

    • Driver dashboard added with an interactive calendar that loads trips for selected date ranges.
    • Calendar selection now updates displayed date range dynamically.
  • Bug Fixes / Improvements

    • Loading indicator and calendar-hidden state while trip data loads.
    • Server-side date validation and driver-specific trip retrieval for accurate results.

@vercel
Copy link

vercel bot commented Jan 26, 2026

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

Project Deployment Actions Updated (UTC)
salvationarmy Ready Ready Preview, Comment Feb 13, 2026 3:11am

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

Adds a DriverDashboard that manages a calendar date range and fetches driver-specific bookings via a new TRPC procedure; CalendarView gains an optional onDateChangeFunction prop that is called when the calendar's visible date range changes; backend adds getDriverTrip with validation and driver-scoped filtering.

Changes

Cohort / File(s) Summary
CalendarView Enhancement
src/app/_components/common/calendar/calendar-view.tsx
Added optional onDateChangeFunction?: (...args:any[]) => void to props and wired a datesSet handler to call it with { startDate, endDate }.
DriverDashboard Component
src/app/_components/drivercomponents/driver-dashboard.tsx
New client component that holds startDate/endDate state, invokes TRPC getDriverTrip when both set, shows LoadingScreen while fetching, and passes bookings + onDateChangeFunction to CalendarView.
Dashboard Styling
src/app/_components/drivercomponents/driver-dashboard.module.scss
Added .hiddenCalendar class to hide/fix-position calendar during loading.
Page Integration
src/app/driver/home/page.tsx
Replaced direct CalendarView usage with DriverDashboard import and rendering; removed prior initial bookings usage.
Backend API
src/server/api/routers/bookings.ts
Added getDriverTrip procedure: authenticates user, requires Role.DRIVER, validates ISO date inputs, and queries bookings for the driver within [startDate, endDate). Note: duplicate procedure definition appears in the diff.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant DD as DriverDashboard
    participant CV as CalendarView
    participant TRPC as TRPC Client
    participant API as Bookings Router
    participant DB as Database

    User->>CV: change visible date range
    CV->>DD: call onDateChangeFunction({startDate,endDate})
    DD->>DD: set startDate/endDate state
    DD->>TRPC: query getDriverTrip(startDate,endDate)
    TRPC->>API: request getDriverTrip
    API->>API: validate user exists & role = DRIVER
    API->>API: validate ISO date formats
    alt validation fails
        API-->>TRPC: return error
        TRPC-->>DD: error
    else validation passes
        API->>DB: fetch bookings where driverId=user.id and date in range
        DB-->>API: bookings[]
        API-->>TRPC: bookings[]
        TRPC-->>DD: bookings[]
        DD->>CV: render bookings
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

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

Poem

🐇 I hopped through code to mark the range,
Dates set, trips fetched — the calendar sings,
DriverDashboard listens, queries with care,
Bookings appear like carrots everywhere,
Hop hop hooray — small changes, big flair!

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a driver trip list API endpoint that enables filtering trips by date range, which is the core objective of this PR.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SANC-80-Driver-Trip-List-API-Endpoint

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/server/api/routers/bookings.ts (2)

305-323: Extract shared date-validation logic to reduce duplication.

The regex-based date validation block (lines 305-323) is nearly identical to the one in getAll (lines 128-143). Consider extracting a shared helper to keep both procedures in sync and reduce copy-paste drift.

♻️ Example helper extraction
+/** Validates ISO date strings; throws BAD_REQUEST on failure. */
+function validateDateRange(startDate: string, endDate: string) {
+  let msg = "Invalid: ";
+  if (!(isoTimeRegex.test(startDate) || isoTimeRegexFourDigitYears.test(startDate))) {
+    msg += "Start Date ";
+  }
+  if (!(isoTimeRegex.test(endDate) || isoTimeRegexFourDigitYears.test(endDate))) {
+    msg += "End Date ";
+  }
+  if (msg !== "Invalid: ") {
+    throw new TRPCError({ code: "BAD_REQUEST", message: msg });
+  }
+}

Then replace the inline blocks in both getAll and getDriverTrip with a call to validateDateRange(startDate, endDate).


279-279: Consider pluralizing the procedure name to getDriverTrips.

The procedure returns a list of bookings, not a single trip. A plural name (getDriverTrips) better communicates the return type and is consistent with getAll.


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

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/driver/home/page.tsx (1)

8-25: Role gate mismatch: admins can enter, but API rejects them.

The page allows Role.ADMIN, yet getDriverTrip explicitly rejects non-drivers, which strands admins on a loading state. Align the page gate with the API (or update the API to support admin flows).

✅ Suggested fix (page gate alignment)
-  const session = await requireRole([Role.ADMIN, Role.DRIVER]);
+  const session = await requireRole([Role.DRIVER]);
🤖 Fix all issues with AI agents
In `@src/app/_components/drivercomponents/driver-dashboard.tsx`:
- Around line 21-34: Replace the placeholder single-element object
initialization for driverTrips with an empty array so calendar code doesn't
receive a partially populated Booking; specifically change the initialization of
driverTrips (currently "let driverTrips = [{}] as Booking[]") to an empty
Booking[] and keep the existing tripQuery handling
(api.bookings.getDriverTrip.useQuery and the if (tripQuery.data) assignment) so
driverTrips only contains real data from tripQuery.data.

In `@src/server/api/routers/bookings.ts`:
- Around line 281-309: The current validation only checks date formats (using
isoTimeRegex and isoTimeRegexFourDigitYears) but does not ensure the start/end
ordering, so add an explicit check after the format checks and before querying
the DB: parse or compare input.startDate and input.endDate (e.g., create Date
objects or compare normalized ISO strings) and if end < start throw a TRPCError
with code "BAD_REQUEST" and a clear message like "Invalid: End Date is before
Start Date"; keep this guard near the existing TRPCError block that validates
formats so the DB query (ctx.db.select().from(bookings) with
gte(bookings.startTime, ...) and lt(bookings.startTime, ...)) only runs for a
valid chronological range.

Comment on lines +281 to +309
let startAndEndDateErrorMessage = "Invalid: ";

if (
!(isoTimeRegex.test(input.startDate) || isoTimeRegexFourDigitYears.test(input.startDate))
) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "Start Date ";
}

if (!(isoTimeRegex.test(input.endDate) || isoTimeRegexFourDigitYears.test(input.endDate))) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "End Date ";
}

if (startAndEndDateErrorMessage !== "Invalid: ") {
//Either (or both) dates failed regex check
throw new TRPCError({
code: "BAD_REQUEST",
message: startAndEndDateErrorMessage,
});
}

return ctx.db
.select()
.from(bookings)
.where(
and(
eq(bookings.driverId, user.id),
gte(bookings.startTime, input.startDate),
lt(bookings.startTime, input.endDate),
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Validate start/end ordering to avoid inverted ranges.

Format checks pass even if endDate is before startDate, which yields confusing empty results. Add an explicit ordering guard.

✅ Suggested fix
       if (startAndEndDateErrorMessage !== "Invalid: ") {
         //Either (or both) dates failed regex check
         throw new TRPCError({
           code: "BAD_REQUEST",
           message: startAndEndDateErrorMessage,
         });
       }
+
+      const start = new Date(input.startDate);
+      const end = new Date(input.endDate);
+      if (!(end > start)) {
+        throw new TRPCError({
+          code: "BAD_REQUEST",
+          message: "End Date must be after Start Date",
+        });
+      }
📝 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
let startAndEndDateErrorMessage = "Invalid: ";
if (
!(isoTimeRegex.test(input.startDate) || isoTimeRegexFourDigitYears.test(input.startDate))
) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "Start Date ";
}
if (!(isoTimeRegex.test(input.endDate) || isoTimeRegexFourDigitYears.test(input.endDate))) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "End Date ";
}
if (startAndEndDateErrorMessage !== "Invalid: ") {
//Either (or both) dates failed regex check
throw new TRPCError({
code: "BAD_REQUEST",
message: startAndEndDateErrorMessage,
});
}
return ctx.db
.select()
.from(bookings)
.where(
and(
eq(bookings.driverId, user.id),
gte(bookings.startTime, input.startDate),
lt(bookings.startTime, input.endDate),
),
let startAndEndDateErrorMessage = "Invalid: ";
if (
!(isoTimeRegex.test(input.startDate) || isoTimeRegexFourDigitYears.test(input.startDate))
) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "Start Date ";
}
if (!(isoTimeRegex.test(input.endDate) || isoTimeRegexFourDigitYears.test(input.endDate))) {
startAndEndDateErrorMessage = startAndEndDateErrorMessage + "End Date ";
}
if (startAndEndDateErrorMessage !== "Invalid: ") {
//Either (or both) dates failed regex check
throw new TRPCError({
code: "BAD_REQUEST",
message: startAndEndDateErrorMessage,
});
}
const start = new Date(input.startDate);
const end = new Date(input.endDate);
if (!(end > start)) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "End Date must be after Start Date",
});
}
return ctx.db
.select()
.from(bookings)
.where(
and(
eq(bookings.driverId, user.id),
gte(bookings.startTime, input.startDate),
lt(bookings.startTime, input.endDate),
),
🤖 Prompt for AI Agents
In `@src/server/api/routers/bookings.ts` around lines 281 - 309, The current
validation only checks date formats (using isoTimeRegex and
isoTimeRegexFourDigitYears) but does not ensure the start/end ordering, so add
an explicit check after the format checks and before querying the DB: parse or
compare input.startDate and input.endDate (e.g., create Date objects or compare
normalized ISO strings) and if end < start throw a TRPCError with code
"BAD_REQUEST" and a clear message like "Invalid: End Date is before Start Date";
keep this guard near the existing TRPCError block that validates formats so the
DB query (ctx.db.select().from(bookings) with gte(bookings.startTime, ...) and
lt(bookings.startTime, ...)) only runs for a valid chronological range.

Comment on lines +274 to +279
if (user.role !== Role.DRIVER) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User is not a driver",
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

what if on the admin page we want to be able to see bookings for a specific driver? this endpoint wouldn't work then, would it?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we could just filter by driver id on the client side, but I think all endpoints should be accessible by admins regardless. Let me know what you think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As of right now, the endpoint is designed to return bookings that are assigned to the user who called it. If we extended this to admins, the endpoint would only return bookings assigned to the admin. I do not think this would be useful in this context

Achieving functionality where the admin could use the endpoint to filter bookings for a specific driver would require modification of the endpoint

@promatty
Copy link
Contributor

also resolve merge conflicts before we review please

…ist-API-Endpoint

# Conflicts:
#	src/app/driver/home/page.tsx
#	src/server/api/routers/bookings.ts
Copy link

@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

🤖 Fix all issues with AI agents
In `@src/server/api/routers/bookings.ts`:
- Around line 298-303: Change the TRPCError thrown when the role check fails to
use code "FORBIDDEN" instead of "UNAUTHORIZED": locate the role check that
compares user.role to Role.DRIVER (the block that currently throws new
TRPCError({ code: "UNAUTHORIZED", message: "User is not a driver" })) and update
the error code to "FORBIDDEN" so the handler returns a 403 for authenticated
users without the driver role; keep the existing message or adjust wording if
desired.
- Around line 287-296: The lookup that fetches the user via
ctx.db.query.user.findFirst is redundant — use the session instead; remove the
DB query and any NOT_FOUND error, read role from ctx.session.user.role and id
from ctx.session.user.id (or assign to userId) and update any subsequent query
that relied on the fetched user to use userId/ctx.session.user.role directly
(e.g., replace uses of the local user variable in this handler with
ctx.session.user.id or a userId constant and ctx.session.user.role).
🧹 Nitpick comments (2)
src/app/driver/home/page.tsx (1)

3-3: Nit: async is unnecessary now.

The function no longer awaits anything. Removing async keeps the signature honest about what this component does.

-export default async function DriverHome() {
+export default function DriverHome() {
src/server/api/routers/bookings.ts (1)

325-336: Query looks correct — minor note on naming.

The procedure is named getDriverTrip (singular) but returns an array of bookings. Consider getDriverTrips for clarity.

- Changed the error code when calling a driver-only endpoint and the
  user is not a driver. Changed from 401 to 403 to improve clarity
Copy link
Contributor

@burtonjong burtonjong left a comment

Choose a reason for hiding this comment

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

k i think its fine then

@burtonjong burtonjong merged commit 269d028 into main Feb 13, 2026
4 checks passed
@burtonjong burtonjong deleted the SANC-80-Driver-Trip-List-API-Endpoint branch February 13, 2026 03:56
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.

3 participants