137 lines
4.7 KiB
Plaintext
137 lines
4.7 KiB
Plaintext
@page "/login"
|
|
@using WebClientTpos.Shared.DTOs
|
|
@using WebClientTpos.Shared
|
|
@inject HttpClient Http
|
|
@inject NavigationManager Navigation
|
|
@inject IStringLocalizer<Login> L
|
|
|
|
@*
|
|
EN: Login page with email/password authentication.
|
|
VI: Trang đăng nhập với xác thực email/mật khẩu.
|
|
*@
|
|
|
|
<PageTitle>@L["Auth_Login_Title"]</PageTitle>
|
|
|
|
<div class="auth-container">
|
|
<section class="auth-card">
|
|
<h1 class="auth-title">@L["Auth_Login_Title"]</h1>
|
|
<p class="auth-subtitle">@L["Auth_Login_Subtitle"]</p>
|
|
|
|
<EditForm Model="@loginModel" OnValidSubmit="HandleLogin" FormName="LoginForm">
|
|
<DataAnnotationsValidator />
|
|
|
|
<div class="form-group">
|
|
<label for="login-email">@L["Auth_Login_Email"] *</label>
|
|
<InputText id="login-email"
|
|
@bind-Value="loginModel.Email"
|
|
class="form-input"
|
|
placeholder="email@example.com"
|
|
autocomplete="email" />
|
|
<ValidationMessage For="() => loginModel.Email" class="validation-message" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="login-password">@L["Auth_Login_Password"] *</label>
|
|
<InputText id="login-password"
|
|
@bind-Value="loginModel.Password"
|
|
type="password"
|
|
class="form-input"
|
|
placeholder="••••••••"
|
|
autocomplete="current-password" />
|
|
<ValidationMessage For="() => loginModel.Password" class="validation-message" />
|
|
</div>
|
|
|
|
<div class="form-actions-row">
|
|
<div class="checkbox-group">
|
|
<InputCheckbox id="remember-me"
|
|
@bind-Value="loginModel.RememberMe"
|
|
class="form-checkbox" />
|
|
<label for="remember-me" class="checkbox-label">@L["Auth_Login_RememberMe"]</label>
|
|
</div>
|
|
|
|
<a href="/forgot-password" class="link-secondary">@L["Auth_Login_ForgotPassword"]</a>
|
|
</div>
|
|
|
|
<button type="submit" class="btn-primary btn-full" disabled="@isSubmitting">
|
|
@if (isSubmitting)
|
|
{
|
|
<span class="spinner-small"></span>
|
|
<span>@L["Common_Loading"]</span>
|
|
}
|
|
else
|
|
{
|
|
@L["Auth_Login_Submit"]
|
|
}
|
|
</button>
|
|
</EditForm>
|
|
|
|
@if (!string.IsNullOrEmpty(message))
|
|
{
|
|
<div class="alert @(success ? "alert-success" : "alert-error")">
|
|
@message
|
|
</div>
|
|
}
|
|
|
|
<div class="auth-footer">
|
|
<span>@L["Auth_Login_NoAccount"]</span>
|
|
<a href="/register" class="link-primary">@L["Auth_Login_RegisterLink"]</a>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
@code {
|
|
private LoginDto loginModel = new();
|
|
private bool isSubmitting = false;
|
|
private string message = "";
|
|
private bool success = false;
|
|
|
|
/// <summary>
|
|
/// EN: Handle login form submission.
|
|
/// VI: Xử lý submit form đăng nhập.
|
|
/// </summary>
|
|
private async Task HandleLogin()
|
|
{
|
|
isSubmitting = true;
|
|
message = "";
|
|
|
|
try
|
|
{
|
|
var response = await Http.PostAsJsonAsync("api/auth/login", loginModel);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var result = await response.Content.ReadFromJsonAsync<ApiResponse<UserProfileDto>>();
|
|
if (result?.Success == true && result.Data != null)
|
|
{
|
|
success = true;
|
|
message = string.Format(L["Auth_Login_Success"], result.Data.DisplayName);
|
|
|
|
// EN: Redirect to home after 1 second
|
|
// VI: Chuyển hướng về trang chủ sau 1 giây
|
|
await Task.Delay(1000);
|
|
Navigation.NavigateTo("/");
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
message = L["Auth_Login_Error"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
message = L["Auth_Login_Error"];
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
success = false;
|
|
message = $"{L["Common_Error"]}: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
isSubmitting = false;
|
|
}
|
|
}
|
|
}
|