Loading angepasst
This commit is contained in:
@@ -48,6 +48,9 @@
|
||||
</MudLayout>
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TenantInfo? PersistentTenantInfo { get; set; }
|
||||
|
||||
private bool _drawerOpen = true;
|
||||
private bool _isDarkMode;
|
||||
|
||||
@@ -55,44 +58,60 @@
|
||||
{
|
||||
UserNotificationService.OnUserDeleted += HandleUserDeleted;
|
||||
|
||||
try
|
||||
if (PersistentTenantInfo != null)
|
||||
{
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo?.IsTenant == true && !string.IsNullOrEmpty(tenantInfo.PrimaryColor))
|
||||
ApplyTheme(PersistentTenantInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var primaryColor = tenantInfo.PrimaryColor;
|
||||
var secondaryColor = tenantInfo.SecondaryColor ?? tenantInfo.PrimaryColor;
|
||||
|
||||
_theme.PaletteLight.Primary = primaryColor;
|
||||
_theme.PaletteLight.Secondary = secondaryColor;
|
||||
_theme.PaletteDark.Primary = primaryColor;
|
||||
_theme.PaletteDark.Secondary = secondaryColor;
|
||||
|
||||
// Dynamically apply background colors to navbar
|
||||
_theme.PaletteLight.AppbarBackground = primaryColor;
|
||||
_theme.PaletteLight.DrawerBackground = primaryColor;
|
||||
_theme.PaletteDark.AppbarBackground = primaryColor;
|
||||
_theme.PaletteDark.DrawerBackground = primaryColor;
|
||||
|
||||
// Adjust text/icon contrast based on background luminance
|
||||
var isLight = IsLightColor(primaryColor);
|
||||
var textCol = isLight ? "#0F172A" : "#FFFFFF";
|
||||
var subTextCol = isLight ? "rgba(15, 23, 42, 0.70)" : "rgba(255, 255, 255, 0.70)";
|
||||
|
||||
_theme.PaletteLight.AppbarText = textCol;
|
||||
_theme.PaletteLight.DrawerText = subTextCol;
|
||||
_theme.PaletteLight.DrawerIcon = subTextCol;
|
||||
_theme.PaletteLight.PrimaryContrastText = textCol;
|
||||
|
||||
_theme.PaletteDark.AppbarText = textCol;
|
||||
_theme.PaletteDark.DrawerText = subTextCol;
|
||||
_theme.PaletteDark.DrawerIcon = subTextCol;
|
||||
_theme.PaletteDark.PrimaryContrastText = textCol;
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo != null)
|
||||
{
|
||||
ApplyTheme(tenantInfo);
|
||||
PersistentTenantInfo = tenantInfo;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore background error
|
||||
}
|
||||
}
|
||||
catch
|
||||
}
|
||||
|
||||
private void ApplyTheme(TenantInfo tenantInfo)
|
||||
{
|
||||
if (tenantInfo.IsTenant && !string.IsNullOrEmpty(tenantInfo.PrimaryColor))
|
||||
{
|
||||
// Ignore background error
|
||||
var primaryColor = tenantInfo.PrimaryColor;
|
||||
var secondaryColor = tenantInfo.SecondaryColor ?? tenantInfo.PrimaryColor;
|
||||
|
||||
_theme.PaletteLight.Primary = primaryColor;
|
||||
_theme.PaletteLight.Secondary = secondaryColor;
|
||||
_theme.PaletteDark.Primary = primaryColor;
|
||||
_theme.PaletteDark.Secondary = secondaryColor;
|
||||
|
||||
// Dynamically apply background colors to navbar
|
||||
_theme.PaletteLight.AppbarBackground = primaryColor;
|
||||
_theme.PaletteLight.DrawerBackground = primaryColor;
|
||||
_theme.PaletteDark.AppbarBackground = primaryColor;
|
||||
_theme.PaletteDark.DrawerBackground = primaryColor;
|
||||
|
||||
// Adjust text/icon contrast based on background luminance
|
||||
var isLight = IsLightColor(primaryColor);
|
||||
var textCol = isLight ? "#0F172A" : "#FFFFFF";
|
||||
var subTextCol = isLight ? "rgba(15, 23, 42, 0.70)" : "rgba(255, 255, 255, 0.70)";
|
||||
|
||||
_theme.PaletteLight.AppbarText = textCol;
|
||||
_theme.PaletteLight.DrawerText = subTextCol;
|
||||
_theme.PaletteLight.DrawerIcon = subTextCol;
|
||||
_theme.PaletteLight.PrimaryContrastText = textCol;
|
||||
|
||||
_theme.PaletteDark.AppbarText = textCol;
|
||||
_theme.PaletteDark.DrawerText = subTextCol;
|
||||
_theme.PaletteDark.DrawerIcon = subTextCol;
|
||||
_theme.PaletteDark.PrimaryContrastText = textCol;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentCompanyName { get; set; }
|
||||
|
||||
[Parameter] public EventCallback OnToggleDrawer { get; set; }
|
||||
|
||||
private bool _showVersionBadge;
|
||||
@@ -103,17 +106,25 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
if (PersistentCompanyName != null)
|
||||
{
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo?.IsTenant == true && !string.IsNullOrEmpty(tenantInfo.Name))
|
||||
{
|
||||
_companyName = tenantInfo.Name;
|
||||
}
|
||||
_companyName = PersistentCompanyName;
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
// Ignore background error
|
||||
try
|
||||
{
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo?.IsTenant == true && !string.IsNullOrEmpty(tenantInfo.Name))
|
||||
{
|
||||
_companyName = tenantInfo.Name;
|
||||
PersistentCompanyName = _companyName;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore background error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,12 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<User>? PersistentUsers { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<TenantStats>? PersistentTenants { get; set; }
|
||||
|
||||
private List<User> _users = [];
|
||||
private List<TenantStats> _tenants = [];
|
||||
private bool _loading = true;
|
||||
@@ -161,23 +167,35 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentUsers != null)
|
||||
{
|
||||
_users = PersistentUsers;
|
||||
_tenants = PersistentTenants ?? [];
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
var claim = (await AuthStateProvider.GetAuthenticationStateAsync())
|
||||
.User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (claim == null) return;
|
||||
|
||||
UserNotificationService.OnUsersChanged += RefreshUsers;
|
||||
|
||||
try
|
||||
if (PersistentUsers == null)
|
||||
{
|
||||
_users = await AuthService.GetAllUsersAsync();
|
||||
_tenants = await Http.GetFromJsonAsync<List<TenantStats>>("api/superadmin/tenants") ?? [];
|
||||
try
|
||||
{
|
||||
_users = await AuthService.GetAllUsersAsync();
|
||||
_tenants = await Http.GetFromJsonAsync<List<TenantStats>>("api/superadmin/tenants") ?? [];
|
||||
|
||||
PersistentUsers = _users;
|
||||
PersistentTenants = _tenants;
|
||||
_loading = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Fehler beim Laden der Daten: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Fehler beim Laden der Daten: {ex.Message}", Severity.Error);
|
||||
}
|
||||
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
private async Task RefreshUsers()
|
||||
|
||||
@@ -60,15 +60,19 @@
|
||||
|
||||
public static readonly List<Release> Releases =
|
||||
[
|
||||
new("1.5", "25.06.2026", true,
|
||||
new("1.7", "27.06.2026", true,
|
||||
[
|
||||
new("Neu", "Excel-Export"),
|
||||
new("Upgrade", "Besseres Page-Loading")
|
||||
], Highlighted: true),
|
||||
new("1.6", "25.06.2026", true,
|
||||
[
|
||||
new("Neu", "Userprofil angelegt"),
|
||||
new("Upgrade", "Früheste Gehenszeit wird angezeigt"),
|
||||
new("Upgrade", "Pausen werden dynamisch berechnet"),
|
||||
new("Neu", "Excel-Export"),
|
||||
new("Neu", "Multi-Tenant Integration"),
|
||||
new("Upgrade", "Verbesserte Single-User Ansicht")
|
||||
], Highlighted: true),
|
||||
]),
|
||||
new("1.4", "08.06.2026", true,
|
||||
[
|
||||
new("Neu", "Timebot implementiert"),
|
||||
|
||||
@@ -183,6 +183,18 @@ else
|
||||
@code {
|
||||
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<PublicHoliday>? PersistentHolidays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentSubLabel { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentYear { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _year = DateTime.Today.Year;
|
||||
private List<PublicHoliday> _holidays = [];
|
||||
@@ -193,15 +205,38 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null && PersistentHolidays != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_holidays = PersistentHolidays;
|
||||
_subLabel = PersistentSubLabel ?? "";
|
||||
_year = PersistentYear ?? DateTime.Today.Year;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var claim = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
if (claim != null)
|
||||
{
|
||||
_userId = int.Parse(claim.Value);
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
|
||||
if (PersistentSettings == null || PersistentHolidays == null)
|
||||
{
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
await LoadHolidays();
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentHolidays = _holidays;
|
||||
PersistentSubLabel = _subLabel;
|
||||
PersistentYear = _year;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await LoadHolidays();
|
||||
_loading = false;
|
||||
}
|
||||
await LoadHolidays();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
private async Task LoadHolidays()
|
||||
|
||||
@@ -388,6 +388,30 @@ else
|
||||
@code {
|
||||
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<DayVm>? PersistentDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TimeSpan? PersistentTotalOvertime { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public Dictionary<DateOnly, string>? PersistentHolidays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentHolidayYear { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<VacationDay>? PersistentVacations { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentWeekLabel { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentWeekSubLabel { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _userId;
|
||||
private DateOnly _monday;
|
||||
@@ -409,18 +433,45 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null && PersistentDays != null && PersistentTotalOvertime != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_days = PersistentDays;
|
||||
_totalOvertime = PersistentTotalOvertime.Value;
|
||||
_holidays = PersistentHolidays ?? [];
|
||||
_holidayYear = PersistentHolidayYear ?? -1;
|
||||
_vacations = PersistentVacations ?? [];
|
||||
_weekLabel = PersistentWeekLabel ?? "";
|
||||
_weekSubLabel = PersistentWeekSubLabel ?? "";
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
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(_userId);
|
||||
|
||||
var loadWeekTask = LoadWeek();
|
||||
var overtimeTask = TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
|
||||
await Task.WhenAll(loadWeekTask, overtimeTask);
|
||||
_totalOvertime = await overtimeTask;
|
||||
if (PersistentSettings == null || PersistentDays == null || PersistentTotalOvertime == null)
|
||||
{
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
|
||||
var loadWeekTask = LoadWeek();
|
||||
var overtimeTask = TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
|
||||
await Task.WhenAll(loadWeekTask, overtimeTask);
|
||||
_totalOvertime = await overtimeTask;
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentDays = _days;
|
||||
PersistentTotalOvertime = _totalOvertime;
|
||||
PersistentHolidays = _holidays;
|
||||
PersistentHolidayYear = _holidayYear;
|
||||
PersistentVacations = _vacations;
|
||||
PersistentWeekLabel = _weekLabel;
|
||||
PersistentWeekSubLabel = _weekSubLabel;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -431,8 +482,6 @@ else
|
||||
{
|
||||
// Ignored during prerendering/SSR
|
||||
}
|
||||
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
private async Task HandleOnboardingFinished()
|
||||
@@ -693,7 +742,7 @@ else
|
||||
}
|
||||
|
||||
// ── ViewModels ────────────────────────────────────────────────
|
||||
private sealed class DayVm
|
||||
public sealed class DayVm
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int UserId { get; set; }
|
||||
@@ -743,7 +792,7 @@ else
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class BreakVm
|
||||
public sealed class BreakVm
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public TimeSpan? Start { get; set; }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<PageTitle>@GetPageTitle()</PageTitle>
|
||||
|
||||
@if (_tenantInfo == null)
|
||||
@if (TenantInfo == null)
|
||||
{
|
||||
@* Loading indicator while resolving Tenant *@
|
||||
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-16 text-center">
|
||||
@@ -17,7 +17,7 @@
|
||||
<MudText Class="mt-4" Color="Color.Secondary">Lade Timetracker…</MudText>
|
||||
</MudContainer>
|
||||
}
|
||||
else if (_tenantInfo.IsTenant && !_tenantInfo.IsApproved)
|
||||
else if (TenantInfo.IsTenant && !TenantInfo.IsApproved)
|
||||
{
|
||||
@* Pending Approval Screen *@
|
||||
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-16">
|
||||
@@ -34,7 +34,7 @@ else if (_tenantInfo.IsTenant && !_tenantInfo.IsApproved)
|
||||
Class="mb-4" />
|
||||
<MudText Typo="Typo.h5" Style="font-weight: 700;" Class="mb-2">Freischaltung ausstehend</MudText>
|
||||
<MudText Typo="Typo.body1" Color="Color.Secondary" Class="mb-6">
|
||||
Die Firma <strong>@_tenantInfo.Name</strong> wurde erfolgreich registriert.
|
||||
Die Firma <strong>@TenantInfo.Name</strong> wurde erfolgreich registriert.
|
||||
Der Zugang wird derzeit von unserem SuperAdmin geprüft und in Kürze freigeschaltet.
|
||||
</MudText>
|
||||
<MudDivider Class="my-4" />
|
||||
@@ -64,9 +64,9 @@ else
|
||||
<MudIcon Icon="@Icons.Material.Filled.AccessTime"
|
||||
Style="font-size:4rem; color:#0EA5E9" />
|
||||
<MudText Typo="Typo.h4" Style="font-weight:700; color:#0EA5E9">
|
||||
@(_tenantInfo.IsTenant ? _tenantInfo.Name : "Timetracker SaaS")
|
||||
@(TenantInfo.IsTenant ? TenantInfo.Name : "Timetracker SaaS")
|
||||
</MudText>
|
||||
@if (!_tenantInfo.IsTenant)
|
||||
@if (!TenantInfo.IsTenant)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Secondary">Zentrales Portal</MudText>
|
||||
}
|
||||
@@ -168,7 +168,7 @@ else
|
||||
}
|
||||
else if (_activeTab == 1)
|
||||
{
|
||||
@if (_tenantInfo.IsTenant)
|
||||
@if (TenantInfo.IsTenant)
|
||||
{
|
||||
@* ── Employee Register Form ── *@
|
||||
<MudStack Spacing="3">
|
||||
@@ -359,7 +359,7 @@ else
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@if (!_tenantInfo.IsTenant && _activeTab != 2)
|
||||
@if (!TenantInfo.IsTenant && _activeTab != 2)
|
||||
{
|
||||
<MudDivider Class="my-4" />
|
||||
<MudStack AlignItems="AlignItems.Center">
|
||||
@@ -378,7 +378,9 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private TenantInfo? _tenantInfo;
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TenantInfo? TenantInfo { get; set; }
|
||||
|
||||
private int _activeTab = 0;
|
||||
private string? _error;
|
||||
private bool _loading;
|
||||
@@ -399,15 +401,18 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
if (TenantInfo is null)
|
||||
{
|
||||
_tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Fehler beim Abrufen der TenantInfo: {ex.Message}");
|
||||
// Fallback
|
||||
_tenantInfo = new TenantInfo { IsTenant = false };
|
||||
try
|
||||
{
|
||||
TenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Fehler beim Abrufen der TenantInfo: {ex.Message}");
|
||||
// Fallback
|
||||
TenantInfo = new TenantInfo { IsTenant = false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,8 +435,8 @@ else
|
||||
|
||||
private string GetPageTitle()
|
||||
{
|
||||
if (_tenantInfo == null) return "Lade…";
|
||||
return _tenantInfo.IsTenant ? $"Anmelden bei {_tenantInfo.Name} – Timetracker" : "Anmelden – Timetracker SaaS";
|
||||
if (TenantInfo == null) return "Lade…";
|
||||
return TenantInfo.IsTenant ? $"Anmelden bei {TenantInfo.Name} – Timetracker" : "Anmelden – Timetracker SaaS";
|
||||
}
|
||||
|
||||
private string GetDomainSuffix()
|
||||
|
||||
@@ -155,6 +155,33 @@ else
|
||||
@code {
|
||||
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<MonthDayVm>? PersistentDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentSubLabel { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TimeSpan? PersistentMonthNet { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TimeSpan? PersistentMonthOvertime { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentRecordedWorkDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentTotalWorkDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentVacationCount { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentHolidayCount { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _userId;
|
||||
private int _year = DateTime.Today.Year;
|
||||
@@ -174,13 +201,41 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null && PersistentDays != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_days = PersistentDays;
|
||||
_subLabel = PersistentSubLabel ?? "";
|
||||
_monthNet = PersistentMonthNet ?? TimeSpan.Zero;
|
||||
_monthOvertime = PersistentMonthOvertime ?? TimeSpan.Zero;
|
||||
_recordedWorkDays = PersistentRecordedWorkDays ?? 0;
|
||||
_totalWorkDays = PersistentTotalWorkDays ?? 0;
|
||||
_vacationCount = PersistentVacationCount ?? 0;
|
||||
_holidayCount = PersistentHolidayCount ?? 0;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (PersistentSettings == null || PersistentDays == null)
|
||||
{
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
await LoadMonth();
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentDays = _days;
|
||||
PersistentSubLabel = _subLabel;
|
||||
PersistentMonthNet = _monthNet;
|
||||
PersistentMonthOvertime = _monthOvertime;
|
||||
PersistentRecordedWorkDays = _recordedWorkDays;
|
||||
PersistentTotalWorkDays = _totalWorkDays;
|
||||
PersistentVacationCount = _vacationCount;
|
||||
PersistentHolidayCount = _holidayCount;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadMonth()
|
||||
@@ -317,7 +372,7 @@ else
|
||||
else Chip("Ausstehend", "#CFD8DC");
|
||||
};
|
||||
|
||||
private sealed class MonthDayVm
|
||||
public sealed class MonthDayVm
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public TimeOnly? StartTime { get; set; }
|
||||
|
||||
@@ -163,6 +163,21 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentUsername { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentNewUsername { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public bool? PersistentIsAdmin { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TimeSpan? PersistentOvertime { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _userId;
|
||||
private string _username = "";
|
||||
@@ -184,18 +199,38 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_username = PersistentUsername ?? "";
|
||||
_newUsername = PersistentNewUsername ?? "";
|
||||
_isAdmin = PersistentIsAdmin ?? false;
|
||||
_overtime = PersistentOvertime ?? TimeSpan.Zero;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (claim == null) return;
|
||||
|
||||
_userId = int.Parse(claim.Value);
|
||||
_username = authState.User.Identity?.Name ?? "";
|
||||
_newUsername = _username;
|
||||
_isAdmin = _username.Equals("marc", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (PersistentSettings == null)
|
||||
{
|
||||
_username = authState.User.Identity?.Name ?? "";
|
||||
_newUsername = _username;
|
||||
_isAdmin = _username.Equals("marc", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
_overtime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
_loading = false;
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
_overtime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentUsername = _username;
|
||||
PersistentNewUsername = _newUsername;
|
||||
PersistentIsAdmin = _isAdmin;
|
||||
PersistentOvertime = _overtime;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetInitials(string? name)
|
||||
|
||||
@@ -511,6 +511,27 @@ else
|
||||
@code {
|
||||
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TenantInfo? PersistentTenantInfo { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<VacationDay>? PersistentVacationDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<PublicHoliday>? PersistentHolHolidays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentTenantName { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentTenantPrimaryColor { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentTenantSecondaryColor { get; set; }
|
||||
|
||||
private AppSettings? _settings;
|
||||
private int _userId;
|
||||
private bool _isTenantAdmin;
|
||||
@@ -571,36 +592,63 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_tenantInfo = PersistentTenantInfo;
|
||||
_vacationDays = PersistentVacationDays ?? [];
|
||||
_holHolidays = PersistentHolHolidays ?? [];
|
||||
_tenantName = PersistentTenantName ?? "";
|
||||
_tenantPrimaryColor = PersistentTenantPrimaryColor ?? "#0EA5E9";
|
||||
_tenantSecondaryColor = PersistentTenantSecondaryColor ?? "#0EA5E9";
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
_isTenantAdmin = authState.User.IsInRole("TenantAdmin") || authState.User.HasClaim("IsTenantAdmin", "true");
|
||||
if (_isTenantAdmin)
|
||||
if (PersistentSettings != null)
|
||||
{
|
||||
try
|
||||
_isTenantAdmin = authState.User.IsInRole("TenantAdmin") || authState.User.HasClaim("IsTenantAdmin", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
_isTenantAdmin = authState.User.IsInRole("TenantAdmin") || authState.User.HasClaim("IsTenantAdmin", "true");
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
|
||||
if (_isTenantAdmin)
|
||||
{
|
||||
_tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (_tenantInfo != null)
|
||||
try
|
||||
{
|
||||
_tenantName = _tenantInfo.Name ?? "";
|
||||
_tenantPrimaryColor = _tenantInfo.PrimaryColor ?? "#0EA5E9";
|
||||
_tenantSecondaryColor = _tenantInfo.SecondaryColor ?? "#0EA5E9";
|
||||
_tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (_tenantInfo != null)
|
||||
{
|
||||
_tenantName = _tenantInfo.Name ?? "";
|
||||
_tenantPrimaryColor = _tenantInfo.PrimaryColor ?? "#0EA5E9";
|
||||
_tenantSecondaryColor = _tenantInfo.SecondaryColor ?? "#0EA5E9";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Fehler beim Laden der Firmen-Einstellungen: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Fehler beim Laden der Firmen-Einstellungen: {ex.Message}", Severity.Error);
|
||||
}
|
||||
|
||||
var loadVacationsTask = LoadVacations();
|
||||
var loadHolidaysTask = HolidayService.GetHolidaysAsync(_holYear, _settings.GermanState);
|
||||
|
||||
await Task.WhenAll(loadVacationsTask, loadHolidaysTask);
|
||||
_holHolidays = await loadHolidaysTask;
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentTenantInfo = _tenantInfo;
|
||||
PersistentVacationDays = _vacationDays;
|
||||
PersistentHolHolidays = _holHolidays;
|
||||
PersistentTenantName = _tenantName;
|
||||
PersistentTenantPrimaryColor = _tenantPrimaryColor;
|
||||
PersistentTenantSecondaryColor = _tenantSecondaryColor;
|
||||
}
|
||||
|
||||
var loadVacationsTask = LoadVacations();
|
||||
var loadHolidaysTask = HolidayService.GetHolidaysAsync(_holYear, _settings.GermanState);
|
||||
|
||||
await Task.WhenAll(loadVacationsTask, loadHolidaysTask);
|
||||
_holHolidays = await loadHolidaysTask;
|
||||
}
|
||||
|
||||
private async Task LoadVacations()
|
||||
|
||||
@@ -324,6 +324,27 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<DayStat>? PersistentDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public TimeSpan? PersistentTotalOvertime { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public double? PersistentWeekWorkedHours { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public double? PersistentWeekTargetHours { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public double? PersistentWeekOvertimeHours { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public double? PersistentMaxHoursValue { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _userId;
|
||||
private AppSettings _settings = new();
|
||||
@@ -349,84 +370,106 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null && PersistentDays != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_days = PersistentDays;
|
||||
_totalOvertime = PersistentTotalOvertime ?? TimeSpan.Zero;
|
||||
_weekWorkedHours = PersistentWeekWorkedHours ?? 0.0;
|
||||
_weekTargetHours = PersistentWeekTargetHours ?? 0.0;
|
||||
_weekOvertimeHours = PersistentWeekOvertimeHours ?? 0.0;
|
||||
_maxHoursValue = PersistentMaxHoursValue ?? 10.0;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var monday = GetMonday(today);
|
||||
|
||||
var dbDays = await TrackerService.GetWeekAsync(_userId, monday);
|
||||
_rawDbDaysCount = dbDays.Count;
|
||||
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
|
||||
_weekTargetHours = 0;
|
||||
_weekWorkedHours = 0;
|
||||
_dbMatchStatus.Clear();
|
||||
|
||||
_days = Enumerable.Range(0, 7).Select(i =>
|
||||
if (PersistentSettings == null || PersistentDays == null)
|
||||
{
|
||||
var date = monday.AddDays(i);
|
||||
bool isWorkDay = _settings.IsWorkDay(date.DayOfWeek);
|
||||
double target = isWorkDay ? _settings.DailyTargetHours : 0.0;
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
|
||||
if (isWorkDay)
|
||||
{
|
||||
_weekTargetHours += _settings.DailyTargetHours;
|
||||
}
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var monday = GetMonday(today);
|
||||
|
||||
var dbDays = await TrackerService.GetWeekAsync(_userId, monday);
|
||||
_rawDbDaysCount = dbDays.Count;
|
||||
_totalOvertime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
|
||||
|
||||
var wd = dbDays.FirstOrDefault(d => d.Date == date);
|
||||
double worked = 0.0;
|
||||
|
||||
if (wd != null)
|
||||
_weekTargetHours = 0;
|
||||
_weekWorkedHours = 0;
|
||||
_dbMatchStatus.Clear();
|
||||
|
||||
_days = Enumerable.Range(0, 7).Select(i =>
|
||||
{
|
||||
_dbMatchStatus[date] = $"Ja (Id={wd.Id}, Start={wd.StartTime}, Ende={wd.EndTime})";
|
||||
var date = monday.AddDays(i);
|
||||
bool isWorkDay = _settings.IsWorkDay(date.DayOfWeek);
|
||||
double target = isWorkDay ? _settings.DailyTargetHours : 0.0;
|
||||
|
||||
if (wd.StartTime != null && wd.EndTime != null)
|
||||
if (isWorkDay)
|
||||
{
|
||||
var gross = wd.EndTime.Value.ToTimeSpan() - wd.StartTime.Value.ToTimeSpan();
|
||||
if (gross > TimeSpan.Zero)
|
||||
_weekTargetHours += _settings.DailyTargetHours;
|
||||
}
|
||||
|
||||
var wd = dbDays.FirstOrDefault(d => d.Date == date);
|
||||
double worked = 0.0;
|
||||
|
||||
if (wd != null)
|
||||
{
|
||||
_dbMatchStatus[date] = $"Ja (Id={wd.Id}, Start={wd.StartTime}, Ende={wd.EndTime})";
|
||||
|
||||
if (wd.StartTime != null && wd.EndTime != null)
|
||||
{
|
||||
var breakTotal = wd.Breaks
|
||||
.Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime)
|
||||
.Aggregate(TimeSpan.Zero, (s, b) =>
|
||||
s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan()));
|
||||
var minMinutes = gross.TotalHours >= 9.0 ? 45 : 30;
|
||||
var minBreak = TimeSpan.FromMinutes(Math.Max(minMinutes, _settings.MinimumBreakMinutes));
|
||||
var finalBreak = breakTotal > minBreak ? breakTotal : minBreak;
|
||||
worked = (gross - finalBreak).TotalHours;
|
||||
if (worked < 0) worked = 0.0;
|
||||
var gross = wd.EndTime.Value.ToTimeSpan() - wd.StartTime.Value.ToTimeSpan();
|
||||
if (gross > TimeSpan.Zero)
|
||||
{
|
||||
var breakTotal = wd.Breaks
|
||||
.Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime)
|
||||
.Aggregate(TimeSpan.Zero, (s, b) =>
|
||||
s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan()));
|
||||
var minMinutes = gross.TotalHours >= 9.0 ? 45 : 30;
|
||||
var minBreak = TimeSpan.FromMinutes(Math.Max(minMinutes, _settings.MinimumBreakMinutes));
|
||||
var finalBreak = breakTotal > minBreak ? breakTotal : minBreak;
|
||||
worked = (gross - finalBreak).TotalHours;
|
||||
if (worked < 0) worked = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbMatchStatus[date] = "Nein";
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbMatchStatus[date] = "Nein";
|
||||
}
|
||||
|
||||
_weekWorkedHours += worked;
|
||||
|
||||
return new DayStat
|
||||
{
|
||||
Date = date,
|
||||
DayName = date.ToString("dddd", _deCulture),
|
||||
DayShortName = date.ToString("ddd", _deCulture),
|
||||
WorkedHours = worked,
|
||||
TargetHours = target,
|
||||
IsToday = date == today,
|
||||
IsWorkDay = isWorkDay
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
_weekOvertimeHours = _weekWorkedHours - _weekTargetHours;
|
||||
|
||||
_weekWorkedHours += worked;
|
||||
var maxWorked = _days.Max(d => d.WorkedHours);
|
||||
_maxHoursValue = Math.Max(10.0, Math.Max(maxWorked, _settings.DailyTargetHours));
|
||||
|
||||
return new DayStat
|
||||
{
|
||||
Date = date,
|
||||
DayName = date.ToString("dddd", _deCulture),
|
||||
DayShortName = date.ToString("ddd", _deCulture),
|
||||
WorkedHours = worked,
|
||||
TargetHours = target,
|
||||
IsToday = date == today,
|
||||
IsWorkDay = isWorkDay
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
_weekOvertimeHours = _weekWorkedHours - _weekTargetHours;
|
||||
|
||||
var maxWorked = _days.Max(d => d.WorkedHours);
|
||||
_maxHoursValue = Math.Max(10.0, Math.Max(maxWorked, _settings.DailyTargetHours));
|
||||
|
||||
_loading = false;
|
||||
PersistentSettings = _settings;
|
||||
PersistentDays = _days;
|
||||
PersistentTotalOvertime = _totalOvertime;
|
||||
PersistentWeekWorkedHours = _weekWorkedHours;
|
||||
PersistentWeekTargetHours = _weekTargetHours;
|
||||
PersistentWeekOvertimeHours = _weekOvertimeHours;
|
||||
PersistentMaxHoursValue = _maxHoursValue;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static DateOnly GetMonday(DateOnly date)
|
||||
@@ -468,7 +511,7 @@ else
|
||||
NavigationManager.NavigateTo($"api/tracker/export/year?year={_exportYear}", forceLoad: true);
|
||||
}
|
||||
|
||||
private sealed class DayStat
|
||||
public sealed class DayStat
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public string DayName { get; set; } = "";
|
||||
|
||||
@@ -167,12 +167,23 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<TenantStats>? PersistentTenants { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private List<TenantStats> _tenants = [];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadTenants();
|
||||
if (PersistentTenants != null)
|
||||
{
|
||||
_tenants = PersistentTenants;
|
||||
_loading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
await LoadTenants();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadTenants()
|
||||
@@ -181,6 +192,7 @@ else
|
||||
try
|
||||
{
|
||||
_tenants = await Http.GetFromJsonAsync<List<TenantStats>>("api/superadmin/tenants") ?? [];
|
||||
PersistentTenants = _tenants;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -226,6 +226,27 @@ else
|
||||
@code {
|
||||
private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public AppSettings? PersistentSettings { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public Dictionary<DateOnly, string>? PersistentHolidays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<DateOnly>? PersistentVacationList { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentRemainingDays { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public List<Suggestion>? PersistentSuggestions { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public string? PersistentSubLabel { get; set; }
|
||||
|
||||
[PersistentState(AllowUpdates = true)]
|
||||
public int? PersistentYear { get; set; }
|
||||
|
||||
private bool _loading = true;
|
||||
private int _year = DateTime.Today.Year;
|
||||
private AppSettings _settings = new();
|
||||
@@ -238,13 +259,37 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PersistentSettings != null && PersistentSuggestions != null)
|
||||
{
|
||||
_settings = PersistentSettings;
|
||||
_holidays = PersistentHolidays ?? [];
|
||||
_vacationSet = (PersistentVacationList ?? []).ToHashSet();
|
||||
_remainingDays = PersistentRemainingDays ?? 0;
|
||||
_suggestions = PersistentSuggestions;
|
||||
_subLabel = PersistentSubLabel ?? "";
|
||||
_year = PersistentYear ?? DateTime.Today.Year;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (PersistentSettings == null || PersistentSuggestions == null)
|
||||
{
|
||||
_settings = await TrackerService.GetSettingsAsync(_userId);
|
||||
await LoadYear();
|
||||
|
||||
PersistentSettings = _settings;
|
||||
PersistentHolidays = _holidays;
|
||||
PersistentVacationList = _vacationSet.ToList();
|
||||
PersistentRemainingDays = _remainingDays;
|
||||
PersistentSuggestions = _suggestions;
|
||||
PersistentSubLabel = _subLabel;
|
||||
PersistentYear = _year;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadYear()
|
||||
@@ -283,7 +328,7 @@ else
|
||||
// ── Algorithmus ──────────────────────────────────────────────────────
|
||||
private enum DayKind { Free, WorkAvailable, WorkTaken }
|
||||
|
||||
private sealed record Suggestion(
|
||||
public sealed record Suggestion(
|
||||
DateOnly SpanStart,
|
||||
DateOnly SpanEnd,
|
||||
List<DateOnly> VacationDaysToTake,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using timetracker.Shared;
|
||||
|
||||
@@ -8,12 +9,14 @@ namespace timetracker.Client.Services;
|
||||
public class HostAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
private readonly PersistentComponentState _state;
|
||||
private static readonly ClaimsPrincipal Anonymous = new(new ClaimsIdentity());
|
||||
private ClaimsPrincipal? _currentUser;
|
||||
|
||||
public HostAuthenticationStateProvider(HttpClient http)
|
||||
public HostAuthenticationStateProvider(HttpClient http, PersistentComponentState state)
|
||||
{
|
||||
_http = http;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
@@ -23,20 +26,40 @@ public class HostAuthenticationStateProvider : AuthenticationStateProvider
|
||||
return new AuthenticationState(_currentUser);
|
||||
}
|
||||
|
||||
if (_state.TryTakeFromJson<UserInfo>("UserInfoState", out var userInfo) && userInfo != null)
|
||||
{
|
||||
if (!userInfo.IsAuthenticated)
|
||||
{
|
||||
_currentUser = Anonymous;
|
||||
return new AuthenticationState(_currentUser);
|
||||
}
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, userInfo.Username),
|
||||
new Claim("IsTenantAdmin", userInfo.IsTenantAdmin ? "true" : "false"),
|
||||
new Claim(ClaimTypes.Role, userInfo.IsTenantAdmin ? "TenantAdmin" : "Employee")
|
||||
};
|
||||
var identity = new ClaimsIdentity(claims, "Cookie");
|
||||
_currentUser = new ClaimsPrincipal(identity);
|
||||
return new AuthenticationState(_currentUser);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _http.GetAsync("api/auth/me");
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var userInfo = await response.Content.ReadFromJsonAsync<UserInfo>();
|
||||
if (userInfo != null)
|
||||
var apiUserInfo = await response.Content.ReadFromJsonAsync<UserInfo>();
|
||||
if (apiUserInfo != null)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, userInfo.Username),
|
||||
new Claim("IsTenantAdmin", userInfo.IsTenantAdmin ? "true" : "false"),
|
||||
new Claim(ClaimTypes.Role, userInfo.IsTenantAdmin ? "TenantAdmin" : "Employee")
|
||||
new Claim(ClaimTypes.NameIdentifier, apiUserInfo.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, apiUserInfo.Username),
|
||||
new Claim("IsTenantAdmin", apiUserInfo.IsTenantAdmin ? "true" : "false"),
|
||||
new Claim(ClaimTypes.Role, apiUserInfo.IsTenantAdmin ? "TenantAdmin" : "Employee")
|
||||
};
|
||||
var identity = new ClaimsIdentity(claims, "Cookie");
|
||||
_currentUser = new ClaimsPrincipal(identity);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<persist-component-state />
|
||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Server;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using timetracker.Shared;
|
||||
|
||||
namespace timetracker.Server;
|
||||
|
||||
public class PersistingServerAuthenticationStateProvider : ServerAuthenticationStateProvider, IDisposable
|
||||
{
|
||||
private readonly PersistentComponentState _state;
|
||||
private readonly PersistingComponentStateSubscription _subscription;
|
||||
|
||||
public PersistingServerAuthenticationStateProvider(PersistentComponentState state)
|
||||
{
|
||||
_state = state;
|
||||
_subscription = state.RegisterOnPersisting(PersistAuthenticationState);
|
||||
}
|
||||
|
||||
private async Task PersistAuthenticationState()
|
||||
{
|
||||
var authState = await GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
if (user.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
var idClaim = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
var name = user.Identity.Name;
|
||||
var isAdminClaim = user.FindFirst("IsTenantAdmin")?.Value;
|
||||
|
||||
if (idClaim != null && name != null)
|
||||
{
|
||||
var userInfo = new UserInfo
|
||||
{
|
||||
Id = int.Parse(idClaim),
|
||||
Username = name,
|
||||
IsTenantAdmin = isAdminClaim == "true",
|
||||
IsAuthenticated = true
|
||||
};
|
||||
_state.PersistAsJson("UserInfoState", userInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var userInfo = new UserInfo
|
||||
{
|
||||
IsAuthenticated = false
|
||||
};
|
||||
_state.PersistAsJson("UserInfoState", userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_subscription.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using MudBlazor.Services;
|
||||
using Serilog;
|
||||
using timetracker.Server;
|
||||
using timetracker.Server.Components;
|
||||
using timetracker.Data;
|
||||
using timetracker.Shared;
|
||||
@@ -68,6 +70,7 @@ builder.Services.AddAuthorization(options =>
|
||||
});
|
||||
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, PersistingServerAuthenticationStateProvider>();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
builder.Services.AddTransient<ITenantProvider, TenantProvider>();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,4 +5,5 @@ public class UserInfo
|
||||
public int Id { get; set; }
|
||||
public string Username { get; set; } = "";
|
||||
public bool IsTenantAdmin { get; set; }
|
||||
public bool IsAuthenticated { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user