initial commit

This commit is contained in:
2026-07-21 14:22:06 +07:00
commit 2d7959f202
572 changed files with 45295 additions and 0 deletions
@@ -0,0 +1,167 @@
@page "/account/forgot-password"
@layout AuthenticationLayout
@using System.ComponentModel.DataAnnotations
@using Indotalent.Shared.Consts
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
@inject ISnackbar Snackbar
@inject NavigationManager Navigation
<PageTitle>Forgot Password - @GlobalConsts.AppInitial</PageTitle>
<style>
.page-header {
text-align: center;
margin-bottom: 1.5rem;
}
.page-header h2 {
font-size: 1.5rem;
font-weight: 700;
color: #1e293b;
margin: 0 0 0.25rem 0;
}
.page-header p {
font-size: 0.875rem;
color: #64748b;
margin: 0;
}
.form-label {
font-size: 0.8125rem;
font-weight: 600;
color: #475569;
margin-bottom: 0.375rem;
margin-left: 0.25rem;
}
.auth-link {
color: #594AE2;
font-weight: 600;
text-decoration: none;
cursor: pointer;
}
.auth-link:hover {
color: #4235B8;
text-decoration: underline;
}
.btn-submit {
text-transform: none;
border-radius: 0.5rem;
height: 48px;
font-weight: 600;
font-size: 0.875rem;
letter-spacing: 0;
}
</style>
<div class="page-header">
<h2>Forgot Password?</h2>
<p>Enter your email address and we'll send you a link to reset your password.</p>
</div>
<MudForm @ref="form" @bind-IsValid="@success">
<div class="mb-4">
<div class="form-label">Email Address</div>
<MudTextField T="string"
@bind-Value="model.Email"
Variant="Variant.Outlined"
Placeholder="name@example.com"
Margin="Margin.Dense"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Email"
Required="true"
Validation="@(new EmailAddressAttribute() { ErrorMessage = "Invalid email address format" })"
For="@(() => model.Email)"
Style="border-radius: 0.5rem;" />
</div>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Large"
FullWidth="true"
Disabled="!success || isProcessing"
OnClick="HandleForgotPassword"
Class="btn-submit">
@if (isProcessing)
{
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
<span class="ms-2">Sending link...</span>
}
else
{
<span>Send Reset Link</span>
}
</MudButton>
</MudForm>
<div style="text-align: center; margin-top: 1.5rem;">
<MudText Typo="Typo.body2">
Remember your password?
<a class="auth-link ms-1" href="/account/login">Back to Sign In</a>
</MudText>
</div>
@code {
private MudForm form = default!;
private bool success;
private bool isProcessing;
private ForgotPasswordModel model = new();
private class ForgotPasswordModel
{
public string Email { get; set; } = "";
}
private async Task HandleForgotPassword()
{
await form.Validate();
if (!success) return;
isProcessing = true;
StateHasChanged();
var result = await JSRuntime.InvokeAsync<ApiResponse>("apiForgotPassword", model.Email);
if (result.Status == 200)
{
Snackbar.Add("If your email is registered, a reset link has been sent.", Severity.Success);
await Task.Delay(500);
Navigation.NavigateTo("/account/login");
}
else
{
Snackbar.Add(result.Title ?? "Failed to send reset link", Severity.Error);
}
isProcessing = false;
StateHasChanged();
}
private class ApiResponse
{
public int Status { get; set; }
public string? Title { get; set; }
}
}
<script>
window.apiForgotPassword = async (email) => {
try {
const response = await fetch('/api/account/forgot-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
if (response.ok) {
return { status: 200, title: 'Success' };
} else {
const data = await response.json();
let msg = 'Error processing request';
if (data.errors && data.errors.length > 0) msg = data.errors[0];
return { status: response.status, title: msg };
}
} catch (error) {
return { status: 500, title: 'Network error' };
}
};
</script>