Auth integration

This commit is contained in:
Wieland, Marc
2026-05-22 10:28:02 +02:00
parent 64c5f6aa2c
commit 7ab824e7c1
39 changed files with 681 additions and 57 deletions
+1
View File
@@ -1,5 +1,6 @@
@page "/feiertage"
@rendermode InteractiveServer
@attribute [Authorize]
@inject HolidayService HolidayService
<PageTitle>Feiertage Timetracker</PageTitle>
+16 -6
View File
@@ -1,8 +1,10 @@
@page "/"
@rendermode InteractiveServer
@attribute [Authorize]
@inject TimetrackerService TrackerService
@inject HolidayService HolidayService
@inject ISnackbar Snackbar
@inject AuthenticationStateProvider AuthStateProvider
<PageTitle>KW @_kw Wochenübersicht Timetracker</PageTitle>
@@ -332,6 +334,7 @@ else
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
private bool _loading = true;
private int _userId;
private DateOnly _monday;
private List<DayVm> _days = [];
private AppSettings _settings = new();
@@ -349,10 +352,14 @@ else
protected override async Task OnInitializedAsync()
{
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return; // Prerender-Pass Circuit noch nicht authentifiziert
_userId = int.Parse(claim.Value);
_monday = GetMonday(DateOnly.FromDateTime(DateTime.Today));
_settings = await TrackerService.GetSettingsAsync();
_settings = await TrackerService.GetSettingsAsync(_userId);
await LoadWeek();
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_settings);
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
_loading = false;
}
@@ -364,11 +371,11 @@ else
_holidays = list.ToDictionary(h => h.Date, h => h.Name);
_holidayYear = _monday.Year;
}
var dbDays = await TrackerService.GetWeekAsync(_monday);
var dbDays = await TrackerService.GetWeekAsync(_userId, _monday);
_days = Enumerable.Range(0, 7).Select(i =>
{
var date = _monday.AddDays(i);
return DayVm.From(dbDays.FirstOrDefault(d => d.Date == date), date);
return DayVm.From(dbDays.FirstOrDefault(d => d.Date == date), date, _userId);
}).ToList();
BuildWeekLabels();
}
@@ -421,7 +428,7 @@ else
private async Task SaveDay(DayVm day)
{
await TrackerService.UpsertWorkDayAsync(day.ToWorkDay());
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_settings);
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
BuildWeekLabels();
}
@@ -486,6 +493,7 @@ else
private sealed class DayVm
{
public int Id { get; set; }
public int UserId { get; set; }
public DateOnly Date { get; set; }
public TimeSpan? Start { get; set; }
public TimeSpan? End { get; set; }
@@ -500,9 +508,10 @@ else
public TimeSpan? NetWork => GrossWork.HasValue ? GrossWork.Value - TotalBreakTime : null;
public static DayVm From(WorkDay? wd, DateOnly date) => new()
public static DayVm From(WorkDay? wd, DateOnly date, int userId) => new()
{
Id = wd?.Id ?? 0,
UserId = wd?.UserId ?? userId,
Date = date,
Start = wd?.StartTime?.ToTimeSpan(),
End = wd?.EndTime?.ToTimeSpan(),
@@ -517,6 +526,7 @@ else
public WorkDay ToWorkDay() => new()
{
Id = Id,
UserId = UserId,
Date = Date,
StartTime = Start.HasValue ? TimeOnly.FromTimeSpan(Start.Value) : null,
EndTime = End.HasValue ? TimeOnly.FromTimeSpan(End.Value) : null,
+130
View File
@@ -0,0 +1,130 @@
@page "/login"
@rendermode InteractiveServer
@attribute [AllowAnonymous]
@inject NavigationManager Nav
@inject ISnackbar Snackbar
<PageTitle>Anmelden Timetracker</PageTitle>
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-16">
<MudStack AlignItems="AlignItems.Center" Spacing="4">
@* ── Logo / Header ── *@
<MudStack AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.AccessTime"
Style="font-size:4rem; color:#1565C0" />
<MudText Typo="Typo.h4" Style="font-weight:700; color:#1565C0">Timetracker</MudText>
</MudStack>
<MudPaper Elevation="4" Class="pa-6 rounded-xl" Style="width:100%">
<MudTabs @bind-ActivePanelIndex="_activeTab" Rounded="true" Centered="true" Color="Color.Primary">
@* ── Login ── *@
<MudTabPanel Text="Anmelden">
<MudStack Spacing="3" Class="mt-4">
@if (_error != null && _activeTab == 0)
{
<MudAlert Severity="Severity.Error" Dense="true">@_error</MudAlert>
}
<form action="/auth/login" method="post">
<MudStack Spacing="3">
<MudTextField @bind-Value="_loginUsername"
Label="Benutzername"
Variant="Variant.Outlined"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Person"
name="username"
AutoFocus="true" />
<MudTextField @bind-Value="_loginPassword"
Label="Passwort"
Variant="Variant.Outlined"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Lock"
InputType="@(_showLoginPw ? InputType.Text : InputType.Password)"
AdornmentAriaLabel="Passwort anzeigen"
name="password"
OnAdornmentClick="@(() => _showLoginPw = !_showLoginPw)" />
<MudButton ButtonType="ButtonType.Submit"
Variant="Variant.Filled"
Color="Color.Primary"
FullWidth="true"
Size="Size.Large"
StartIcon="@Icons.Material.Filled.Login">
Anmelden
</MudButton>
</MudStack>
</form>
</MudStack>
</MudTabPanel>
@* ── Registrieren ── *@
<MudTabPanel Text="Registrieren">
<MudStack Spacing="3" Class="mt-4">
@if (_error != null && _activeTab == 1)
{
<MudAlert Severity="Severity.Error" Dense="true">@_error</MudAlert>
}
<form action="/auth/register" method="post">
<MudStack Spacing="3">
<MudTextField @bind-Value="_regUsername"
Label="Benutzername"
Variant="Variant.Outlined"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Person"
name="username"
HelperText="Mindestens 3 Zeichen" />
<MudTextField @bind-Value="_regPassword"
Label="Passwort"
Variant="Variant.Outlined"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Lock"
InputType="@(_showRegPw ? InputType.Text : InputType.Password)"
AdornmentAriaLabel="Passwort anzeigen"
name="password"
HelperText="Mindestens 6 Zeichen"
OnAdornmentClick="@(() => _showRegPw = !_showRegPw)" />
<MudButton ButtonType="ButtonType.Submit"
Variant="Variant.Filled"
Color="Color.Secondary"
FullWidth="true"
Size="Size.Large"
StartIcon="@Icons.Material.Filled.PersonAdd">
Konto erstellen
</MudButton>
</MudStack>
</form>
</MudStack>
</MudTabPanel>
</MudTabs>
</MudPaper>
</MudStack>
</MudContainer>
@code {
private int _activeTab = 0;
private string? _error;
private string _loginUsername = "";
private string _loginPassword = "";
private string _regUsername = "";
private string _regPassword = "";
private bool _showLoginPw;
private bool _showRegPw;
[SupplyParameterFromQuery(Name = "error")]
public string? ErrorParam { get; set; }
[SupplyParameterFromQuery(Name = "tab")]
public string? TabParam { get; set; }
protected override void OnParametersSet()
{
_error = ErrorParam switch
{
"invalid" => "Benutzername oder Passwort falsch.",
not null => Uri.UnescapeDataString(ErrorParam),
_ => null
};
_activeTab = TabParam == "register" ? 1 : 0;
}
}
+10 -3
View File
@@ -1,7 +1,9 @@
@page "/month"
@rendermode InteractiveServer
@attribute [Authorize]
@inject TimetrackerService TrackerService
@inject HolidayService HolidayService
@inject AuthenticationStateProvider AuthStateProvider
<PageTitle>@_deCulture.DateTimeFormat.GetMonthName(_month) @_year Monatsübersicht Timetracker</PageTitle>
@@ -150,6 +152,7 @@ else
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
private bool _loading = true;
private int _userId;
private int _year = DateTime.Today.Year;
private int _month = DateTime.Today.Month;
private List<MonthDayVm> _days = [];
@@ -167,16 +170,20 @@ else
protected override async Task OnInitializedAsync()
{
_settings = await TrackerService.GetSettingsAsync();
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return;
_userId = int.Parse(claim.Value);
_settings = await TrackerService.GetSettingsAsync(_userId);
await LoadMonth();
_loading = false;
}
private async Task LoadMonth()
{
var workDays = await TrackerService.GetMonthAsync(_year, _month);
var workDays = await TrackerService.GetMonthAsync(_userId, _year, _month);
var holidays = await HolidayService.GetHolidaysAsync(_year);
var vacations = await TrackerService.GetVacationDaysAsync(_year);
var vacations = await TrackerService.GetVacationDaysAsync(_userId, _year);
var holidayMap = holidays.ToDictionary(h => h.Date, h => h.Name);
var vacationSet = vacations.Select(v => v.Date).ToHashSet();
+11 -4
View File
@@ -1,8 +1,10 @@
@page "/settings"
@rendermode InteractiveServer
@attribute [Authorize]
@inject TimetrackerService TrackerService
@inject HolidayService HolidayService
@inject ISnackbar Snackbar
@inject AuthenticationStateProvider AuthStateProvider
<PageTitle>Einstellungen Timetracker</PageTitle>
@@ -375,6 +377,7 @@ else
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
private AppSettings? _settings;
private int _userId;
private int _vacYear = DateTime.Today.Year;
private List<VacationDay> _vacationDays = [];
private DateTime? _newVacDateFrom;
@@ -402,14 +405,18 @@ else
protected override async Task OnInitializedAsync()
{
_settings = await TrackerService.GetSettingsAsync();
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return;
_userId = int.Parse(claim.Value);
_settings = await TrackerService.GetSettingsAsync(_userId);
await LoadVacations();
_holHolidays = await HolidayService.GetHolidaysAsync(_holYear);
}
private async Task LoadVacations()
{
_vacationDays = await TrackerService.GetVacationDaysAsync(_vacYear);
_vacationDays = await TrackerService.GetVacationDaysAsync(_userId, _vacYear);
}
private async Task ChangeYear(int delta)
@@ -438,7 +445,7 @@ else
{
if (_settings!.IsWorkDay(current.DayOfWeek))
{
await TrackerService.AddVacationDayAsync(new VacationDay { Date = current, Note = note });
await TrackerService.AddVacationDayAsync(new VacationDay { UserId = _userId, Date = current, Note = note });
added++;
}
current = current.AddDays(1);
@@ -452,7 +459,7 @@ else
private async Task RemoveVacation(int id)
{
await TrackerService.RemoveVacationDayAsync(id);
await TrackerService.RemoveVacationDayAsync(_userId, id);
await LoadVacations();
Snackbar.Add("Urlaubstag entfernt", Severity.Info);
}
+10 -3
View File
@@ -1,8 +1,10 @@
@page "/urlaub-maximizer"
@rendermode InteractiveServer
@attribute [Authorize]
@inject TimetrackerService TrackerService
@inject HolidayService HolidayService
@inject ISnackbar Snackbar
@inject AuthenticationStateProvider AuthStateProvider
<PageTitle>Urlaubs-Maximizer Timetracker</PageTitle>
@@ -232,10 +234,15 @@ else
private int _remainingDays;
private List<Suggestion> _suggestions = [];
private string _subLabel = "";
private int _userId;
protected override async Task OnInitializedAsync()
{
_settings = await TrackerService.GetSettingsAsync();
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return;
_userId = int.Parse(claim.Value);
_settings = await TrackerService.GetSettingsAsync(_userId);
await LoadYear();
_loading = false;
}
@@ -243,7 +250,7 @@ else
private async Task LoadYear()
{
var holidays = await HolidayService.GetHolidaysAsync(_year);
var vacations = await TrackerService.GetVacationDaysAsync(_year);
var vacations = await TrackerService.GetVacationDaysAsync(_userId, _year);
_holidays = holidays.ToDictionary(h => h.Date, h => h.Name);
_vacationSet = vacations.Select(v => v.Date).ToHashSet();
_remainingDays = Math.Max(0, _settings.VacationDaysPerYear - vacations.Count);
@@ -262,7 +269,7 @@ else
private async Task TakeSuggestion(Suggestion s)
{
foreach (var d in s.VacationDaysToTake.Where(d => !_vacationSet.Contains(d)))
await TrackerService.AddVacationDayAsync(new VacationDay { Date = d, Note = "Urlaubs-Maximizer" });
await TrackerService.AddVacationDayAsync(new VacationDay { UserId = _userId, Date = d, Note = "Urlaubs-Maximizer" });
await LoadYear();
var word = s.VacationDaysNeeded == 1 ? "Urlaubstag" : "Urlaubstage";
Snackbar.Add($"{s.VacationDaysNeeded} {word} eingetragen {s.TotalFreeDays} Tage frei!", Severity.Success);