Skip to content

SANC-104: Fix Create Bookings#75

Open
jason-duong4509 wants to merge 3 commits intomainfrom
SANC-104-Fix-Create-Bookings
Open

SANC-104: Fix Create Bookings#75
jason-duong4509 wants to merge 3 commits intomainfrom
SANC-104-Fix-Create-Bookings

Conversation

@jason-duong4509
Copy link
Copy Markdown
Contributor

@jason-duong4509 jason-duong4509 commented Apr 6, 2026

Made a few fixes to the create bookings feature to address various bugs associated with it

1. Phone number validation
Phone number validation was added to the form to prevent malformed input from existing in the DB

1.mp4

A regex check was added to the front-end and the back-end uses an additional library to confirm that the phone number is valid

2.mp4

2. Better UI response for invalid start/end time
The start and end time fields silently reject the user's input when invalid. This has been changed to show a red outline and an error message, like all other fields in the modal

3.mp4

3. Back-end address validation
There's no back-end validation for addresses, allowing one to put anything into the field if they modify the front-end

4.mp4

The back-end now validates address inputs and can fix inputs if needed

5.mp4

Summary by CodeRabbit

  • New Features

    • Normalized and stricter phone number validation for bookings.
    • Stronger address validation to ensure usable pickup/destination addresses.
  • Improvements

    • Improved date/time field validation: start/end time errors display more appropriately (end-time errors suppressed until a start time is set).
    • Clearer, more actionable error messages during create/update flows.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 6, 2026

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

Project Deployment Actions Updated (UTC)
salvationarmy Ready Ready Preview, Comment Apr 14, 2026 9:32pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc1ff588-323a-4653-9284-3b9d760a3b8e

📥 Commits

Reviewing files that changed from the base of the PR and between 9758633 and 3effdcf.

📒 Files selected for processing (5)
  • src/app/_components/agencycomponents/agency-form.tsx
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/bookings.ts
  • src/server/api/routers/form.ts
  • src/server/api/routers/trip.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/trip.ts
  • src/server/api/routers/bookings.ts

📝 Walkthrough

Walkthrough

Adds client-side error propagation for date/time fields and strengthens validation/normalization of phone numbers and addresses by introducing server-side address validation and applying it in trip and booking creation/update flows.

Changes

Cohort / File(s) Summary
Frontend form components
src/app/_components/agencycomponents/agency-form.tsx, src/app/_components/agencycomponents/agency-interactive-area.tsx
DateTimePicker components now receive error props; endTime validation suppressed until startTime exists. Phone number validation switched from length checks to Zod schema parsing and returns detailed schema errors.
Address validation router
src/server/api/routers/form.ts
Replaced validateDestinationAddress (public) with validateAddress (protected), changed input { address } array shape, added early-return guard for invalid input, and refined success criteria to permit addresses considered "good enough" by missing-component rules.
Trip router
src/server/api/routers/trip.ts
Added phone number normalization/validation (Zod + parsePhoneNumber) and synchronous server-side address validation calls via internal caller; rejects invalid phone/address with TRPC BAD_REQUEST and writes normalized values back to input.
Bookings router
src/server/api/routers/bookings.ts
On create/update, trims and normalizes phoneNumber (persisting canonical number), validates/normalizes pickup and destination addresses via form.validateAddress, and treats empty-string updates as undefined when filtering updates to apply.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FrontendForm as Frontend Form
    participant ServerAPI as Server API (bookings/trip)
    participant FormRouter as form.validateAddress
    participant GoogleAPI as Google Address Validation
    participant DB

    Client->>FrontendForm: submit booking (phone, pickup, destination, start/end)
    FrontendForm->>ServerAPI: call create mutation (trimmed inputs)
    ServerAPI->>ServerAPI: validate phone (Zod + parsePhoneNumber)
    ServerAPI->>FormRouter: validateAddress({ regionCode, address: [..] })
    FormRouter->>GoogleAPI: request address validation
    GoogleAPI-->>FormRouter: response (verdict, missingComponentTypes)
    FormRouter-->>ServerAPI: validated/formatted address OR null
    alt address invalid
        ServerAPI-->>FrontendForm: TRPCError BAD_REQUEST (Invalid Address)
    else address valid
        ServerAPI->>DB: persist normalized phone and addresses
        DB-->>ServerAPI: success
        ServerAPI-->>FrontendForm: success response
    end
    FrontendForm-->>Client: show success or error feedback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

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

