Dugout Value
Baseball research on pitching, hitting, and projections, plus tools for comparing players, contracts, and trade value.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
TCM App – Vereins-App des TC Muckensturm Ablösung der bisherigen eBuSy-Lösung durch eine eigene App für iOS, Android und Web. Rund 300 aktive Mitglieder, 8 Sandplätze. Fünf Zustände, jeder mit eigenem Riegel. Er gilt für jede Forderungsart gleich – Beitrag, Getränke, Arbeitsdienst, Gastgebühr:查看完整 README ↗
Ablösung der bisherigen eBuSy-Lösung durch eine eigene App für iOS, Android und Web. Rund 300 aktive Mitglieder, 8 Sandplätze.
| Modul | Inhalt |
|---|---|
| Mitgliederverwaltung | Stammdaten, Mitgliedschaften, Rollen, Zahler-Beziehungen |
| Einstellungen | Alle Regelwerte im Admin-Dashboard änderbar |
| Freiplatzbuchung | 8 Plätze, 60-Minuten-Buchungen zur vollen oder halben Stunde, Serien-Blockungen |
| Getränkeabrechnung | Self-Service in der App und Kiosk-Tablet, monatliche Abrechnung |
| Beiträge & SEPA | Jahresbeitragslauf, Mandatsverwaltung, pain.008-Export |
| Arbeitsdienst | Soll-/Ist-Stunden, Ausgleich in Geld zum Jahresende |
Fünf Zustände, jeder mit eigenem Riegel. Er gilt für jede Forderungsart gleich – Beitrag, Getränke, Arbeitsdienst, Gastgebühr:
Forderung entsteht → Vorabankündigung → Lastschriftlauf → Datei → Rückläufer
charges.open .notified debit_items pain.008 .returned
Zwei Regeln tragen den Ablauf. Angekündigt und eingezogen wird je Zahler,
nicht je Forderung: ein Elternteil mit zwei Kindern liest eine Nachricht über den
Gesamtbetrag und sieht eine Buchung auf dem Kontoauszug. Und die
Vorabankündigungsfrist ist nicht umgehbar – sie hängt an der Forderung
(notified_at), wird bei der Auswahl geprüft und noch einmal im Trigger auf
debit_items; die direkten Schreibrechte auf beide Tabellen sind entzogen.
Wo die App endet: bei der fertigen .xml. Der Vorstand lädt sie im
Onlinebanking hoch; von der Bank kommt keine Rückmeldung. Rückläufer trägt
deshalb ein Mensch ein – über die EndToEndId, also genau das Feld, das ein
späterer camt-Import liefern würde.
apps/web Next.js – Mitglieder-Web, Admin-Dashboard, Kiosk-Route
apps/mobile Expo – Mitglieder-App für iOS und Android
packages/core Geschäftslogik, Zod-Schemas, generierte Datenbank-Typen
packages/ui geteilte Komponenten
supabase/ Migrationen, RLS-Policies, RPCs, Edge Functions, pgTAP-Tests
tools/import eBuSy-Import (läuft erst zum Cutover)
Doppelbuchungen sind durch die Datenbank ausgeschlossen, nicht durch Anwendungslogik:
alter table bookings add constraint bookings_no_overlap
exclude using gist (court_id with =, slot with &&)
where (status = 'active');
Zwei gleichzeitige Anfragen auf denselben Slot können nicht beide durchkommen – unabhängig davon, was der Client tut.
Buchungsregeln laufen serverseitig. Direkte Inserts auf bookings sind per RLS verboten;
der einzige Weg führt über die Funktion create_booking, die alle Regeln prüft – inklusive des
Kontingents jedes eingetragenen Mitspielers. Das Kontingent steht derzeit auf 0, also
unbegrenzt; die Regel bleibt vollständig im Code, damit der Vorstand sie in knappen Zeiten
über das Admin-Dashboard wieder einschalten kann.
Es gibt genau zwei Stufen: Admin und Mitglied. Admins sehen und ändern alles – Mitglieder, Beiträge, Serien, Einstellungen und jede fremde Buchung. Alle anderen sehen ihre eigenen Daten und verwalten ihre eigenen Buchungen bis zum Spielbeginn. Zwischenrollen (Kassenwart, Sportwart, Trainer, Thekendienst) gibt es bewusst nicht mehr: sie waren in einem Verein dieser Größe schwerer zu pflegen als sie nützten.
Der Belegungsplan zeigt volle Stunden von 08 bis 21 Uhr. Gebucht wird zur vollen oder halben Stunde, immer 60 Minuten – die Feinwahl passiert im Buchungsfenster. Eine Belegung sperrt jede Stunde, die sie berührt: das Dienstagstraining von 18:30 bis 20:00 wäre sonst im Stundenraster unsichtbar und der 18-Uhr-Platz sähe frei aus.
corepack enable pnpm
pnpm install
cp .env.example .env # ausfüllen
pnpm db:reset # Schema + synthetischer Testbestand
pnpm test # Unit- und Datenbanktests
pnpm test:functions # Edge Functions unter Deno (lädt Deno per pnpm dlx)
pnpm dev # Web-App
Das Web läuft als Docker-Container auf dem eigenen Server (187.124.4.243)
unter https://app.tennisclub-muckensturm.de, hinter dem dortigen Traefik.
Gearbeitet wird über Pull Requests: ein Merge auf main deployt Dev, ein
Merge auf production nach Freigabe Prod. Branches, Releases und Hotfixes
beschreibt CONTRIBUTING.md.
Abbild: apps/web/Dockerfile, Dienst: deploy/web/docker-compose.yml. Die
Werte (Domain, Supabase-URL und -Key) stehen auf dem Server in
/docker/tcm-web*/.env.
Die App unter apps/mobile liest ihre Supabase-Werte aus apps/mobile/.env
(EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_ANON_KEY) und startet mit
pnpm --filter @tcm/mobile ios. Wie sie in TestFlight kommt, steht in
apps/mobile/RELEASE.md.
Während der gesamten Entwicklung enthält die Datenbank ausschließlich synthetische Daten. Echte Mitgliederdaten kommen erst beim Cutover ins Spiel.
Die App verarbeitet Bankverbindungen und Geburtsdaten Minderjähriger. Vor dem Produktivgang
werden ein Verzeichnis von Verarbeitungstätigkeiten und ein AV-Vertrag mit Supabase (EU-Region)
benötigt. Zugangsdaten gehören ausschließlich in .env bzw. Supabase Vault – niemals ins Repo.