Faustball UX ist ein modernes Frontend für die Ergebnis- und Ligadaten von faustball.com – Tabellen, Spielpläne, Vereine und Personen, schnell und auf jedem Gerät lesbar.
Überblick • Funktionen • Schnellstart • Skripte • Proxy & CORS • Architektur • Projektstruktur • Deployment • Rechtliches
Die offizielle Seite faustball.com ist eine AngularJS-Anwendung, die von einer JSON-API
unter /ui/... gespeist wird. Faustball UX konsumiert dieselbe API direkt – kein HTML-Scraping, keine eigene
Datenbank, keine Zweitpflege von Daten. Die Inhalte sind damit so aktuell wie die offizielle Quelle, und die Anwendung
liest ausschließlich (GET).
Im Mittelpunkt steht die Bedienbarkeit auf mobilen Geräten wie am Desktop: kurze Wege zu Tabelle und Spielplan, teilbare Links auf jede Ansicht, ein durchgängiger Dunkelmodus und Live-Ergebnisse, die sich selbstständig aktualisieren.
- Navigation – Region → Kategorie → Saison, bis hinunter zu Bezirks- und Regionalgruppen
- Wettbewerbe – eigene Reiter für Meisterschaft, Liga, Pokal und Aufstiegsspiele, gefiltert nach Geschlecht und Altersklasse
- Tabellen – auch bei parallelen Gruppen, inklusive Abschlusstabelle nach K.-o.-Runden
- Spielplan – nach Spieltag und Spielort gruppiert, mit Datum, Ausrichter und Halle
- Kreuztabelle – alle Begegnungen auf einen Blick, offene Paarungen als Platzhalter
- Wettbewerbs-Info – Wettkampfleitung, Verband, Diagramm des Spielformats, Saisonwechsler über die Jahre
- Spielseite – Satzergebnisse, Aufstellungen, Schiedsrichter und Ticker aus dem digitalen Spielformular
- Stammdaten – Vereine, Mannschaften, Personen und Spielorte samt Karte, durchgehend untereinander verlinkt
- Suche – Volltext über Vereine und Personen, direkt aus der Kopfzeile
- Favoriten – Ligen anheften und per Drag-and-drop sortieren, lokal gespeichert
- Live-Ergebnisse – wählbares Intervall, gepollt wird nur bei offenen Spielen
- Teilbare Links – jede Auswahl aus Reiter, Saison, Filter und Suche steht in der URL
- Oberfläche – Hell / Dunkel / System ohne Flackern, responsiv, deutschsprachig, PWA- und Open-Graph-Metadaten
Voraussetzungen: Node.js 22.12+ und pnpm – CI läuft auf Node 24.
git clone https://github.com/fabieu/faustball-ux.git
cd faustball-ux
pnpm install
pnpm devDie App läuft danach auf http://localhost:5173. Ein API-Schlüssel oder eine .env wird nicht benötigt – der
Dev-Server holt sich alles Nötige selbst, siehe Proxy & CORS.
| Befehl | Zweck |
|---|---|
pnpm dev |
Dev-Server mit HMR auf Port 5173 |
pnpm build |
Typecheck (tsc -b) und Produktions-Build nach dist/ |
pnpm typecheck |
Nur Typprüfung, ohne Build |
pnpm lint |
ESLint 9 (type-aware, plus React-Hooks-, a11y- und Query-Regeln) |
pnpm test |
Vitest einmalig ausführen (test:watch für den Watch-Modus) |
pnpm test:coverage |
Tests mit Coverage-Report und Coverage-Thresholds |
pnpm format |
Prettier über das Repository laufen lassen |
pnpm format:check |
Formatierung prüfen, ohne zu schreiben |
pnpm verify |
Formatierung, Lint, Typen und Tests am Stück – das Hook-Gate |
pnpm preview |
Den Produktions-Build lokal ausliefern |
pnpm check:api-contract |
Echte faustball.com-Endpunkte gegen die Zod-Schemas prüfen |
Die API verlangt bei jedem Aufruf den Header x-auth-version und sendet keine CORS-Header. Ein Browser kann sie daher
nicht direkt (cross-origin) aufrufen – alle /ui-Anfragen laufen deshalb same-origin über einen Proxy, der den Header
serverseitig ergänzt:
- Development: Vite proxyt
/uianhttps://www.faustball.com(siehevite.config.ts) - Produktion: ein Cloudflare Worker (
worker/index.ts) tut dasselbe. Nur/ui/*erreicht ihn, das gebaute SPA wird als statisches Asset ausgeliefert (siehewrangler.jsonc)
Der Header-Wert ist nirgends fest hinterlegt: Er muss zur aktuellen Release-Version von faustball.com passen und wird
deshalb von deren /t/status-Endpunkt gelesen – von Vite beim Start des Dev-Servers, vom Worker stündlich gecacht. Nach
einem Release genügt ein Neustart des Dev-Servers. Vereinswappen sind statische Bilder und werden ohne Proxy direkt
eingebunden.
Drei Eigenheiten der API prägen den Code – wer daran arbeitet, sollte sie kennen:
- Locators. Spiele, Ranglisten und Kreuztabelle referenzieren keine Mannschaften, sondern abstrakte Platzhalter-IDs.
locatorNamesist die Join-Tabelle, über die Name und Wappen aufgelöst werden. - Server-berechnete Tabellen. Die Platzierung liefert der Server (
tablePositions); Sortierung und Punktgleichheit werden bewusst nicht nachgebaut. - Wettbewerbsbäume. Hinter einem Contest steht eine verschachtelte Competition aus sequenziellen Phasen und parallelen Gruppen; Tabelle und Kreuztabelle hängen an deren Blättern, nicht am Contest selbst.
Die ausführliche Fassung – inklusive Spielplan-Gruppierung, Regionen-Graph und URL-State – steht in
CLAUDE.md.
src/
├── app/ # Shell: Router, Navbar, Fehlerseiten, Toasts, Impressum/Datenschutz
├── features/ # Ein Modul je Bereich – home, contest, match, club, team, region,
│ # person, search, venue – mit Seiten, Query-Hooks, reinen
│ # Ableitungen und den zugehörigen Tests
├── shared/
│ ├── api/ # Client, Zod-Schemas, Stammdaten-Lookups, Bild- und Routen-URLs
│ ├── lib/ # Hooks und Helfer ohne UI: URL-State, Favoriten, Auto-Refresh, Formate
│ ├── ui/ # Wiederverwendbare Bausteine (Karten, Tabs, Wappen, Selects, Modal)
│ └── theme/ # Hell/Dunkel/System-Umschaltung
└── test/ # Vitest-Setup, MSW-Server, Fixtures
worker/ # Cloudflare Worker: /ui-Reverse-Proxy für die Produktion
scripts/ # API-Contract-Check gegen die echte Quelle
public/ # Favicons, Manifest, robots.txt, sitemap.xml
Die Module importieren einander direkt, es gibt keine Barrel Files. Datenbeschaffung und -aufbereitung stecken in
Feature-Hooks (useContestData, useTeamSchedule, useRegionCompetitions), die Seitenkomponenten bleiben
darstellend.
Deployt wird über Workers Builds, die Git-Integration von Cloudflare: Ein Push auf main baut und veröffentlicht die
Anwendung automatisch, ein eigener Deploy-Workflow ist deshalb nicht nötig. Von Hand geht es genauso:
pnpm build
pnpm exec wrangler deploydist/ wird als statisches Asset-Bundle ausgeliefert, unbekannte Pfade fallen auf die SPA zurück
(not_found_handling: single-page-application). Nur /ui/* wird an den Worker geroutet, alles andere kommt direkt vom
CDN.
Faustball UX ist ein privates, inoffizielles Projekt und stellt lediglich eine alternative Benutzeroberfläche zu faustball.com dar; es besteht keine Verbindung zu deren Betreibern. Alle angezeigten Daten stammen aus der dortigen öffentlichen API und liegen in der Verantwortung ihrer Betreiber; eine Gewähr für Richtigkeit, Vollständigkeit und Aktualität wird nicht übernommen. Impressum, Datenschutzerklärung und Haftungsausschluss sind in der Anwendung über die Fußzeile erreichbar.
Der Quellcode steht unter der MIT-Lizenz; die über die API bezogenen Daten sind davon nicht berührt.