Poem

🐰 I hopped through forms both near and far,
Parsing numbers, checking every char,
Addresses tidy, times now clear,
I cheer the checks that keep data dear,
A little rabbit's validation star 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'SANC-104: Fix Create Bookings' is partially related to the changeset. It mentions the ticket number and 'Fix Create Bookings', but the actual changes address validation and error handling across multiple components (agency form, interactive area, and multiple routers), not just the create bookings feature. Consider a more specific title that reflects the broader scope, such as 'SANC-104: Add validation and error feedback across booking flows' or 'SANC-104: Implement phone/address validation and UI error handling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SANC-104-Fix-Create-Bookings

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: 2

🤖 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/server/api/routers/form.ts`:
- Around line 142-154: The code reads data.result.address.missingComponentTypes
without guarding for undefined which can throw; update the logic around
addrIsPerfect/addrIsGoodEnough to first normalize missingFromAddr (e.g., const
missingFromAddr = Array.isArray(data.result.address.missingComponentTypes) ?
data.result.address.missingComponentTypes : []) so subsequent length checks and
index accesses are safe, then use that normalized missingFromAddr in the
existing addrIsGoodEnough computation and the final return that references
data.result.address.formattedAddress.

In `@src/server/api/routers/trip.ts`:
- Around line 29-38: The parser call
parsePhoneNumberWithError(input.phoneNumber) can throw before your guard and you
also replace the value with a national display string; wrap the
parsePhoneNumberWithError call in a try-catch around the block that validates
phoneNumberSchema and call phoneNumber.isValid(), and on error throw the same
TRPCError; on success assign input.phoneNumber = phoneNumber.number (the E.164
canonical form) instead of "+1 " + phoneNumber.formatNational() so it matches
phoneNumberSchema and downstream validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b76c2762-f8c5-45d9-834d-3369a9822517

📥 Commits

Reviewing files that changed from the base of the PR and between edfd191 and cfea8b5.

📒 Files selected for processing (4)
  • src/app/_components/agencycomponents/agency-form.tsx
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/form.ts
  • src/server/api/routers/trip.ts

Comment thread src/server/api/routers/form.ts
Comment thread src/server/api/routers/trip.ts Outdated
Did the following to improve the create bookings feature
- Added front-end phone number validation
- Fixed start/end time field boxes not turning red on invalid input
- Added back-end validation for creating/updating bookings
- Updated validate address endpoint to allow some leniency in validation
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: 1

🧹 Nitpick comments (1)
src/server/api/routers/bookings.ts (1)

629-665: Consider extracting address validation into a helper to reduce duplication.

The serverCaller is created twice (lines 631 and 650), and both pickup and destination validation blocks follow an identical pattern. Extracting a helper would reduce repetition and simplify future maintenance.

Suggested refactor
// Helper function (can be placed above the router or inline)
async function validateAndNormalizeAddress(
  serverCaller: ReturnType<typeof appRouter.createCaller>,
  address: string,
  fieldName: string,
): Promise<string> {
  const result = await serverCaller.form.validateAddress({
    regionCode: "ca",
    address: [address],
  });
  if (result === null) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: `Invalid ${fieldName}`,
    });
  }
  return result;
}

// Usage in the update mutation:
const serverCaller = appRouter.createCaller(ctx);

if (updatesToApply.pickupAddress) {
  updatesToApply.pickupAddress = await validateAndNormalizeAddress(
    serverCaller,
    updatesToApply.pickupAddress,
    "Pickup Address",
  );
}

if (updatesToApply.destinationAddress) {
  updatesToApply.destinationAddress = await validateAndNormalizeAddress(
    serverCaller,
    updatesToApply.destinationAddress,
    "Destination Address",
  );
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/api/routers/bookings.ts` around lines 629 - 665, Duplicate address
validation logic: create a single helper (e.g., validateAndNormalizeAddress)
that accepts the serverCaller returned by appRouter.createCaller(ctx), the raw
address string, and a fieldName; have it call
serverCaller.form.validateAddress({ regionCode: "ca", address: [address] }),
throw the same TRPCError when result is null, and return the normalized result.
Instantiate serverCaller once via appRouter.createCaller(ctx) and replace both
inline validation blocks for updatesToApply.pickupAddress and
updatesToApply.destinationAddress by awaiting validateAndNormalizeAddress(...)
and assigning the returned normalized address back to the respective
updatesToApply field.
🤖 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/server/api/routers/bookings.ts`:
- Around line 608-627: The current phone-number update flow can swallow
non-Error throwables and trims only the value passed to phoneNumberSchema but
not to parsePhoneNumberWithError; to fix, pass the trimmed string (e.g., const
raw = updatesToApply.phoneNumber.trim()) to both phoneNumberSchema.safeParse and
parsePhoneNumberWithError, use the parsed phoneNumber result only if
phoneNumber.isValid(), and make the catch unconditionally rethrow a TRPCError
for any thrown value (not just when e instanceof Error)—map ParseError to
"Invalid phone number" otherwise include the thrown value/message—then set
updatesToApply.phoneNumber = phoneNumber.number only after successful
validation.

---

Nitpick comments:
In `@src/server/api/routers/bookings.ts`:
- Around line 629-665: Duplicate address validation logic: create a single
helper (e.g., validateAndNormalizeAddress) that accepts the serverCaller
returned by appRouter.createCaller(ctx), the raw address string, and a
fieldName; have it call serverCaller.form.validateAddress({ regionCode: "ca",
address: [address] }), throw the same TRPCError when result is null, and return
the normalized result. Instantiate serverCaller once via
appRouter.createCaller(ctx) and replace both inline validation blocks for
updatesToApply.pickupAddress and updatesToApply.destinationAddress by awaiting
validateAndNormalizeAddress(...) and assigning the returned normalized address
back to the respective updatesToApply field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89730b17-4370-42c0-b941-8379faebccb5

📥 Commits

Reviewing files that changed from the base of the PR and between cfea8b5 and 9758633.

📒 Files selected for processing (5)
  • src/app/_components/agencycomponents/agency-form.tsx
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/bookings.ts
  • src/server/api/routers/form.ts
  • src/server/api/routers/trip.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/app/_components/agencycomponents/agency-form.tsx
  • src/server/api/routers/trip.ts

Comment thread src/server/api/routers/bookings.ts
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: 3

♻️ Duplicate comments (1)
src/server/api/routers/bookings.ts (1)

608-627: ⚠️ Potential issue | 🟠 Major

Trim once and rethrow every phone-validation failure.

Lines 611-612 validate two different values, and Line 619 only converts Error instances into TRPCError. If anything else is thrown, the mutation continues with updatesToApply.phoneNumber untouched.

🛠️ Proposed fix
       if (updatesToApply.phoneNumber) {
-        //Wants to change phone number
+        const trimmedPhone = updatesToApply.phoneNumber.trim();
         try {
-          const res = phoneNumberSchema.safeParse(updatesToApply.phoneNumber.trim()); //Check if phone num is valid
-          const phoneNumber = parsePhoneNumberWithError(updatesToApply.phoneNumber);
+          const res = phoneNumberSchema.safeParse(trimmedPhone);
+          const phoneNumber = parsePhoneNumberWithError(trimmedPhone);
           if (!res.success || !phoneNumber.isValid()) {
-            // Failed regex check or API check
             throw new Error(res.error?.issues[0]?.message ?? "Invalid phone number");
           }
           updatesToApply.phoneNumber = phoneNumber.number;
         } catch (e) {
-          if (e instanceof Error) {
-            // Failed regex check or API check
-            throw new TRPCError({
-              code: "BAD_REQUEST",
-              message: e instanceof ParseError ? "Invalid phone number" : e.message,
-            });
-          }
+          throw new TRPCError({
+            code: "BAD_REQUEST",
+            message:
+              e instanceof ParseError
+                ? "Invalid phone number"
+                : e instanceof Error
+                  ? e.message
+                  : "Invalid phone number",
+          });
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/api/routers/bookings.ts` around lines 608 - 627, The phone
validation uses different inputs and fails to rethrow non-Error throwables;
first trim the input once into a local const (e.g., const raw =
updatesToApply.phoneNumber.trim()), use that same raw for
phoneNumberSchema.safeParse(raw) and parsePhoneNumberWithError(raw), and after
success assign updatesToApply.phoneNumber = phoneNumber.number; in the catch
block always throw a TRPCError (not only for Error instances) and map ParseError
to a "Invalid phone number" message while preserving other error messages for
debugging (referencing phoneNumberSchema, parsePhoneNumberWithError, ParseError,
TRPCError, and updatesToApply.phoneNumber).
🤖 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/server/api/routers/bookings.ts`:
- Around line 608-665: The create mutation still copies input.phoneNumber,
input.pickupAddress, and input.destinationAddress directly into bookingData, so
replicate the same validation/normalization logic used in the update path: reuse
phoneNumberSchema + parsePhoneNumberWithError (as used in the update block) to
validate/normalize input.phoneNumber before assigning to
bookingData.phoneNumber, and call
appRouter.createCaller(ctx).form.validateAddress for input.pickupAddress and
input.destinationAddress (same params and null checks as the update flow) to
replace bookingData.pickupAddress and bookingData.destinationAddress with the
normalized results; surface TRPCError BAD_REQUEST messages on failures just like
the update logic.
- Around line 608-665: The current falsy guards (if
(updatesToApply.phoneNumber), if (updatesToApply.pickupAddress), if
(updatesToApply.destinationAddress)) let empty strings bypass validation and be
written to the DB; change these checks to explicit undefined checks (e.g.,
updatesToApply.phoneNumber !== undefined) and add empty-string handling: for
phoneNumber, either reject empty string with a BAD_REQUEST TRPCError or
normalize it to null before validation (update logic around phoneNumberSchema,
parsePhoneNumberWithError, and the TRPCError thrown); for pickupAddress and
destinationAddress, explicitly throw a BAD_REQUEST TRPCError with "Invalid
Pickup Address"/"Invalid Destination Address" when the value === "" before
calling serverCaller.form.validateAddress (serverCaller created via
appRouter.createCaller and using form.validateAddress).

In `@src/server/api/routers/form.ts`:
- Around line 111-112: Update the Zod input schema for validateAddress (the
protectedProcedure with .input(...)) to reject empty arrays and whitespace-only
strings: change address to z.array(z.string().min(1).refine(s => s.trim().length
> 0, { message: "address entries cannot be blank" })).nonempty({ message:
"address must contain at least one entry" }) so validation fails locally before
calling the Google Maps API.

---

Duplicate comments:
In `@src/server/api/routers/bookings.ts`:
- Around line 608-627: The phone validation uses different inputs and fails to
rethrow non-Error throwables; first trim the input once into a local const
(e.g., const raw = updatesToApply.phoneNumber.trim()), use that same raw for
phoneNumberSchema.safeParse(raw) and parsePhoneNumberWithError(raw), and after
success assign updatesToApply.phoneNumber = phoneNumber.number; in the catch
block always throw a TRPCError (not only for Error instances) and map ParseError
to a "Invalid phone number" message while preserving other error messages for
debugging (referencing phoneNumberSchema, parsePhoneNumberWithError, ParseError,
TRPCError, and updatesToApply.phoneNumber).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f56ebb38-742c-4733-bdca-470de0dd5f5e

📥 Commits

Reviewing files that changed from the base of the PR and between cfea8b5 and 9758633.

📒 Files selected for processing (5)
  • src/app/_components/agencycomponents/agency-form.tsx
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/bookings.ts
  • src/server/api/routers/form.ts
  • src/server/api/routers/trip.ts
✅ Files skipped from review due to trivial changes (1)
  • src/app/_components/agencycomponents/agency-form.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/_components/agencycomponents/agency-interactive-area.tsx
  • src/server/api/routers/trip.ts

Comment thread src/server/api/routers/bookings.ts
Comment thread src/server/api/routers/form.ts
@promatty
Copy link
Copy Markdown
Contributor

merge conflicts @jason-duong4509. also please take a look at the coderabbit comments

…ookings

# Conflicts:
#	src/server/api/routers/trip.ts
- Carried over input validation to from bookings.update endpoint to
  bookings.create endpoint
- Prevented empty strings from being added to the DB
- Rejected input early in form.validateAddress to avoid unnecessary API
  costs
- Added handling for rare errors when phone number inputs are invalid
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