diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index 33dc782..e282d9b 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -333,7 +333,7 @@ NavMenu.razor: | ✓ | 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 | +| ✓ | 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** | diff --git a/src/Koogle.Web/Components/Layout/MainLayout.razor b/src/Koogle.Web/Components/Layout/MainLayout.razor index 00eb08f..29e0332 100644 --- a/src/Koogle.Web/Components/Layout/MainLayout.razor +++ b/src/Koogle.Web/Components/Layout/MainLayout.razor @@ -2,6 +2,9 @@ @inject IState AuthState @inject IDispatcher Dispatcher +@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery +@inject IHttpContextAccessor HttpContextAccessor +@inject IJSRuntime JS @@ -18,6 +21,18 @@ + + KOOGLE + + @if (AuthState.Value.IsAuthenticated) + { + + Profil + + Abmelden + + } + 🗙 + + + @code { private bool _drawerOpen = true; - private bool _isDarkMode = false; + private string _logoutToken = ""; + + protected override void OnInitialized() + { + base.OnInitialized(); + var http = HttpContextAccessor.HttpContext; + if (http != null) + { + var tokens = Antiforgery.GetAndStoreTokens(http); + _logoutToken = tokens.RequestToken ?? ""; + } + } + + private async Task Logout() + { + await JS.InvokeVoidAsync("eval", "document.getElementById('logout-form').submit()"); + } private readonly MudTheme _theme = new() { diff --git a/src/Koogle.Web/Components/Pages/Account/Profile.razor b/src/Koogle.Web/Components/Pages/Account/Profile.razor new file mode 100644 index 0000000..be60d30 --- /dev/null +++ b/src/Koogle.Web/Components/Pages/Account/Profile.razor @@ -0,0 +1,218 @@ +@page "/account/profile" +@attribute [Authorize] + +@using Fluxor +@using Koogle.Application.DTOs +@using Koogle.Application.Interfaces +@using Koogle.Web.Store.AuthState +@using Microsoft.AspNetCore.Authorization + +@inject IState AuthState +@inject IUserService UserService +@inject ISnackbar Snackbar + +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +Profil + + + Mein Profil + + @if (_isLoading) + { + + } + else if (_user == null) + { + Benutzer nicht gefunden. + } + else + { + + Kontoinformationen + + + + + + + Deutsch + English + + + + Europe/Berlin + Europe/Vienna + Europe/Zurich + UTC + + + + @if (_isSaving) + { + + } + Speichern + + + + + Club-Mitgliedschaften + + @if (_user.ClubMemberships.Count == 0) + { + Keine Club-Mitgliedschaften vorhanden. + } + else + { + + + Club + Rollen + Standard + + + @context.ClubName + + @foreach (var role in context.Roles) + { + @role + } + + + @if (context.IsDefault) + { + + } + else + { + + Als Standard + + } + + + + } + + + + Sicherheit + + Passwort aendern + + + } + + +@code { + private UserDto? _user; + private string _displayName = ""; + private string _locale = "de-DE"; + private string _timeZone = "Europe/Berlin"; + private bool _isLoading = true; + private bool _isSaving; + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + await LoadUser(); + } + + private async Task LoadUser() + { + _isLoading = true; + try + { + _user = await UserService.GetCurrentUserAsync(); + if (_user != null) + { + _displayName = _user.DisplayName; + _locale = _user.Locale ?? "de-DE"; + _timeZone = _user.TimeZone ?? "Europe/Berlin"; + } + } + finally + { + _isLoading = false; + } + } + + private async Task SaveProfile() + { + _isSaving = true; + try + { + var dto = new UpdateUserProfileDto + { + DisplayName = _displayName, + Locale = _locale, + TimeZone = _timeZone + }; + + var result = await UserService.UpdateProfileAsync(dto); + if (result != null) + { + _user = result; + Snackbar.Add("Profil gespeichert", Severity.Success); + } + else + { + Snackbar.Add("Fehler beim Speichern", Severity.Error); + } + } + finally + { + _isSaving = false; + } + } + + private async Task SetDefaultClub(Guid clubId) + { + _isSaving = true; + try + { + var dto = new UpdateUserProfileDto + { + DefaultClubId = clubId + }; + + var result = await UserService.UpdateProfileAsync(dto); + if (result != null) + { + _user = result; + Snackbar.Add("Standard-Club geaendert", Severity.Success); + } + else + { + Snackbar.Add("Fehler beim Aendern", Severity.Error); + } + } + finally + { + _isSaving = false; + } + } +}