@inherits LayoutComponentBase @implements IDisposable @inject UserSessionService UserSession @inject IAuthService AuthService @inject NavigationManager Navigation @inject IJSRuntime JS @if (IsPublicPage()) {
@Body
} else { @switch (state) { case AuthState.Loading:

Lade...

break; case AuthState.Authenticated:
@Body
break; case AuthState.NotAuthenticated:

Anmeldung erforderlich

Sie müssen angemeldet sein, um auf diese Seite zuzugreifen.

Zum Login
break; } } @* Logout Modal *@ @if (showLogoutModal) { } @* Password Change Modal *@ @if (showPasswordModal) { } @code { private enum AuthState { Loading, Authenticated, NotAuthenticated } private AuthState state = AuthState.Loading; private bool showMobileMenu; private bool showUserMenu; private bool showLogoutModal; private bool showPasswordModal; private bool isLoggingOut; // Password change form private string currentPassword = ""; private string newPassword = ""; private string confirmPassword = ""; private string? passwordError; private string? passwordSuccess; private bool isChangingPassword; private bool CanChangePassword => !string.IsNullOrWhiteSpace(currentPassword) && !string.IsNullOrWhiteSpace(newPassword) && newPassword.Length >= 4 && newPassword == confirmPassword; private ErrorBoundary? errorBoundary; private DotNetObjectReference? dotNetRef; private bool userMenuClickOutsideRegistered; private bool lastAuthMarker; protected override void OnInitialized() { UserSession.OnChange += HandleUserChanged; if (UserSession.IsAuthenticated) { state = AuthState.Authenticated; } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { dotNetRef = DotNetObjectReference.Create(this); if (!IsPublicPage() && state == AuthState.Loading) { await RestoreSessionAsync(); } } // Keep in sync so app-interop.js can // auto-start/stop the idle watcher. var isAuthed = state == AuthState.Authenticated; if (isAuthed != lastAuthMarker) { lastAuthMarker = isAuthed; try { await JS.InvokeVoidAsync("document.body.setAttribute", "data-authenticated", isAuthed ? "true" : "false"); if (!isAuthed) { await JS.InvokeVoidAsync("appInterop.stopIdleWatcher"); } } catch { /* circuit may be mid-reconnect */ } } // Register / unregister click-outside for the user dropdown. try { if (showUserMenu && !userMenuClickOutsideRegistered && dotNetRef != null) { await JS.InvokeVoidAsync("appInterop.registerClickOutside", dotNetRef, nameof(CloseUserMenuFromJs), ".user-dropdown"); userMenuClickOutsideRegistered = true; } else if (!showUserMenu && userMenuClickOutsideRegistered) { await JS.InvokeVoidAsync("appInterop.unregisterClickOutside"); userMenuClickOutsideRegistered = false; } } catch { /* non-fatal */ } } [JSInvokable] public Task CloseUserMenuFromJs() { if (showUserMenu) { showUserMenu = false; StateHasChanged(); } return Task.CompletedTask; } private void ReloadPage() { errorBoundary?.Recover(); Navigation.NavigateTo(Navigation.Uri, forceLoad: true); } private async Task RestoreSessionAsync() { try { var userId = await JS.InvokeAsync("localStorage.getItem", "userId"); if (!string.IsNullOrWhiteSpace(userId) && Guid.TryParse(userId, out var guid)) { var user = await AuthService.GetUserByIdAsync(guid); if (user != null && user.IsActive) { UserSession.SetUser(user); state = AuthState.Authenticated; await InvokeAsync(StateHasChanged); return; } // Clear invalid session await JS.InvokeVoidAsync("localStorage.removeItem", "userId"); } } catch (Exception) { // Continue to not authenticated state } state = AuthState.NotAuthenticated; await InvokeAsync(StateHasChanged); } private void HandleUserChanged() { state = UserSession.IsAuthenticated ? AuthState.Authenticated : AuthState.NotAuthenticated; InvokeAsync(StateHasChanged); } private bool IsPublicPage() { var uri = Navigation.ToBaseRelativePath(Navigation.Uri).ToLowerInvariant(); return string.IsNullOrEmpty(uri) || uri == "/" || uri.StartsWith("login") || uri.StartsWith("impressum") || uri.StartsWith("datenschutz"); } private bool IsCurrentPage(string path) { var uri = "/" + Navigation.ToBaseRelativePath(Navigation.Uri); return uri.Equals(path, StringComparison.OrdinalIgnoreCase); } private void ToggleMobileMenu() { showMobileMenu = !showMobileMenu; showUserMenu = false; } private void CloseMobileMenu() => showMobileMenu = false; private void ToggleUserMenu() => showUserMenu = !showUserMenu; private void OpenLogoutModal() { CloseMenus(); showLogoutModal = true; } private void OpenPasswordModal() { CloseMenus(); ResetPasswordForm(); showPasswordModal = true; } private void CloseMenus() { showUserMenu = false; showMobileMenu = false; } private void CloseAllModals() { showLogoutModal = false; showPasswordModal = false; showUserMenu = false; showMobileMenu = false; ResetPasswordForm(); } private void ResetPasswordForm() { currentPassword = ""; newPassword = ""; confirmPassword = ""; passwordError = null; passwordSuccess = null; isChangingPassword = false; } private async Task ChangePassword() { passwordError = null; passwordSuccess = null; if (string.IsNullOrWhiteSpace(currentPassword)) { passwordError = "Bitte geben Sie Ihr aktuelles Passwort ein."; return; } var validation = AuthService.ValidatePassword(newPassword); if (!validation.IsValid) { passwordError = validation.ErrorMessage; return; } if (newPassword != confirmPassword) { passwordError = "Die Passwörter stimmen nicht überein."; return; } isChangingPassword = true; StateHasChanged(); try { var user = UserSession.CurrentUser; if (user == null) { passwordError = "Benutzer nicht gefunden."; return; } var verifiedUser = await AuthService.AuthenticateAsync(user.Username, currentPassword); if (verifiedUser == null) { passwordError = "Das aktuelle Passwort ist falsch."; await PlayFeedback("error"); return; } var success = await AuthService.ChangePasswordAsync(user.Id, newPassword); if (success) { passwordSuccess = "Passwort wurde erfolgreich geändert!"; currentPassword = ""; newPassword = ""; confirmPassword = ""; await PlayFeedback("success"); StateHasChanged(); await Task.Delay(1500); CloseAllModals(); } else { passwordError = "Fehler beim Ändern des Passworts."; await PlayFeedback("error"); } } catch (Exception ex) { passwordError = $"Fehler: {ex.Message}"; await PlayFeedback("error"); } finally { isChangingPassword = false; StateHasChanged(); } } private async Task ExecuteLogout() { isLoggingOut = true; StateHasChanged(); try { await JS.InvokeVoidAsync("localStorage.removeItem", "userId"); } catch { } UserSession.ClearUser(); showLogoutModal = false; Navigation.NavigateTo("/", forceLoad: true); } private async Task PlayFeedback(string type) { try { await JS.InvokeVoidAsync("qrScanner.playSound", type); } catch { } } public void Dispose() { UserSession.OnChange -= HandleUserChanged; dotNetRef?.Dispose(); } }