Commit Graph

214 Commits

Author SHA1 Message Date
beo3000 5bd0e19f06 K16 fertig.
Erstellte Dateien:
  - src/Koogle.Application/Interfaces/ICashBookExportService.cs - Interface mit ExportToExcelAsync + ExportToPdfAsync
  - src/Koogle.Application/Services/CashBookExportService.cs - Excel-Export via ClosedXML

  Excel-Struktur (3 Sheets):
  1. Zusammenfassung - Salden, Summen, Statistik
  2. Kategorien - Einnahmen/Ausgaben nach Kategorie
  3. Buchungen - Alle Einträge mit Summenzeile

  NuGet: ClosedXML 0.105.0 hinzugefügt.
2026-01-03 21:59:13 +01:00
beo3000 dfe74adf01 ● K15 fertig. Erstellte Dateien:
- src/Koogle.Web/Components/Pages/CashBook/Reports.razor - UI mit Monat/Jahr-Auswahl, Saldo-Karten, Donut-Charts für Kategorien, expandierbare Buchungsliste
  - src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs - Code-behind

  Route: /cashbook/reports | Policy: ClubViewer

  Export-Buttons (Excel/PDF) zeigen Info-Message - werden in K16/K17 implementiert.
2026-01-03 21:55:02 +01:00
beo3000 05d1622bd4 K14: add Categories UI page + dialog
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 21:49:11 +01:00
beo3000 3a9e891174 K13: add CashBook UI page + entry dialog
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 21:42:55 +01:00
beo3000 e74fb07711 K12: add Fluxor CashBookState
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 21:35:28 +01:00
beo3000 6917fa6bed K11: add Fluxor CategoryState
- CategoryState: categories list, loading, error
- CategoryActions: CRUD + select/clear
- CategoryReducers: state transitions
- CategoryEffects: async service calls
2026-01-03 15:05:29 +01:00
beo3000 595c92df76 K10: create penalty entries on day close
- ICashBookService.CreatePenaltyEntriesForDayAsync
- Groups PersonExpenses by person, sums amounts
- Marks PersonExpenses as Done
- DayService calls after SaveChanges
2026-01-03 15:00:42 +01:00
beo3000 21c3d03c61 K9: seed system categories on club creation
- EnsureSystemCategoriesAsync accepts optional clubId
- ClubService calls seeder after GIF seeding
2026-01-03 14:56:50 +01:00
beo3000 2c09cfa991 K8: add cashbook service implementations
- BookingCategoryService: CRUD + EnsureSystemCategoriesAsync
- CashBookService: entries, balance, reports, membership fees
- CashBookMappingProfile: entity to DTO mappings
- DI registration for both services
2026-01-03 14:53:48 +01:00
beo3000 1a7ba8837d K7: add cashbook service interfaces
- IBookingCategoryService: CRUD + EnsureSystemCategories
- ICashBookService: entries, balance, report, membership fees
2026-01-03 14:45:07 +01:00
beo3000 96c5b4d196 K6: add cashbook DTOs
- BookingCategoryDto, CreateBookingCategoryDto, UpdateBookingCategoryDto
- CashBookEntryDto, CreateCashBookEntryDto, UpdateCashBookEntryDto
- CashBookReportDto, CategorySummaryDto, CreateMembershipFeesDto
2026-01-03 14:39:40 +01:00
beo3000 9c4a6f2ab6 K5: add cashbook repositories
- IBookingCategoryRepository + implementation
- ICashBookEntryRepository + implementation
- GetBalanceAsync calculates income - expense + initial
- ExistsMembershipFeeForMonthAsync for duplicate check

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 14:34:57 +01:00
beo3000 2f2e93ffce K4: add Kassenwart role + policy
- UserRole.Treasurer = "Kassenwart"
- IdentityRoleSeeder: seed Kassenwart role
- ClubRoleRequirement: rank Admin=4, Kassenwart=3
- ClubTreasurer policy in DI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 14:28:45 +01:00
beo3000 9719667cb7 K3: add Kassenbuch migration
- BookingCategories table with unique name per club
- CashBookEntries table with FK to Category, Day, Person
- Club: InitialBalance, MonthlyMembershipFee columns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 14:24:17 +01:00
beo3000 0280e0e05e K2: add EF configurations for cashbook
- BookingCategoryConfiguration with unique index
- CashBookEntryConfiguration with FK relationships
- ClubConfiguration: InitialBalance, MonthlyMembershipFee
- AppDbContext: BookingCategories, CashBookEntries DbSets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 14:21:59 +01:00
beo3000 012548e8db K1: add cashbook domain entities + enums
- BookingCategoryType enum (Income/Expense)
- CashBookEntryType enum (Income/Expense)
- BookingCategory entity
- CashBookEntry entity
- Club: InitialBalance, MonthlyMembershipFee, navigation props

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 14:16:59 +01:00
beo3000 4cee96408b mod design gradient, like on home-page 2026-01-03 10:57:23 +01:00
beo3000 587b92409d added ParticipantsMode.FreeToChoose:
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)
2026-01-03 10:28:13 +01:00
beo3000 39d4629e72 fix switch club:
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
2026-01-02 23:07:48 +01:00
beo3000 3b65f9f1cd fix switch club and RefreshSign 2026-01-02 22:48:00 +01:00
beo3000 7a5580e57d mod default culture de-DE 2026-01-02 21:09:37 +01:00
beo3000 51b8c2dbd4 edit users in club-settings 2026-01-02 20:58:49 +01:00
beo3000 f9e72d095b mod home 2026-01-02 20:22:01 +01:00
beo3000 c2f85cca57 mod home style 2026-01-02 19:49:44 +01:00
beo3000 f0b0d617a5 handling club memberships and creation of clubs:
Neue Dateien erstellt:

  - Domain/Enums/ClubRequestStatus.cs - Enum
  - Domain/Entities/ClubRequest.cs - Entity
  - Infrastructure/Data/Configurations/ClubRequestConfiguration.cs - EF Config
  - Application/DTOs/ClubRequestDto.cs - DTOs
  - Application/Interfaces/IClubRequestService.cs - Interface
  - Application/Services/ClubRequestService.cs - Service
  - Web/Controllers/ClubSwitchController.cs - API
  - Web/Components/Pages/Account/ClubSetup.razor - Club-Setup Seite
  - Web/Components/Shared/ClubSwitcher.razor - NavMenu Dropdown
  - Web/Components/Pages/Admin/ClubRequests.razor - SuperAdmin Freigaben

  Geänderte Dateien:

  - Login/Register: ClubName-Feld entfernt
  - UserService: Auto-Select Logic, SwitchClubAsync
  - AuthController: Routing basierend auf Club-Anzahl
  - MainLayout: ClubSwitcher statt statischer Anzeige
  - AuthState/Effects: AvailableClubs laden

  Flow:

  Login → Clubs vorhanden?
    ├─ Nein → /account/club-setup
    ├─ 1 Club → Auto-Select → /dashboard
    └─ Mehrere → IsDefault wählen → /dashboard
