SquadMetric
Rate your FPL squad and turn projections into clear transfer, captaincy, bench, and chip decisions.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.