# Koogle App - Analyse & Vorschlag für Bereiche und Pages ## Zweck der Anwendung (abgeleitet aus Datenmodell) **Koogle** ist eine Vereinsverwaltung für Kegelvereine mit Schwerpunkt auf: ### Kernfunktionen 1. **Vereinsverwaltung**: Multi-Mandanten-System, jeder Verein = eigener Scope 2. **Mitglieder & Gäste**: Verwaltung von Personen mit Status (Member/Guest) 3. **Spieltagsorganisation**: Planung und Durchführung von Kegeltagen (Days) - Status-Workflow: New → Started → Closed (oder Postponed) - Zuordnung von Teilnehmern pro Spieltag 4. **Spielverwaltung**: Mehrere Games pro Day möglich - JSON-basierte Spielstände (GameData) - Teilnehmerzuordnung pro Game 5. **Kostenmanagement**: - Vordefinierte Kosten/Strafen (Expenses) pro Verein - Automatische Trigger (z.B. Pudel, Pumpe, Aus, Kranz, etc.) - Variable/Fixe Preise, Inverse Kosten (alle außer einem zahlen) - One-Click Kosten für schnelle Erfassung 6. **Abrechnung**: - Pro Person, pro Tag, pro Spiel - Berechnungsmethoden: None, Average, Maximum (für fehlende Personen) - Status: Open/Done für PersonExpenses 7. **Benutzer & Berechtigungen**: - ASP.NET Identity mit Custom UserProfile - Rollen pro Verein: Viewer, Editor, Admin - SuperAdmin für vereinsübergreifende Verwaltung - Multi-Club Zugehörigkeit möglich --- ## gerenelle Implementierungshinweise - Der IMPLEMENTATION_PLAN ist in Deutscher Sprache formuliert. - Die Anwendung wird in deutscher Sprache implementiert. Alle Klassen, Methoden, Interfaces und sonstigen Artikefakte werden dennoch in englischer Sprache benannt. Auch die XMLDOC Kommentare sind Englisch zu formulieren. - Der Anwender wird vom Programm in lockerem Ton und in der Du-Form angesprochen. ## Bestehende Implementierung ### Vollständig implementiert - ✅ Authentication/Authorization Framework - ✅ Login mit Vereinsauswahl - ✅ Fluxor State Management (AuthState) - ✅ Rollenbasierte Berechtigungen - ✅ MudBlazor UI Framework - ✅ Dual DbContext (Domain + Identity) - ✅ Clean Architecture Struktur - ✅ Seeders (SuperAdmin, Roles) ### Teilweise implementiert - ⚠️ DayService (vorhanden, aber auskommentiert) - ⚠️ Navigation (Skelett vorhanden, kaum Menüpunkte) ### Nicht implementiert - ❌ UI für Clubs, Days, Games, Persons, Expenses - ❌ Services für Person, Game, Expense - ❌ DTOs für die meisten Domain Entities - ❌ Fluxor States für Domain-Daten - ❌ Reporting/Export-Funktionalität --- ## Vorgeschlagene Bereiche & Pages ### 1. DASHBOARD-BEREICH (Startseite) **Route:** `/` oder `/dashboard` **Zweck:** Übersicht über aktuelle Aktivitäten im ausgewählten Verein **Pages:** - **Dashboard.razor**: - Nächste geplante Spieltage - Aktuelle offene Kosten - Quick-Actions (neuer Spieltag, neue Person) - Statistiken (Anzahl Mitglieder, Gäste, offene Abrechnungen) --- ### 2. SPIELTAG-BEREICH (Day Management) **Route:** `/days` **Pages:** - **DayList.razor** (`/days`): - Tabelle: PostDate, Status, Teilnehmeranzahl - Filter: Jahr, Monat, Status - Actions: Neu, Bearbeiten, Löschen, Schließen - **DayDetail.razor** (`/days/{id}`): - Spieltag-Info (Datum, Status) - Teilnehmerliste mit Anwesenheit - Games des Tages - PersonExpenses des Tages (Übersicht) - Actions: Teilnehmer hinzufügen, Spiel hinzufügen, Status ändern - **DayCreate.razor** (`/days/new`): - Formular: Datum, Vorauswahl Teilnehmer aus Mitgliedern - **DayEdit.razor** (`/days/{id}/edit`): - Datum ändern, Teilnehmer hinzufügen/entfernen --- ### 3. SPIEL-BEREICH (Game Management) **Route:** `/days/{dayId}/games` **Pages:** - **GameList.razor** (`/days/{dayId}/games`): - Liste der Spiele eines Spieltags - Actions: Neues Spiel, Bearbeiten, Löschen - **GameDetail.razor** (`/days/{dayId}/games/{gameId}`): - GameData anzeigen/bearbeiten (JSON-Editor oder strukturierte Eingabe) - Teilnehmer des Spiels - Kosten des Spiels (PersonExpenses mit GameId) - Trigger-Events erfassen (z.B. "Pudel" → Expense zuweisen) - **GameCreate.razor** (`/days/{dayId}/games/new`): - Teilnehmer auswählen (aus DayPersons) - Optionale GameData-Initialisierung --- ### 4. KOSTEN-BEREICH (Expense Management) **Route:** `/expenses` **Pages:** - **ExpenseList.razor** (`/expenses`): - Vordefinierte Expenses des Vereins - Spalten: Name, Preis, Typ, IsOneClick, IsInverse, IsVariable - Actions: Neu, Bearbeiten, Löschen - **ExpenseCreate.razor** (`/expenses/new`): - Formular für neue Expense-Vorlage - Trigger-Zuordnung optional - **PersonExpenseList.razor** (`/expenses/assigned`): - Alle zugewiesenen Kosten (PersonExpenses) - Filter: Person, Tag, Status (Open/Done), Datum - Bulk-Actions: Als bezahlt markieren, Löschen - **ExpenseTriggerConfig.razor** (`/expenses/triggers`): - Trigger-Typen anzeigen - Zuordnung Trigger → Expense --- ### 5. PERSONEN-BEREICH (Person Management) **Route:** `/people` **Pages:** - **PersonList.razor** (`/people`): - Tabelle: Name, Status (Member/Guest), Actions - Filter: Status - Actions: Neu, Bearbeiten, Löschen - **PersonCreate.razor** (`/people/new`): - Name, Status - **PersonDetail.razor** (`/people/{id}`): - Personen-Info - Teilnahme-Historie (DayPersons) - Kosten-Historie (PersonExpenses) - Statistiken (Gesamtkosten, Anzahl Teilnahmen) --- ### 6. AUSWERTUNGEN-BEREICH (Reports/Evaluations) **Route:** `/reports` **Pages:** - **ReportOverview.razor** (`/reports`): - Auswahl: Pro Person, Pro Tag, Pro Spiel - **PersonReport.razor** (`/reports/person/{id}`): - Alle Kosten einer Person - Summen pro Tag, pro Spiel - Zeitraum-Filter - **DayReport.razor** (`/reports/day/{id}`): - Alle Kosten eines Spieltags - Aufschlüsselung pro Person - Export-Option (PDF, CSV) - **PeriodReport.razor** (`/reports/period`): - Zeitraum wählen (von-bis) - Aggregierte Statistiken - Top-Spieler, teuerste Tage, etc. --- ### 7. STAMMDATEN-BEREICH (Master Data - Vereins-spezifisch) **Route:** `/masterdata` **Pages:** - **ClubSettings.razor** (`/masterdata/club`): - Vereins-Name - ExpenseCalculation-Methode - Weitere Einstellungen - **ExpenseTemplates.razor** (`/masterdata/expenses`): - Siehe Expense-Bereich (evtl. Duplikat) - **TriggerConfig.razor** (`/masterdata/triggers`): - Siehe ExpenseTriggerConfig --- ### 8. ADMIN-BEREICH (SuperAdmin - Vereinsübergreifend) **Route:** `/admin` **Pages:** - **ClubList.razor** (`/admin/clubs`): - Alle Vereine (SuperAdmin only) - Actions: Neu, Bearbeiten, Löschen - **ClubCreate.razor** (`/admin/clubs/new`): - Name, ExpenseCalculation - **UserManagement.razor** (`/admin/users`): - Alle UserProfiles - Vereinszuordnung (UserProfileClub) - Rollen-Zuordnung pro Verein - **SystemSettings.razor** (`/admin/system`): - Globale Einstellungen --- ### 9. PROFIL-BEREICH (User Profile) **Route:** `/profile` **Pages:** - **UserProfile.razor** (`/profile`): - DisplayName, Locale, TimeZone ändern - Passwort ändern - Standard-Verein festlegen (UserProfileClub.IsDefault) - Vereins-Mitgliedschaften anzeigen --- ## Vorgeschlagene Navigation-Struktur ``` NavMenu.razor: ├── 🏠 Dashboard (/) ├── 📅 Spieltage (/days) ├── 👥 Personen (/people) ├── 💰 Kosten │ ├── Vorlagen (/expenses) │ ├── Zugewiesen (/expenses/assigned) │ └── Trigger (/expenses/triggers) ├── 📊 Auswertungen (/reports) │ ├── Pro Person │ ├── Pro Tag │ └── Zeitraum ├── ⚙️ Stammdaten (IsClubEditor+) │ ├── Verein (/masterdata/club) │ └── Kosten-Vorlagen ├── 🔧 Admin (IsSuperAdmin) │ ├── Vereine (/admin/clubs) │ ├── Benutzer (/admin/users) │ └── System (/admin/system) └── 👤 Profil (/profile) ``` --- ## Prioritäts-Vorschlag (Phasen) - basierend auf User-Feedback ### Phase 1: MVP - Basis-Verwaltung (DIESE PLANUNG) **Scope:** User/Account-Mgmt, Club, Personen (Teilnehmer), Tage, Strafen 1. **User/Account-Verwaltung** - User-Registrierung (self-service oder Admin) - Passwort zurücksetzen - User-zu-Club Zuordnung (UserProfileClub) - Rollen pro Club zuweisen (UserProfileClubRoleAssignment) - User-Profil (DisplayName, Locale, TimeZone) 2. **Club-Verwaltung** (SuperAdmin) - Liste, Create, Edit, Delete - ExpenseCalculation-Methode 3. **Person-Verwaltung** (Club-Teilnehmer: Members + Guests) - Liste, Create, Edit, Delete - Status (Member/Guest) - **KEINE Login** - reine Stammdaten für Kegelclub 4. **Day-Verwaltung** - Liste, Create, Edit, Delete - Datum, Status (New, Started, Closed, Postponed) - Teilnehmer zuordnen (DayPerson) 5. **PersonExpense - Strafen manuell erfassen** - Pro Teilnehmer pro Tag Strafen hinzufügen - Expense-Vorlagen (Name, Preis) - Status: Open/Done 6. **Dashboard** - Übersicht: Nächste Tage, offene Kosten - Quick-Actions 7. **Einfache Auswertung** - Pro Tag: Wer schuldet was - Pro Person: Summe offener Kosten ### Phase 2: Detaillierte Spielverwaltung (SEPARATE PLANUNG SPÄTER) **NICHT in dieser Planung:** - Wurf-für-Wurf Eingabe - Undo-Funktion - Plugin-System für verschiedene Kegelspiele - GameData strukturiert (JSON für jetzt) - Automatische Trigger (Pudel, Pumpe, etc.) - Expense-Trigger-Engine ### Phase 3: Advanced Features (SPÄTER) - Export (PDF, CSV) - Benutzer-Rollen verwalten - Erweiterte Reports --- --- # IMPLEMENTIERUNGSPLAN - Phase 1 MVP ## Umsetzungsreihenfolge - Übersicht | ✓ | Phase | Bereich | Beschreibung | Dateien | |---|-------|---------|--------------|---------| | ✓ | A1 | Foundation | Repository Interfaces | 5 Interface-Dateien | | ✓ | A2 | Foundation | Repository Implementations | 5 Repository-Dateien | | ✓ | A3 | Foundation | DTOs | 5 DTO-Dateien | | ✓ | A4 | Foundation | Service Interfaces | 5 Service-Interface-Dateien | | ✓ | A5 | Foundation | Service Implementations | 5 Service-Dateien | | ✓ | A6 | Foundation | AutoMapper Profiles | 5 Mapping-Dateien | | ✓ | A7 | Foundation | DI Registration | 2 DI-Dateien ändern | | ✓ | B1 | User/Account | UserService erweitern | 1 Service, 1 Interface, 1 DTO | | ✓ | B2 | User/Account | Register Page | 1 Razor | | ✓ | B3 | User/Account | Password Reset Pages | 2 Razor | | ✓ | B4 | User/Account | Profile Page | 1 Razor | | ✓ | B5 | User/Account | Admin Users Page | 1 Razor | | ✓ | C1 | Clubs | ClubState Fluxor | 4 State-Dateien | | ✓ | C2 | Clubs | Club Pages - ERSTES TESTBARES MVP | 1 Razor | | ✓ | D1 | Personen | PersonState Fluxor | 4 State-Dateien | | ✓ | D2 | Personen | Person Pages | 1 Razor | | ✓ | D3 | Expenses | ExpenseState Fluxor | 4 State-Dateien | | ✓ | D4 | Expenses | Expense Pages | 1 Razor | | ✓ | E1 | Days | DayState Fluxor | 4 State-Dateien | | ✓ | E2 | Days | Days List Page | 1 Razor | | ✓ | E3 | Days | Day Details Page | 1 Razor | | ✓ | E4 | Days | PersonExpense Management | Components in DayDetails | | ✓ | F1 | Dashboard | Dashboard Page | 1 Razor | | ✓ | F2 | Dashboard | Evaluation Components | 2 Shared Components | | ✓ | F3 | Navigation | NavMenu finalisieren | 1 Razor ändern | | ✓ | G1 | Erweiterte Reg. | MembershipStatus + UserProfileClub | 2 Dateien + Migration | | ✓ | G2 | Erweiterte Reg. | ClubInvitation Entity | 1 Entity + Migration | | ✓ | G3 | Erweiterte Reg. | IEmailService (Stub) | 2 Dateien | | ✓ | G4 | Erweiterte Reg. | Services erweitern | 2 Services | | ✓ | G5 | Erweiterte Reg. | Dashboard Pending-Widget | 1 Component | | ✓ | G6 | Erweiterte Reg. | Admin Users Page erweitern | 1 Razor | | ✓ | G7 | Erweiterte Reg. | Club-Beitritt UI | 1 Razor | | ✓ | G8 | Erweiterte Reg. | Einladungslink-Handling | 2 Dateien | **Legende:** ☐ = Offen | ☑ = In Arbeit | ✓ = Fertig **Geschätzte Dateien insgesamt: ~90 Dateien** --- ## Detaillierte Phasen ### **Phase A1: Repository Interfaces erstellen** **Dateien:** 1. `src/Koogle.Domain/Interfaces/IClubRepository.cs` 2. `src/Koogle.Domain/Interfaces/IPersonRepository.cs` 3. `src/Koogle.Domain/Interfaces/IExpenseRepository.cs` 4. `src/Koogle.Domain/Interfaces/IDayRepository.cs` (erweitern) 5. `src/Koogle.Domain/Interfaces/IPersonExpenseRepository.cs` **Methoden pro Interface:** GetAllAsync/GetByClubIdAsync, GetByIdAsync, AddAsync, UpdateAsync, DeleteAsync --- ### **Phase A2: Repository Implementations erstellen** **Dateien:** 1. `src/Koogle.Infrastructure/Repositories/ClubRepository.cs` 2. `src/Koogle.Infrastructure/Repositories/PersonRepository.cs` 3. `src/Koogle.Infrastructure/Repositories/ExpenseRepository.cs` 4. `src/Koogle.Infrastructure/Repositories/DayRepository.cs` 5. `src/Koogle.Infrastructure/Repositories/PersonExpenseRepository.cs` **Pattern:** IDbContextFactory, ClubId-Filter, Include Navigation Properties --- ### **Phase A3: DTOs erstellen** **Dateien:** 1. `src/Koogle.Application/DTOs/ClubDto.cs` (ClubDto, CreateClubDto, UpdateClubDto) 2. `src/Koogle.Application/DTOs/PersonDto.cs` (PersonDto, CreatePersonDto, UpdatePersonDto) 3. `src/Koogle.Application/DTOs/ExpenseDto.cs` (ExpenseDto, CreateExpenseDto, UpdateExpenseDto) 4. `src/Koogle.Application/DTOs/DayDto.cs` erweitern (DayDto, CreateDayDto, UpdateDayDto, DayParticipantDto) 5. `src/Koogle.Application/DTOs/PersonExpenseDto.cs` (PersonExpenseDto, CreatePersonExpenseDto, DayEvaluationDto, PersonDayEvaluationDto) --- ### **Phase A4: Service Interfaces erstellen** **Dateien:** 1. `src/Koogle.Application/Interfaces/IClubService.cs` 2. `src/Koogle.Application/Interfaces/IPersonService.cs` 3. `src/Koogle.Application/Interfaces/IExpenseService.cs` 4. `src/Koogle.Application/Interfaces/IDayService.cs` (erweitern) 5. `src/Koogle.Application/Interfaces/IPersonExpenseService.cs` **Methoden:** GetAllAsync, GetByIdAsync, CreateAsync, UpdateAsync, DeleteAsync + spezifische Methoden --- ### **Phase A5: Service Implementations erstellen** **Dateien:** 1. `src/Koogle.Application/Services/ClubService.cs` 2. `src/Koogle.Application/Services/PersonService.cs` 3. `src/Koogle.Application/Services/ExpenseService.cs` 4. `src/Koogle.Application/Services/DayService.cs` (erweitern) 5. `src/Koogle.Application/Services/PersonExpenseService.cs` **Dependencies:** Repository, ICurrentClubContext, ICurrentUserService, IMapper **Business Logic:** ClubId-Injection, Audit-Felder, Validierung (mittels FluentValidation) --- ### **Phase A6: AutoMapper Profiles erstellen** **Dateien:** 1. `src/Koogle.Application/Mapping/ClubMappingProfile.cs` 2. `src/Koogle.Application/Mapping/PersonMappingProfile.cs` 3. `src/Koogle.Application/Mapping/ExpenseMappingProfile.cs` 4. `src/Koogle.Application/Mapping/DayMappingProfile.cs` 5. `src/Koogle.Application/Mapping/PersonExpenseMappingProfile.cs` **Pattern:** CreateMap() bidirektional --- ### **Phase A7: DI Registration** **Dateien ändern:** 1. `src/Koogle.Infrastructure/DependencyInjection.cs` (5 Repositories registrieren) 2. `src/Koogle.Application/DependencyInjection.cs` (5 Services registrieren) --- ### **Phase B1: UserService erweitern** **Dateien:** 1. `src/Koogle.Application/Interfaces/IUserService.cs` erweitern 2. `src/Koogle.Application/Services/UserService.cs` erweitern 3. `src/Koogle.Application/DTOs/UserDto.cs` erweitern (RegisterUserDto, ResetPasswordDto, UpdateUserProfileDto) **Neue Methoden:** - RegisterUserAsync - RequestPasswordResetAsync - ResetPasswordAsync - UpdateProfileAsync - AssignUserToClubAsync, RemoveUserFromClubAsync - AssignClubRoleAsync, RemoveClubRoleAsync --- ### **Phase B2: Register Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Account/Register.razor` **Features:** Email, Password, DisplayName, Optional ClubName für initiale Zuordnung --- ### **Phase B3: Password Reset Pages** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Account/ForgotPassword.razor` 2. `src/Koogle.Web/Components/Pages/Account/ResetPassword.razor` **Flow:** Email eingeben → Token per Email → Passwort zurücksetzen --- ### **Phase B4: Profile Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Account/Profile.razor` **Features:** DisplayName, Locale, TimeZone, Club-Memberships anzeigen, Standard-Club setzen --- ### **Phase B5: Admin Users Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Admin/Users.razor` **Features:** User-Liste, Club-Zuordnungen, Rollen pro Club --- ### **Phase C1: ClubState (Fluxor)** **Dateien:** 1. `src/Koogle.Web/Store/ClubState/ClubState.cs` 2. `src/Koogle.Web/Store/ClubState/ClubActions.cs` 3. `src/Koogle.Web/Store/ClubState/ClubReducers.cs` 4. `src/Koogle.Web/Store/ClubState/ClubEffects.cs` **Actions:** Load, Create, Update, Delete --- ### **Phase C2: Club Pages** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Admin/Clubs.razor` **Features:** MudTable, CRUD-Dialogs, SuperAdmin only --- ### **Phase D1: PersonState (Fluxor)** **Dateien:** 1. `src/Koogle.Web/Store/PersonState/PersonState.cs` 2. `src/Koogle.Web/Store/PersonState/PersonActions.cs` 3. `src/Koogle.Web/Store/PersonState/PersonReducers.cs` 4. `src/Koogle.Web/Store/PersonState/PersonEffects.cs` --- ### **Phase D2: Person Pages** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Persons/Persons.razor` **Features:** MudTable mit Filter (Member/Guest), CRUD --- ### **Phase D3: ExpenseState (Fluxor)** **Dateien:** 1. `src/Koogle.Web/Store/ExpenseState/ExpenseState.cs` 2. `src/Koogle.Web/Store/ExpenseState/ExpenseActions.cs` 3. `src/Koogle.Web/Store/ExpenseState/ExpenseReducers.cs` 4. `src/Koogle.Web/Store/ExpenseState/ExpenseEffects.cs` --- ### **Phase D4: Expense Pages** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Expenses/Expenses.razor` **Features:** MudTable mit Expense-Vorlagen, CRUD --- ### **Phase E1: DayState (Fluxor)** **Dateien:** 1. `src/Koogle.Web/Store/DayState/DayState.cs` 2. `src/Koogle.Web/Store/DayState/DayActions.cs` 3. `src/Koogle.Web/Store/DayState/DayReducers.cs` 4. `src/Koogle.Web/Store/DayState/DayEffects.cs` **State:** Days, SelectedDay, SelectedDayExpenses, AvailablePersons --- ### **Phase E2: Days List Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Days/Days.razor` **Features:** MudTable mit Jahr-Filter, Create Day --- ### **Phase E3: Day Details Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Days/DayDetails.razor` **Features:** - Day-Header (Datum, Status) - Status-Workflow Buttons (New→Started→Closed) - Teilnehmer-Sektion (Add/Remove) - PersonExpense-Sektion --- ### **Phase E4: PersonExpense Management** **Components in DayDetails:** - PersonExpense-Tabelle - Add Expense Dialog (Person auswählen, Expense auswählen, Preis editierbar wenn IsVariable) - Delete PersonExpense (nur in New/Started) - IsInverse Logic: 1 Person auswählen → alle anderen bekommen Expense --- ### **Phase F1: Dashboard Page** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Dashboard.razor` **Features:** Summary Cards, Recent Days, Top Penalty Recipients --- ### **Phase F2: Evaluation Components** **Dateien:** 1. `src/Koogle.Web/Components/Shared/DayEvaluationComponent.razor` 2. `src/Koogle.Web/Components/Shared/PersonEvaluationComponent.razor` --- ### **Phase F3: Navigation finalisieren** **Dateien ändern:** 1. `src/Koogle.Web/Components/Layout/NavMenu.razor` **Features:** Dashboard, Spieltage, Stammdaten, Admin, Profil --- ### **Phase G1: MembershipStatus + UserProfileClub erweitern** **Dateien:** 1. `src/Koogle.Domain/Enums/MembershipStatus.cs` (neu) - Pending, Approved, Rejected 2. `src/Koogle.Domain/Entities/UserProfileClub.cs` (erweitern) - MembershipStatus Status (default: Pending) - string? RejectionReason - DateTime? ApprovedAt, Guid? ApprovedById - DateTime? RejectedAt, Guid? RejectedById 3. Migration erstellen --- ### **Phase G2: ClubInvitation Entity** **Dateien:** 1. `src/Koogle.Domain/Entities/ClubInvitation.cs` (neu) - Guid Id, Guid ClubId - string Token (unique, für URL) - DateTime ExpiresAt, DateTime CreatedAt, Guid CreatedById - int? MaxUses (null = unbegrenzt), int UsedCount 2. `src/Koogle.Infrastructure/Data/AppDbContext.cs` - DbSet hinzufügen 3. Migration erstellen --- ### **Phase G3: IEmailService (Stub)** **Dateien:** 1. `src/Koogle.Application/Interfaces/IEmailService.cs` (neu) - SendMembershipRequestNotificationAsync - SendMembershipApprovedAsync - SendMembershipRejectedAsync 2. `src/Koogle.Infrastructure/Services/StubEmailService.cs` (neu) - Logging statt echtem Versand - TODO-Kommentare für SMTP 3. DI Registration --- ### **Phase G4: Services erweitern (Membership-Logik)** **UserService erweitern:** - RequestClubMembershipAsync(userProfileId, clubId) - RequestClubMembershipByNameAsync(userProfileId, clubName) - RequestClubMembershipByInviteAsync(userProfileId, inviteToken) - ApproveMembershipAsync(userProfileId, clubId, approvedById) - RejectMembershipAsync(userProfileId, clubId, rejectedById, reason) - GetPendingMembershipsAsync(clubId) **ClubService erweitern:** - CreateInvitationAsync(clubId, createdById, expiresAt, maxUses) - GetInvitationByTokenAsync(token) - ValidateInvitationAsync(token) --- ### **Phase G5: Dashboard Pending-Widget** **Dateien:** 1. `src/Koogle.Web/Components/Shared/PendingMembershipsWidget.razor` (neu) - Anzahl ausstehender Anträge für Club-Admins - Link zur Admin Users Page 2. Dashboard.razor erweitern - Widget für Admins einbinden --- ### **Phase G6: Admin Users Page erweitern** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Admin/Users.razor` (ändern) - Tab/Filter für "Ausstehende Anträge" - Approve/Reject Buttons - Reject-Dialog mit Begründungsfeld --- ### **Phase G7: Club-Beitritt UI** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Account/JoinClub.razor` (neu) - Eingabefeld für Club-Name - Suche/Validierung - Beitrittsantrag senden - Erfolgsmeldung "Antrag gestellt" 2. Dashboard.razor erweitern - "Keinem Club zugeordnet" Meldung mit Link zu JoinClub --- ### **Phase G8: Einladungslink-Handling** **Dateien:** 1. `src/Koogle.Web/Components/Pages/Club/JoinByInvite.razor` (neu) - Route: `/club/join/{token}` - Token validieren - Wenn eingeloggt: direkt Beitritt (Pending) - Wenn nicht eingeloggt: Redirect zu Register mit Token 2. `src/Koogle.Web/Controllers/AuthController.cs` (erweitern) - InviteToken als optionalen Parameter bei Register --- ## Zentrale Anforderungen (User-Feedback) ### Business Rules 1. **PersonExpense.Price**: Bei IsVariable=true editierbar, aber vorbelegt aus Expense 2. **Day Status-Workflow**: Strikt New→Started→Closed, keine Sprünge 3. **User-Registrierung**: Self-Service (öffentlich) 4. **Passwort-Reset**: Email-basiert mit Token 5. **Day-Create**: Alle Members vorbelegt, Gäste manuell 6. **PersonExpense löschen**: Nur in Status New/Started 7. **Expense.IsInverse**: Automatisch - 1 Person auswählen, alle anderen bekommen Expense 8. **Dashboard Zeitraum**: Aktuelles Jahr --- ## Code-Patterns & Konventionen ### Repository Pattern - Interface in Domain/Interfaces - Implementation in Infrastructure/Repositories - Standard-Methoden: GetAllAsync, GetByIdAsync, AddAsync, UpdateAsync, DeleteAsync - ClubId-Filter via ICurrentClubContext - IDbContextFactory für Scoping ### Service Pattern - Interface in Application/Interfaces - Implementation in Application/Services - Dependencies: Repository, ICurrentClubContext, ICurrentUserService, IMapper - ClubService: IsSuperAdmin Check - Audit-Felder auto-setzen (CreatedById, CreatedAt, ModifiedById, ModifiedAt) ### Fluxor Pattern (Redux) - State: Record mit Collections + IsLoading + Error - Actions: Record per Operation (Load, LoadSuccess, LoadFailure, Create, Update, Delete) - Reducers: Pure functions, State transformieren - Effects: Async Operations, Service-Calls, Dispatcher ### UI Pattern (Blazor + MudBlazor) - @inherits FluxorComponent - @attribute [Authorize(Policy = "...")] - MudTable für Listen - MudDialog für Create/Edit - MudForm mit Validation --- ## Referenzdateien für Patterns **Service-Pattern:** - `src/Koogle.Application/Services/UserService.cs` **Fluxor-Pattern:** - `src/Koogle.Web/Store/AuthState/AuthState.cs` - `src/Koogle.Web/Store/AuthState/AuthActions.cs` - `src/Koogle.Web/Store/AuthState/AuthReducers.cs` - `src/Koogle.Web/Store/AuthState/AuthEffects.cs` **AutoMapper:** - `src/Koogle.Application/Mapping/UserProfile.cs` **DI:** - `src/Koogle.Infrastructure/DependencyInjection.cs` - `src/Koogle.Application/DependencyInjection.cs` **Domain:** - `src/Koogle.Domain/Entities/BaseEntity.cs` --- ## Berechtigungen pro Feature - **Club-Verwaltung**: SuperAdmin only - **Person/Expense/Day CRUD**: ClubEditor+ - **Dashboard/Auswertungen**: ClubViewer+ Policy: `@attribute [Authorize(Policy = "ClubViewer|ClubEditor|ClubAdmin")]` --- ## Zusammenfassung **23 feine Phasen** → **~75 Dateien** → **MVP Phase 1 komplett** Bereit für Implementierung Phase A1