@inherits LayoutComponentBase @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager Nav @inject IUserNotificationService UserNotificationService @inject IJSRuntime JSRuntime @inject HttpClient Http @implements IDisposable @using System.Security.Claims @Body @code { [PersistentState(AllowUpdates = true)] public TenantInfo? PersistentTenantInfo { get; set; } private bool _drawerOpen = true; private bool _isDarkMode; protected override async Task OnInitializedAsync() { UserNotificationService.OnUserDeleted += HandleUserDeleted; if (PersistentTenantInfo != null) { ApplyTheme(PersistentTenantInfo); } else { try { var tenantInfo = await Http.GetFromJsonAsync("api/tenant/current"); if (tenantInfo != null) { ApplyTheme(tenantInfo); PersistentTenantInfo = tenantInfo; } } catch { // Ignore background error } } } private void ApplyTheme(TenantInfo tenantInfo) { if (tenantInfo.IsTenant && !string.IsNullOrEmpty(tenantInfo.PrimaryColor)) { 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; } } private bool IsLightColor(string hexColor) { if (string.IsNullOrEmpty(hexColor)) return false; hexColor = hexColor.TrimStart('#'); if (hexColor.Length == 3) { hexColor = new string(new[] { hexColor[0], hexColor[0], hexColor[1], hexColor[1], hexColor[2], hexColor[2] }); } if (hexColor.Length != 6) return false; try { var r = Convert.ToInt32(hexColor.Substring(0, 2), 16); var g = Convert.ToInt32(hexColor.Substring(2, 2), 16); var b = Convert.ToInt32(hexColor.Substring(4, 2), 16); var luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255.0; return luminance > 0.6; // slightly conservative threshold for white text readability } catch { return false; } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { try { var saved = await JSRuntime.InvokeAsync("localStorage.getItem", "darkMode"); if (!string.IsNullOrEmpty(saved)) { _isDarkMode = bool.Parse(saved); StateHasChanged(); } } catch { // Ignored during prerendering/SSR } } } private async Task ToggleDarkMode() { _isDarkMode = !_isDarkMode; await JSRuntime.InvokeVoidAsync("localStorage.setItem", "darkMode", _isDarkMode.ToString().ToLower()); } private async Task HandleUserDeleted(int deletedUserId) { var state = await AuthStateProvider.GetAuthenticationStateAsync(); var idClaim = state.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (idClaim != null && int.TryParse(idClaim, out var myId) && myId == deletedUserId) { await InvokeAsync(() => Nav.NavigateTo("/auth/logout", forceLoad: true)); } } public void Dispose() { UserNotificationService.OnUserDeleted -= HandleUserDeleted; } private readonly MudTheme _theme = new() { PaletteLight = new PaletteLight { Primary = "#1E293B", PrimaryDarken = "#0F172A", PrimaryLighten = "#475569", Secondary = "#0EA5E9", SecondaryDarken = "#0284C7", AppbarBackground = "#0F172A", Background = "#F8FAFC", DrawerBackground = "#0F172A", DrawerText = "#94A3B8", DrawerIcon = "#94A3B8", Surface = "#FFFFFF", TextPrimary = "#0F172A", TextSecondary = "#475569", }, PaletteDark = new PaletteDark { Primary = "#0EA5E9", PrimaryDarken = "#0284C7", PrimaryLighten = "#38BDF8", Secondary = "#0EA5E9", SecondaryDarken = "#0284C7", AppbarBackground = "#0F172A", AppbarText = "#F8FAFC", Background = "#0B0F19", DrawerBackground = "#0F172A", DrawerText = "#94A3B8", DrawerIcon = "#94A3B8", Surface = "#1E293B", TextPrimary = "#F8FAFC", TextSecondary = "#94A3B8", ActionDefault = "#94A3B8", Divider = "rgba(255, 255, 255, 0.08)", } }; private void ToggleDrawer() => _drawerOpen = !_drawerOpen; }