Gimbo's Universe / AI Builder · Tennis Buddy
Side project · 2026 · v1 已上线生产

Tennis
Buddy

给业余网球玩家的社交 + 比赛记录 app —— 找到搭子、约起来、把比分记下来、攒出自己的赛季。 现在已经上线,真实球友在真实球场用起来了。

Role
设计 + 前端 + 后端 + 部署
Status
v1 · 已上线 tennis.gimbo.co.nz
Stack
React · Supabase · Vercel · Resend
Design
Roland-Garros × Material 3
Milestone
第一场真实比赛 · 2026-07-18

一个代码小白,把它 ship 了

我的本职是供应链计划员 —— 天天打交道的是表格和采购单,不是代码。 React 我写不来,也不装会。但我打网球,而且和所有球友撞上同一堵墙: 约球散落在各个群里,比分只活在脑子里。这个项目,就是 AI 时代一个代码小白 把这个痒处当真之后发生的事 —— 产品自己设计,施工交给 AI,最后 ship 给真实朋友用。 四步,不掺水地讲。

Step 01

从自己的痒处出发

每次约球都散在群聊里,每个比分都只在我脑子里。我想要一个地方,既能找到搭子,又能把历史记下来 —— 首先为自己做,不是为市场做。

Step 02

先设计,后写码

一行代码都还没有的时候,先把整个产品画完:34 个画板、每个页面每种状态,外加一份带验收标准的交付规格。真正的功夫,是决定不做什么。

Step 03

AI 写码,我拍板

React 和 SQL 是 Claude 写的;周边每个决定是我做的 —— 下一步 ship 什么、一个页面该是什么手感、哪个取舍赢。每次生产数据变更,依然由我亲手审、亲手执行。

Step 04

交给真人用

不是 demo:朋友们注册进来,反馈几天内改写了路线图,产品在一个周六早上的球场上证明了自己。Ship 出去,胜过完美。

两个老问题,放进同一个 app

业余网球玩家最常撞上的两堵墙:"今天没人陪我打""我上次跟谁打的什么比分?" Tennis Buddy 把"发现搭子"和"记录比赛"做进同一条闭环 —— 现在还加上了社交层,让每场比分都有地方聊。

Match

从邀请到最终比分

定向邀请,或谁都能加入的 open game。接受/婉拒、赛前聊天、一键加进手机日历 —— 打完按盘录入比分,胜负自动判定,比赛沉淀进历史记录。

Discover

真实俱乐部,真实球友

奥克兰东区五家真实俱乐部、真实球场数,按 NTRP 等级筛球友,看附近的 open game。访客不注册也能直接浏览真实首页。

Community

比分值得被聊起来

真实的社区 feed:发帖可附上战绩卡,评论、点赞、关注,球员主页有赛季数据和交手记录(H2H)。这是 Phase 3,已上线生产环境,不是效果图。

一周晚上,重建上线

5 月上线的第一版是砍过的简化版。7 月我决定回到最初的 34 画板完整设计, 按 phase 一段段重建 —— 每一步都直接发上生产环境,真实朋友一边施工一边注册进来。

Jul 11

拍板重建

回到最初的设计:五个 phase 的施工计划,一条规则 —— 约球主循环跑通就上线,不等完整路线图。

Jul 13

约球主循环上线

Phase 1–2 进生产:数据层地基 + 完整比赛生命周期 —— 邀请、open game、赛前聊天、日历文件、按盘记分。

Jul 14

第一批真实球友

朋友们注册进来,mock 数据当晚退役。加上三步引导向导,首页对访客开放 —— 不注册也能逛。

Jul 16

自有域名 + 邮件基建

App 搬到 tennis.gimbo.co.nz,事务邮件走自己的子域名 —— 登录链接从每小时 2 封提到 30 封,直落收件箱不进垃圾箱。

Jul 17

朋友群首发

群里发了一条:当晚 +4 注册,登录链接零流失,还有人填了我从没列过的主场俱乐部。访客分析上线。

Jul 18

闭环在真实世界跑通

