Spaces:
Running
Running
File size: 8,491 Bytes
3418204 691d646 3418204 8c88a89 3418204 f6d34ff 3418204 f6d34ff be17a46 f6d34ff 3418204 8c88a89 3418204 f6d34ff 3418204 f6d34ff 3418204 8c88a89 3418204 f6d34ff 3418204 8c88a89 3418204 8c88a89 3418204 8c88a89 3418204 8c88a89 3418204 01be06d 8c88a89 01be06d 3418204 7f6afc2 8c88a89 7f6afc2 3418204 f6d34ff 8c88a89 3418204 7f6afc2 8c88a89 7f6afc2 3418204 01be06d 3418204 01be06d 3418204 691d646 3418204 691d646 3418204 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | @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;
}
}
|