尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证BJJ Belt Progress Tracker
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Faixa BJJ keeps one registry for everybody who trains at the gym — students and instructors alike — counts the hours they put on the mat, and tells the head coach who is getting close to their next stripe or belt. It does that one thing and stops there.
It is not a gym management system. No payments, no online enrollment, no competition management. It is meant to sit alongside whatever software the gym already uses for the business side, and cover the part those tools handle badly: technical progression, with as little extra work for the instructor as possible. One deployment hosts many gyms, each isolated from the others.
Generic gym software treats belt rank as a text field. Promotions end up decided by feel, or tracked in a notebook that lives in one person's bag. Faixa BJJ gives the instructor an objective baseline — accumulated hours, time at the current belt, attendance over the last weeks — and then gets out of the way.
[!IMPORTANT] Eligibility is always a suggestion. The app flags who has met the thresholds; the promotion itself is recorded by the head coach, never automatically, and a member can never change their own belt. A member never sees remaining hours, their next grade or a verdict.
/members/criteria) drive a count of eligible members, an
eligibility filter and a marker on each row — one computation, seen three ways./check-in; members
scan it with the phone camera or with the in-app scanner on their dashboard./members/requests./logs. Ids only, no names or emails.| Layer | Choice |
|---|---|
| Frontend | Next.js 16 (App Router), React 19, Tailwind CSS v4 |
| Database | Supabase (Postgres + Auth) |
| Permission | Postgres Row Level Security, driven by the active role and gym |
| Bot check | Cloudflare Turnstile, verified by Supabase Auth |
| Tests | Vitest (pure functions in frontend/utils/), SQL replay in Docker |
| Hosting | Vercel |
There is no API layer on purpose. The browser talks to Supabase directly, so the user's JWT reaches Postgres and RLS is what actually enforces the rules — the UI only hides what the database would refuse anyway.
Requirements: Node.js 20+, a Supabase project, and Docker if you want to replay the migrations locally.
git clone <repo-url>
cd faixabjj/frontend
npm install
cp .env.example .env.local # fill in the values below
npm run dev # http://localhost:3000
| Variable | Required | What it is |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | yes | Project URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | yes | Publishable key (the newer key, not the legacy anon one) |
SUPABASE_SECRET_KEY | yes | Service-role key, server-only — account management and the app log need it |
NEXT_PUBLIC_SITE_URL | yes | Absolute deployment URL, no trailing slash — canonical links, sitemap, QR |
NEXT_PUBLIC_TURNSTILE_SITEKEY | yes | Turnstile sitekey; its secret lives in Supabase Auth → Attack Protection |
TURNSTILE_SECRET_KEY | for /join | Server-only; the invite registration verifies its challenge itself |
[!WARNING]
SUPABASE_SECRET_KEYbypasses Row Level Security. It carries noNEXT_PUBLIC_prefix on purpose and must never reach the browser.
Apply the SQL in supabase/migrations/ in filename order, either with the
Supabase CLI or by pasting each file into the project's SQL Editor. Every
migration is safe to run twice. Some must be applied before or after deploying
the matching app version — docs/claude/database.md says which.
To check the whole history locally, in a throwaway Postgres:
bash supabase/tests/replay.sh --isolation # from the repo root, Docker running
It applies every migration twice and runs the tenant-isolation checks. Never
point anything in supabase/tests/ at a real project.
After the migrations nobody has a privilege. Grants are made by hand, on purpose:
supabase/scripts/bootstrap-platform-admin.sql in the SQL Editor (adjust the
email inside)./gyms — timezone, go-live date
and location included — and add its manager./members.[!NOTE] Set each gym's go-live date to the day it really starts recording attendance. Hours before it are estimated, hours from it on are counted; a date in the future counts the same weeks twice.
All of these run from frontend/:
npm run dev # dev server (Turbopack)
npm run build # production build
npm run start # serve the production build
npm run lint # ESLint
npm test # Vitest, single run
npm run test:watch
Roles come from an active assigned_role row and decide what a person can
reach inside their gym:
| Role | Members list | Courses / Attendance | Edit registry | Manage accounts | Promote |
|---|---|---|---|---|---|
student | — | Attendance only, check-in for self | — | — | — |
assistant | — | Attendance only, check-in for self | — | — | — |
instructor | read-only | full | — | — | — |
head_coach | full | full | yes | yes | yes |
admin | full | full | yes | yes | — |
admin means "runs the portal without teaching": a portal-only admin appears in
no people list and shows no belt. The platform superadmin sits outside every
gym: it reaches only the platform dashboard, /gyms, /logs and its own
account.
Each privilege is one SQL predicate, called by the frontend rather than reimplemented in it, and enforced three times over: the nav hides the link, the page checks the predicate and answers 404, and RLS refuses the query. Hiding a nav link is never the access control.
faixabjj/
├── frontend/ # Next.js app (App Router) — run every command from here
│ ├── app/ # routes: /members, /courses, /attendance, /check-in, /dashboard,
│ │ # /account, /gym, /gyms, /logs, /join, public pages
│ ├── components/ # shell, belt graphics, hand-rolled icons, no UI dependency
│ └── utils/ # pure helpers (unit-tested), Supabase clients, i18n dictionaries
├── supabase/
│ ├── migrations/ # schema, RLS policies, views, functions — plain SQL, in order
│ ├── scripts/ # hand-run SQL (bootstrap, diagnostics), not part of history
│ └── tests/ # Docker replay and tenant-isolation checks
├── docs/
│ ├── claude/ # detailed guidance per area (database, access, members, …)
│ ├── superpowers/ # design specs and implementation plans
│ └── handoff/ # session handoffs: what was decided and why
├── directives/ # SOPs (see AGENT.md)
├── execution/ # deterministic scripts (see AGENT.md)
├── AGENT.md # the 3-tier agent architecture this repo follows
├── CLAUDE.md # working notes for contributors and coding agents
├── belt-criteria.md # promotion criteria source (IBJJF + academy practice)
└── brand-guidelines.md # colours and typography
The app runs against real data end to end: registry, promotions and eligibility, courses, attendance with geofenced and QR check-in, invite registration, dashboards, multi-gym administration and three languages. What is missing before a gym relies on it is the pilot itself.