Phase 3 社交 feed 当天上线,邀请邮件也开始直接从数据库里发出。而真正重要的里程碑:朋友发来邀请 → 接受 → 赛前聊天 → 周六早上真的站上球场 → 比分录进 app。

5-2 · 2-5 · 3-5 —— 输了,如实记录

怎么搭起来的

前端 React 19 + Vite + MUI v6,设计语言取自法网(Roland-Garros)的色调和字体, 用 Material 3 的形状语言落地。Supabase 负责数据、登录、安全 —— 连邮件都从 Postgres 里直接发。 Vercel 上 push 即部署,没有需要照看的服务器。

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
  • 一张 matches 表 四种状态通吃
  • Row Level Security 规则住在数据库里
  • 列级权限 访客永远看不到邮箱
  • Magic-link 登录 无密码
Email

Outbound

  • Resend 自有子域名发信
  • 自定义 SMTP 登录链接直落收件箱
  • 邀请邮件 Postgres 触发器 + pg_net
  • Vault 密钥 代码里零 key
Deploy

Infra

  • Vercel edge CDN
  • tennis.gimbo.co.nz 自有域名
  • GitHub source of truth
  • CI/CD git push = deploy
  • Web Analytics 真实访客数据
User
Browser
React app + 本地 session
Hosting
Vercel CDN
静态资源全球分发
Source
GitHub
Source of truth · 自动部署
Backend
Supabase
Postgres · Auth · RLS
Email
Resend
登录链接 + 邀请邮件 · 自有子域名
↓ Browser ← CDN · CDN built from GitHub · Browser ↔ Supabase · Postgres → Resend → Inbox ↓

真实上线了什么

一个人端到端 —— 设计、前端、后端、部署,加上真实用户的运营。 下面每一项都在生产环境上跑着,不在路线图里。

3/5
Phase 已上线

数据层地基、完整约球主循环、社交层都已经在生产环境。剩下发现层和通知层 —— 不过邀请邮件已经提前插队上线了。

11
SQL migrations

建表、行级安全、引导流程、公开首页、社交图谱、库内邀请邮件 —— 每一次生产数据变更都人工审过、亲手执行。

8+
真实球友

是朋友,不是测试账号 —— 通过 magic link 注册,填的是真实资料和真实主场,包括我从没列进去的俱乐部。

1
真实世界完整闭环

一场比赛从邀请 → 接受 → 聊天 → 球场 → 录分,全程跑在产品里。这是最重要的那个数字。

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

这次重建学到的

过程比结果有意思 —— 用一周晚上把真产品交到真人手里,这几个想法我会一直带着。

Shipping

先跑通一环,别等路线图

Phase 1–2 上线时,社交层和发现层都还不存在。真实用户在施工中途就进来了 —— 他们的反馈几天内就改写了下一个 phase 该做什么。

Data

一张表,四种状态

定向邀请、open game、待确认、已完赛,全住在同一张 matches 表里。更少的 join、更少的同步 bug,整条闭环只有一个 source of truth。

Security

RLS 比前端校验扎实

规则住在 Postgres 里:访客能浏览真实 open game,但 email 列永远被 revoke。前端就算被绕过,数据库这堵墙也不放行。

Design

借力用户行为,省掉 cron

过期没约满的 open game 只有发起人自己能看见 —— 那就让发起人下次打开 app 时顺手清掉。全覆盖、零定时任务、零新基建。

Users

真实用户帮你找到你看不见的

注册流程从没问过用户叫什么 —— 真人进来第一天就暴露。比分存反了,提交者自己看是对的,只有对手那侧才能看出来。

Growth

邮件就是产品基建

共享 SMTP 每小时只发 2 封登录邮件 —— 同一小时注册的第三个朋友永远进不来。自有发信域名解决了任何代码都解决不了的增长问题。

来打一场 →

Tennis Buddy 已经上线。任意邮箱登录,magic link 几秒到 —— 或者不注册,先逛逛现在开着的 open game。

打开 Tennis Buddy →

永不存储密码,登录只用邮箱 magic link(Supabase Auth)。 你的邮箱只用于发送登录链接和你主动请求的比赛邀请。