multi-tenant implementierung
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
@inject NavigationManager Nav
|
||||
@inject IUserNotificationService UserNotificationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject HttpClient Http
|
||||
@implements IDisposable
|
||||
@using System.Security.Claims
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="0" Style="background: #0F172A; border-bottom: 1px solid rgba(255, 255, 255, 0.08);">
|
||||
<MudAppBar Elevation="0" Style="background: color-mix(in srgb, var(--mud-palette-appbar-background) 85%, transparent); border-bottom: 1px solid rgba(255, 255, 255, 0.08); color: var(--mud-palette-appbar-text);">
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="ToggleDrawer" Class="d-flex d-md-none mr-2" />
|
||||
@@ -50,9 +51,74 @@
|
||||
private bool _drawerOpen = true;
|
||||
private bool _isDarkMode;
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
UserNotificationService.OnUserDeleted += HandleUserDeleted;
|
||||
|
||||
try
|
||||
{
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo?.IsTenant == true && !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;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore background error
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
}
|
||||
|
||||
::deep .modern-drawer {
|
||||
background-color: var(--mud-palette-drawer-background) !important;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.08) !important;
|
||||
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.1) !important;
|
||||
transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
@@ -21,7 +22,7 @@
|
||||
|
||||
/* Glassmorphism for the appbar */
|
||||
::deep .mud-appbar {
|
||||
background: rgba(15, 23, 42, 0.85) !important;
|
||||
background: color-mix(in srgb, var(--mud-palette-appbar-background) 85%, transparent) !important;
|
||||
backdrop-filter: blur(12px) !important;
|
||||
-webkit-backdrop-filter: blur(12px) !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject HttpClient Http
|
||||
<div class="nav-container onboarding-nav-menu">
|
||||
@* --- Brand / Logo Section --- *@
|
||||
<div class="nav-header">
|
||||
@@ -37,6 +38,11 @@
|
||||
<span class="admin-divider-text">Admin</span>
|
||||
<hr class="admin-divider-line" />
|
||||
</div>
|
||||
<MudTooltip Text="Firmen verwalten (SaaS)" Placement="Placement.Right">
|
||||
<MudNavLink Href="superadmin" Icon="@Icons.Material.Filled.Business" Class="custom-nav-link admin-link">
|
||||
Firmen verwalten
|
||||
</MudNavLink>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Benutzerverwaltung" Placement="Placement.Right">
|
||||
<MudNavLink Href="admin/users" Icon="@Icons.Material.Filled.AdminPanelSettings" Class="custom-nav-link admin-link">
|
||||
Benutzerverwaltung
|
||||
@@ -61,7 +67,7 @@
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="user-name">@context.User.Identity?.Name</span>
|
||||
<span class="user-status">Online</span>
|
||||
<span class="user-status">@_companyName</span>
|
||||
</div>
|
||||
</div>
|
||||
</NavLink>
|
||||
@@ -93,6 +99,23 @@
|
||||
[Parameter] public EventCallback OnToggleDrawer { get; set; }
|
||||
|
||||
private bool _showVersionBadge;
|
||||
private string _companyName = "Hauptdomain";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tenantInfo = await Http.GetFromJsonAsync<TenantInfo>("api/tenant/current");
|
||||
if (tenantInfo?.IsTenant == true && !string.IsNullOrEmpty(tenantInfo.Name))
|
||||
{
|
||||
_companyName = tenantInfo.Name;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore background error
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
}
|
||||
|
||||
::deep .logo-icon {
|
||||
color: #0EA5E9 !important; /* Sky 500 */
|
||||
color: var(--mud-palette-secondary) !important;
|
||||
font-size: 1.6rem !important;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
@@ -52,7 +52,7 @@
|
||||
.brand-text {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 800;
|
||||
color: #FFFFFF;
|
||||
color: var(--mud-palette-appbar-text);
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
@@ -104,7 +104,7 @@
|
||||
::deep .custom-nav-link,
|
||||
::deep .custom-nav-link .mud-nav-link-text,
|
||||
::deep .custom-nav-link .mud-nav-link-icon-default {
|
||||
color: #94A3B8 !important; /* Slate 400 */
|
||||
color: var(--mud-palette-drawer-text) !important;
|
||||
}
|
||||
|
||||
/* NavLink hover */
|
||||
@@ -115,15 +115,15 @@
|
||||
::deep .custom-nav-link:hover,
|
||||
::deep .custom-nav-link:hover .mud-nav-link-text,
|
||||
::deep .custom-nav-link:hover .mud-nav-link-icon-default {
|
||||
color: #F8FAFC !important; /* Slate 50 */
|
||||
color: var(--mud-palette-appbar-text) !important;
|
||||
}
|
||||
|
||||
/* NavLink active state (Fixes active color contrast issue) */
|
||||
::deep .custom-nav-link.active,
|
||||
::deep .custom-nav-link .active {
|
||||
background: linear-gradient(90deg, rgba(14, 165, 233, 0.15) 0%, rgba(14, 165, 233, 0.04) 100%) !important;
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--mud-palette-secondary) 15%, transparent) 0%, color-mix(in srgb, var(--mud-palette-secondary) 4%, transparent) 100%) !important;
|
||||
font-weight: 600 !important;
|
||||
box-shadow: inset 3px 0 0 #0EA5E9 !important;
|
||||
box-shadow: inset 3px 0 0 var(--mud-palette-secondary) !important;
|
||||
}
|
||||
|
||||
::deep .custom-nav-link.active,
|
||||
@@ -132,7 +132,7 @@
|
||||
::deep .custom-nav-link .active .mud-nav-link-text,
|
||||
::deep .custom-nav-link.active .mud-nav-link-icon-default,
|
||||
::deep .custom-nav-link .active .mud-nav-link-icon-default {
|
||||
color: #0EA5E9 !important; /* Sky 500 */
|
||||
color: var(--mud-palette-secondary) !important;
|
||||
}
|
||||
|
||||
/* Admin link color overrides & active state */
|
||||
@@ -208,7 +208,7 @@
|
||||
::deep .user-avatar {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
background: linear-gradient(135deg, #0EA5E9 0%, #0284C7 100%) !important;
|
||||
background: linear-gradient(135deg, var(--mud-palette-secondary) 0%, var(--mud-palette-secondary-darken) 100%) !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 0.875rem !important;
|
||||
@@ -222,7 +222,7 @@
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #10B981; /* Emerald 500 */
|
||||
border: 2px solid #0F172A;
|
||||
border: 2px solid var(--mud-palette-drawer-background);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
.user-name {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
color: var(--mud-palette-appbar-text);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user