Built the platform to move an event staffing business off spreadsheets and text messages
A Phoenix event staffing company ran on a yearly Excel workbook, a calendar spreadsheet, and text messages. In five weeks it got a request-to-billing platform with a spreadsheet-style estimate grid, a client portal, crew booking, and SMS and email messaging, built to replace all three.

Outcomes
- First working release
- 2 days
- Releases
- 8 in 5 weeks
- Workflow covered
- Request → final billing
- User roles
- 4
Estimates, calendar, crew, booking, invoicing, and Excel import, from first commit to a shipped v1.
Client portal, spreadsheet-style grid, position management, request redesign, and messaging followed the first release.
Client request, estimate, two-step approval, crew booking, confirmations, invoices, and payment.
Owner, assistant, bookkeeper, and client portal, each seeing only what they should.
The challenge
The company supplied audio, video, lighting, and production crews to event producers, and ran the whole business on one Excel workbook per year on OneDrive, a separate show-calendar spreadsheet with crew as rows and dates as columns, Apple Notes, and text messages. Estimates, crew booking, confirmations, invoices, and payments all lived in cells only the owner fully understood, and clients requested shows and approved pricing by phone and email.
Approach
- Mapped the real workflow from the workbook and the calendar before writing code: estimate templates, overtime tiers, rate sheets, confirmation tracking, and how a show changes on the day
- Shipped a working first release in two days covering estimates, calendar, crew, booking, invoicing, and payment tracking, then iterated in short milestones with the owner's feedback between each
- Kept the owner's Excel mental model: estimates and billing live in a spreadsheet-style grid with inline editing, live overtime math, and PDFs that match the screen
- Built a client portal with progressive disclosure, so clients submit requests and approve pricing and then crew names without ever seeing rates, margins, or crew pay
- Imported years of history from the spreadsheets with a dry-run validator and fuzzy crew matching, so the app started full rather than empty
- Added messaging last: SMS and email templates, bulk sends, delivery tracking through webhooks, and STOP-keyword opt-out handling
What shipped
The owner's view is a dashboard and a show grid: positions down the side, dates across the top, crew assigned in the cells, with call times, hours, overtime tiers, and margin computed as they type. Versions replace the old V1, V2, V3 file copies, and the same grid renders the branded estimate and final billing PDFs, so what the client receives is what the owner saw.
Clients get their own portal. They submit a show request with positions and dates, see pricing once the estimate is sent, approve it, then see and approve crew names once assigned. Comments run alongside each estimate, with internal notes hidden from the client at the query level rather than in the UI.
Underneath: role-based access for owner, assistant, bookkeeper, and client; positions and rate cards managed in the database instead of in code; and a messaging layer that sends confirmations and reminders by text or email according to each person's preference, logs delivery, and honors opt-outs.
Stack
- Next.js 16
- React 19
- TypeScript
- Drizzle ORM
- Neon Postgres
- Better Auth
- TanStack Table
- Twilio
- Resend
- React PDF
Services involved
Have a problem shaped like this one?
Tell me what you're working with and you'll get an honest read on whether it's a fit.
Start a project