2026-01-02 19:15:30 +01:00
beo3000 028909c24f manage invitations and requests by club-admin 2026-01-02 14:47:09 +01:00
beo3000 7d0c26ad0c mod mailtext 2026-01-02 14:01:27 +01:00
beo3000 c326541604 mod mailtext 2026-01-02 14:00:13 +01:00
beo3000 f5c8e07730 add reset password mail 2026-01-02 13:58:28 +01:00
beo3000 7ba1307bac fix typo 2026-01-01 20:15:13 +01:00
beo3000 c3b71bcbec mod home-page 2026-01-01 20:13:28 +01:00
beo3000 b163fb30c7 send day protocol by email 2026-01-01 20:04:55 +01:00
beo3000 0d8289563d add email service:
Neue Dateien

  - src/Koogle.Domain/Interfaces/IEmailService.cs - Interface
  - src/Koogle.Infrastructure/Configuration/SmtpSettings.cs - SMTP-Konfiguration
  - src/Koogle.Infrastructure/Services/SmtpEmailService.cs - MailKit-basierte Implementierung

  Geänderte Dateien

  | Datei                  | Änderung                                    |
  |------------------------|---------------------------------------------|
  | Club.cs                | + SenderEmail Property                      |
  | Person.cs              | + UserProfileId + Navigation zu UserProfile |
  | ClubConfiguration.cs   | + SenderEmail Config                        |
  | PersonConfiguration.cs | + UserProfileId FK + Index                  |
  | DayService.cs          | + E-Mail-Versand bei Day-Close              |
  | ClubDto.cs             | + SenderEmail in allen DTOs                 |
  | PersonDto.cs           | + UserProfileId                             |
  | DependencyInjection.cs | + SmtpSettings + SmtpEmailService           |
  | appsettings.json       | + SmtpSettings Section                      |

  Gelöschte Dateien

  - StubEmailService.cs
  - IEmailService.cs (Application → Domain verschoben)

  Migration

  - AddEmailFeatures - Neue Spalten SenderEmail (Clubs) und UserProfileId (Persons)

  Konfiguration (appsettings.json)

  "SmtpSettings": {
    "Host": "smtp.example.com",
    "Port": 587,
    "Enabled": false  // Auf true setzen um E-Mails zu aktivieren
  }

  Funktionen

  1. Club-spezifische Absender-Adresse - Club.SenderEmail (Fallback: DefaultSenderEmail)
  2. Spieltag-Protokoll - HTML-E-Mail mit Tabelle der Strafen pro Teilnehmer
  3. Empfänger - Nur Teilnehmer mit verknüpftem UserProfile
  4. Error Handling - Log & Continue (Day-Close schlägt nicht fehl)
