User
Deploy refreshed UI theme
be17a46
Raw
History Blame Contribute Delete
8.49 kB
@page "/login"
@layout Layout.EmptyLayout
@rendermode @(new InteractiveServerRenderMode(prerender: false))
@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
@inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthStateProvider
@inject MenuAuthorizationService MenuAuthorization
@inject IJSRuntime JS
<PageTitle>@AppLocalization.Text("common.signIn", "Sign In") - Taskify</PageTitle>
<div class="flex min-h-screen items-center justify-center bg-slate-50 px-4 py-10 sm:px-6 lg:px-8">
<div class="w-full max-w-md space-y-7 rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
<div>
<div class="flex justify-center">
<span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white shadow-sm">
<i data-lucide="clipboard-check" class="h-6 w-6"></i>
</span>
</div>
<h2 class="mt-5 text-center text-2xl font-bold text-slate-900">@AppLocalization.Text("common.signIn", "Sign In") - Taskify</h2>
<p class="mt-2 text-center text-sm text-slate-500">@AppLocalization.PageDescription("dashboard", "Continue to your work dashboard.")</p>
</div>
<form method="post" action="/account/login" class="space-y-5">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
<div class="space-y-4">
<div>
<label for="usernameOrEmail" class="block text-sm font-medium text-slate-700 mb-1">@AppLocalization.Text("common.user", "Username or Email")</label>
<input id="usernameOrEmail" name="UsernameOrEmail" required placeholder="@AppLocalization.Text("common.searchPlaceholder", "Enter username or email")"
class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all" />
</div>
<div class="relative">
<label for="password" class="block text-sm font-medium text-slate-700 mb-1">Password</label>
<input id="password" name="Password" type="@(showPassword ? "text" : "password")" required placeholder="Password"
class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all pr-10" />
<button type="button" @onclick="TogglePassword" class="absolute right-3 top-[28px] text-slate-400 hover:text-slate-600 focus:outline-none">
<i data-lucide="@(showPassword ? "eye-off" : "eye")" class="h-5 w-5"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<input id="rememberMe" name="RememberMe" type="checkbox" value="true"
class="h-4 w-4 rounded border-slate-350 text-slate-900 focus:ring-slate-900 focus:outline-none" />
<label for="rememberMe" class="ml-2 block text-sm text-slate-600 font-medium">@AppLocalization.Text("common.rememberMe", "Remember me")</label>
</div>
</div>
@if (ShowUnavailableError)
{
<div class="p-3 rounded-lg bg-amber-50 border border-amber-100">
<p class="text-sm text-amber-800 text-center">@AppLocalization.Text("result.apiUnavailable", "Unable to reach the API server. Start TaskTrackingSystem.WebApi and try again.")</p>
</div>
}
else if (ShowError)
{
<div class="p-3 rounded-lg bg-red-50 border border-red-100">
<p class="text-sm text-red-600 text-center">@ResultMessages.InvalidCredentials</p>
</div>
}
@if (ShowLoggedOutMessage)
{
<div class="p-3 rounded-lg bg-emerald-50 border border-emerald-100">
<p class="text-sm text-emerald-700 text-center">@AppLocalization.Text("result.signedOut", "You have been signed out successfully.")</p>
</div>
}
else if (ShowResetMessage)
{
<div class="p-3 rounded-lg bg-emerald-50 border border-emerald-100">
<p class="text-sm text-emerald-700 text-center">@AppLocalization.Text("result.passwordUpdated", "Password updated successfully. Please sign in with your new password.")</p>
</div>
}
<p class="text-center text-sm text-slate-600">
@AppLocalization.Text("auth.needAccount", "Need an account?")
<a href="/register" class="font-medium text-slate-900 hover:underline">@AppLocalization.Text("auth.register", "Register")</a>
</p>
<div>
<button type="submit" class="group relative flex w-full justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-700">
@AppLocalization.Text("common.signIn", "Sign In")
</button>
</div>
<p class="text-center text-sm text-slate-600">
<a href="/reset-password" class="font-medium text-slate-900 hover:underline">@AppLocalization.Text("auth.resetPassword", "Reset password")</a>
</p>
</form>
</div>
</div>
@code {
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
[SupplyParameterFromQuery(Name = "error")]
private string? Error { get; set; }
[SupplyParameterFromQuery(Name = "loggedOut")]
private string? LoggedOut { get; set; }
[SupplyParameterFromQuery(Name = "reset")]
private string? Reset { get; set; }
private bool ShowError => string.Equals(Error, "invalid", StringComparison.OrdinalIgnoreCase);
private bool ShowUnavailableError => string.Equals(Error, "unavailable", StringComparison.OrdinalIgnoreCase);
private bool ShowLoggedOutMessage => string.Equals(LoggedOut, "true", StringComparison.OrdinalIgnoreCase);
private bool ShowResetMessage => string.Equals(Reset, "true", StringComparison.OrdinalIgnoreCase);
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JS.InvokeVoidAsync("initIcons");
if (firstRender)
{
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity?.IsAuthenticated == true)
{
Navigation.NavigateTo(await ResolveLandingPageAsync(authState.User), forceLoad: true);
}
}
}
private bool showPassword = false;
private async Task TogglePassword()
{
showPassword = !showPassword;
await JS.InvokeVoidAsync("initIcons");
}
private async Task<string> ResolveLandingPageAsync(System.Security.Claims.ClaimsPrincipal user)
{
try
{
var menus = await MenuAuthorization.GetUserMenusAsync(user);
var landing = FindFirstDashboardMenu(menus);
if (!string.IsNullOrWhiteSpace(landing))
{
return landing;
}
}
catch
{
}
return "/dashboard";
}
private static string? FindFirstDashboardMenu(IEnumerable<TaskTrackingSystem.Shared.Models.Menu.MenuDto> menus)
{
foreach (var menu in menus)
{
var found = FindFirstDashboardMenu(menu);
if (!string.IsNullOrWhiteSpace(found))
{
return found;
}
}
return null;
}
private static string? FindFirstDashboardMenu(TaskTrackingSystem.Shared.Models.Menu.MenuDto menu)
{
if (!string.IsNullOrWhiteSpace(menu.MenuUrl) &&
menu.MenuUrl.StartsWith("/dashboard", StringComparison.OrdinalIgnoreCase))
{
return menu.MenuUrl;
}
foreach (var subMenu in menu.SubMenus)
{
var found = FindFirstDashboardMenu(subMenu);
if (!string.IsNullOrWhiteSpace(found))
{
return found;
}
}
return null;
}
}