From 27a3ea76c5458de8625eaadca92f4a88aabce798 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Sun, 20 Sep 2026 23:33:18 +0330 Subject: [PATCH] feat(profile): add invitees section and inviter mobile functionality - Introduced a new section in the Profile hub for viewing invitees, enhancing user engagement and tracking of referrals. - Added buttons for accessing the invitees page from the Profile index, improving navigation. - Enhanced the AuthDialog and PhoneVerifyForm components to support inviter mobile input, allowing users to specify who referred them. - Updated RouteConstants to include a new route for invitees, ensuring proper navigation within the application. These changes improve the user experience by facilitating better referral tracking and enhancing the profile management features. --- src/FrontOffice.Main/FrontOffice.Main.csproj | 2 +- src/FrontOffice.Main/Pages/Profile/Hub.razor | 15 ++ .../Pages/Profile/Index.razor | 14 ++ .../Pages/Profile/Invitees.razor | 159 ++++++++++++++++++ src/FrontOffice.Main/Shared/AuthDialog.razor | 8 +- .../Shared/AuthDialog.razor.cs | 22 +++ .../Shared/PhoneVerifyForm.razor | 16 +- .../Shared/PhoneVerifyForm.razor.cs | 4 + .../Utilities/RouteConstants.cs | 1 + 9 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 src/FrontOffice.Main/Pages/Profile/Invitees.razor diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index 689439a..7ef57ee 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/FrontOffice.Main/Pages/Profile/Hub.razor b/src/FrontOffice.Main/Pages/Profile/Hub.razor index 83f24db..7e38ce3 100644 --- a/src/FrontOffice.Main/Pages/Profile/Hub.razor +++ b/src/FrontOffice.Main/Pages/Profile/Hub.razor @@ -46,6 +46,21 @@ + + + + + + + دعوت‌شدگان من + کسانی که با موبایل شما ثبت‌نام کرده‌اند + + + + + + + diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor b/src/FrontOffice.Main/Pages/Profile/Index.razor index 0189e5b..462721f 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor @@ -109,6 +109,13 @@ { @(_copyMessage) } + + مشاهده دعوت‌شدگان من + } else @@ -121,6 +128,13 @@ برای فعال‌سازی لینک دعوت، ابتدا یکی از پکیج‌ها را تهیه کنید و سپس در باشگاه مشتریان عضو شوید. + + دعوت‌شدگان من + از مزایای ویژه عضویت بهره‌مند شده و با فعالیت در زمینه توسعه فروشگاه‌ها پاداش دریافت کنید. diff --git a/src/FrontOffice.Main/Pages/Profile/Invitees.razor b/src/FrontOffice.Main/Pages/Profile/Invitees.razor new file mode 100644 index 0000000..601b4b0 --- /dev/null +++ b/src/FrontOffice.Main/Pages/Profile/Invitees.razor @@ -0,0 +1,159 @@ +@page "/profile/invitees" +@attribute [Authorize] +@attribute [Route(RouteConstants.Profile.Invitees)] +@using CMSMicroservice.Protobuf.Protos.User +@using CMSMicroservice.Protobuf.Protos.City +@using DateTimeConverterCL +@inject UserContract.UserContractClient UserClient +@inject ISnackbar Snackbar + +دعوت‌شدگان من | کارا بازار سلامت + + + + + + افرادی که با شماره موبایل شما به‌عنوان دعوت‌کننده ثبت‌نام کرده‌اند (متفاوت از زیرمجموعه شبکه). + + + @if (_loading) + { + + + + } + else + { + + + + کل دعوت‌ها + @_stats.TotalInvitees + + + + + فعال در باشگاه + @_stats.ClubActiveCount + + + + + غیرفعال باشگاه + @_stats.ClubInactiveCount + + + + + + + همه + فعال باشگاه + غیرفعال باشگاه + + + + @if (!_invitees.Any()) + { + هنوز کسی با کد دعوت شما ثبت‌نام نکرده است. + } + else + { + + + نام + تاریخ ثبت‌نام + باشگاه + + + + @(string.IsNullOrWhiteSpace($"{context.FirstName} {context.LastName}".Trim()) + ? "—" + : $"{context.FirstName} {context.LastName}".Trim()) + + + @FormatDate(context.RegisteredAt) + + + @if (context.IsClubActive) + { + فعال + } + else + { + غیرفعال + } + + + + + @if (_totalPages > 1) + { + + + + } + } + } + + +@code { + private bool _loading = true; + private string _clubFilter = "ALL"; + private int _page = 1; + private const int PageSize = 10; + private int _totalPages; + private InviteeStats _stats = new(); + private List _invitees = new(); + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + private async Task OnFilterChanged(string value) + { + _clubFilter = value; + _page = 1; + await LoadAsync(); + } + + private async Task OnPageChanged(int page) + { + _page = page; + await LoadAsync(); + } + + private async Task LoadAsync() + { + _loading = true; + try + { + var response = await UserClient.GetMyInviteesAsync(new GetMyInviteesRequest + { + PaginationState = new PaginationState { PageNumber = _page, PageSize = PageSize }, + ClubStatusFilter = _clubFilter + }); + + _stats = response.Stats ?? new InviteeStats(); + _invitees = response.Invitees.ToList(); + _totalPages = (int)(response.MetaData?.TotalPage ?? 0); + if (_totalPages < 1 && _invitees.Count > 0) _totalPages = 1; + } + catch (Exception ex) + { + Snackbar.Add($"خطا در بارگذاری دعوت‌شدگان: {ex.Message}", Severity.Error); + } + finally + { + _loading = false; + } + } + + private static string FormatDate(Google.Protobuf.WellKnownTypes.Timestamp? ts) + { + if (ts == null) return "—"; + return ts.ToDateTime().ToLocalTime().MiladiToJalali(); + } +} diff --git a/src/FrontOffice.Main/Shared/AuthDialog.razor b/src/FrontOffice.Main/Shared/AuthDialog.razor index d4e7b30..a99e45a 100644 --- a/src/FrontOffice.Main/Shared/AuthDialog.razor +++ b/src/FrontOffice.Main/Shared/AuthDialog.razor @@ -24,7 +24,9 @@ OnChangePhone="ChangePhoneAsync" OnResendOtp="ResendOtpAsync" ReferralCode="@_referralCode" - ReferralCodeChanged="OnReferralCodeChanged" /> + ReferralCodeChanged="OnReferralCodeChanged" + InviterMobile="@_inviterMobile" + InviterMobileChanged="OnInviterMobileChanged" /> } else @@ -55,7 +57,9 @@ else OnChangePhone="ChangePhoneAsync" OnResendOtp="ResendOtpAsync" ReferralCode="@_referralCode" - ReferralCodeChanged="OnReferralCodeChanged" /> + ReferralCodeChanged="OnReferralCodeChanged" + InviterMobile="@_inviterMobile" + InviterMobileChanged="OnInviterMobileChanged" /> @if (_currentStep == AuthStep.Phone) diff --git a/src/FrontOffice.Main/Shared/AuthDialog.razor.cs b/src/FrontOffice.Main/Shared/AuthDialog.razor.cs index 0ce1755..23da086 100644 --- a/src/FrontOffice.Main/Shared/AuthDialog.razor.cs +++ b/src/FrontOffice.Main/Shared/AuthDialog.razor.cs @@ -44,6 +44,8 @@ public partial class AuthDialog : IDisposable // Referral code field private string? _referralCode; + // Inviter mobile (دعوت‌کننده) + private string? _inviterMobile; [Inject] private ILocalStorageService LocalStorage { get; set; } = default!; [Inject] private UserContract.UserContractClient UserClient { get; set; } = default!; @@ -71,6 +73,7 @@ public partial class AuthDialog : IDisposable } _referralCode = await LocalStorage.GetItemAsync("referral:code"); + _inviterMobile = await LocalStorage.GetItemAsync("inviter:mobile"); // await LocalStorage.RemoveItemAsync(TokenStorageKey); } @@ -83,6 +86,15 @@ public partial class AuthDialog : IDisposable await LocalStorage.RemoveItemAsync("referral:code"); } + private async Task OnInviterMobileChanged(string? value) + { + _inviterMobile = value; + if (!string.IsNullOrWhiteSpace(value)) + await LocalStorage.SetItemAsync("inviter:mobile", value); + else + await LocalStorage.RemoveItemAsync("inviter:mobile"); + } + private void GenerateCaptcha() { _captchaCode = Guid.NewGuid().ToString("N")[..6].ToUpperInvariant(); @@ -204,6 +216,15 @@ public partial class AuthDialog : IDisposable _verifyRequest.ParentReferralCode = effectiveReferralCode; } + var storedInviterMobile = await LocalStorage.GetItemAsync("inviter:mobile"); + var effectiveInviterMobile = !string.IsNullOrWhiteSpace(_inviterMobile) + ? _inviterMobile + : storedInviterMobile; + if (!string.IsNullOrWhiteSpace(effectiveInviterMobile)) + { + _verifyRequest.InviterMobile = effectiveInviterMobile; + } + var validationResult = true; // _verifyRequestValidator.Validate(_verifyRequest); if (!validationResult) { @@ -236,6 +257,7 @@ public partial class AuthDialog : IDisposable await LocalStorage.RemoveItemAsync(PhoneStorageKey); await LocalStorage.RemoveItemAsync(RedirectStorageKey); await LocalStorage.RemoveItemAsync("referral:code"); + await LocalStorage.RemoveItemAsync("inviter:mobile"); _attemptsLeft = MaxVerificationAttempts; _verifyRequest.Code = string.Empty; diff --git a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor index 3ca37a4..d6ea5ae 100644 --- a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor +++ b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor @@ -38,11 +38,21 @@ + Placeholder="کد معرف ۱۰ رقمی" + Class="mb-3" + HelperText="جایگاه شما در شبکه — با لینک معرف پر می‌شود" /> + + @if (!string.IsNullOrWhiteSpace(ErrorMessage)) { diff --git a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs index e7f353e..f917d88 100644 --- a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs +++ b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs @@ -33,6 +33,10 @@ public partial class PhoneVerifyForm [Parameter] public string? ReferralCode { get; set; } [Parameter] public EventCallback ReferralCodeChanged { get; set; } + // ── Inviter (دعوت‌کننده) ── + [Parameter] public string? InviterMobile { get; set; } + [Parameter] public EventCallback InviterMobileChanged { get; set; } + // ── Verify actions ── [Parameter] public EventCallback OnChangePhone { get; set; } [Parameter] public EventCallback OnResendOtp { get; set; } diff --git a/src/FrontOffice.Main/Utilities/RouteConstants.cs b/src/FrontOffice.Main/Utilities/RouteConstants.cs index 7000f3f..cc1dddd 100644 --- a/src/FrontOffice.Main/Utilities/RouteConstants.cs +++ b/src/FrontOffice.Main/Utilities/RouteConstants.cs @@ -21,6 +21,7 @@ public static class RouteConstants public const string Settings = "/profile/settings"; public const string ChangePassword = "/profile/change-password"; public const string Tree = "/profile/tree"; + public const string Invitees = "/profile/invitees"; public const string Wallet = "/profile/wallet"; public const string MagicWallet = "/profile/magic-wallet"; public const string ChargeDiscountWallet = "/profile/charge-discount-wallet";