2026-01-01 19:13:05 +01:00
beo3000 bd8a562183 del former KoogleApp 2026-01-01 14:55:33 +01:00
beo3000 49463ba6cf typo 2026-01-01 14:52:50 +01:00
beo3000 c25fa0c0ec mod Club-Editor for demo 2026-01-01 14:42:11 +01:00
beo3000 fc9dfcaca0 added club-settings:
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)
2026-01-01 14:34:02 +01:00
beo3000 4666056e24 add expenses, on day closing
1. Neue Methode CreateAbsentMemberExpensesAsync() (Zeile 322-421)
    - Prüft Club.ExpenseCalculation (None → skip)
    - Berechnet Average/Maximum aller Tages-Expenses
    - Findet Absent-Expense via ExpenseTriggerType.Absent
    - Erstellt PersonExpense für alle abwesenden Members
  2. Aufruf in AdvanceStatusAsync() (Zeile 312-316)
    - Wird aufgerufen wenn Status → Closed wechselt
    - Vor SaveChangesAsync() → eine Transaktion
2026-01-01 14:18:34 +01:00
beo3000 8fd8c37952 added Home-Advertising page 2026-01-01 13:23:49 +01:00
beo3000 504daec8d7 fix ro media folder on linux:
1. IMediaStorageService (src/Koogle.Domain/Interfaces/):
  - Neue Methoden GetMediaBasePath() und GetFilePath() hinzugefügt

  2. MediaStorageService (src/Koogle.Infrastructure/Services/):
  - Erkennt Production+Linux → verwendet /home/data/club-media
  - Development → verwendet wwwroot/club-media

  3. Program.cs (src/Koogle.Web/):
  - StaticFileOptions für /club-media aus /home/data/club-media in Production

  4. DemoSeeder (src/Koogle.Infrastructure/Data/):
  - Alle Methoden nutzen jetzt IMediaStorageService statt hardkodierte Pfade

  5. ClubGifService (src/Koogle.Application/Services/):
  - Alle Path.Combine("wwwroot", ...) durch _mediaStorage.GetFilePath() ersetzt

  Das Verhalten:
  - Windows/Development: weiterhin wwwroot/club-media
  - Linux/Production (Azure): /home/data/club-media (persistent und beschreibbar)
