149 lines
6.2 KiB
Plaintext
149 lines
6.2 KiB
Plaintext
|
|
@page "/account/login"
|
||
|
|
@using TrueCV.Web.Components.Layout
|
||
|
|
@layout MainLayout
|
||
|
|
@rendermode InteractiveServer
|
||
|
|
|
||
|
|
@using Microsoft.AspNetCore.Identity
|
||
|
|
@using TrueCV.Infrastructure.Identity
|
||
|
|
|
||
|
|
@inject SignInManager<ApplicationUser> SignInManager
|
||
|
|
@inject NavigationManager NavigationManager
|
||
|
|
|
||
|
|
<PageTitle>Login - TrueCV</PageTitle>
|
||
|
|
|
||
|
|
<div class="container py-5">
|
||
|
|
<div class="row justify-content-center">
|
||
|
|
<div class="col-md-5">
|
||
|
|
<div class="card border-0 shadow">
|
||
|
|
<div class="card-body p-5">
|
||
|
|
<div class="text-center mb-4">
|
||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" class="bi bi-patch-check-fill text-primary mb-3" viewBox="0 0 16 16">
|
||
|
|
<path d="M10.067.87a2.89 2.89 0 0 0-4.134 0l-.622.638-.89-.011a2.89 2.89 0 0 0-2.924 2.924l.01.89-.636.622a2.89 2.89 0 0 0 0 4.134l.637.622-.011.89a2.89 2.89 0 0 0 2.924 2.924l.89-.01.622.636a2.89 2.89 0 0 0 4.134 0l.622-.637.89.011a2.89 2.89 0 0 0 2.924-2.924l-.01-.89.636-.622a2.89 2.89 0 0 0 0-4.134l-.637-.622.011-.89a2.89 2.89 0 0 0-2.924-2.924l-.89.01-.622-.636zm.287 5.984-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7 8.793l2.646-2.647a.5.5 0 0 1 .708.708z"/>
|
||
|
|
</svg>
|
||
|
|
<h3 class="fw-bold">Welcome Back</h3>
|
||
|
|
<p class="text-muted">Sign in to your TrueCV account</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
@if (!string.IsNullOrEmpty(_errorMessage))
|
||
|
|
{
|
||
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||
|
|
@_errorMessage
|
||
|
|
<button type="button" class="btn-close" @onclick="() => _errorMessage = null" aria-label="Close"></button>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
|
||
|
|
<EditForm Model="_model" OnValidSubmit="HandleLogin" FormName="login">
|
||
|
|
<DataAnnotationsValidator />
|
||
|
|
|
||
|
|
<div class="mb-3">
|
||
|
|
<label for="email" class="form-label">Email address</label>
|
||
|
|
<InputText id="email" class="form-control form-control-lg" @bind-Value="_model.Email"
|
||
|
|
placeholder="name@example.com" />
|
||
|
|
<ValidationMessage For="() => _model.Email" class="text-danger" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="mb-3">
|
||
|
|
<label for="password" class="form-label">Password</label>
|
||
|
|
<InputText id="password" type="password" class="form-control form-control-lg"
|
||
|
|
@bind-Value="_model.Password" placeholder="Enter your password" />
|
||
|
|
<ValidationMessage For="() => _model.Password" class="text-danger" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="mb-3 form-check">
|
||
|
|
<InputCheckbox id="rememberMe" class="form-check-input" @bind-Value="_model.RememberMe" />
|
||
|
|
<label class="form-check-label" for="rememberMe">
|
||
|
|
Remember me
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="d-grid">
|
||
|
|
<button type="submit" class="btn btn-primary btn-lg" disabled="@_isLoading">
|
||
|
|
@if (_isLoading)
|
||
|
|
{
|
||
|
|
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||
|
|
<span>Signing in...</span>
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
<span>Sign In</span>
|
||
|
|
}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</EditForm>
|
||
|
|
|
||
|
|
<hr class="my-4" />
|
||
|
|
|
||
|
|
<div class="text-center">
|
||
|
|
<p class="mb-0">
|
||
|
|
Don't have an account?
|
||
|
|
<a href="/account/register" class="text-decoration-none fw-medium">Create one</a>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
@code {
|
||
|
|
private LoginModel _model = new();
|
||
|
|
private bool _isLoading;
|
||
|
|
private string? _errorMessage;
|
||
|
|
|
||
|
|
[SupplyParameterFromQuery]
|
||
|
|
public string? ReturnUrl { get; set; }
|
||
|
|
|
||
|
|
private async Task HandleLogin()
|
||
|
|
{
|
||
|
|
_isLoading = true;
|
||
|
|
_errorMessage = null;
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var result = await SignInManager.PasswordSignInAsync(
|
||
|
|
_model.Email,
|
||
|
|
_model.Password,
|
||
|
|
_model.RememberMe,
|
||
|
|
lockoutOnFailure: false);
|
||
|
|
|
||
|
|
if (result.Succeeded)
|
||
|
|
{
|
||
|
|
var returnUrl = string.IsNullOrEmpty(ReturnUrl) ? "/dashboard" : ReturnUrl;
|
||
|
|
NavigationManager.NavigateTo(returnUrl, forceLoad: true);
|
||
|
|
}
|
||
|
|
else if (result.IsLockedOut)
|
||
|
|
{
|
||
|
|
_errorMessage = "This account has been locked out. Please try again later.";
|
||
|
|
}
|
||
|
|
else if (result.IsNotAllowed)
|
||
|
|
{
|
||
|
|
_errorMessage = "This account is not allowed to sign in.";
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_errorMessage = "Invalid email or password.";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
_errorMessage = $"An error occurred: {ex.Message}";
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
_isLoading = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private sealed class LoginModel
|
||
|
|
{
|
||
|
|
[System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Email is required")]
|
||
|
|
[System.ComponentModel.DataAnnotations.EmailAddress(ErrorMessage = "Invalid email format")]
|
||
|
|
public string Email { get; set; } = string.Empty;
|
||
|
|
|
||
|
|
[System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Password is required")]
|
||
|
|
public string Password { get; set; } = string.Empty;
|
||
|
|
|
||
|
|
public bool RememberMe { get; set; }
|
||
|
|
}
|
||
|
|
}
|