From e967f36f6ccebcd259efaf264278404965bfdd11 Mon Sep 17 00:00:00 2001 From: Ronny Mueller Date: Fri, 3 Apr 2026 22:02:05 +0200 Subject: [PATCH] feat: WM2026 Tippspiel - Initial Backend + Frontend --- .DS_Store | Bin 0 -> 6148 bytes .vscode/settings.json | 5 + projektplan_wm2026_tippspiel.docx | Bin 0 -> 49902 bytes projektplan_wm2026_tippspiel.md | 403 +++++++ prototyp_wm2026.html | 1123 ++++++++++++++++++++ prototyp_wm2026_mobile.html | 1650 +++++++++++++++++++++++++++++ prototyp_wm2026_v2_home.html | 1272 ++++++++++++++++++++++ 7 files changed, 4453 insertions(+) create mode 100644 .DS_Store create mode 100644 .vscode/settings.json create mode 100644 projektplan_wm2026_tippspiel.docx create mode 100644 projektplan_wm2026_tippspiel.md create mode 100644 prototyp_wm2026.html create mode 100644 prototyp_wm2026_mobile.html create mode 100644 prototyp_wm2026_v2_home.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dce1321878e0ab181f0bcc9b2f598c737e6d535e GIT binary patch literal 6148 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 R^ZA#G0vrIKrtLS|{|9n1Br^a2 literal 0 HcmV?d00001 diff --git a/projektplan_wm2026_tippspiel.md b/projektplan_wm2026_tippspiel.md new file mode 100644 index 0000000..f76050e --- /dev/null +++ b/projektplan_wm2026_tippspiel.md @@ -0,0 +1,403 @@ +# Projektplan: WM 2026 Tippspiel — Staffbase Custom Plugin + +**Projekt:** WM 2026 Tippspiel +**Auftraggeber:** GEALAN Fenster-Systeme GmbH +**Erstellt:** April 2026 +**Version:** 1.0 +**Status:** Initialisierung + +--- + +## 1. Projektziel & Vision + +### 1.1 Zielsetzung + +Entwicklung eines interaktiven Tippspiel-Plugins für die FIFA Weltmeisterschaft 2026 (USA/Kanada/Mexiko, 11. Juni – 19. Juli 2026), das nahtlos in die bestehende Staffbase-Mitarbeiter-App von GEALAN integriert wird. Alle Mitarbeitenden können über die gewohnte Staffbase-Oberfläche — auf Mobile und Desktop — ihre Tipps abgeben, Ergebnisse verfolgen und die Rangliste einsehen. + +### 1.2 Kernziele + +- Mitarbeiterbindung und -engagement rund um die WM 2026 stärken +- Niedrigschwellige Teilnahme: kein separates Login, keine externe App nötig +- Vollständige Integration in die bestehende Staffbase-Instanz von GEALAN +- Mehrsprachige Unterstützung (Deutsch, Englisch — erweiterbar) +- Skalierbar für zukünftige Turniere (EM, CL etc.) + +### 1.3 Nicht-Ziele (Out of Scope v1.0) + +- Echte Geldwetten oder externe Zahlungsabwicklung +- Integration mit externen sozialen Netzwerken +- Eigene mobile App (kein separates iOS/Android-App-Release) + +--- + +## 2. Technische Architektur + +### 2.1 Integrationsmodell: Staffbase Custom Plugin + +Das Tippspiel wird als **Staffbase Custom Plugin** realisiert. Custom Plugins sind externe Web-Applikationen, die per **iFrame** in die Staffbase-Plattform eingebettet werden. Staffbase übernimmt die Authentifizierung der Mitarbeitenden automatisch über **JWT-Tokens (RS256)**, sodass kein separates Benutzerkonto für das Tippspiel benötigt wird. + +**Vorteile dieses Ansatzes:** +- Vollständige technische Freiheit bei Backend, Frontend und Datenbank +- Automatisches Single Sign-On via Staffbase JWT +- Funktioniert auf iOS-App, Android-App und Web-Browser +- Keine Änderungen an der Staffbase-Instanz selbst notwendig + +### 2.2 Architektur-Übersicht + +``` +┌─────────────────────────────────────────────┐ +│ Staffbase-Plattform (GEALAN) │ +│ │ +│ ┌──────────────────────────────────────┐ │ +│ │ Custom Plugin (iFrame) │ │ +│ │ → Tippspiel-Frontend (React) │ │ +│ └──────────────┬───────────────────────┘ │ +│ │ JWT Token (automatisch) │ +└─────────────────│───────────────────────────┘ + │ HTTPS + ┌────────▼─────────┐ + │ Backend API │ + │ (Node.js/Express)│ + │ + JWT-Validierung│ + └────────┬──────────┘ + │ + ┌────────▼──────────┐ + │ Datenbank │ + │ (PostgreSQL) │ + │ Supabase Managed │ + └───────────────────┘ + │ + ┌────────▼──────────┐ + │ WM-Spielplan API │ + │ football-data.org│ + └───────────────────┘ +``` + +### 2.3 Technologie-Stack + +| Schicht | Technologie | Begründung | +|---|---|---| +| **Frontend** | React + TypeScript | Moderner Standard, große Community, passt zu Staffbase Client SDK | +| **Staffbase Integration** | @staffbase/plugins-client-sdk | Offizielle Brücke zwischen iFrame und Staffbase-App | +| **Backend** | Node.js + Express | Passt nahtlos zum Staffbase Node.js SDK | +| **Auth / SSO** | @staffbase/staffbase-plugin-sdk | JWT-Validierung (RS256/PKCS8) | +| **Datenbank** | PostgreSQL via Supabase | Managed, DSGVO-konform (EU-Region wählbar), einfaches Setup | +| **Hosting** | Railway oder Render | HTTPS out-of-the-box, einfaches Deployment, kostengünstig | +| **WM-Spielplan** | football-data.org API | Kostenfreie WM-Daten inkl. Ergebnisse, Teams, Gruppen | +| **Caching** | Redis (optional) | Spielplandaten cachen, Rate-Limit-Entlastung | +| **CI/CD** | GitHub Actions | Automatisiertes Deployment | + +### 2.4 Staffbase SDK — Verfügbare Nutzerdaten via JWT + +Bei jedem Aufruf des Plugins stellt Staffbase automatisch folgende Nutzerdaten bereit: + +| Feld | Beschreibung | Verwendung im Tippspiel | +|---|---|---| +| `userId` | Eindeutige Staffbase-User-ID | Primärschlüssel für Tipps & Rangliste | +| `fullName` | Vollständiger Name | Anzeigename in der Rangliste | +| `locale` | Spracheinstellung (z.B. `de_DE`) | Automatische Sprachumschaltung | +| `role` | Staffbase-Rolle (admin, reader…) | Admin-Bereich nur für Admins sichtbar | +| `branchId` | Instanz-/Bereichs-ID | Für standortbezogene Auswertungen | + +--- + +## 3. Funktionsumfang (Features) + +### 3.1 Kernfunktionen (MVP — Pflicht für v1.0) + +#### F1 — Benutzer & Authentifizierung +- Automatischer Login via Staffbase SSO (JWT) — kein separates Konto +- Benutzerprofil mit Name und Gesamtpunkten +- Rollenbasierter Zugriff: Normaler Nutzer vs. Admin + +#### F2 — WM-Spielplan +- Anzeige aller 104 WM-Spiele (Gruppenphase + KO-Runde) +- Automatische Synchronisation mit football-data.org API +- Anzeige von Datum, Uhrzeit (lokalisiert), Stadion, Teams, aktueller Ergebnisstatus + +#### F3 — Tipp-Eingabe +- Tipp abgeben: Ergebnis (Heimtore : Auswärtstore) pro Spiel +- Tipp nur möglich bis Anpfiff (Deadline-Schutz) +- Tipp-Änderung bis zur Deadline erlaubt +- Visuelle Bestätigung bei gespeichertem Tipp + +#### F4 — Punkte-System +- **Ergebnis exakt richtig:** 3 Punkte +- **Tendenz richtig (Sieg/Remis/Niederlage):** 1 Punkt +- **Falsch:** 0 Punkte +- Bonus-Punkte für KO-Runde konfigurierbar (z.B. ×2 ab Halbfinale) +- Automatische Berechnung nach Ergebnis-Eintrag durch Admin + +#### F5 — Rangliste +- Live-Gesamtrangliste aller Teilnehmenden +- Anzeige: Platz, Name, Punkte, Tipp-Quote +- Filterbar nach Abteilung oder Standort (via Staffbase-Gruppen) +- Eigene Position stets sichtbar (auch wenn außerhalb der Top 10) + +#### F6 — Admin-Bereich +- Spielergebnisse manuell eintragen / korrigieren +- Punkte automatisch neu berechnen nach Eintrag +- Übersicht aller Tipps pro Spiel +- Teilnahme-Statistiken (Anzahl Tipps, aktive Nutzer) + +### 3.2 Erweiterungen (Nice-to-have — v1.1 oder später) + +| Feature | Beschreibung | Aufwand | +|---|---|---| +| **Push-Benachrichtigungen** | Ergebnisse-Update via Staffbase News API | Mittel | +| **Gruppenphase-Tipper** | Tipp auf Gruppensieger/-zweiten | Mittel | +| **Turniersieger-Tipp** | Einmaliger Tipp auf WM-Sieger (×5 Punkte) | Klein | +| **Mini-Ligen** | Interne Teams (z.B. nach Abteilung) | Groß | +| **Kommentar-Funktion** | Tipps kommentieren | Klein | +| **Tipp-Analyse** | Auswertung: beliebteste Tipps, Überraschungen | Mittel | +| **Automatische Ergebnis-Synchronisation** | Polling football-data.org, kein manuelles Eintragen | Mittel | + +--- + +## 4. Datenbankschema + +### 4.1 Kerntabellen + +```sql +-- Nutzer (befüllt automatisch via JWT bei erstem Login) +CREATE TABLE users ( + id VARCHAR(64) PRIMARY KEY, -- Staffbase userId + full_name VARCHAR(255) NOT NULL, + locale VARCHAR(10) DEFAULT 'de_DE', + role VARCHAR(20) DEFAULT 'reader', + branch_id VARCHAR(64), + created_at TIMESTAMPTZ DEFAULT NOW(), + last_seen_at TIMESTAMPTZ DEFAULT NOW() +); + +-- WM-Spiele (synchronisiert via football-data.org) +CREATE TABLE matches ( + id SERIAL PRIMARY KEY, + external_id VARCHAR(32) UNIQUE NOT NULL, -- ID aus football-data.org + stage VARCHAR(50) NOT NULL, -- 'GROUP_STAGE', 'ROUND_OF_16', ... + group_name VARCHAR(10), -- 'A', 'B', ... (NULL in KO-Runde) + home_team VARCHAR(100) NOT NULL, + away_team VARCHAR(100) NOT NULL, + kickoff_at TIMESTAMPTZ NOT NULL, + home_score SMALLINT, -- NULL bis Abpfiff + away_score SMALLINT, + status VARCHAR(20) DEFAULT 'SCHEDULED' -- SCHEDULED, IN_PLAY, FINISHED +); + +-- Tipps +CREATE TABLE predictions ( + id SERIAL PRIMARY KEY, + user_id VARCHAR(64) REFERENCES users(id), + match_id INTEGER REFERENCES matches(id), + predicted_home SMALLINT NOT NULL, + predicted_away SMALLINT NOT NULL, + points_earned SMALLINT, -- NULL bis Spiel ausgewertet + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + UNIQUE (user_id, match_id) +); + +-- Rangliste (materialized view oder separate Tabelle für Performance) +CREATE MATERIALIZED VIEW leaderboard AS + SELECT + u.id AS user_id, + u.full_name, + u.branch_id, + COALESCE(SUM(p.points_earned), 0) AS total_points, + COUNT(p.id) AS total_predictions, + COUNT(CASE WHEN p.points_earned = 3 THEN 1 END) AS exact_hits, + RANK() OVER (ORDER BY COALESCE(SUM(p.points_earned), 0) DESC) AS rank + FROM users u + LEFT JOIN predictions p ON p.user_id = u.id + GROUP BY u.id, u.full_name, u.branch_id; +``` + +--- + +## 5. API-Design (Backend) + +### 5.1 Endpunkte + +| Method | Endpunkt | Beschreibung | Auth | +|---|---|---|---| +| `GET` | `/api/matches` | Alle Spiele mit Status | JWT | +| `GET` | `/api/matches/:id` | Einzelnes Spiel | JWT | +| `GET` | `/api/predictions/me` | Eigene Tipps | JWT | +| `POST` | `/api/predictions` | Tipp abgeben | JWT | +| `PUT` | `/api/predictions/:id` | Tipp ändern (vor Deadline) | JWT | +| `GET` | `/api/leaderboard` | Gesamtrangliste | JWT | +| `GET` | `/api/leaderboard/me` | Eigene Position | JWT | +| `PUT` | `/api/admin/matches/:id/result` | Ergebnis eintragen | JWT + Admin | +| `POST` | `/api/admin/recalculate` | Punkte neu berechnen | JWT + Admin | +| `GET` | `/api/admin/stats` | Teilnahmestatistiken | JWT + Admin | + +### 5.2 Authentifizierung + +Jeder Request vom Frontend sendet den Staffbase JWT im Header: + +``` +Authorization: Bearer +``` + +Das Backend validiert den Token mit dem `@staffbase/staffbase-plugin-sdk` (RS256, Public Key aus Staffbase Studio). Bei gültigem Token werden Nutzer-ID, Name, Rolle und Locale aus dem Token extrahiert und der Nutzer automatisch in der `users`-Tabelle angelegt (upsert). + +--- + +## 6. Projektphasen & Zeitplan + +### Gesamtlaufzeit: 10 Wochen (April — Mitte Juni 2026) + +### Phase 1 — Setup & Fundament (Woche 1–2) + +| Aufgabe | Verantwortlich | Aufwand | +|---|---|---| +| Staffbase Plugin registrieren (Plugin-ID + Public Key) | Admin | 0,5 Tage | +| Node.js Projekt-Skeleton generieren (`create-staffbase-plugin-nodejs`) | Entwicklung | 0,5 Tage | +| Git-Repository anlegen, CI/CD konfigurieren (GitHub Actions) | Entwicklung | 1 Tag | +| Hosting-Umgebung aufsetzen (Railway/Render) | Entwicklung | 0,5 Tage | +| Supabase-Projekt anlegen, Datenbankschema deployen | Entwicklung | 1 Tag | +| JWT-Authentifizierung implementieren und testen | Entwicklung | 1 Tag | +| football-data.org API-Key beantragen und testen | Entwicklung | 0,5 Tage | + +**Meilenstein:** Authentifizierter API-Call von Staffbase-Plugin zu Backend funktioniert. + +### Phase 2 — Kern-Backend (Woche 3–4) + +| Aufgabe | Verantwortlich | Aufwand | +|---|---|---| +| Spielplan-Synchronisation (football-data.org → DB) | Entwicklung | 2 Tage | +| REST API vollständig implementieren | Entwicklung | 3 Tage | +| Tipp-Logik mit Deadline-Prüfung | Entwicklung | 1 Tag | +| Punkte-Berechnungslogik | Entwicklung | 1 Tag | +| Ranglisten-Abfrage (inkl. Materialized View) | Entwicklung | 1 Tag | +| Unit-Tests für Kernlogik | Entwicklung | 1 Tag | + +**Meilenstein:** Vollständige Backend-API testbar via Postman/API-Client. + +### Phase 3 — Frontend (Woche 5–7) + +| Aufgabe | Verantwortlich | Aufwand | +|---|---|---| +| React-Projekt aufsetzen, Staffbase Client SDK integrieren | Entwicklung | 1 Tag | +| Spielplan-Übersicht (nach Datum / Gruppe filtern) | Entwicklung | 2 Tage | +| Tipp-Eingabeformular mit Validierung | Entwicklung | 2 Tage | +| Rangliste mit Pagination | Entwicklung | 1,5 Tage | +| Admin-Bereich (Ergebnis eintragen, Statistiken) | Entwicklung | 2 Tage | +| Responsive Design (Mobile-first für Staffbase App) | Entwicklung | 1,5 Tage | +| Mehrsprachigkeit DE/EN | Entwicklung | 1 Tag | + +**Meilenstein:** Vollständige App funktionsfähig im Browser (ohne Staffbase-Integration). + +### Phase 4 — Staffbase-Integration & Test (Woche 8–9) + +| Aufgabe | Verantwortlich | Aufwand | +|---|---|---| +| Plugin-URL in Staffbase Studio hinterlegen | Admin | 0,5 Tage | +| iFrame-Test in Staffbase Web und Mobile App | Entwicklung | 1 Tag | +| End-to-End-Tests mit echten Staffbase-Nutzern | Entwicklung + QA | 2 Tage | +| Bugfixing aus Testphase | Entwicklung | 2 Tage | +| Performance-Test (Last: viele gleichzeitige Tipps) | Entwicklung | 1 Tag | +| DSGVO-Check: keine unautorisierten Daten gespeichert | PM/Legal | 1 Tag | + +**Meilenstein:** Plugin läuft stabil in der Staffbase-Staging-Umgebung. + +### Phase 5 — Launch & Betrieb (Woche 10 + laufend) + +| Aufgabe | Verantwortlich | Aufwand | +|---|---|---| +| Go-Live in Staffbase Produktivumgebung | Admin + Entwicklung | 1 Tag | +| Kommunikation an Mitarbeitende (via Staffbase News Channel) | Kommunikation | 0,5 Tage | +| Monitoring & Alerting einrichten (Uptime, Fehler-Logs) | Entwicklung | 1 Tag | +| WM-Start-Phase begleiten (Hotfixes, Support) | Entwicklung | laufend | +| Tägliche Ergebnispflege (manuell oder automatisch) | Admin | laufend | + +--- + +## 7. Ressourcen & Rollen + +| Rolle | Verantwortlichkeiten | Zeitaufwand | +|---|---|---| +| **Projektleiter/PM** | Steuerung, Stakeholder-Kommunikation, Abnahme | ~20% (verteilt) | +| **Fullstack-Entwickler (1–2)** | Backend, Frontend, Deployment, Tests | 100% über 8 Wochen | +| **Staffbase-Admin** | Plugin-Registrierung, Studio-Konfiguration, Rollout | ~5% | +| **Kommunikation** | Launch-Kommunikation, Mitarbeitende informieren | ~5% | +| **QA (optional)** | Testen, Bugmeldungen | ~20% in Phase 4 | + +--- + +## 8. Infrastruktur & Kosten + +### 8.1 Hosting (monatlich, geschätzt) + +| Dienst | Zweck | Kosten/Monat | +|---|---|---| +| **Railway / Render** | Backend-Hosting (Node.js) | ~5–15 € | +| **Supabase** | PostgreSQL (Free Tier reicht für ~500 User) | 0 € (Free) / 25 € (Pro) | +| **football-data.org** | WM-Spielplan API | 0 € (Free Tier) | +| **GitHub** | Code-Repository + CI/CD | 0 € (Free) | +| **Gesamt** | | ~5–40 € / Monat | + +### 8.2 Einmalige Kosten (Entwicklung) + +Gemäß separater Aufwandsschätzung (siehe Projektphasen). Bei internem Entwicklerteam: reine Zeitkosten. Bei externer Vergabe: ca. 15.000–25.000 € (je nach Erfahrungsniveau und Scope). + +--- + +## 9. Risiken & Maßnahmen + +| Risiko | Wahrscheinlichkeit | Auswirkung | Maßnahme | +|---|---|---|---| +| Staffbase JWT-Integration schlägt fehl | Niedrig | Hoch | Frühzeitig in Phase 1 testen, Staffbase Support einbinden | +| football-data.org API unzuverlässig | Mittel | Mittel | Spielplandaten lokal cachen, manuelle Eingabe als Fallback | +| Zu viele gleichzeitige Nutzer (WM-Start) | Mittel | Mittel | Load-Test vor Go-Live, Auto-Scaling auf Hosting-Plattform | +| DSGVO: unbeabsichtigte Datenspeicherung | Niedrig | Hoch | Nur JWT-Daten speichern, kein Tracking, Datenschutzprüfung | +| Entwicklungsrückstand (Urlaube, Krankheit) | Mittel | Mittel | Puffer von 1 Woche eingeplant, MVP-Scope reduzierbar | +| WM-Spielplan ändert sich (Absagen, Verlegungen) | Niedrig | Niedrig | Automatische API-Synchronisation | + +--- + +## 10. Definition of Done (DoD) + +Ein Feature gilt als fertig, wenn: + +1. Funktionalität implementiert und manuell getestet +2. Unit-Tests vorhanden (Backend-Logik) +3. Responsive auf Mobile und Desktop +4. In Staffbase-iFrame getestet (Web + App) +5. Kein Fehler in den Browser-Konsolen-Logs +6. Code reviewed und in `main`-Branch gemergt + +Das Gesamtprojekt gilt als abgenommen, wenn: + +1. Alle MVP-Features (F1–F6) funktionsfähig sind +2. Mind. 20 Test-Nutzer einen Tipp abgegeben haben (Pilottest) +3. Performance-Test bestanden (< 2 Sekunden Ladezeit) +4. DSGVO-Prüfung abgeschlossen und dokumentiert +5. Go-Live in Produktivumgebung erfolgt + +--- + +## 11. Nächste Schritte (Sofortmaßnahmen) + +1. **Plugin bei Staffbase registrieren** → Staffbase Studio → Einstellungen → Plugins → neues Plugin anlegen → Plugin-ID und Public Key notieren +2. **Entwicklungsumgebung aufsetzen** → `npx create-staffbase-plugin-nodejs@latest wm2026-tippspiel` +3. **football-data.org API-Key beantragen** → https://www.football-data.org/client/register +4. **Supabase-Projekt anlegen** → https://supabase.com (EU-Region Frankfurt) +5. **Kickoff-Meeting** mit allen Beteiligten (PM, Entwicklung, Staffbase-Admin, Kommunikation) + +--- + +## Anhang A: Nützliche Links & Ressourcen + +| Ressource | URL | +|---|---| +| Staffbase Developer Portal | https://developers.staffbase.com | +| Staffbase Custom Plugin Übersicht | https://developers.staffbase.com/concepts/customplugin-overview/ | +| Staffbase Plugins Client SDK | https://github.com/Staffbase/plugins-client-sdk | +| Staffbase Node.js SDK | https://github.com/Staffbase/plugins-sdk-nodejs | +| Staffbase Plugin Skeleton Generator | https://github.com/Staffbase/create-staffbase-plugin-nodejs | +| football-data.org API | https://www.football-data.org | +| Supabase | https://supabase.com | +| Railway Hosting | https://railway.app | +| Render Hosting | https://render.com | +| WM 2026 Spielplan (FIFA) | https://www.fifa.com/worldcup | diff --git a/prototyp_wm2026.html b/prototyp_wm2026.html new file mode 100644 index 0000000..adc6f2f --- /dev/null +++ b/prototyp_wm2026.html @@ -0,0 +1,1123 @@ + + + + + + WM 2026 Tippspiel – GEALAN + + + + + +