2026-01-01 12:21:17 +01:00
beo3000 2f1a2a159b mod Submit Page 2026-01-01 11:03:36 +01:00
beo3000 3b12a82982 submit gifs by URL:
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
2026-01-01 10:58:02 +01:00
beo3000 968026729d fix UI details 2025-12-31 18:23:15 +01:00
beo3000 6b2715cec9 added deplayment-scripts
fix club-permissions
2025-12-31 18:08:32 +01:00
beo3000 6c82d16ddd fix comment 2025-12-30 21:06:06 +01:00
beo3000 49a03a8fbb fix Bell-Handling 2025-12-30 20:37:21 +01:00
beo3000 6bc79b0102 fix bellValue
Zusammenfassung des Problems:
  - BellValue wurde in Zeile 357 auf false zurückgesetzt, BEVOR es für Trigger ausgewertet wurde
  - afterThrowState.ThrowPanel.BellValue war dadurch immer false
  - Bell-Expenses und Bell-GIFs wurden nie ausgelöst

  Lösung:
  - bellValue wird jetzt als separater Parameter aus action.AfterThrowState.BellValue übergeben
  - Die Auswertung erfolgt mit dem ursprünglichen Wert, bevor er zurückgesetzt wird
2025-12-30 20:19:30 +01:00
beo3000 3af02d2d0a gifs 2025-12-30 20:15:49 +01:00
beo3000 dd4317bf80 fix Seeder, QR-Copde Copy Clipboard 2025-12-30 18:21:46 +01:00
beo3000 9aee6b50c0 add gif-Seeder and gif-Templates:
Zusammenfassung der Änderungen

  Template-GIF Seeding implementiert:

  1. DemoSeeder.cs aktualisiert:
  - SeedTemplateGifsAsync() - Öffentliche Methode für externes Seeding
  - SeedTemplateGifsInternalAsync() - Kopiert GIFs vom Template-Ordner und erstellt DB-Einträge
  - DeleteClubGifsAsync() - Löscht GIF-Dateien und DB-Einträge beim Reset
  - ResetDemoClubAsync() - Löscht nun auch GIFs und seedet sie neu
  - SeedAsync() - Seedet Template-GIFs beim Demo-Club-Setup

  2. IClubGifService.cs erweitert:
  - SeedTemplateGifsAsync(Guid clubId) - Neue Interface-Methode

  3. ClubGifService.cs erweitert:
  - SeedTemplateGifsAsync() implementiert - Liest giftemplates.json, kopiert Dateien und erstellt ClubGif-Einträge

  4. ClubService.cs aktualisiert:
  - Injiziert IClubGifService
  - CreateAsync() seedet automatisch Template-GIFs für neue Clubs

  Ablauf:

  1. Neuer Club: Template-GIFs werden automatisch kopiert nach wwwroot/club-media/{LoginName}/gifs/
  2. Demo-Reset: Alte GIFs werden gelöscht, Templates neu kopiert
  3. Demo-Seed: Template-GIFs werden geseedet falls noch keine vorhanden

  Template-Struktur:

  wwwroot/club-template/gifs/
  ├── giftemplates.json        # Definition der GIFs
  ├── 44f5bc11-...-d1215.gif   # Strike GIF
  └── 297970f3-...-a2a93.gif   # NoWood GIF
2025-12-30 18:00:11 +01:00
beo3000 a3a5c288a1 added giftype NoWood 2025-12-30 17:43:10 +01:00
beo3000 752523fe78 added gif-Feature:
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
2025-12-30 14:57:06 +01:00
beo3000 0e4e1530ed added LoginName for Clubs 2025-12-30 11:57:01 +01:00
beo3000 965ec24d33 change participants when creating a new day 2025-12-30 10:09:34 +01:00
beo3000 e75ea84e66 fix 401 authorize problem 2025-12-29 17:16:44 +01:00
beo3000 6f419b3373 SinalR Sync part 2:
DayDetails.razor

  1. GameHubService injiziert (Zeile 26)
  2. IAsyncDisposable implementiert statt IDisposable
  3. SignalR-Events abonniert:
    - OnGameStateUpdated → aktualisiert lokalen State via RemoteGameStateUpdatedAction
    - OnThrowRecorded → zeigt Snackbar-Benachrichtigung
    - OnGameStarted → lädt aktives Spiel neu
    - OnGameEnded → lädt abgeschlossene Spiele neu
  4. Hub-Verbindung initialisiert in OnAfterRenderAsync:
    - StartAsync() - verbindet zum Hub
    - JoinDayAsync(DayId) - tritt der Day-Gruppe bei
    - JoinGameAsync() - tritt der Game-Gruppe bei (wenn aktiv)
  5. Automatisches Join/Leave bei Spielwechsel via OnGameStateChanged
  6. Cleanup in DisposeAsync:
    - Unsubscribe von Events
    - LeaveGameAsync/LeaveDayAsync aufrufen
    - Hub-Verbindung disposen

  GameEffects.cs

  BroadcastThrowAsync hinzugefügt (Zeile 451-458) - broadcast Wurf sofort an andere Clients

  GameActions.cs / GameReducers.cs

  RemoteGameStateUpdatedAction und Reducer hinzugefügt für Remote-State-Updates
