diff --git a/src/FrontOffice.Main/ConfigureServices.cs b/src/FrontOffice.Main/ConfigureServices.cs index ba78960..dd24665 100644 --- a/src/FrontOffice.Main/ConfigureServices.cs +++ b/src/FrontOffice.Main/ConfigureServices.cs @@ -18,6 +18,7 @@ using FrontOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership; using FrontOffice.BFF.Commission.Protobuf.Protos.Commission; using FrontOffice.BFF.NetworkMembership.Protobuf.Protos.NetworkMembership; using FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop; +using FrontOffice.BFF.City.Protobuf; using FrontOffice.Main.Utilities; namespace Microsoft.Extensions.DependencyInjection; @@ -101,6 +102,7 @@ public static class ConfigureServices services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); + services.AddScoped(CreateAuthenticatedClient); return services; } diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index ed49ccd..baadf84 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -10,7 +10,7 @@ - + @@ -37,6 +37,8 @@ + + diff --git a/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor b/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor index 260d7cc..c8b12bb 100644 --- a/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor +++ b/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor @@ -29,13 +29,34 @@ Required="true" RequiredError="کد پستی الزامی است." /> - + + + + + @city.Native + (@city.StateName) + + + + شهری یافت نشد + + diff --git a/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor.cs b/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor.cs index 0733d97..7664d36 100644 --- a/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor.cs +++ b/src/FrontOffice.Main/Pages/Profile/Components/AddAddressDialog.razor.cs @@ -3,6 +3,7 @@ using FrontOffice.BFF.UserAddress.Protobuf.Validator; using Microsoft.AspNetCore.Components; using MudBlazor; using Severity = MudBlazor.Severity; +using CityProto = FrontOffice.BFF.City.Protobuf; namespace FrontOffice.Main.Pages.Profile.Components; @@ -10,19 +11,48 @@ public partial class AddAddressDialog : ComponentBase { [CascadingParameter] private IDialogReference MudDialog { get; set; } = default!; [Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!; + [Inject] private CityProto.CityContract.CityContractClient CityContract { get; set; } = default!; private MudForm? _form; private readonly CreateNewUserAddressRequestValidator _validator = new(); private bool _isSaving; private CreateNewUserAddressRequest _request = new(); + private CityProto.GetAllCitiesByFilterResponseModel? _selectedCity; + + private async Task> SearchCities(string value, CancellationToken ct) + { + if (string.IsNullOrWhiteSpace(value) || value.Length < 2) + return Enumerable.Empty(); + + try + { + var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest + { + PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 20 }, + Filter = new CityProto.GetAllCitiesByFilterFilter { Native = value } + }, cancellationToken: ct); + + return response?.Models?.ToList() ?? new List(); + } + catch + { + return Enumerable.Empty(); + } + } private async Task SaveAddress() { if (_form == null) return; await _form.Validate(); - if (!_form.IsValid) return; + if (!_form.IsValid || _selectedCity == null) + { + if (_selectedCity == null) + Snackbar.Add("لطفاً شهر را انتخاب کنید.", Severity.Warning); + return; + } + _request.CityId = _selectedCity.Id; _isSaving = true; try { diff --git a/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor b/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor index 40e96b4..29c3c8c 100644 --- a/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor +++ b/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor @@ -28,13 +28,34 @@ Required="true" RequiredError="کد پستی الزامی است." /> - + + + + + @city.Native + (@city.StateName) + + + + شهری یافت نشد + + diff --git a/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor.cs b/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor.cs index babb8cf..1a61d9f 100644 --- a/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor.cs +++ b/src/FrontOffice.Main/Pages/Profile/Components/EditAddressDialog.razor.cs @@ -4,14 +4,15 @@ using Mapster; using Microsoft.AspNetCore.Components; using MudBlazor; using Severity = MudBlazor.Severity; +using CityProto = FrontOffice.BFF.City.Protobuf; namespace FrontOffice.Main.Pages.Profile.Components; public partial class EditAddressDialog : ComponentBase { - [CascadingParameter] private IDialogReference MudDialog { get; set; } = default!; // updated type + [CascadingParameter] private IDialogReference MudDialog { get; set; } = default!; [Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!; - // removed duplicate Snackbar injection; provided by Razor partial via _Imports + [Inject] private CityProto.CityContract.CityContractClient CityContract { get; set; } = default!; [Parameter] public GetAllUserAddressByFilterResponseModel? Model { get; set; } @@ -19,12 +20,60 @@ public partial class EditAddressDialog : ComponentBase private readonly UpdateUserAddressRequestValidator _validator = new(); private bool _isSaving; private UpdateUserAddressRequest _request = new(); + private CityProto.GetAllCitiesByFilterResponseModel? _selectedCity; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - base.OnInitialized(); + await base.OnInitializedAsync(); if (Model != null) + { _request = Model.Adapt(); + + // Load selected city if CityId exists + if (Model.CityId > 0) + { + await LoadCurrentCity(Model.CityId); + } + } + } + + private async Task LoadCurrentCity(long cityId) + { + try + { + var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest + { + PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 1 }, + Filter = new CityProto.GetAllCitiesByFilterFilter { Id = cityId } + }); + + _selectedCity = response?.Models?.FirstOrDefault(); + } + catch + { + // Ignore error loading city + } + } + + private async Task> SearchCities(string value, CancellationToken ct) + { + if (string.IsNullOrWhiteSpace(value) || value.Length < 2) + return Enumerable.Empty(); + + try + { + var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest + { + PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 20 }, + Filter = new CityProto.GetAllCitiesByFilterFilter { Native = value } + }, cancellationToken: ct); + + return response?.Models?.ToList() ?? new List(); + } + catch + { + return Enumerable.Empty(); + } } private async Task SaveAddress() @@ -32,8 +81,14 @@ public partial class EditAddressDialog : ComponentBase if (_form == null) return; await _form.Validate(); - if (!_form.IsValid) return; + if (!_form.IsValid || _selectedCity == null) + { + if (_selectedCity == null) + Snackbar.Add("لطفاً شهر را انتخاب کنید.", Severity.Warning); + return; + } + _request.CityId = _selectedCity.Id; _isSaving = true; try { diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs index e7e84a7..b170b1f 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs @@ -60,6 +60,9 @@ public partial class Index await LoadUserProfile(); await LoadAddresses(); await LoadWallet(); + + // نمایش Modal قرارداد باشگاه اگر پکیج خریده ولی باشگاه فعال نشده + await CheckAndShowClubContractModal(); } } @@ -78,6 +81,39 @@ public partial class Index } StateHasChanged(); } + + /// + /// بررسی و نمایش Modal قرارداد باشگاه مشتریان + /// اگر کاربر پکیج خریده ولی هنوز قرارداد باشگاه را امضا نکرده + /// + private async Task CheckAndShowClubContractModal() + { + // اگر پکیج خریده ولی باشگاه فعال نیست = باید قرارداد امضا کند + if (_hasPurchasedPackage && !_isClubMemberActive) + { + var options = new DialogOptions + { + BackdropClick = false, // غیرقابل بسته شدن با کلیک بیرون + CloseOnEscapeKey = false, // غیرقابل بسته شدن با Escape + CloseButton = false, // بدون دکمه بستن + MaxWidth = MaxWidth.Medium, + FullWidth = true + }; + + var dialog = await DialogService.ShowAsync( + "قرارداد باشگاه مشتریان", + options); + + var result = await dialog.Result; + + // اگر موفق بود، صفحه را refresh کنیم + if (!result.Canceled) + { + await LoadUserAuthInfo(); + StateHasChanged(); + } + } + } private string _walletCredit = "-"; private string _walletDiscount = "-"; diff --git a/src/FrontOffice.Main/Shared/ClubMembershipContractDialog.razor b/src/FrontOffice.Main/Shared/ClubMembershipContractDialog.razor new file mode 100644 index 0000000..b491e98 --- /dev/null +++ b/src/FrontOffice.Main/Shared/ClubMembershipContractDialog.razor @@ -0,0 +1,271 @@ +@using FrontOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership +@using Blazored.LocalStorage +@using FrontOffice.Main.Utilities +@using MudBlazor +@inject ClubMembershipContract.ClubMembershipContractClient ClubMembershipClient +@inject ILocalStorageService LocalStorage +@inject AuthService AuthService +@inject ISnackbar Snackbar +@inject NavigationManager Navigation + + + + + @if (_step == ContractStep.ReadContract) + { + + + قرارداد باشگاه مشتریان + + + + لطفاً قرارداد باشگاه مشتریان را به دقت مطالعه کنید و در صورت موافقت تایید نمایید. + + + + @((MarkupString)GetClubContractHtml()) + + + + + + @if (_isLoading) + { + + } + ادامه و دریافت کد تایید + + } + else if (_step == ContractStep.EnterOtp) + { + + + تایید قرارداد + + + + کد تایید به شماره موبایل شما ارسال شد. لطفاً کد را وارد کنید. + + + + + @if (_remainingSeconds > 0) + { + + زمان باقیمانده: @_remainingSeconds ثانیه + + } + else + { + + ارسال مجدد کد + + } + + + @if (_isLoading) + { + + } + تایید و فعال‌سازی عضویت + + } + else if (_step == ContractStep.Success) + { +
+ + + عضویت شما در باشگاه مشتریان فعال شد! + + + اکنون می‌توانید از لینک دعوت خود استفاده کنید و دوستان‌تان را دعوت کنید. + + + متوجه شدم + +
+ } +
+
+
+ +@code { + [CascadingParameter] IMudDialogInstance MudDialog { get; set; } + + private enum ContractStep { ReadContract, EnterOtp, Success } + private ContractStep _step = ContractStep.ReadContract; + + private bool _acceptedTerms; + private string _otpCode = ""; + private bool _isLoading; + private int _remainingSeconds; + private readonly Guid _signGuid = Guid.NewGuid(); + private System.Timers.Timer? _timer; + private const string TokenStorageKey = "auth:token"; + + // این Modal غیرقابل بسته شدن است - Options در ShowAsync تنظیم می‌شوند + + private async Task RequestOtp() + { + _isLoading = true; + try + { + var response = await ClubMembershipClient.RequestClubContractOtpAsync( + new RequestClubContractOtpRequest + { + SignGuid = _signGuid.ToString() + }); + + if (response.Success) + { + _step = ContractStep.EnterOtp; + _remainingSeconds = response.RemainingSeconds > 0 ? response.RemainingSeconds : 120; + StartCountdown(); + } + else + { + Snackbar.Add(response.Message, Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"خطا در ارسال کد: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + StateHasChanged(); + } + } + + private async Task AcceptContract() + { + _isLoading = true; + try + { + var response = await ClubMembershipClient.AcceptClubMembershipContractAsync( + new AcceptClubMembershipContractRequest + { + OtpCode = _otpCode, + SignGuid = _signGuid.ToString(), + ContractHtml = GetClubContractHtml() + }); + + if (response.Success) + { + // ذخیره توکن جدید + if (!string.IsNullOrEmpty(response.Token)) + { + await LocalStorage.SetItemAsync(TokenStorageKey, response.Token); + await AuthService.InitUserAuthInfo(); + } + + _step = ContractStep.Success; + StopCountdown(); + } + else + { + Snackbar.Add(response.Message, Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"خطا در ثبت قرارداد: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + StateHasChanged(); + } + } + + private void CloseAndRefresh() + { + MudDialog.Close(DialogResult.Ok(true)); + Navigation.NavigateTo(Navigation.Uri, forceLoad: true); + } + + private void StartCountdown() + { + _timer = new System.Timers.Timer(1000); + _timer.Elapsed += (s, e) => + { + _remainingSeconds--; + if (_remainingSeconds <= 0) + { + StopCountdown(); + } + InvokeAsync(StateHasChanged); + }; + _timer.Start(); + } + + private void StopCountdown() + { + _timer?.Stop(); + _timer?.Dispose(); + _timer = null; + } + + public void Dispose() + { + StopCountdown(); + } + + private string GetClubContractHtml() + { + return @" +
+

قرارداد عضویت در باشگاه مشتریان کارابازار

+ +

ماده ۱ - تعاریف

+

۱-۱. باشگاه مشتریان: سامانه‌ای که امکان معرفی دوستان و دریافت پاداش را فراهم می‌کند.

+

۱-۲. لینک دعوت: لینک اختصاصی هر کاربر برای معرفی دیگران.

+

۱-۳. پاداش معرفی: مبلغی که به ازای هر معرفی موفق به کاربر تعلق می‌گیرد.

+ +

ماده ۲ - شرایط عضویت

+

۲-۱. کاربر با پرداخت مبلغ ۵۶ میلیون تومان (پکیج پایه)، امکان عضویت در باشگاه مشتریان را خواهد داشت.

+

۲-۲. پس از امضای این قرارداد، لینک دعوت کاربر فعال خواهد شد.

+

۲-۳. کاربر می‌تواند حداکثر ۲ نفر را مستقیماً دعوت کند (یک نفر در شاخه چپ و یک نفر در شاخه راست).

+ +

ماده ۳ - تعهدات کاربر

+

۳-۱. کاربر متعهد است از لینک دعوت فقط برای معرفی افراد واقعی استفاده کند.

+

۳-۲. هرگونه سوءاستفاده از سیستم منجر به لغو عضویت خواهد شد.

+

۳-۳. کاربر حق انتقال یا فروش عضویت خود را ندارد.

+ +

ماده ۴ - حقوق کاربر

+

۴-۱. دریافت پاداش معرفی به ازای هر عضو جدید که از طریق شبکه کاربر عضو شود.

+

۴-۲. دسترسی به داشبورد مدیریت شبکه و مشاهده وضعیت زیرمجموعه‌ها.

+

۴-۳. امکان برداشت موجودی کیف پول طبق قوانین سایت.

+ +

ماده ۵ - شرایط فسخ

+

۵-۱. در صورت نقض هر یک از مفاد این قرارداد، کارابازار حق لغو عضویت کاربر را دارد.

+

۵-۲. در صورت فسخ قرارداد، موجودی کیف پول طبق قوانین سایت تسویه خواهد شد.

+ +

ماده ۶ - حل اختلاف

+

۶-۱. در صورت بروز اختلاف، طرفین ابتدا از طریق مذاکره اقدام به حل و فصل خواهند کرد.

+

۶-۲. در صورت عدم توافق، مراجع قضایی صلاحیت‌دار رسیدگی خواهند کرد.

+ +

شناسه قرارداد: " + _signGuid + @"

+
"; + } +} diff --git a/src/FrontOffice.Main/_Imports.razor b/src/FrontOffice.Main/_Imports.razor index 8212a98..08f16aa 100644 --- a/src/FrontOffice.Main/_Imports.razor +++ b/src/FrontOffice.Main/_Imports.razor @@ -12,6 +12,7 @@ @using MudBlazor @using FrontOffice.Main.Utilities @using DateTimeConverterCL +@using CityProto = FrontOffice.BFF.City.Protobuf @inject NavigationManager Navigation @inject IDialogService DialogService diff --git a/src/FrontOffice.Main/appsettings.json b/src/FrontOffice.Main/appsettings.json index 6ab1deb..bd05277 100644 --- a/src/FrontOffice.Main/appsettings.json +++ b/src/FrontOffice.Main/appsettings.json @@ -1,6 +1,6 @@ { - "GwUrl": "https://frontoffice-bff.foursat.afrino.co", -// "GwUrl": "https://localhost:34781", +// "GwUrl": "https://frontoffice-bff.foursat.afrino.co", + "GwUrl": "https://localhost:34781", "DownloadUrl": "https://dl.afrino.co", "EncryptionSettings": { "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=",