1. ClubService.cs:134 - MonthlyMembershipFee in UpdateAsync hinzugefügt
2. Settings.razor:358-364 - Laden der Kassenbuch-Einstellungen beim Init
3. Settings.razor:665-689 - SaveCashBookSettings() Methode implementiert
Der Kassenbuch-Tab in den Vereins-Einstellungen funktioniert jetzt:
- Zeigt aktuellen Kontostand
- Erlaubt Ändern des monatlichen Mitgliedsbeitrags
- Speichert Änderungen via Fluxor-Action
- Quick-Links zu Kassenbuch, Kategorien, Berichten
Neue Datei erstellt:
- src/Koogle.Web/Components/Game/PlayerSelectorDialog.razor - Dialog zur manuellen Spielerauswahl
Geändert:
- src/Koogle.Web/Components/Pages/Days/DayDetails.razor:863-891 - ShowPlayerSelector implementiert
Funktionsweise:
1. Bei ParticipantsMode.FreeToChoose zeigt GameInputPanel.razor:23-32 den Button "Spieler wechseln"
2. Der neue Dialog listet alle Spieler aus Participants.PlayerIds
3. Bei DeathBox werden ausgeschiedene Spieler standardmäßig ausgeblendet (Toggle verfügbar)
4. Bei Spielerauswahl wird SetCurrentPlayerAction dispatcht
Features:
- Aktueller Spieler ist markiert (grün)
- Ausgeschiedene Spieler sind durchgestrichen mit "Ausgeschieden"-Badge
- Optional können ausgeschiedene Spieler eingeblendet werden (falls nötig)
- Funktioniert mit allen Game-Typen (DeathBox, Shit, Training)
Problem: RefreshSignInAsync in UserService.SwitchClubAsync (Zeile 719) setzte zwar den neuen Cookie, aber HttpClient.PostAsJsonAsync empfing diesen Cookie nur intern - der Browser bekam ihn nie.
Lösung: Form-POST statt HttpClient-API-Call, wie bei Login/Logout.
Änderungen:
1. ClubSwitcher.razor - Komplett überarbeitet:
- HttpClient durch natives <form method="post"> ersetzt
- AntiForgery-Token manuell gesetzt (wie LogoutButton)
- Kein JavaScript/Client-Code mehr nötig
2. AuthController.cs (Zeile 146):
- [FromBody] → [FromForm]
- [ValidateAntiForgeryToken] aktiviert
Ablauf jetzt:
1. User klickt auf Club im Dropdown
2. Form-POST an /auth/switch-club
3. Controller ruft SwitchClubAsync → DB-Update + RefreshSignInAsync
4. LocalRedirect("/dashboard") → Browser erhält neuen Cookie direkt
5. Claims sind beim Reload korrekt
Neue Datei: src/Koogle.Web/Components/Pages/Settings.razor
- Route /settings mit ClubAdmin-Policy
- Zeigt Name, LoginName, Kostenberechnung
- "Bearbeiten" Button öffnet bestehenden ClubEditDialog
- Kein Löschen, keine Neuanlage
Geändert: src/Koogle.Web/Components/Layout/NavMenu.razor
- Neuer Link "Vereins-Einstellungen" in "Stammdaten"-Gruppe (Icon: Tune)
UI (Submit.razor)
- Toggle zwischen "Datei" und "URL" Modus
- URL-Eingabefeld mit Validierung (nur HTTP/HTTPS)
- Beide Modi teilen Submit-Button und Erfolgsanzeige
Service (IClubGifService, ClubGifService)
- Neue Methode SubmitAnonymousFromUrlAsync(token, url, name)
- Nutzt bestehende SaveGifFromUrlAsync von MediaStorageService
Serverseitige Validierung (bereits in MediaStorageService vorhanden):
- Content-Type Prüfung: nur image/gif, video/mp4, video/webm
- Dateigröße max. 20MB
- Datei wird nach Download nochmals auf Größe geprüft
- Ungültige Content-Types werden mit Exception abgelehnt
Summary of Changes:
Phase 1-3 (Domain/Infrastructure/Application):
- ThrowEventType enum with Strike, Circle, Bell, Gutter flags
- ClubGif, ClubGifRating, GifSubmissionToken entities
- EF configurations with proper indexes
- ClubGifRepository with weighted random selection
- MediaStorageService for file storage (wwwroot/club-media/{LoginName}/gifs/)
- ClubGifService with rating system and auto-disable at -5 threshold
Phase 4-5 (Admin UI & Playback):
- Admin page at /club/{ClubId}/admin/gifs with Upload, Import, Edit, Token management
- QR code generation for submission tokens
- GifPlayer component with overlay, rating buttons, and dismiss
- Fluxor state management (GifState, actions, reducers, effects)
Phase 6 (Anonymous Submission):
- Public page at /gif/submit/{Token} with EmptyLayout
- Token validation and file upload
Phase 7 (Game Integration + Migration):
- Database migration AddClubGifFeature applied
- GameEffects.TriggerGifForEventsAsync triggers GIFs on special throws
- SignalR broadcast via BroadcastGifTriggeredAsync to all clients
- DayDetails.razor includes GifPlayer and handles SignalR GIF events
Key Features:
- GIFs play on Strike (alle 9), Circle (Kranz), Bell (Glocke), Gutter (Rinne)
- Weighted random selection based on ratings
- Real-time sync across all connected clients via SignalR
- Thumbs up/down rating with auto-disable at -5
- Max file size: 20MB, Video duration: 15s
- Supports GIF, MP4, WebM formats
Zusammenfassung der Fixes:
1. Fix 1 (HandlePinClick und HandleNumberClick): _hasModifiedPins = true wird jetzt VOR dem Dispatch gesetzt, um zu verhindern, dass OnGameStateChanged den Before-State während der Pin-Modifikation überschreibt.
2. Fix 2 (ConfirmThrow): Nach dem Dispatch wird explizit CaptureBeforeThrowState() aufgerufen. Das ist der wichtigste Fix:
- Nach RecordThrowAction Dispatch sind alle Reducer/Effects durchgelaufen
- Der State ist bereits mit den neuen Pin-Zuständen aktualisiert
- _beforeThrowState wird mit dem NEUEN Zustand für den nächsten Wurf erfasst
Vorher wurde _beforeThrowState nie nach einem Wurf aktualisiert (weil _hasModifiedPins zum falschen Zeitpunkt gesetzt war), sodass beim zweiten Wurf der stale Before-State verwendet wurde.
- Add UndoButton.razor as reusable component
- Refactor GameInputPanel to use UndoButton component
- Enhance GameEffects with proper UndoThrowAction handler
- IState injection for state access in effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- GameBoardPanel: Dynamic board component rendering
- CompletedGamesList: Shows game history for day
- DayDetails: 3 tabs (Details/Eingabe/Tafel) + Start/End Game buttons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- GameTypeSelector: Select game type from registry
- ParticipantSelector: Multi-select participants from day
- CommonSetupOptions: ThrowMode, ThrowsPerRound, ParticipantsMode
- GameSetupDialog: Full setup wizard with DynamicComponent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- TrainingGameDefinition with game type metadata
- TrainingGameModel + TrainingPlayerStats for stats tracking
- TrainingGameLogicService implementing game logic
- TrainingSetup.razor for config (ThrowMode, ThrowsPerRound, ParticipantsMode)
- TrainingBoard.razor showing player stats table with totals/averages
- Game type registration in Program.cs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- new PendingMembershipsWidget.razor shows pending membership count
- only visible to club admins/super admins
- integrated into Dashboard.razor
- click navigates to admin users page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Dashboard link (all authenticated users)
- Spieltage, Personen (club-specific, require selected club)
- Stammdaten with Kostenvorlagen (ClubEditor+)
- Administration section with Vereine/Benutzer (SuperAdmin)
- Profile link for authenticated users
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- DayEvaluationComponent: shows day expense summary per person
- PersonEvaluationComponent: shows person expense summary + details
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- DashboardDto, IDashboardService, DashboardService
- Summary cards: members, guests, days, open expenses
- Recent days list with navigation
- Top penalty recipients list
- Home redirects to /dashboard
- Updated HomePageTests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extend DayState with SelectedDayExpenses, AvailableExpenses
- Add Fluxor actions for load/create/update/delete expenses
- Add reducers + effects for PersonExpense operations
- Create AddPersonExpenseDialog with inverse expense support
- Update DayDetails with expense table, summary, status toggle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Persons.razor: MudTable with search/filter, CRUD dialogs
- PersonEditDialog.razor: Create/Edit form for persons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Clubs.razor: MudTable with search, create/edit/delete
- ClubEditDialog.razor: Form for club name + expense calculation
- ConfirmDialog.razor: Reusable confirmation dialog
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>