2025-12-29 17:13:17 +01:00
beo3000 7cbdfe28e6 fix Typo 2025-12-29 16:14:51 +01:00
beo3000 1fbf4763d9 add dayStatisticChart 2025-12-29 16:11:22 +01:00
beo3000 21130895d4 add statistics:
Erfasste Metriken:
  - ThrowCount, PinCount, ClearedCount, GutterCount, CircleCount, StrikeCount, BellCount

  Widget zeigt:
  - Jahres-Übersicht (Spiele, Würfe, Kegel, Durchschnitt)
  - Top 5 Kegler Rangliste
  - Monatstrend-Tabelle
2025-12-29 15:27:25 +01:00
beo3000 f5d2ceb628 fix unit tests 2025-12-29 14:33:53 +01:00
beo3000 e2d1792cec fix deathbox, first player starts with a X 2025-12-29 09:59:20 +01:00
beo3000 7795c3064f fix deathbox handling 2025-12-28 22:43:21 +01:00
beo3000 f75b08d98d fix Gutte in Decrease-Mode 2025-12-28 21:51:35 +01:00
beo3000 62000e1291 fix PlayerOrder DeathBox 2025-12-28 21:42:13 +01:00
beo3000 cafd38519f add deathbox 2025-12-28 21:25:04 +01:00
beo3000 83b0e6a91b expense handling from gamelogic:
Problem: HandleRecordThrow ignorierte throwResult.Triggers aus ProcessThrow - speziell die ExpensePoint-Trigger vom Scheißspiel für Verlierer.

  Lösung:

  1. HandleRecordThrow (Zeile 358, 370, 427-436):
    - Neue Variable gameLogicTriggers
    - Zuweisung aus throwResult.Triggers
    - Aufruf von FireGameLogicTriggersAsync
  2. HandleExecuteGameAction (Zeile 457, 497-506):
    - Auf async geändert
    - Verarbeitet result.Triggers für Game-Actions wie "Passen"
  3. Neue Methode FireGameLogicTriggersAsync (Zeile 985-1058):
    - Parst TriggerType als ExpenseTriggerType
    - Ruft IGameEventService.RegisterExpensePointsAsync für ExpensePoint-Trigger (mit Multiplier)
    - Ruft IGameEventService.RegisterEliminatedAsync für Eliminated-Trigger
    - Dispatcht TriggerExpensesCreatedAction für UI-Update
2025-12-28 17:41:50 +01:00
beo3000 b8bd1b0939 fix ShitGameLogic 2025-12-28 17:23:10 +01:00
beo3000 fc97a266d4 create and assign expenses:
Datenfluss bei Gutter:
  Spieler wirft Gosse
    → RecordThrowAction (IsGutter=true)
    → HandleRecordThrow
    → FireThrowTriggersAsync
      → GameEventService.RegisterGutterAsync
        → TriggerService.FireTriggerAsync
          → PersonExpense in DB gespeichert ✓
          → PersonExpenseDto zurückgegeben
      → dispatcher.Dispatch(TriggerExpensesCreatedAction)
        → DayReducer fügt Expenses zu SelectedDayExpenses hinzu
          → UI aktualisiert sich automatisch ✓
