Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c815f8d358 | |||
| 27a3ea76c5 |
@@ -10,8 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||
<!-- Local protobuf until 0.0.212 is published to Nexus -->
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.212" />
|
||||
|
||||
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||
@@ -23,9 +22,18 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- <ProjectReference Include="..\..\..\CMS\src\CMSMicroservice.Protobuf\CMSMicroservice.Protobuf.csproj" /> -->
|
||||
</ItemGroup>
|
||||
<!-- ============================================================ -->
|
||||
<!-- Proto References: Direct reference to CMS Protobuf project -->
|
||||
<!-- This enables direct communication with CMS (no BFF needed) -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<!-- CMS Protobuf: NuGet in CI/Docker; ProjectReference for local monorepo dev -->
|
||||
<ItemGroup Condition="Exists('../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj')">
|
||||
<ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!Exists('../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj')">
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.213" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Local Proto Project Reference for development -->
|
||||
<!-- <ItemGroup>-->
|
||||
|
||||
@@ -46,6 +46,21 @@
|
||||
</MudPaper>
|
||||
</MudLink>
|
||||
|
||||
<MudLink Href="@RouteConstants.Profile.Invitees" Underline="Underline.None">
|
||||
<MudPaper Elevation="0" Class="pa-4 rounded-xl profile-hub-item">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
|
||||
<MudIcon Icon="@Icons.Material.Outlined.GroupAdd" Color="Color.Info" />
|
||||
<MudStack Spacing="0">
|
||||
<MudText Typo="Typo.subtitle2">دعوتشدگان من</MudText>
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">کسانی که با موبایل شما ثبتنام کردهاند</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
<MudIcon Icon="@Icons.Material.Filled.ChevronLeft" Class="mud-text-secondary" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</MudLink>
|
||||
|
||||
<MudLink Href="@RouteConstants.Profile.Settings" Underline="Underline.None">
|
||||
<MudPaper Elevation="0" Class="pa-4 rounded-xl profile-hub-item">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||
|
||||
@@ -109,6 +109,13 @@
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Success" Class="mt-2">@(_copyMessage)</MudText>
|
||||
}
|
||||
<MudButton Variant="Variant.Text"
|
||||
Color="Color.Primary"
|
||||
Class="mt-3"
|
||||
Href="@RouteConstants.Profile.Invitees"
|
||||
StartIcon="@Icons.Material.Filled.GroupAdd">
|
||||
مشاهده دعوتشدگان من
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
}
|
||||
else
|
||||
@@ -121,6 +128,13 @@
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-1" Style="max-width:560px; margin:0 auto;">
|
||||
برای فعالسازی لینک دعوت، ابتدا یکی از <strong>پکیجها</strong> را تهیه کنید و سپس در <strong>باشگاه مشتریان</strong> عضو شوید.
|
||||
</MudText>
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
Class="mt-3"
|
||||
Href="@RouteConstants.Profile.Invitees"
|
||||
StartIcon="@Icons.Material.Filled.GroupAdd">
|
||||
دعوتشدگان من
|
||||
</MudButton>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-4" Style="max-width:560px; margin:0 auto;">
|
||||
از مزایای ویژه عضویت بهرهمند شده و با فعالیت در زمینه توسعه فروشگاهها پاداش دریافت کنید.
|
||||
</MudText>
|
||||
|
||||
@@ -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
|
||||
|
||||
<PageTitle>دعوتشدگان من | کارا بازار سلامت</PageTitle>
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.Medium" Class="py-6">
|
||||
<PageHeader Title="دعوتشدگان من" BackHref="@RouteConstants.Profile.Index" />
|
||||
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-4">
|
||||
افرادی که با شماره موبایل شما بهعنوان دعوتکننده ثبتنام کردهاند (متفاوت از زیرمجموعه شبکه).
|
||||
</MudText>
|
||||
|
||||
@if (_loading)
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="py-8">
|
||||
<MudProgressCircular Indeterminate="true" Color="Color.Primary" />
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudGrid Spacing="3" Class="mb-4">
|
||||
<MudItem xs="12" sm="4">
|
||||
<MudPaper Class="pa-4 rounded-xl" Elevation="1">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">کل دعوتها</MudText>
|
||||
<MudText Typo="Typo.h5">@_stats.TotalInvitees</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="4">
|
||||
<MudPaper Class="pa-4 rounded-xl" Elevation="1">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">فعال در باشگاه</MudText>
|
||||
<MudText Typo="Typo.h5" Color="Color.Success">@_stats.ClubActiveCount</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="4">
|
||||
<MudPaper Class="pa-4 rounded-xl" Elevation="1">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">غیرفعال باشگاه</MudText>
|
||||
<MudText Typo="Typo.h5" Color="Color.Warning">@_stats.ClubInactiveCount</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<MudPaper Class="pa-3 rounded-xl mb-3" Elevation="0">
|
||||
<MudSelect T="string" Label="فیلتر وضعیت باشگاه" Value="_clubFilter" ValueChanged="OnFilterChanged"
|
||||
Variant="Variant.Outlined" Dense="true" Margin="Margin.Dense">
|
||||
<MudSelectItem Value="@("ALL")">همه</MudSelectItem>
|
||||
<MudSelectItem Value="@("ACTIVE")">فعال باشگاه</MudSelectItem>
|
||||
<MudSelectItem Value="@("INACTIVE")">غیرفعال باشگاه</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudPaper>
|
||||
|
||||
@if (!_invitees.Any())
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Dense="true">هنوز کسی با کد دعوت شما ثبتنام نکرده است.</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="_invitees" Hover="true" Dense="true" Breakpoint="Breakpoint.Sm" Class="rounded-xl">
|
||||
<HeaderContent>
|
||||
<MudTh>نام</MudTh>
|
||||
<MudTh>تاریخ ثبتنام</MudTh>
|
||||
<MudTh>باشگاه</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="نام">
|
||||
@(string.IsNullOrWhiteSpace($"{context.FirstName} {context.LastName}".Trim())
|
||||
? "—"
|
||||
: $"{context.FirstName} {context.LastName}".Trim())
|
||||
</MudTd>
|
||||
<MudTd DataLabel="تاریخ ثبتنام">
|
||||
@FormatDate(context.RegisteredAt)
|
||||
</MudTd>
|
||||
<MudTd DataLabel="باشگاه">
|
||||
@if (context.IsClubActive)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Success" Variant="Variant.Filled">فعال</MudChip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">غیرفعال</MudChip>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@if (_totalPages > 1)
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="mt-4">
|
||||
<MudPagination Count="_totalPages" Selected="_page" SelectedChanged="OnPageChanged" Color="Color.Primary" />
|
||||
</MudStack>
|
||||
}
|
||||
}
|
||||
}
|
||||
</MudContainer>
|
||||
|
||||
@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<InviteeModel> _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();
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@
|
||||
OnChangePhone="ChangePhoneAsync"
|
||||
OnResendOtp="ResendOtpAsync"
|
||||
ReferralCode="@_referralCode"
|
||||
ReferralCodeChanged="OnReferralCodeChanged" />
|
||||
ReferralCodeChanged="OnReferralCodeChanged"
|
||||
InviterMobile="@_inviterMobile"
|
||||
InviterMobileChanged="OnInviterMobileChanged" />
|
||||
</div>
|
||||
}
|
||||
else
|
||||
@@ -55,7 +57,9 @@ else
|
||||
OnChangePhone="ChangePhoneAsync"
|
||||
OnResendOtp="ResendOtpAsync"
|
||||
ReferralCode="@_referralCode"
|
||||
ReferralCodeChanged="OnReferralCodeChanged" />
|
||||
ReferralCodeChanged="OnReferralCodeChanged"
|
||||
InviterMobile="@_inviterMobile"
|
||||
InviterMobileChanged="OnInviterMobileChanged" />
|
||||
|
||||
<MudStack Class="mt-4" Spacing="2">
|
||||
@if (_currentStep == AuthStep.Phone)
|
||||
|
||||
@@ -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<string>("referral:code");
|
||||
_inviterMobile = await LocalStorage.GetItemAsync<string>("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<string>("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;
|
||||
|
||||
@@ -38,11 +38,21 @@
|
||||
|
||||
<MudTextField Value="ReferralCode"
|
||||
ValueChanged="@((string? v) => ReferralCodeChanged.InvokeAsync(v))"
|
||||
Label="کد معرف (اختیاری)"
|
||||
Label="کد معرف شبکه (اختیاری)"
|
||||
Variant="Variant.Outlined"
|
||||
Immediate="true"
|
||||
Placeholder="کد معرف را وارد کنید"
|
||||
Class="mb-3" />
|
||||
Placeholder="کد معرف ۱۰ رقمی"
|
||||
Class="mb-3"
|
||||
HelperText="جایگاه شما در شبکه — با لینک معرف پر میشود" />
|
||||
|
||||
<MudTextField Value="InviterMobile"
|
||||
ValueChanged="@((string? v) => InviterMobileChanged.InvokeAsync(v))"
|
||||
Label="شماره موبایل دعوتکننده (اختیاری)"
|
||||
Variant="Variant.Outlined"
|
||||
Immediate="true"
|
||||
Placeholder="مثلاً 0912xxxxxxx"
|
||||
Class="mb-3"
|
||||
HelperText="کسی که شما را با سیستم آشنا کرده — متفاوت از معرف شبکه" />
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
||||
{
|
||||
|
||||
@@ -33,6 +33,10 @@ public partial class PhoneVerifyForm
|
||||
[Parameter] public string? ReferralCode { get; set; }
|
||||
[Parameter] public EventCallback<string?> ReferralCodeChanged { get; set; }
|
||||
|
||||
// ── Inviter (دعوتکننده) ──
|
||||
[Parameter] public string? InviterMobile { get; set; }
|
||||
[Parameter] public EventCallback<string?> InviterMobileChanged { get; set; }
|
||||
|
||||
// ── Verify actions ──
|
||||
[Parameter] public EventCallback OnChangePhone { get; set; }
|
||||
[Parameter] public EventCallback OnResendOtp { get; set; }
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user