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.");
}
}