From 2f95fa0e3e29463bea62c3357ffdc96068ff6456 Mon Sep 17 00:00:00 2001 From: MarcWieland Date: Thu, 25 Jun 2026 20:15:07 +0200 Subject: [PATCH] =?UTF-8?q?auch=20einzelnutzer=20k=C3=B6nnen=20sich=20regi?= =?UTF-8?q?strieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Layout/NavMenu.razor | 10 +- .../Components/Pages/AdminUsers.razor | 2 +- .../Components/Pages/Login.razor | 221 ++++++++++++++---- timetracker.Server/Data/AuthService.cs | 10 +- timetracker.Server/Program.cs | 4 +- 5 files changed, 188 insertions(+), 59 deletions(-) diff --git a/timetracker.Client/Components/Layout/NavMenu.razor b/timetracker.Client/Components/Layout/NavMenu.razor index e52c1ed..de9ac32 100644 --- a/timetracker.Client/Components/Layout/NavMenu.razor +++ b/timetracker.Client/Components/Layout/NavMenu.razor @@ -67,7 +67,7 @@
@context.User.Identity?.Name - @_companyName + @GetUserStatus(context.User.Identity?.Name)
@@ -159,4 +159,12 @@ return parts[0][..Math.Min(2, parts[0].Length)].ToUpper(); return $"{parts[0][0]}{parts[1][0]}".ToUpper(); } + + private string GetUserStatus(string? username) + { + if (string.IsNullOrWhiteSpace(username)) return ""; + if (username.Equals("marc", StringComparison.OrdinalIgnoreCase)) return "SuperAdmin"; + if (_companyName == "Hauptdomain") return "Einzelnutzer"; + return _companyName; + } } \ No newline at end of file diff --git a/timetracker.Client/Components/Pages/AdminUsers.razor b/timetracker.Client/Components/Pages/AdminUsers.razor index 7e1a26c..3033be3 100644 --- a/timetracker.Client/Components/Pages/AdminUsers.razor +++ b/timetracker.Client/Components/Pages/AdminUsers.razor @@ -102,7 +102,7 @@ else else { - Hauptdomain (SuperUser) + @(context.Username == "marc" ? "SuperAdmin" : "Einzelnutzer") } } diff --git a/timetracker.Client/Components/Pages/Login.razor b/timetracker.Client/Components/Pages/Login.razor index 7a127b0..5ab214b 100644 --- a/timetracker.Client/Components/Pages/Login.razor +++ b/timetracker.Client/Components/Pages/Login.razor @@ -91,26 +91,36 @@ else Zurück zum Login - } - else - { - @* ── Tab Navigation ── *@ - - - Anmelden - - - @(_tenantInfo.IsTenant ? "Registrieren" : "Firma registrieren") - - - - + } else { + @* ── Header / Navigation ── *@ + @if (_activeTab == 2) + { + @* ── Back Button for Firm Registration ── *@ + + + Firma registrieren + + + } + else + { + @* ── Tab Navigation ── *@ + + + Anmelden + + + Registrieren + + + + } @if (_activeTab == 0) { @@ -156,7 +166,7 @@ else } - else + else if (_activeTab == 1) { @if (_tenantInfo.IsTenant) { @@ -211,61 +221,41 @@ else } else { - @* ── Firm Register Form ── *@ + @* ── Single User Register Form ── *@ @if (_error != null) { @_error } - - + - - - - - - Administrator-Konto - - - - + + Ich akzeptiere die AGB. @@ -275,20 +265,112 @@ else Color="Color.Secondary" FullWidth="true" Size="Size.Large" - StartIcon="@Icons.Material.Filled.Business" + StartIcon="@Icons.Material.Filled.PersonAdd" Class="mt-2" Disabled="_loading || !_acceptAgb"> @if (_loading) { } - Firma registrieren + Konto erstellen } } + else if (_activeTab == 2) + { + @* ── Firm Register Form ── *@ + + @if (_error != null) + { + @_error + } + + + + + + + + + Administrator-Konto + + + + + + + + + Ich akzeptiere die AGB. + + + + @if (_loading) + { + + } + Firma registrieren + + + + + } + + @if (!_tenantInfo.IsTenant && _activeTab != 2) + { + + + + Sie möchten Timetracker für Ihr Unternehmen nutzen? + + + Eigene Firma registrieren + + + } } @@ -303,6 +385,7 @@ else private string _honeypot = ""; private bool _acceptAgb; private bool _firmRegisteredSuccessfully; + private string _confirmPassword = ""; private readonly AuthModel _loginModel = new(); private readonly AuthModel _registerModel = new(); @@ -432,6 +515,44 @@ else } } + private async Task HandleSingleUserRegister() + { + if (!_acceptAgb) + { + _error = "Du musst die AGB akzeptieren."; + return; + } + if (_registerModel.Password != _confirmPassword) + { + _error = "Passwörter stimmen nicht überein."; + return; + } + + _loading = true; + _error = null; + try + { + var (user, error) = await AuthService.RegisterAsync(_registerModel.Username, _registerModel.Password, _honeypot); + if (user != null) + { + await JSRuntime.InvokeVoidAsync("localStorage.setItem", "showOnboarding", "true"); + Nav.NavigateTo("/", forceLoad: true); + } + else + { + _error = error ?? "Registrierung fehlgeschlagen."; + } + } + catch (Exception ex) + { + _error = $"Registrierungs Fehler: {ex.Message}"; + } + finally + { + _loading = false; + } + } + private async Task HandleRegisterFirm() { if (!_acceptAgb) diff --git a/timetracker.Server/Data/AuthService.cs b/timetracker.Server/Data/AuthService.cs index 34d4688..c7b6574 100644 --- a/timetracker.Server/Data/AuthService.cs +++ b/timetracker.Server/Data/AuthService.cs @@ -97,15 +97,15 @@ public class AuthService( return (null, "Passwort muss mindestens 6 Zeichen lang sein."); var tenantId = tenantProvider.TenantId; - if (tenantId == null) + if (tenantId == null && username.Equals("marc", StringComparison.OrdinalIgnoreCase)) { - return (null, "Mitarbeiter-Registrierung ist nur über die Firmen-Subdomain möglich."); + return (null, "Dieser Benutzername ist reserviert."); } await using var db = await factory.CreateDbContextAsync(); - // Filter handles checking uniqueness within the same tenant + // Filter handles checking uniqueness within the same tenant context if (await db.Users.AnyAsync(u => u.Username == username)) - return (null, "Benutzername in dieser Firma bereits vergeben."); + return (null, tenantId.HasValue ? "Benutzername in dieser Firma bereits vergeben." : "Benutzername bereits vergeben."); var (hash, salt) = HashPassword(password); var user = new User @@ -113,7 +113,7 @@ public class AuthService( Username = username, PasswordHash = hash, PasswordSalt = salt, - TenantId = tenantId.Value + TenantId = tenantId }; db.Users.Add(user); await db.SaveChangesAsync(); diff --git a/timetracker.Server/Program.cs b/timetracker.Server/Program.cs index 6506ab2..0640bf7 100644 --- a/timetracker.Server/Program.cs +++ b/timetracker.Server/Program.cs @@ -219,9 +219,9 @@ app.MapPost("/api/auth/login", async (HttpContext ctx, [FromBody] LoginRequest r } else { - if (user.Username != "marc") + if (user.Username != "marc" && user.TenantId != null) { - return Results.BadRequest("Anmeldung auf der Hauptdomain nur für Administratoren."); + return Results.BadRequest("Benutzer gehört zu einer Firma. Bitte melden Sie sich über Ihre Firmen-Subdomain an."); } }