This commit is contained in:
MeysamMoghaddam
2025-09-28 05:36:45 +03:30
parent d073995e02
commit 2352b7ca17
7 changed files with 116 additions and 100 deletions

View File

@@ -2,15 +2,15 @@
<PageTitle>ورود | تأیید شماره موبایل</PageTitle>
<MudPaper Class="d-flex flex-column align-center justify-center min-vh-100 pa-4">
<MudStack AlignItems="AlignItems.Center">
<MudCard Class="pa-6" Style="max-width:420px;width:100%;">
<MudCardContent>
<MudText Typo="Typo.h5" Class="mb-1" Align="Align.Center">ورود به حساب کاربری</MudText>
<MudText Typo="Typo.body2" Class="mb-4" Align="Align.Center">لطفاً شماره موبایل خود را وارد کنید تا رمز پویا ارسال شود.</MudText>
<MudForm @ref="_form" Model="_model">
<MudTextField @bind-Value="_model.PhoneNumber"
For="@(() => _model.PhoneNumber)"
<MudForm @ref="_form" Model="_request" Validation="@(_requestValidator.ValidateValue)">
<MudTextField @bind-Value="_request.Mobile"
For="@(() => _request.Mobile)"
Label="شماره موبایل"
InputType="InputType.Text"
Variant="Variant.Outlined"
@@ -21,10 +21,8 @@
Class="mb-2" />
<MudCheckBox T="bool"
@bind-Value="_model.AcceptTerms"
For="@(() => _model.AcceptTerms)"
Label="شرایط و قوانین را می‌پذیرم"
Class="mb-2" />
Label="شرایط و قوانین را می‌پذیرم"
Class="mb-2" />
@if (!string.IsNullOrWhiteSpace(_errorMessage))
{
@@ -42,14 +40,9 @@
</MudForm>
</MudCardContent>
<MudCardActions Class="justify-center">
<MudText Typo="Typo.caption" Align="Align.Center" Class="px-2">
<MudText Typo="Typo.body1" Align="Align.Center" Class="px-2">
با ورود، شرایط استفاده و سیاست حفظ حریم خصوصی را می‌پذیرید.
</MudText>
</MudCardActions>
</MudCard>
</MudPaper>
</MudStack>

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations;
using Blazored.LocalStorage;
using Blazored.LocalStorage;
using FrontOffice.BFF.User.Protobuf.Protos.User;
using FrontOffice.BFF.User.Protobuf.Validator;
using FrontOffice.Main.Utilities;
@@ -17,10 +16,10 @@ public partial class Phone : IDisposable
private const string TokenStorageKey = "auth:token";
private const string OtpPurpose = "Login";
private static readonly CreateNewOtpTokenRequestValidator RequestValidator = new();
private readonly PhoneInputModel _model = new();
private CreateNewOtpTokenRequestValidator _requestValidator = new();
private CreateNewOtpTokenRequest _request = new();
private MudForm? _form;
private bool _isBusy;
private string? _errorMessage;
private string? _redirect;
@@ -31,6 +30,7 @@ public partial class Phone : IDisposable
protected override async Task OnInitializedAsync()
{
_request.Purpose = OtpPurpose;
var uri = Navigation.ToAbsoluteUri(Navigation.Uri);
var query = QueryHelpers.ParseQuery(uri.Query);
if (query.TryGetValue("redirect", out var redirectValues))
@@ -41,7 +41,7 @@ public partial class Phone : IDisposable
var storedPhone = await LocalStorage.GetItemAsync<string>(PhoneStorageKey);
if (!string.IsNullOrWhiteSpace(storedPhone))
{
_model.PhoneNumber = storedPhone;
_request.Mobile = storedPhone;
}
if (string.IsNullOrWhiteSpace(_redirect))
@@ -58,15 +58,11 @@ public partial class Phone : IDisposable
{
_errorMessage = null;
if (_form is null)
{
return;
}
await _form.Validate();
if (!_form.IsValid)
{
return;
}
return;
_isBusy = true;
_sendCts?.Cancel();
@@ -75,13 +71,7 @@ public partial class Phone : IDisposable
try
{
var request = new CreateNewOtpTokenRequest
{
Mobile = _model.PhoneNumber,
Purpose = OtpPurpose
};
var validationResult = RequestValidator.Validate(request);
var validationResult = _requestValidator.Validate(_request);
if (!validationResult.IsValid)
{
_errorMessage = string.Join(" ", validationResult.Errors.Select(e => e.ErrorMessage).Distinct());
@@ -92,11 +82,11 @@ public partial class Phone : IDisposable
CreateNewOtpTokenResponse response;
if (metadata is not null)
{
response = await UserClient.CreateNewOtpTokenAsync(request, metadata, cancellationToken: _sendCts.Token);
response = await UserClient.CreateNewOtpTokenAsync(_request, metadata, cancellationToken: _sendCts.Token);
}
else
{
response = await UserClient.CreateNewOtpTokenAsync(request, cancellationToken: _sendCts.Token);
response = await UserClient.CreateNewOtpTokenAsync(_request, cancellationToken: _sendCts.Token);
}
if (response?.Success != true)
@@ -107,7 +97,7 @@ public partial class Phone : IDisposable
return;
}
await LocalStorage.SetItemAsync(PhoneStorageKey, _model.PhoneNumber);
await LocalStorage.SetItemAsync(PhoneStorageKey, _request.Mobile);
if (!string.IsNullOrWhiteSpace(_redirect))
{
await LocalStorage.SetItemAsync(RedirectStorageKey, _redirect);
@@ -117,7 +107,7 @@ public partial class Phone : IDisposable
await LocalStorage.RemoveItemAsync(RedirectStorageKey);
}
var target = $"{RouteConstants.Auth.Verify}?phone={Uri.EscapeDataString(_model.PhoneNumber)}";
var target = $"{RouteConstants.Auth.Verify}?phone={Uri.EscapeDataString(_request.Mobile)}";
if (!string.IsNullOrEmpty(_redirect))
{
target += "&redirect=" + Uri.EscapeDataString(_redirect);
@@ -167,14 +157,4 @@ public partial class Phone : IDisposable
_sendCts?.Dispose();
_sendCts = null;
}
private sealed class PhoneInputModel
{
[Required(ErrorMessage = "???? ???? ????? ?????? ?????? ???.")]
[RegularExpression(@"^09\\d{9}$", ErrorMessage = "????? ?????? ????? ????.")]
public string PhoneNumber { get; set; } = string.Empty;
[Range(typeof(bool), "true", "true", ErrorMessage = "????? ????? ? ?????? ????? ???.")]
public bool AcceptTerms { get; set; }
}
}

View File

@@ -2,7 +2,7 @@
<PageTitle>تأیید رمز پویا</PageTitle>
<MudPaper Class="d-flex flex-column align-center justify-center min-vh-100 pa-4">
<MudStack AlignItems="AlignItems.Center">
<MudCard Class="pa-6" Style="max-width:420px;width:100%;">
<MudCardContent>
<MudText Typo="Typo.h5" Class="mb-1" Align="Align.Center">تأیید رمز پویا</MudText>
@@ -15,20 +15,20 @@
<MudText Typo="Typo.body2" Class="mb-4" Align="Align.Center">کد ارسال‌شده را وارد کنید.</MudText>
}
<MudForm @ref="_form" Model="_model">
<MudTextField @bind-Value="_model.Code"
For="@(() => _model.Code)"
<MudForm @ref="_form" Model="_request" Validation="@(_requestValidator.ValidateValue)">
<MudTextField @bind-Value="_request.Code"
For="@(() => _request.Code)"
Label="رمز پویا"
InputType="InputType.Text"
Variant="Variant.Outlined"
Immediate="true"
Required="true"
RequiredError="وارد کردن رمز پویا الزامی است."
HelperText="کد ۵ یا ۶ رقمی"
HelperText="کد ۶ رقمی"
Class="mb-2"
MaxLength="6" />
<MudText Typo="Typo.caption" Align="Align.Center" Class="mb-2">
<MudText Typo="Typo.body1" Align="Align.Center" Class="mb-2">
تلاش باقی‌مانده: @_attemptsLeft از @MaxVerificationAttempts
</MudText>
@@ -62,7 +62,7 @@
@if (_resendRemaining > 0)
{
<MudText Typo="Typo.caption" Align="Align.Center">
<MudText Typo="Typo.body1" Align="Align.Center">
امکان ارسال مجدد تا @_resendRemaining ثانیه دیگر
</MudText>
}
@@ -78,8 +78,7 @@
</MudForm>
</MudCardContent>
</MudCard>
</MudPaper>
</MudStack>

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations;
using Blazored.LocalStorage;
using Blazored.LocalStorage;
using FrontOffice.BFF.User.Protobuf.Protos.User;
using FrontOffice.BFF.User.Protobuf.Validator;
using FrontOffice.Main.Utilities;
@@ -19,10 +18,10 @@ public partial class Verify : IDisposable
private const string RedirectStorageKey = "auth:redirect";
private const string TokenStorageKey = "auth:token";
private static readonly VerifyOtpTokenRequestValidator VerifyRequestValidator = new();
private readonly OtpInputModel _model = new();
private VerifyOtpTokenRequestValidator _requestValidator = new();
private VerifyOtpTokenRequest _request = new();
private MudForm? _form;
private bool _isBusy;
private string? _phoneNumber;
private string? _redirect;
@@ -40,6 +39,7 @@ public partial class Verify : IDisposable
protected override async Task OnInitializedAsync()
{
_request.Purpose = OtpPurpose;
var uri = Navigation.ToAbsoluteUri(Navigation.Uri);
var query = QueryHelpers.ParseQuery(uri.Query);
if (query.TryGetValue("redirect", out var redirectValues))
@@ -84,15 +84,11 @@ public partial class Verify : IDisposable
_infoMessage = null;
if (_form is null)
{
return;
}
return;
await _form.Validate();
if (!_form.IsValid)
{
return;
}
if (IsVerificationLocked)
{
@@ -112,14 +108,8 @@ public partial class Verify : IDisposable
try
{
var request = new VerifyOtpTokenRequest
{
Mobile = _phoneNumber,
Purpose = OtpPurpose,
Code = _model.Code
};
var validationResult = VerifyRequestValidator.Validate(request);
_request.Mobile = _phoneNumber;
var validationResult = _requestValidator.Validate(_request);
if (!validationResult.IsValid)
{
_errorMessage = string.Join(" ", validationResult.Errors.Select(e => e.ErrorMessage).Distinct());
@@ -130,11 +120,11 @@ public partial class Verify : IDisposable
VerifyOtpTokenResponse response;
if (metadata is not null)
{
response = await UserClient.VerifyOtpTokenAsync(request, metadata, cancellationToken: cancellationToken);
response = await UserClient.VerifyOtpTokenAsync(_request, metadata, cancellationToken: cancellationToken);
}
else
{
response = await UserClient.VerifyOtpTokenAsync(request, cancellationToken: cancellationToken);
response = await UserClient.VerifyOtpTokenAsync(_request, cancellationToken: cancellationToken);
}
if (response is null)
@@ -158,7 +148,7 @@ public partial class Verify : IDisposable
await LocalStorage.RemoveItemAsync(RedirectStorageKey);
_attemptsLeft = MaxVerificationAttempts;
_model.Code = string.Empty;
_request.Code = string.Empty;
var target = !string.IsNullOrWhiteSpace(_redirect) ? _redirect : RouteConstants.Main.MainPage;
if (!string.IsNullOrWhiteSpace(target))
@@ -274,7 +264,7 @@ public partial class Verify : IDisposable
: response.Message;
_attemptsLeft = MaxVerificationAttempts;
_model.Code = string.Empty;
_request.Code = string.Empty;
StartResendCountdown();
}
catch (RpcException rpcEx)
@@ -392,11 +382,4 @@ public partial class Verify : IDisposable
_resendTimer?.Dispose();
_resendTimer = null;
}
private sealed class OtpInputModel
{
[Required(ErrorMessage = "???? ???? ??? ???? ?????? ???.")]
[RegularExpression(@"^\\d{5,6}$", ErrorMessage = "??? ???? ???? ? ?? ? ??? ????.")]
public string Code { get; set; } = string.Empty;
}
}