All work / 01 of 07 · AI engineering

Tee Off For A Cause.

A Next.js charity-golf registration platform built solo and shipped to 1,000+ golfers for the 9th annual Ryann Reed Design Build outing — captain-led teams, Stripe promo redemption, sponsor self-service, and a 55-test safety net.

Client
Ryann Reed Design Build (Breast Cancer Awareness benefit)
Role
Solo full-stack engineer
Year
2025–2026
Status
Live in production at golf.ryannreed.com
TOFAC homepage shown on a laptop — marketing mockup
01 · Context

A 9-year-old outing outgrowing its WordPress form.

A 9-year-old charity golf outing was outgrowing its old WordPress form. Last year they handled ~700 golfers with a spreadsheet and a lot of manual reconciling.

This year the goal was 1,000+, with captain-led foursomes, sponsor packages, a buffet add-on, and zero day-of confusion.

02 · Problem

Three real constraints, all easy to get wrong.

  • Captains buy a foursome up front then invite 3 friends — the claim flow needs to handle race conditions when two friends click the same invite link at the same time.
  • Sponsor tiers come with logos that the marketing team should NOT have to manually FTP somewhere.
  • Day-of, ~250 attendees walk into a single check-in tent — needs to scale beyond a clipboard.
03 · What I built

One Next.js app on Vercel, doing the whole job.

Single Next.js 16 app on Vercel; Supabase for auth + Postgres + storage; Stripe for payments and admin-managed promo codes; Resend for email; Twilio for SMS; Sentry + Better Stack for uptime/error tracking on a public /uptime widget.

Architecture

Captain buys foursome ───▶ Stripe Checkout ───▶ Webhook (idempotent) │ │ ▼ ▼ Supabase: team row Resend: 3 invite emails │ │ ▼ ▼ Invite link click ─▶ Claim lock (race-safe) ─▶ Golfer attached to seat │ ▼ QR code mailed/printed ─▶ Day-of scanner ─▶ Attendance + buffet

Stack

Next.js 16 React Server Components Tailwind v4 Supabase (SSR auth + Postgres + storage) Stripe Checkout + promo codes Resend Twilio Sentry Better Stack Vercel Vitest + Testing Library

Key decisions

  • Claim lock at the DB layer, not the app layer. A claim_token + atomic update so two simultaneous clicks can’t both win the same seat. Worth more than any clever React state code.
  • Stripe promo codes as the source of truth, not a custom coupon table. Admins create codes in Stripe; the app reads them back. Less to maintain, no double-spend.
  • Sponsor self-service for logo + display name from inside My Account. Marketing’s day got smaller; sponsors saw their logo live within minutes of upload.
  • Six My Account states (anonymous, invited, captain, golfer-on-team, solo, sponsor) handled by one role-aware layout, not six routes. Easier to reason about, easier to test.
  • Uptime widget at /uptime/embed — pulled from Better Stack, lives inside GHL too. Same pattern is reused for the upcoming GHL Scorecard.
  • 55+ test files covering the captain claim race, invite token expiry, email HTML sanitization, Stripe price tampering, captain orphan recovery, and rate limiting.
In motion

The system, narrated.

Three flows from the live app, animated. Left pane prompts what’s happening; right pane renders the actual UI as the system builds it.

Live leaderboard

Score events stream in, rows flash, ranks reshuffle in real time.

prompt
live leaderboard
Vol 9 · live
Pos
Team
Strk
Thru

Public squad page

Captain hits publish, a shareable team URL gets provisioned, hero renders, lineup card assembles, share menu wires up.

prompt
squad page
https://
The lineup
Live position
4thof 56 · thru 13
Share
iMessageWhatsAppEmail

Day-of check-in

QR scan, bag tag prints, cart key pairs, entitlements light up, progress against the 248-golfer target ticks forward.

prompt
check-in
Welcome, 
Player · TeamBailey Cox · The Putter Kings
042
Cart · pairedCart 1410:00 shotgun · hole 7
Range Buffet Raffle ×3
Checked in
041 / 248
04 · Selects

What the team actually uses.

Real product, shot like a product. The full TOFAC system — public-facing pages, admin tools, and mobile flow — staged as a series of marketing-grade renders generated from live screenshots.

Public

Homepage
Homepage
Registration
Registration
Teams
Teams
Scoreboards
Scoreboards
Mini-games
Mini-games
Foursome builder
Foursome builder
Sponsors wall
Sponsors wall
Mobile
Mobile flow

Admin

Admin dashboard
Admin dashboard
QR check-in
QR check-in
Stripe coupons
Stripe coupons
Registrations admin
Registrations admin
05 · Outcome

Marketing got the day back.

1,000+
Golfers handled (vs. ~700 last year)
55+
Test files protecting the critical paths
0
Production rollbacks since launch

Marketing got the day back. Sponsors uploaded their own logos. Day-of check-in moved at the speed of QR scans. The captain claim race that worried me most got hit live on launch day, the lock held, and nobody noticed.

06 · Lessons

What I’d do the same. What I’d do differently.

Keep: Race conditions live at the DB. Anywhere a token gets redeemed, lock at the row.

Keep: Use the payment platform’s primitives (Stripe promo codes) before building your own.

Change: Started without a public /uptime page. Added it after the first hiccup. Should have shipped that on day one — costs nothing, buys trust.

Change: Underestimated how many “small” admin features sponsors would want. Next year’s v2 has those on the roadmap (mini-games in My Account, walk-up foursomes).