+
+
GEALAN Tippspiel
+ ⚽ WM 2026 +
+
+
RK
+ Ronny K. + 12 Pkt. +
+
+ + + + + + + +
+
+
+
+
12
+
Meine Punkte
+
+
+
7.
+
Mein Platz
+
+
+
18
+
Tipps abgegeben
+
+
+
4
+
Exakte Treffer
+
+
+ +
+ +
+
+
+ 📅 Nächste Spiele zum Tippen + 3 offen +
+
+
+
+
+
+ +
+
+
📊 Meine letzten Auswertungen
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SpielTippErg.Pkt.
🇩🇪 vs 🇯🇵2:12:13
🇧🇷 vs 🇫🇷1:02:01
🇦🇷 vs 🇲🇽0:12:00
🇵🇹 vs 🇺🇸3:13:13
+
+
+
+
+ + +
+
+
+ 🏆 Aktuelle Rangliste (Top 5) + Alle anzeigen → +
+
+ + + + + + + + + +
#NameTippsExaktPunkte
🥇Sandra W.22621
🥈Markus H.20518
🥉Julia B.21416
4.Thomas K.19415
5.Anna S.20313
+
+ + + + + + + + + +
7.Ronny K. (ich)18412
+
+
+
+
+
+
+ + + + +
+
+
⚽ Spielplan & Tippen
+
+ So funktioniert's: Klicke auf „Tippen" bei einem Spiel, um dein Ergebnis einzugeben. Tipps sind bis zum Anpfiff möglich. Exakter Tipp = 3 Punkte, richtige Tendenz = 1 Punkt. +
+ + +
+ + + + +
+ +
+
+
+ + + + +
+
+
🏆 Gesamtrangliste
+
+ Stand nach Spieltag 3 der Gruppenphase. Rangliste aktualisiert sich automatisch nach Eintrag der Ergebnisse. +
+
+
+ Rangliste — 47 Teilnehmer + nach 22 Spielen +
+ + + + + + + + + + + + + + +
#NameAbt.TippsExaktTendenzPunkteTrend
+
+
+
+ + + + +
+
+
🗂 Gruppenphase WM 2026
+
+ WM 2026 Format: 48 Teams in 12 Gruppen à 4 Teams. Die Top 2 jeder Gruppe sowie die 8 besten Dritten ziehen ins Achtelfinale ein. +
+
+
+
+ + + + + +
+ + + + + diff --git a/prototyp_wm2026_mobile.html b/prototyp_wm2026_mobile.html new file mode 100644 index 0000000..cb4b1d6 --- /dev/null +++ b/prototyp_wm2026_mobile.html @@ -0,0 +1,1650 @@ + + + + + +WM 2026 Tippspiel – Mobile + + + + +
+
+ + +
+ 9:41 + ●●●●○ 5G 🔋 +
+ + +
+
+ GEALAN + ⚽ WM 2026 +
+
RK
+
+ + +
+ + +
+ + +
+
🏆
+
+
FIFA World Cup 2026
+
USA · Kanada · Mexiko
+
+
+
+
Tage noch
+
+
+ + +
+
Willkommen zurück,
+
Ronny Kramer 👋
+
+
+ 12 + Punkte +
+
+ 7. + Platz +
+
+ 4 + Exakt +
+
+ 18 + Tipps +
+
+
+ + +
+
+ Jetzt tippen + Alle → +
+
+
+ + +
+
+ Letzte Ergebnisse +
+
+
+
🇩🇪 vs 🇯🇵
+
+ 2:1 + + 2:1 +
+ +3 Pkt. ✓✓ +
+
+
🇧🇷 vs 🇫🇷
+
+ 1:0 + + 2:0 +
+ +1 Pkt. ✓ +
+
+
🇦🇷 vs 🇲🇽
+
+ 0:1 + + 2:0 +
+ 0 Pkt. ✗ +
+
+
🇵🇹 vs 🇺🇸
+
+ 3:1 + + 3:1 +
+ +3 Pkt. ✓✓ +
+
+
🇪🇸 vs 🇲🇦
+
+ 2:0 + + 1:0 +
+ +1 Pkt. ✓ +
+
+
+ + +
+
+ Rangliste + Alle → +
+
+
+ +
+ + +
+
+
Spielplan & Tippen
+
+
+ + + + +
+
+
+ + +
+
+
Gesamtrangliste
+
+
+
+ + +
+
+
Gruppen WM 2026
+
+
+
+ + +
+
+
RK
+
Ronny Kramer
+
IT · GEALAN Fenster-Systeme
+
🏆 Platz 7 von 47
+
+
+
+
12
+
Punkte
+
+
+
4
+
Exakt
+
+
+
18
+
Tipps
+
+
+
67%
+
Trefferquote
+
+
+
7
+
Tendenz
+
+
+
0.67
+
Pkt/Tipp
+
+
+
+
Tipp-Genauigkeit
+
+ Exakt (3 Pkt.) +
+ 22% +
+
+ Tendenz (1 Pkt.) +
+ 39% +
+
+ Falsch (0 Pkt.) +
+ 39% +
+
+
+ +
+ + + + + +
+
+
+
+
Gruppe A
+
+
+
+
+ 🏳 + +
+ : +
+ 🏳 + +
+
+
+
+ +
1
+ +
+
:
+
+ +
1
+ +
+
+
⏰ Deadline:
+ +
+
+ + +
+ +
+
+ + + + diff --git a/prototyp_wm2026_v2_home.html b/prototyp_wm2026_v2_home.html new file mode 100644 index 0000000..81ca1f8 --- /dev/null +++ b/prototyp_wm2026_v2_home.html @@ -0,0 +1,1272 @@ + + + + + +WM 2026 Tippspiel – Home v2 + + + + +
+ + +
+ 9:41 + ●●●● 5G + 🔋 87% +
+ + +
+
+ + WM 2026 Tippspiel +
+
+ 🔔 +
+
+
+ + +
+ + +
+
🏆
+
The Countdown Begins
+
WM 2026
+
WORLD CUP FINALS
+
+
+
69
+
Days
+
+
:
+
+
14
+
Hrs
+
+
:
+
+
08
+
Min
+
+
:
+
+
33
+
Sec
+
+
+
+ + +
+
+
+
Points
+
12
+
+3 letzte Runde
+
+
+
🏆
+
Global Rank
+
#7
+
Top 15% Overall
+
+
+
🎯
+
Exact Hits
+
4
+
Perfect Predictions
+
+
+
📋
+
Total Tips
+
18
+
von 104 Spielen
+
+
+ + +
+ Upcoming Matches + View All → +
+
+ + +
+
+ Gruppe B · Match 15 +
+
+
+
🇩🇪
+ Germany +
+
+
VS
+
14. Jun
21:00
+
+
+
🇪🇸
+ Spain +
+
+ +
+ + +
+
+ Gruppe C · Match 18 +
+
+
+
🇧🇷
+ Brazil +
+
+
VS
+
15. Jun
18:00
+
+
+
🇫🇷
+ France +
+
+ +
✅ Tipp gespeichert
+
+ + +
+
+ Gruppe A · Match 6 + Live 67' +
+
+
+
🇦🇷
+ Argentina +
+
+
2:0
+
Laufend
+
+
+
🇨🇦
+ Canada +
+
+ +
+ +
+ + +
+ Letzte Ergebnisse +
+
+
+
🇩🇪🇯🇵
+
GER vs JPN
+
+ 2:1 + + 2:1 +
+ +3 Pkt ✓✓ +
+
+
🇧🇷🇫🇷
+
BRA vs FRA
+
+ 1:0 + + 2:0 +
+ +1 Pkt ✓ +
+
+
🇦🇷🇲🇽
+
ARG vs MEX
+
+ 0:1 + + 2:0 +
+ 0 Pkt ✗ +
+
+
🇵🇹🇺🇸
+
POR vs USA
+
+ 3:1 + + 3:1 +
+ +3 Pkt ✓✓ +
+
+ + +
+ Office Top 3 + Full Leaderboard → +
+
+ +
+
+ MH +
2
+
+
Markus H.
+
18 Pkt
+
+ +
+
+
👑
+ SW +
1
+
+
Sandra W.
+
21 Pkt
+
+ +
+
+ JB +
3
+
+
Julia B.
+
16 Pkt
+
+
+ + +
+
+ 4. +
TK
+
+
Thomas K.
+
Einkauf
+
+
+ 15 + +
+
+
+ 5. +
AS
+
+
Anna S.
+
HR
+
+
+ 13 + +
+
+
+ 6. +
KM
+
+
Klaus M.
+
IT
+
+
+ 13 + +
+
+ +
+ 7. +
RK
+
+
Ronny K. (Ich)
+
IT · Aufholjagd! ↑
+
+
+ 12 + +
+
+
+ + +
+
+
+
Punkte sichern!
+
Tippe die nächsten Spiele und klettere nach oben.
+
+ +
+
+ +
+ + + + +
+ + + +