Updated sidebar and header
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.card-body {
|
||||
@apply px-5 py-5;
|
||||
@apply px-5 py-5 text-gray-300;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<div class="col-span-12 md:col-span-6 xl:col-span-3">
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<div class="font-medium leading-[1.1] tracking-tight">
|
||||
<div class="animate-shimmer bg-gradient-to-r from-violet-400 via-sky-400 to-purple-400 bg-clip-text font-semibold text-transparent text-3xl" style="animation-duration: 5s; background-size: 200% 100%">
|
||||
Welcome, @(Username)
|
||||
|
||||
@@ -1,57 +1,28 @@
|
||||
@using MoonCore.Exceptions
|
||||
@using Moonlight.Client.Interfaces
|
||||
@using Moonlight.Client.Services
|
||||
@using Moonlight.Client.UI.Partials
|
||||
@using Moonlight.Shared.Misc
|
||||
@using Moonlight.Client.UI.Components
|
||||
@using Moonlight.Client.UI.Partials
|
||||
@using MoonCore.Blazor.Tailwind.Toasts
|
||||
@using MoonCore.Blazor.Tailwind.Modals
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject ILogger<MainLayout> Logger
|
||||
@inject FrontendConfiguration Configuration
|
||||
|
||||
<PageTitle>@Configuration.Title</PageTitle>
|
||||
|
||||
<ThemeLoader />
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
<div class="h-full w-full min-h-[100dvh] flex items-center justify-center">
|
||||
<div id="loader"></div>
|
||||
</div>
|
||||
}
|
||||
else if (CurrentScreen != null)
|
||||
{
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
@CurrentScreen
|
||||
</CascadingValue>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
<AppSidebar Layout="this"/>
|
||||
|
||||
<div class="lg:pl-72">
|
||||
<AppHeader Layout="this"/>
|
||||
|
||||
<main class="py-10">
|
||||
<div class="px-4 sm:px-6 lg:px-8">
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="relative isolate flex min-h-svh w-full max-lg:flex-col bg-gray-950">
|
||||
<AppSidebar Layout="this"/>
|
||||
<AppHeader Layout="this" />
|
||||
|
||||
<main class="flex flex-1 flex-col lg:pb-5 lg:min-w-0 lg:pt-5 lg:pr-3.5 lg:pl-64">
|
||||
<div class="grow p-6 lg:rounded-lg lg:p-10 lg:ring-1 lg:shadow-xs lg:bg-gray-900 lg:ring-white/10">
|
||||
<div class="mx-auto max-w-7xl">
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
||||
<ToastLauncher/>
|
||||
<ModalLauncher/>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
// Mobile navigation
|
||||
@@ -63,26 +34,4 @@ else
|
||||
ShowMobileNavigation = !ShowMobileNavigation;
|
||||
await OnStateChanged();
|
||||
}
|
||||
|
||||
// App loaders & screens
|
||||
private bool IsLoading = true;
|
||||
private RenderFragment? CurrentScreen;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender)
|
||||
return;
|
||||
|
||||
await Load();
|
||||
}
|
||||
|
||||
public async Task Load()
|
||||
{
|
||||
IsLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
//
|
||||
IsLoading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +1,49 @@
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using MoonCore.Blazor.Tailwind.Auth
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
@inject ToastService ToastService
|
||||
@inject NavigationManager Navigation
|
||||
@inject AuthenticationStateManager AuthStateManager
|
||||
|
||||
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 bg-gray-800/60 backdrop-blur px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
|
||||
@if (!Layout.ShowMobileNavigation)
|
||||
{
|
||||
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
|
||||
<span class="sr-only">Open sidebar</span>
|
||||
<i class="icon-menu text-xl"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
<div class="h-6 w-px bg-gray-800 lg:hidden" aria-hidden="true"></div>
|
||||
|
||||
<div class="flex justify-between gap-x-4 lg:gap-x-6 w-full">
|
||||
<div></div>
|
||||
<div class="flex items-center gap-x-4 lg:gap-x-6">
|
||||
<div class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10" aria-hidden="true"></div>
|
||||
|
||||
<div class="relative">
|
||||
<button @onclick="ToggleProfileNav" @onfocusout="ProfileNav_OnFocusOut" type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<img class="h-8 w-8 rounded-full" src="/img/pfp_placeholder.png" alt="">
|
||||
<span class="hidden lg:flex lg:items-center">
|
||||
<span class="ml-4 text-sm font-semibold leading-6 text-gray-100" aria-hidden="true">
|
||||
@("@" + Username)
|
||||
</span>
|
||||
<svg class="ml-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="@(ShowProfileNav ? "opacity-100" : "opacity-0 hidden") transition ease-out duration-100 absolute right-0 z-10 mt-2.5 w-44 origin-top-right rounded-md bg-gray-750 py-2 shadow-lg ring-1 ring-gray-100/5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
|
||||
<a @onclick="Logout" @onclick:preventDefault href="#" class="block px-3 py-1 text-sm leading-6 text-gray-100 hover:text-primary-500" role="menuitem" tabindex="-1" id="user-menu-item-1">Sign out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="flex items-center px-4 lg:hidden border-b border-white/5">
|
||||
<div class="py-2.5">
|
||||
<span class="relative">
|
||||
<button @onclick="Layout.ToggleMobileNavigation" aria-label="Open navigation"
|
||||
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 sm:text-sm/5 text-white"
|
||||
type="button">
|
||||
<i class="icon-menu text-xl"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<nav class="flex flex-1 items-center gap-4 py-2.5">
|
||||
<div aria-hidden="true" class="-ml-4 flex-1">
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="relative">
|
||||
<div class="relative flex min-w-0 cursor-default items-center gap-3 rounded-lg p-2 text-left text-base/6 font-medium sm:text-sm/5 text-white">
|
||||
<div data-slot="avatar"
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img
|
||||
class="h-8 rounded-full"
|
||||
src="/img/pfp_placeholder.png"
|
||||
alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public MainLayout Layout { get; set; }
|
||||
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
|
||||
|
||||
private bool ShowProfileNav = false;
|
||||
private string Username;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var identity = await AuthState;
|
||||
var usernameClaim = identity.User.Claims.ToArray().First(x => x.Type == "username");
|
||||
Username = usernameClaim.Value;
|
||||
}
|
||||
|
||||
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if(!firstRender)
|
||||
if (!firstRender)
|
||||
return Task.CompletedTask;
|
||||
|
||||
Layout.OnStateChanged += () => InvokeAsync(StateHasChanged);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ToggleProfileNav()
|
||||
{
|
||||
ShowProfileNav = !ShowProfileNav;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private Task ProfileNav_OnFocusOut()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(200);
|
||||
|
||||
ShowProfileNav = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Logout()
|
||||
=> await AuthStateManager.Logout();
|
||||
}
|
||||
@@ -1,141 +1,215 @@
|
||||
@using Moonlight.Client.Interfaces
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using MoonCore.Blazor.Tailwind.Auth
|
||||
@using Moonlight.Client.Interfaces
|
||||
@using Moonlight.Client.Models
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
@inject ToastService ToastService
|
||||
@inject NavigationManager Navigation
|
||||
@inject AuthenticationStateManager AuthStateManager
|
||||
@inject IEnumerable<ISidebarItemProvider> SidebarItemProviders
|
||||
|
||||
@{
|
||||
var url = new Uri(Navigation.Uri);
|
||||
}
|
||||
|
||||
<div
|
||||
class="relative z-50 lg:hidden transition-opacity ease-linear duration-300 @(Layout.ShowMobileNavigation ? "opacity-100" : "opacity-0 pointer-events-none")"
|
||||
role="dialog" aria-modal="true">
|
||||
<div class="fixed inset-0 bg-gray-900/80"></div>
|
||||
<div class="fixed inset-y-0 left-0 w-64 max-lg:hidden">
|
||||
<nav class="flex h-full min-h-0 flex-col">
|
||||
<div class="flex flex-col border-b p-4 border-white/5">
|
||||
<span class="relative">
|
||||
<div type="button"
|
||||
class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-lg font-medium text-gray-100">
|
||||
<span
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img class="h-8 rounded-full"
|
||||
src="/svg/logo.svg"
|
||||
alt=""/>
|
||||
</span>
|
||||
<span class="truncate">Moonlight</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col overflow-y-auto p-4 mt-1">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
@foreach (var item in Items)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.Key))
|
||||
{
|
||||
<h3 class="mt-4 px-2 text-sm/5 font-medium text-gray-400">
|
||||
@item.Key
|
||||
</h3>
|
||||
}
|
||||
|
||||
<div class="fixed inset-0 flex">
|
||||
<div
|
||||
class="relative mr-16 flex w-full max-w-xs flex-1 transition ease-in-out duration-300 transform @(Layout.ShowMobileNavigation ? "translate-x-0" : "-translate-x-full")">
|
||||
foreach (var sidebarItem in item.Value)
|
||||
{
|
||||
var isMatch = sidebarItem.RequiresExactMatch
|
||||
? url.LocalPath == sidebarItem.Path
|
||||
: url.LocalPath.StartsWith(sidebarItem.Path);
|
||||
|
||||
@if (isMatch)
|
||||
{
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-2 -left-4 w-0.5 rounded-full bg-white"
|
||||
style="opacity: 1;">
|
||||
</span>
|
||||
<a class="flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-left text-base/6 font-normal bg-white/5 sm:py-2 sm:text-sm/5 text-white"
|
||||
href="@sidebarItem.Path">
|
||||
<i class="@sidebarItem.Icon text-lg"></i>
|
||||
<span class="truncate">
|
||||
@sidebarItem.Name
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="relative">
|
||||
<a class="flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-left text-base/6 font-normal sm:py-2 sm:text-sm/5 text-white hover:bg-white/5"
|
||||
href="@sidebarItem.Path">
|
||||
<i class="@sidebarItem.Icon text-lg"></i>
|
||||
<span class="truncate">
|
||||
@sidebarItem.Name
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col border-t p-4 max-lg:hidden border-white/5 mt-2.5">
|
||||
<div
|
||||
class="absolute left-full top-0 flex w-16 justify-center pt-5 ease-in-out duration-300 @(Layout.ShowMobileNavigation ? "opacity-100" : "opacity-0")">
|
||||
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5">
|
||||
<span class="sr-only">Close sidebar</span>
|
||||
class="flex w-full items-center px-2 py-2.5 gap-6 rounded-lg text-left text-base/6 font-medium sm:py-2 sm:text-sm/5 text-white">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span class="inline-grid shrink-0 align-middle">
|
||||
<img class="h-8 rounded-full"
|
||||
src="/img/pfp_placeholder.png"
|
||||
alt=""/>
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<div class="block truncate text-sm/5 font-medium text-white">
|
||||
@Username
|
||||
</div>
|
||||
<div class="block truncate text-xs/5 font-normal text-gray-400">
|
||||
@Email
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" @onclick:preventDefault @onclick="Logout" class="flex items-center">
|
||||
<i class="icon-log-out text-lg"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="lg:hidden z-50 transition-opacity ease-linear duration-300 @(Layout.ShowMobileNavigation ? "opacity-100" : "opacity-0 pointer-events-none")"
|
||||
role="dialog" tabindex="-1">
|
||||
<div class="fixed inset-0 bg-black/30"></div>
|
||||
<div class="fixed inset-y-0 w-full max-w-80 p-2">
|
||||
<div
|
||||
class="relative flex h-full flex-col rounded-lg shadow-xs ring-1 bg-gray-900 ring-white/10 transition ease-in-out duration-300 transform @(Layout.ShowMobileNavigation ? "translate-x-0" : "-translate-x-full")">
|
||||
<div class="border-b p-4 border-white/5 flex justify-between px-5 pt-3">
|
||||
<div class="flex items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 font-medium sm:py-2 sm:text-sm/5 text-white">
|
||||
<div data-slot="avatar"
|
||||
class="inline-grid shrink-0 align-middle">
|
||||
<img
|
||||
class="h-8 rounded-full" src="/svg/logo.svg" alt=""/>
|
||||
</div>
|
||||
<div class="truncate">Moonlight</div>
|
||||
</div>
|
||||
|
||||
<button @onclick="Layout.ToggleMobileNavigation" aria-label="Close navigation" type="button"
|
||||
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 text-white">
|
||||
<i class="icon-x text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-900 px-6 pb-4 ring-1 ring-white/10">
|
||||
<div class="flex h-16 shrink-0 items-center">
|
||||
<img class="h-8 w-auto" src="/svg/logo.svg" alt="Logo">
|
||||
</div>
|
||||
|
||||
<nav class="flex flex-1 flex-col">
|
||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||
@foreach (var group in Items)
|
||||
<nav class="flex h-full min-h-0 flex-col">
|
||||
<div
|
||||
class="flex flex-1 flex-col overflow-y-auto p-4">
|
||||
<div data-slot="section" class="flex flex-col gap-0.5">
|
||||
@foreach (var item in Items)
|
||||
{
|
||||
<li>
|
||||
@if (!string.IsNullOrEmpty(group.Key))
|
||||
if (!string.IsNullOrEmpty(item.Key))
|
||||
{
|
||||
<h3 class="mt-4 px-2 text-sm/5 font-medium text-gray-400">
|
||||
@item.Key
|
||||
</h3>
|
||||
}
|
||||
|
||||
foreach (var sidebarItem in item.Value)
|
||||
{
|
||||
var isMatch = sidebarItem.RequiresExactMatch
|
||||
? url.LocalPath == sidebarItem.Path
|
||||
: url.LocalPath.StartsWith(sidebarItem.Path);
|
||||
|
||||
@if (isMatch)
|
||||
{
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400">
|
||||
@group.Key
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-2 -left-4 w-0.5 rounded-full bg-white"
|
||||
style="opacity: 1;">
|
||||
</span>
|
||||
<a class="flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-left text-base/6 font-normal bg-white/5 sm:py-2 sm:text-sm/5 text-white"
|
||||
href="@sidebarItem.Path">
|
||||
<i class="@sidebarItem.Icon text-lg"></i>
|
||||
<span class="truncate">
|
||||
@sidebarItem.Name
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul role="list" class="-mx-2 space-y-1">
|
||||
@foreach (var item in group.Value)
|
||||
{
|
||||
var isMatch = item.RequiresExactMatch
|
||||
? url.LocalPath == item.Path
|
||||
: url.LocalPath.StartsWith(item.Path);
|
||||
|
||||
<li>
|
||||
@if (isMatch)
|
||||
{
|
||||
<a href="@item.Path"
|
||||
class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@item.Path"
|
||||
class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
|
||||
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-800/60 px-6 pb-4">
|
||||
<div class="flex h-16 shrink-0 items-center">
|
||||
<img class="h-10 w-auto" src="/svg/logo.svg" alt="Logo">
|
||||
</div>
|
||||
<nav class="flex flex-1 flex-col">
|
||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||
@foreach (var group in Items)
|
||||
{
|
||||
<li>
|
||||
@if (!string.IsNullOrEmpty(group.Key))
|
||||
{
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400 my-2">
|
||||
@group.Key
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul role="list" class="-mx-2 space-y-1">
|
||||
@foreach (var item in group.Value)
|
||||
{
|
||||
var isMatch = item.RequiresExactMatch
|
||||
? url.LocalPath == item.Path
|
||||
: url.LocalPath.StartsWith(item.Path);
|
||||
|
||||
<li>
|
||||
@if (isMatch)
|
||||
{
|
||||
<a href="@item.Path"
|
||||
class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
else
|
||||
{
|
||||
<div class="relative">
|
||||
<a class="flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-left text-base/6 font-normal sm:py-2 sm:text-sm/5 text-white hover:bg-white/5"
|
||||
href="@sidebarItem.Path">
|
||||
<i class="@sidebarItem.Icon text-lg"></i>
|
||||
<span class="truncate">
|
||||
@sidebarItem.Name
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@item.Path"
|
||||
class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex-1"></div>
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="relative">
|
||||
|
||||
<a class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 sm:py-2 sm:text-sm/5 text-white"
|
||||
href="#" @onclick:preventDefault @onclick="Logout">
|
||||
<i class="icon-log-out"></i>
|
||||
<span class="truncate">Logout</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public MainLayout Layout { get; set; }
|
||||
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
|
||||
|
||||
private Dictionary<string, SidebarItem[]> Items = new();
|
||||
|
||||
private string Username;
|
||||
private string Email;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var identity = await AuthState;
|
||||
|
||||
Username = identity.User.Claims.First(x => x.Type == "username").Value;
|
||||
Email = identity.User.Claims.First(x => x.Type == "email").Value;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var sidebarItems = new List<SidebarItem>();
|
||||
@@ -167,4 +241,9 @@
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Logout()
|
||||
{
|
||||
await AuthStateManager.Logout();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user