OpenAthlete
AGPLv3 endurance training platform: EU-oriented hosting, transparent CTL/ATL/TSB logic in code, self-hosting, and full export. Built in Grenoble.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗One platform, two products: Club2Coach (clubs ↔ coaches) and Coach2Mentor (coaches ↔ mentors). One login covers both — but every role you activate (Club2Coach coach, Club2Coach club, Coach2Mentor coach, Coach2Mentor mentor) is paid for separately.
This replaces the earlier club2coach project entirely. That was a
single-product, browse-and-swipe prototype. This is a full rebuild
around your actual Club2Coach and Coach2Mentor designs: private
admin-curated listings for Club2Coach, browsable mentor profiles for
Coach2Mentor, weighted match scoring for both, and a shared identity
with per-role payment gating. Don't try to merge the old project with
this one — start fresh from this folder.
people — one row per login: name, mobile, email, gender,
region, current licence. Shared across both products.club2coach_coach_listings,
club2coach_club_vacancies, coach2mentor_coach_listings,
coach2mentor_mentor_listings. Each has its own paid flag. Paying
for one never unlocks another, even for the same person./club2coach/admin and choose
which to share — that's what club2coach_shares records.people.mobile and
people.email are never broadly readable. A coach only gains access
to a mentor's actual contact record once that mentor accepts their
request (see the RLS policy comments in schema.sql). Club2Coach
works the same way via club2coach_shares.In the SQL Editor, paste and run supabase/schema.sql in full. It sets
up every table, the scoring-support indexes, and Row Level Security —
including the admin PIN functions.
Still in the SQL Editor, run (replacing with your actual 6-digit PIN):
select set_admin_pin('123456');
This is the one PIN that unlocks /club2coach/admin and
/coach2mentor/admin — shared across both products, tied to whichever
logged-in person enters it correctly (see grant_admin_pin_session in
the schema for exactly how). You can change it later from the same SQL
editor using select change_admin_pin('old', 'new');, or add that as
an in-app admin settings screen later.
Copy .env.example to .env.local, fill in your Supabase Project URL
and anon/publishable key.
npm install
npm run dev
Push to GitHub, import into Vercel, add the same two environment variables there.
Every listing has a paid boolean that gates whether it's included in
matching (Club2Coach) or browsable (Coach2Mentor mentor listings). Right
now, marking something paid is a manual admin action — you click
"Mark paid" in the admin panel after receiving payment however you're
currently collecting it (bank transfer, in person, whatever). This
calls one of four mark_*_paid database functions, which is admin-only
by RLS (checked server-side, not just hidden in the UI).
This is deliberate for now, matching what we discussed: no live Stripe
integration until there's real demand. When you're ready for real
payment collection, the architecture is ready for it — each price
already lives as a constant in lib/constants.ts
(ROLE_PRICES_AUD), and swapping "manual mark as paid" for "Stripe
Checkout marks it paid via webhook" is a contained change, not a
rebuild.
lib/scoring.ts has the two scoring functions:
scoreClub2CoachMatch — accreditation, ability, competition level,
age group, geography, salary, gender — each weighted per your
sliders in /club2coach/admin.scoreCoach2MentorMatch — specialism overlap, career stage,
geography, availability, budget fit, gender — weighted via
/coach2mentor/admin.One honest limitation worth knowing: the "gender" sub-score in both
functions compares stated preferences, not verified actual gender —
because actual gender lives in the private people table, which isn't
broadly readable (by design, to protect contact info sitting in that
same table). If you want a truer gender match later, the clean fix is
adding a denormalised gender column directly on each listing table
(low sensitivity, unlike mobile/email) rather than loosening people
access.
lib/constants.ts holds every dropdown list — regions, competition
levels, accreditation ladder, specialisms — keyed by state
(REGIONS_BY_STATE, COMPETITION_LEVELS_BY_STATE), which the forms
already read from a single source. Some states have a fuller ladder
than others; bringing a state's list up to date, or adding a new one,
is a constants-file and form-dropdown change, not a schema rewrite.