Gimbo's Universe / AI Builder · Tennis Buddy
Side project · 2026 · v1 live in production

Tennis
Buddy

A social + match-tracking app for amateur tennis players — find a hit, set up the match, record the score, build your season. Now live with real players on real courts.

Role
Design + Frontend + Backend + Deploy
Status
v1 · live at tennis.gimbo.co.nz
Stack
React · Supabase · Vercel · Resend
Design
Roland-Garros × Material 3
Milestone
First real-world match · 18 Jul 2026

A non-coder shipped this

My day job is planning supply chains — spreadsheets and purchase orders, not code. I can't write React and I don't pretend to. But I play tennis, and I kept hitting the same wall as everyone I play with: organizing a hit lives in group chats, and scores live in nobody's memory. This project is what happens when a non-coder takes that itch seriously in the AI era — design the product yourself, direct AI to build it, ship it to real friends. Four steps, honestly told.

Step 01

Start from a real itch

Every hit I organized was scattered across group chats; every score lived only in my head. I wanted one place to find a partner and keep the history — built for myself first, not for a market.

Step 02

Design before code

Before any code existed I drew the whole product — 34 frames, every screen and state, plus a delivery spec with acceptance criteria. The real work was deciding what not to build.

Step 03

AI writes, I decide

Claude writes the React and SQL; I make every call around it — what ships next, how a screen should feel, which trade-off wins. Every production data change is still reviewed and run by my own hand.

Step 04

Ship to real people

Not a demo: friends signed up, their feedback reshaped the roadmap within days, and the product proved itself on a Saturday-morning court. Shipped beats perfect.

Two old problems, one app

Amateur players keep hitting the same two walls: "no one to hit with today" and "what was the score last time we played?" Tennis Buddy folds finding a partner and tracking your matches into one loop — and now adds the social layer that makes results worth sharing.

Match

From invite to final score

Direct invites or open games anyone can join. Accept or decline, chat before the match, drop it into your calendar — then enter the score set by set. Winner is derived automatically and the match lands in your history.

Discover

Real clubs, real players

Five real East Auckland clubs with their actual court counts, players filtered by NTRP level, open games near you. Visitors can browse the live homepage before ever creating an account.

Community

Results worth talking about

A real feed: posts with attached match results, comments and likes, follows, and player pages with season stats and head-to-head records. Shipped as Phase 3 — live in production, not a mockup.

Rebuilt in a week of evenings

The first version that shipped in May was a simplified cut. In July I went back to the original 34-frame design and rebuilt it phase by phase — shipping to production at every step, with real friends signing up mid-build.

Jul 11

The rebuild decision

Back to the original design: a five-phase build plan with one rule — ship as soon as the match loop works, don't wait for the full roadmap.

Jul 13

Match loop live

Phases 1–2 in production: the data foundation plus the full match lifecycle — invites, open games, pre-match chat, calendar files, set-by-set scoring.

Jul 14

First real players

Friends signed up and mock data retired the same night. A three-step onboarding wizard went in, and the homepage opened up so visitors can browse without an account.

Jul 16

Own domain + email infrastructure

The app moved to tennis.gimbo.co.nz with transactional email on its own subdomain — sign-in links went from 2 per hour to 30, landing in the inbox instead of spam.

Jul 17

Friend-group launch

One post in the group chat: four signups that evening, zero lost sign-in links, and players adding home clubs I'd never listed. Analytics switched on.

Jul 18

The loop closed in the real world

Phase 3 — the social feed — shipped the same day invite emails started sending straight from the database. And the milestone that actually matters: a friend's invite → accepted → pre-match chat → Saturday morning on court → score recorded in the app.

5-2 · 2-5 · 3-5 — a loss, logged honestly

How it's built

React 19 + Vite + MUI v6 on the front, with a design language borrowed from Roland-Garros and grounded in Material 3 shapes. Supabase handles data, auth, security — and even sends the emails from inside Postgres. Vercel deploys every push. No servers to babysit.

Frontend

UI Layer

  • React 19 SPA
  • Vite build tool
  • MUI v6 components
  • Tailwind v3 layout utils
  • React Router 7 client routing
Backend

Data + Auth

  • Postgres via Supabase
  • One matches table 4 states, no joins
  • Row Level Security rules live in the DB
  • Column grants visitors never see emails
  • Magic-link auth passwordless
Email

Outbound

  • Resend own subdomain sender
  • Custom SMTP sign-in links to inbox
  • Invite emails Postgres trigger + pg_net
  • Vault secrets no keys in code
Deploy

Infra

  • Vercel edge CDN
  • tennis.gimbo.co.nz custom domain
  • GitHub source of truth
  • CI/CD git push = deploy
  • Web Analytics real visitor data
User
Browser
React app + cached session
Hosting
Vercel CDN
Static assets globally
Source
GitHub
Source of truth · auto-deploy
Backend
Supabase
Postgres · Auth · RLS
Email
Resend
Magic links + invites · own subdomain
↓ Browser ← CDN · CDN built from GitHub · Browser ↔ Supabase · Postgres → Resend → Inbox ↓

What's actually shipped

Solo, end to end — design, frontend, backend, deploy, and the ops of real users. Everything below is live in production, not on a roadmap.

3/5
Phases in production

Data foundation, the full match loop, and the social layer are live. Discovery and notifications remain — though invite emails already jumped the queue.

11
SQL migrations

Schema, row-level security, onboarding, the public homepage, the social graph, in-database invite email — every production change reviewed and run by hand.

8+
Real players

Friends, not test accounts — signed up via magic links, filling real profiles with real home clubs, including clubs I'd never listed myself.

1
Real-world full loop

One match went invite → accept → chat → court → recorded score, entirely inside the product. The single number that matters most.

Product Design React Postgres RLS / Auth SQL Migrations Email Infrastructure CI/CD Design System UX Writing Product Ops Full-stack

What the rebuild taught me

The process was more interesting than the result — these are the ideas I'm keeping from shipping a real product to real people in a week of evenings.

Shipping

Ship the loop, not the roadmap

Phases 1–2 went to production before social or discovery existed. Real users arrived mid-build — and their feedback reshaped the next phase within days.

Data

One table, four states

Direct invites, open games, pending requests and completed matches all live in one matches table. Fewer joins, fewer sync bugs, one source of truth for the whole loop.

Security

RLS beats frontend checks

Rules live in Postgres: visitors browse real open games while the email column stays revoked. Even a bypassed frontend hits a wall at the database.

Design

Exploit behavior, skip the cron

Expired unfilled games are only visible to their host — so the host's next app-open cleans them up. Full coverage, zero scheduled jobs, zero new infrastructure.

Users

Real users find what you can't

Signup never asked for a name — found on day one. A score stored flipped reads correctly to the submitter and wrong to the opponent — only visible from the other side.

Growth

Email is product infrastructure

Shared SMTP allowed 2 sign-in emails per hour — the third friend that hour never got in. An owned sender domain fixed growth in a way no code could.

Come have a hit →

Tennis Buddy is live. Sign in with any email — a magic link lands in seconds — or just browse the open games without an account.

Open Tennis Buddy →

No password is ever stored. Login uses email-only magic links via Supabase Auth. Your email is used solely for sign-in links and the match invites you ask for.