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.