C2J Auto Spa
Online booking and a back office for a mobile detailing business that used to book by phone and text.
- Role
- Lead engineer at G2, with Nick Giacchi
- Year
- 2026
- Stack
- React 19, Vite, Express on Vercel Functions, Firebase Auth and Firestore, Google Calendar API, Stripe (built, off), Twilio (built, off), GitHub Actions
c2jautospa.com
- paying client takes bookings on itRésumé · source
- 1
- admin tabs with 14 management panelsCounted in the repo · source
- 9
- merged pull requestsCounted in the repo · source
- 38
The problem
The owner took every booking by phone and text. Prices depend on the vehicle size and the add-ons, and one person's calendar has to hold the whole business.
What I built
- 01Double booking prevented inside a Firestore transaction: the server re-reads the day's bookings and blocked slots, checks interval overlap, and returns 409 if the slot was just taken.
- 02One shared time module for the browser and the server, which fixes the classic bug where a date string parses as UTC midnight and shifts every booking a day in Eastern time.
- 03A Stripe webhook that stamps a sent marker before sending, so a retry never messages a customer twice, and awaits every send because a serverless function freezes when it responds.
- 04Per-channel notification outcomes saved on each booking, so 'the client never got a confirmation' can be diagnosed from the console.
- 05Catalog version history: every admin change snapshots first, bursts of drag reorders merge into one snapshot, and a restore can itself be undone.
- 06Security rules deployed by CI in separate steps, so a Storage failure can never block the Firestore rules.
More screens

c2jautospa.com

c2jautospa.com

c2jautospa.com

c2jautospa.com


Decisions
Every integration is optional
No Stripe means request-only booking. No Twilio means SMS is a no-op. The site degrades instead of crashing, which let it go live before every account was approved.
SMS gated on explicit consent
Customer texts require a consent checkbox to satisfy the TCPA and A2P 10DLC. Older bookings with no answer are treated as no consent.
Moved the AI key server-side
An inherited Gemini key had been inlined into the browser bundle. The AI endpoint now accepts a fixed set of operations with admin authentication, not an open proxy.
Not claimed
- Stripe deposits and SMS confirmations are built and switched off until the client enables them.
- The AI marketing hub is built and switched off.
- The admin console is not shown because it holds customer data.
- The laptop image is a generated render with a real screen capture composited in.