Work2026
Flow Society MX
An e-bike tour operator in Jalisco, with a six step booking flow, a per departure availability calendar, and an operator dashboard behind it.
- Role
- Product design and full stack build
- Year
- 2026
- Next.js 15
- React 19
- Prisma 6
- PostgreSQL
- Radix UI
- Resend

Flow Society runs small group e-bike rides in Bosque La Primavera, plus a membership and a two day camping trip. Before this build the bookings arrived through direct messages, and the person answering them was also the person leading the ride.
The problem underneath the brief
A tour operator's real constraint is not the marketing page, it is the fact that a departure has a fixed number of seats and a fixed number of bikes, and both are consumed by a booking that has not been confirmed yet. Any form that ignores the held state will eventually sell the same seat twice on a Saturday morning, and the person who finds out is standing in a car park.
6
Steps in the booking flow
Date, trip basics, riders, equipment, logistics, review.
12
Fields captured
Including accessory rentals, which used to be a separate message thread.
5
Experiences seeded
Brunch rides, the hybrid couples format, membership, and the camping trip.
How it is built
Prisma over Postgres holds experiences, departures, bikes, and bookings. Availability is derived from the departure rather than stored on it, so a held booking and a confirmed one both reduce what the calendar offers without anyone remembering to decrement a counter. The form is React Hook Form with Zod schemas shared between the client and the server action, which means the validation the visitor sees and the validation the database trusts are the same rules.

Sold out and held departures stay on the calendar. Removing them makes the season look emptier than it is and invites the question of whether the operator runs rides at all that month.
Playable
Departure picker with unavailable dates left in
Ported from Flow Society MX
6 departures, 4 still bookable.
Try the level filter. Unavailable departures stay visible, stay in the count, and refuse the click, which is what a paper calendar with a line through it has always done.
The operator dashboard at /admin confirms or declines bookings and blocks dates. Email runs
through Resend, with a console fallback so the whole flow can be developed and demonstrated
without an account or an API key.
::callout{title="A note on the free tier" } Every service in the stack sits inside a free plan: Neon for Postgres, Resend for mail. That was a requirement, not a shortcut. A small operator will abandon a system whose running cost arrives before its first booking does. ::