2025-12-28 15:41:57 +01:00
beo3000 dbb59ed54f fix endOfGame:
Zusammenfassung der Änderungen:

  1. GameState.cs - Neue Properties hinzugefügt:
    - IsGameOver - zeigt an, dass das Spiel beendet ist
    - WinnerId - ID des Gewinners
  2. GameReducers.cs:
    - OnProcessThrowResult setzt jetzt IsGameOver und WinnerId
    - OnExecuteGameActionSuccess setzt jetzt IsGameOver und WinnerId
    - OnStartGameSuccess setzt IsGameOver=false
    - OnEndGameSuccess setzt IsGameOver=false
  3. GameEffects.cs:
    - HandleRecordThrow blockiert Eingaben wenn IsGameOver=true
    - HandleExecuteGameAction blockiert Eingaben wenn IsGameOver=true
    - EndGameAction wird nicht automatisch dispatcht - der Benutzer muss das Spiel explizit über die UI beenden

  Verhalten jetzt:
  - Wenn ProcessThrow oder ExecuteAction IsGameOver=true zurückgibt, wird der State auf IsGameOver=true gesetzt
  - Die Tafel bleibt sichtbar mit dem Endergebnis
  - Weitere Würfe/Actions werden blockiert
  - Der Benutzer muss EndGameAction explizit über einen Button in der UI auslösen

  Die UI muss jetzt GameState.IsGameOver und GameState.WinnerId nutzen, um:
  1. Eingaben zu deaktivieren
  2. Eine "Spiel beenden"-Schaltfläche anzuzeigen
2025-12-28 14:09:21 +01:00
beo3000 5c088345b3 add demo-club:
Neue Dateien (5)

  | Datei                                                  | Zweck                |
  |--------------------------------------------------------|----------------------|
  | src/Koogle.Infrastructure/Data/DemoSeeder.cs           | Seeder + Reset-Logik |
  | src/Koogle.Domain/Interfaces/IDemoResetService.cs      | Interface            |
  | src/Koogle.Infrastructure/Services/DemoResetService.cs | Service              |
  | src/Koogle.Web/Store/Demo/DemoActions.cs               | Fluxor Actions       |
  | src/Koogle.Web/Store/Demo/DemoEffects.cs               | Fluxor Effects       |

  Geaenderte Dateien (6)

  | Datei                        | Aenderung                     |
  |------------------------------|-------------------------------|
  | appsettings.Development.json | Demo-Config hinzugefuegt      |
  | Program.cs                   | DemoSeeder.SeedAsync() Aufruf |
  | DependencyInjection.cs       | IDemoResetService registriert |
  | Login.razor                  | Demo-Hinweis-Box              |
  | Clubs.razor                  | Reset-Button + Confirm-Dialog |

  Demo-Daten

  - User: demo@koogle.de / demo123 (ClubAdmin, kein SuperAdmin)
  - Club: "Demo"
  - 8 Mitglieder: Hans Maier, Klaus Schmidt, Werner Braun, Dieter Fischer, Juergen Weber, Heinz Mueller, Rolf Schneider, Karl Hoffmann
  - 2 Gaeste: Stefan Gast, Thomas Besucher
  - 10 Expenses mit Trigger-Zuordnung (Gosse 0.50, Pudel 0.30, ... Abwesenheit 5.00)
  - 3 Spieltage mit Games
2025-12-28 12:10:15 +01:00
beo3000 fcfbbae94e fix CurrentClub Id
Änderungen:

  1. UserProfile.cs:47-52 - Neues Feld CurrentClubId (nullable FK zu Club)
  2. CustomClaimsPrincipalFactory.cs:38-50 - Lädt jetzt CurrentClub und setzt current_club_id/current_club_name Claims bei jeder Cookie-Erneuerung
  3. UserService.cs:113-118 - Speichert CurrentClubId bei Login, nutzt jetzt einfaches SignInAsync (Claims kommen via Factory)
  4. Migration AddCurrentClubIdToUserProfile erstellt + angewandt

  Ablauf jetzt:
  - Login → CurrentClubId wird in DB gespeichert
  - Cookie-Erneuerung → CustomClaimsPrincipalFactory liest CurrentClubId aus DB → Claims bleiben erhalten
2025-12-28 11:25:30 +01:00
beo3000 44b04ec2de dev Trigger / Expense Masterdata, Seeder for triggers 2025-12-28 11:16:51 +01:00
beo3000 954c2589e6 added Trigger and Trigger/Expense Releations 2025-12-28 10:18:39 +01:00
beo3000 22fbb79801 added GameAction Framework 2025-12-28 08:34:23 +01:00
beo3000 82c6c2d91d mod layout 2025-12-27 22:54:51 +01:00
beo3000 37b8fc0e8d add: IsCleared 2025-12-27 22:46:54 +01:00
beo3000 125226127d add appsettings, undo/redo without saving 2025-12-27 22:11:30 +01:00
beo3000 03364291aa fix undo/redo handling 2025-12-27 21:46:20 +01:00
beo3000 191fb3db3f fix undo/redo handling 2025-12-27 20:51:03 +01:00
beo3000 0a7fb1f0af del UndoButton 2025-12-27 19:31:44 +01:00
beo3000 ea08ff9a5b fix throwhandle:
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.
2025-12-27 19:14:36 +01:00
beo3000 e50b13f092 add undo/redo 2025-12-27 18:23:24 +01:00
beo3000 48b96cec0c fix gutter 2025-12-27 16:59:07 +01:00
beo3000 6596dc32f2 add SignalR hub for real-time game updates
- IGameHubClient: client interface for SignalR messages
- GameHub: SignalR hub with group management for games/days
- GameHubService: scoped service wrapper with auto-reconnect
- GameEffects: broadcasts game start/end/state via SignalR
- EndGameAction: extended with winner info and final scores
- Program.cs: AddSignalR + MapHub configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 16:37:05 +01:00
beo3000 c10329c468 fix: use DayState.AvailablePersons in game boards
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 14:51:39 +01:00
beo3000 fcb46ff387 fix next player rotation 2025-12-27 14:44:33 +01:00
beo3000 f297317b71 call gamelogic 2025-12-27 14:39:15 +01:00
beo3000 c33a6f9d91 add ThrowTimer 2025-12-27 14:18:45 +01:00
beo3000 5db650f2b2 fix "kein Holz" 2025-12-27 14:07:35 +01:00
beo3000 061ba4dbdb fix decrease mode 2025-12-27 14:01:32 +01:00
beo3000 aa8cf4d83a refactoring gameinput 2025-12-27 13:42:32 +01:00
beo3000 92dfb47a08 refactoring GameSetup 2025-12-27 13:15:18 +01:00
beo3000 f1880f08a8 save setup model 2025-12-27 11:45:19 +01:00
beo3000 65df43ed23 fix load game OnInitialized and OnParametersSet 2025-12-27 10:59:59 +01:00
beo3000 9f53aa0b04 fix set club guid 2025-12-27 10:54:21 +01:00
beo3000 a84b192055 Complete phase H9: DB Persistence & Recovery
- Extend Game entity with GameType, Status, StartedAt, CompletedAt, RowVersion
- Add GameConfiguration with RowVersion for optimistic concurrency
- Create IGamePersistenceService interface
- Implement GamePersistenceService with CRUD operations
- Create GameStateSerializationDto for JSON serialization
- Extend GameEffects with full persistence lifecycle:
  - HandleStartGame: Creates game in DB
  - HandleEndGame: Updates status to Completed/Aborted
  - HandleLoadActiveGame: Recovery from page reload
  - HandleRecordThrow: Debounced save (500ms)
  - HandleSaveGameState: Explicit save with concurrency check
- Add migration ExtendGameEntity

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 09:23:58 +01:00
beo3000 e436ee2754 Complete phase H8: Undo functionality (unbegrenzt)
- 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>
2025-12-27 09:11:40 +01:00
beo3000 aefa676d62 Fix: Register GameLogicServices as transient
Scoped services can't be resolved from singleton (GameDefinitionRegistry).
Logic services are stateless, so transient is appropriate.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 09:03:50 +01:00
beo3000 23a1008a31 Complete phase H7: DayDetails Tabs Integration
- 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>
2025-12-27 08:33:34 +01:00
beo3000 5afc8fd251 Complete phase H6: Game Setup Dialog
- 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>
2025-12-27 08:24:52 +01:00