feat(dashboard): enhance dashboard layout and loading indicators, update week selector and package display
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m30s

feat(network): add activation type and package name display in network tree viewer
style(admin-org-chart): refine styles for node cards and add new activation/renewal badges
fix(admin-org-chart): adjust node dimensions for better visibility and add tooltip enhancements
This commit is contained in:
masoodafar-web
2026-05-01 00:06:53 +03:30
parent 797bfef964
commit 7fb2062721
5 changed files with 366 additions and 224 deletions
+1 -1
View File
@@ -138,7 +138,7 @@
<!-- CMS Protobuf NuGet package (used for both local dev and Docker builds) -->
<ItemGroup>
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.193" />
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.195" />
</ItemGroup>
<!-- ============================================================ -->
+256 -211
View File
@@ -5,152 +5,89 @@
@using MudBlazor
@using BackOffice.Pages.AutoComplete
<div>
<MudText Typo="Typo.h4" Class="mb-4">داشبورد کمیسیون</MudText>
<MudStack Spacing="4">
@* ── G11: راهنمای کمیسیون (Q28) ── *@
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="true" Class="mb-3" Icon="@Icons.Material.Filled.Info">
هر پکیج Pool پورسانت مستقل دارد. مبالغ ActivationFee به Pool پکیج مربوطه واریز می‌شود.
</MudAlert>
@* ── عنوان صفحه ── *@
<div>
<MudText Typo="Typo.h5" Class="mb-1">داشبورد کمیسیون</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">
مبلغ پورسانت هر هفته بین اعضاء تقسیم می‌شود. هر پکیج استخر مستقل دارد.
</MudText>
</div>
@if (_isLoading)
{
<MudProgressCircular Color="Color.Primary" Indeterminate="true" />
}
else
{
<!-- Pool Summary Section -->
<MudGrid>
<MudItem xs="12" sm="6" md="3">
<MudCard Elevation="2">
<MudCardContent>
<MudText Typo="Typo.h6" Color="Color.Primary">استخر هفتگی</MudText>
<MudText Typo="Typo.h4">@(_poolData?.TotalPoolAmount.ToString("N0") ?? "0") تومان</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">@_currentWeekDisplayText</MudText>
</MudCardContent>
</MudCard>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudCard Elevation="2">
<MudCardContent>
<MudText Typo="Typo.h6" Color="Color.Success">تعداد تعادلها</MudText>
<MudText Typo="Typo.h4">@(_poolData?.TotalBalances.ToString("N0") ?? "0")</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">مجموع تعادل های فعال</MudText>
</MudCardContent>
</MudCard>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudCard Elevation="2">
<MudCardContent>
<MudText Typo="Typo.h6" Color="Color.Warning">ارزش هر تعادل</MudText>
<MudText Typo="Typo.h4">@(_poolData?.ValuePerBalance.ToString("N0") ?? "0") تومان</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">قیمت واحد تعادل</MudText>
</MudCardContent>
</MudCard>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudCard Elevation="2">
<MudCardContent>
<MudText Typo="Typo.h6" Color="@(_poolData?.IsCalculated == true ? Color.Success : Color.Error)">
وضعیت محاسبه
</MudText>
<MudText Typo="Typo.h4">
@(_poolData?.IsCalculated == true ? "محاسبه شده ✓" : "محاسبه نشده ✗")
</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">
@if (_poolData?.CalculatedAt != null)
{
var calculatedDate = _poolData.CalculatedAt.ToDateTime().ToLocalTime();
var persianDate = PersianDateTime.ConvertToPersianDateTime(calculatedDate);
@($"در تاریخ {persianDate}")
}
else
{
@("در انتظار محاسبه")
}
</MudText>
</MudCardContent>
</MudCard>
</MudItem>
</MudGrid>
<!-- Week Selector -->
<MudPaper Class="pa-4 mt-4" Elevation="2">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3" Wrap="Wrap.Wrap">
<MudText Typo="Typo.h6">انتخاب هفته:</MudText>
@* ── فیلتر (اول از همه) ── *@
<MudPaper Elevation="1" Class="pa-4">
<MudGrid AlignItems="AlignItems.End" Spacing="3">
<MudItem xs="12" sm="5">
@if (_allWeeks.Any())
{
<MudSelect T="long"
Value="_currentWeekDefinitionId"
Label="شماره هفته"
Variant="Variant.Outlined"
Margin="Margin.Dense"
AnchorOrigin="Origin.BottomLeft"
TransformOrigin="Origin.TopLeft"
Style="min-width: 400px;"
ValueChanged="OnWeekChanged">
<MudSelect T="long"
Value="_currentWeekDefinitionId"
Label="هفته"
Variant="Variant.Outlined"
Margin="Margin.Dense"
AnchorOrigin="Origin.BottomLeft"
TransformOrigin="Origin.TopLeft"
AdornmentIcon="@Icons.Material.Filled.CalendarToday"
Adornment="Adornment.Start"
ValueChanged="OnWeekChanged">
@if (_availableWeeks?.CurrentWeek != null)
{
<MudSelectItem T="long" Value="@_availableWeeks.CurrentWeek.WeekDefinitionId">
<MudText>
<MudIcon Icon="@Icons.Material.Filled.Today" Size="Size.Small" Class="ml-2" />
@_availableWeeks.CurrentWeek.DisplayText
</MudText>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.Today" Size="Size.Small" Color="Color.Primary" />
<span>@_availableWeeks.CurrentWeek.DisplayText</span>
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Text">جاری</MudChip>
</MudStack>
</MudSelectItem>
<MudDivider />
}
@if (_availableWeeks?.CalculatedWeeks?.Any() == true)
{
<MudListSubheader>هفته‌های محاسبه شده</MudListSubheader>
<MudListSubheader>تسویه‌شده</MudListSubheader>
@foreach (var week in _availableWeeks.CalculatedWeeks)
{
<MudSelectItem T="long" Value="@week.WeekDefinitionId">
<MudText>
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Small" Class="ml-2" />
@week.DisplayText
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Size="Size.Small" Color="Color.Success" />
<span>@week.DisplayText</span>
@if (week.TotalPoolAmount > 0)
{
<MudChip T="string" Size="Size.Small" Color="Color.Success" Class="mr-2">
@week.TotalPoolAmount.ToString("N0") تومان
</MudChip>
<MudText Typo="Typo.caption" Color="Color.Secondary">(@week.TotalPoolAmount.ToString("N0") T)</MudText>
}
</MudText>
</MudStack>
</MudSelectItem>
}
<MudDivider />
}
@if (_availableWeeks?.PendingWeeks?.Any() == true)
{
<MudListSubheader>هفته‌های محاسبه نشده</MudListSubheader>
<MudListSubheader>در انتظار تسویه</MudListSubheader>
@foreach (var week in _availableWeeks.PendingWeeks)
{
<MudSelectItem T="long" Value="@week.WeekDefinitionId">
<MudText>
<MudIcon Icon="@Icons.Material.Filled.AccessTime" Color="Color.Warning" Size="Size.Small" Class="ml-2" />
@week.DisplayText
</MudText>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.AccessTime" Size="Size.Small" Color="Color.Warning" />
<span>@week.DisplayText</span>
</MudStack>
</MudSelectItem>
}
<MudDivider />
}
@if (_availableWeeks?.FutureWeeks?.Any() == true)
{
<MudListSubheader>هفته‌های آینده</MudListSubheader>
@foreach (var week in _availableWeeks.FutureWeeks)
{
<MudSelectItem T="long" Value="@week.WeekDefinitionId">
<MudText>
<MudIcon Icon="@Icons.Material.Filled.CalendarMonth" Color="Color.Info" Size="Size.Small" Class="ml-2" />
@week.DisplayText
</MudText>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.CalendarMonth" Size="Size.Small" Color="Color.Tertiary" />
<span>@week.DisplayText</span>
</MudStack>
</MudSelectItem>
}
}
@@ -160,114 +97,222 @@
{
<MudText Color="Color.Warning">لیست هفته‌ها بارگذاری نشده</MudText>
}
</MudItem>
<MudItem xs="12" sm="5">
<PackageSelect Label="پکیج"
SelectedPackageId="_selectedPackageId"
SelectedPackageIdChanged="OnPackageChanged" />
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
OnClick="LoadPoolData"
StartIcon="@Icons.Material.Filled.Refresh">
بارگذاری
</MudButton>
</MudStack>
</MudPaper>
</MudItem>
<!-- Pool Details -->
@if (_poolData != null)
{
<MudPaper Class="pa-4 mt-4" Elevation="2">
<MudText Typo="Typo.h6" Class="mb-3">جزئیات استخر</MudText>
<MudSimpleTable Hover="true" Dense="true">
<tbody>
<tr>
<td><strong>شناسه استخر:</strong></td>
<td>@_poolData.Id</td>
</tr>
<tr>
<td><strong>پکیج:</strong></td>
<td>
@if (!string.IsNullOrEmpty(_poolData.PackageTitle))
{
<MudChip T="string" Size="Size.Small" Color="Color.Primary">@_poolData.PackageTitle</MudChip>
}
else
{
<MudText Color="Color.Secondary">-</MudText>
}
</td>
</tr>
<tr>
<td><strong>شماره هفته:</strong></td>
<td>@_currentWeekDisplayName</td>
</tr>
<tr>
<td><strong>مجموع استخر:</strong></td>
<td>@_poolData.TotalPoolAmount.ToString("N0") تومان</td>
</tr>
<tr>
<td><strong>تعداد تعادل:</strong></td>
<td>@_poolData.TotalBalances</td>
</tr>
<tr>
<td><strong>ارزش هر تعادل:</strong></td>
<td>@_poolData.ValuePerBalance.ToString("N0") تومان</td>
</tr>
<tr>
<td><strong>وضعیت:</strong></td>
<td>
<MudChip T="string" Color="@(_poolData.IsCalculated ? Color.Success : Color.Error)" Size="Size.Small">
@(_poolData.IsCalculated ? "محاسبه شده" : "محاسبه نشده")
</MudChip>
</td>
</tr>
<tr>
<td><strong>تاریخ محاسبه:</strong></td>
<td>
@if (_poolData.CalculatedAt != null)
{
@_poolData.CalculatedAt.ToDateTime().ToLocalTime().MiladiToJalaliWithTime()
}
else
{
<MudText Color="Color.Secondary">-</MudText>
}
</td>
</tr>
<tr>
<td><strong>تاریخ ایجاد:</strong></td>
<td>@_poolData.Created.ToDateTime().ToLocalTime().MiladiToJalaliWithTime()</td>
</tr>
</tbody>
</MudSimpleTable>
</MudPaper>
}
<MudItem xs="12" sm="2">
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
FullWidth="true"
OnClick="LoadPoolData"
StartIcon="@Icons.Material.Filled.Search"
Disabled="_isLoading">
نمایش
</MudButton>
</MudItem>
</MudGrid>
</MudPaper>
<!-- Quick Actions -->
<MudPaper Class="pa-4 mt-4" Elevation="2">
<MudText Typo="Typo.h6" Class="mb-3">عملیات سریع</MudText>
<MudStack Row="true" Spacing="2">
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Payments"
Href="/commission/payouts">
مشاهده پرداخت‌ها
</MudButton>
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.AccountBalance"
Href="/commission/withdrawals">
درخواست‌های برداشت
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Info"
StartIcon="@Icons.Material.Filled.Calculate"
Disabled="@(_poolData?.IsCalculated == true)"
OnClick="TriggerCalculation">
اجرای محاسبه دستی
</MudButton>
@* ── لودینگ ── *@
@if (_isLoading)
{
<MudPaper Elevation="0" Class="pa-6 d-flex justify-center align-center">
<MudStack AlignItems="AlignItems.Center" Spacing="2">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Medium" />
<MudText Typo="Typo.body2" Color="Color.Secondary">در حال بارگذاری اطلاعات استخر…</MudText>
</MudStack>
</MudPaper>
}
</div>
else
{
@* ── نوار وضعیت هفته / پکیج ── *@
<MudPaper Elevation="0" Class="pa-3 mud-background-gray rounded">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Wrap="Wrap.Wrap">
<MudIcon Icon="@Icons.Material.Filled.DateRange" Color="Color.Default" Size="Size.Small" />
<MudText Typo="Typo.body1">@_currentWeekDisplayText</MudText>
@if (!string.IsNullOrEmpty(_poolData?.PackageTitle))
{
<MudDivider Vertical="true" FlexItem="true" />
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Icon="@Icons.Material.Filled.WorkspacePremium">
@_poolData.PackageTitle
</MudChip>
}
<MudSpacer />
@if (_poolData != null && _poolData.Id > 0)
{
@if (_poolData.IsCalculated)
{
<MudChip T="string" Color="Color.Success" Size="Size.Small"
Icon="@Icons.Material.Filled.CheckCircle" Variant="Variant.Filled">
تسویه انجام شده
</MudChip>
}
else
{
<MudChip T="string" Color="Color.Warning" Size="Size.Small"
Icon="@Icons.Material.Filled.Pending" Variant="Variant.Filled">
در انتظار تسویه
</MudChip>
}
}
</MudStack>
</MudPaper>
@* ── ۳ کارت اصلی ── *@
<MudGrid Spacing="3">
@* کارت ۱: کل مبلغ استخر *@
<MudItem xs="12" sm="4">
<MudPaper Elevation="2" Class="pa-4" Style="border-top: 4px solid var(--mud-palette-primary);">
<MudStack Spacing="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.AccountBalanceWallet" Color="Color.Primary" Size="Size.Small" />
<MudText Typo="Typo.caption" Color="Color.Secondary">کل مبلغ استخر پورسانت</MudText>
</MudStack>
<MudText Typo="Typo.h5" Style="font-weight:700; direction:ltr; text-align:right;">
@((_poolData?.TotalPoolAmount ?? 0).ToString("N0"))
</MudText>
<MudText Typo="Typo.caption" Color="Color.Secondary">تومان — جمع کارمزد فعال‌سازی‌های این هفته</MudText>
</MudStack>
</MudPaper>
</MudItem>
@* کارت ۲: تعداد واحدهای پورسانت *@
<MudItem xs="12" sm="4">
<MudPaper Elevation="2" Class="pa-4" Style="border-top: 4px solid var(--mud-palette-success);">
<MudStack Spacing="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.People" Color="Color.Success" Size="Size.Small" />
<MudText Typo="Typo.caption" Color="Color.Secondary">واحدهای پورسانت (بالانس)</MudText>
<MudTooltip Text="تعداد واحدهایی که پورسانت به آن‌ها تعلق می‌گیرد. هر عضو بر اساس شبکه‌اش چند واحد دریافت می‌کند.">
<MudIcon Icon="@Icons.Material.Filled.HelpOutline" Size="Size.Small" Color="Color.Tertiary" />
</MudTooltip>
</MudStack>
<MudText Typo="Typo.h5" Style="font-weight:700; direction:ltr; text-align:right;">
@((_poolData?.TotalBalances ?? 0).ToString("N0"))
</MudText>
<MudText Typo="Typo.caption" Color="Color.Secondary">واحد — مجموع همه اعضاء فعال</MudText>
</MudStack>
</MudPaper>
</MudItem>
@* کارت ۳: سهم هر واحد *@
<MudItem xs="12" sm="4">
<MudPaper Elevation="2" Class="pa-4" Style="border-top: 4px solid var(--mud-palette-warning);">
<MudStack Spacing="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.LocalAtm" Color="Color.Warning" Size="Size.Small" />
<MudText Typo="Typo.caption" Color="Color.Secondary">سهم پورسانت هر واحد</MudText>
</MudStack>
<MudText Typo="Typo.h5" Style="font-weight:700; direction:ltr; text-align:right;">
@((_poolData?.ValuePerBalance ?? 0).ToString("N0"))
</MudText>
<MudText Typo="Typo.caption" Color="Color.Secondary">
تومان — @((_poolData?.IsCalculated == true) ? "قطعی شده" : "قطعی نشده")
</MudText>
</MudStack>
</MudPaper>
</MudItem>
</MudGrid>
@* ── اعتبارسنجی مبلغ استخر ── *@
@if (_poolData != null && _poolData.Id > 0)
{
@if (!_poolData.ValidationIsValid)
{
<MudAlert Severity="Severity.Error" Variant="Variant.Filled" Icon="@Icons.Material.Filled.ReportProblem" NoIcon="false">
<MudText Typo="Typo.subtitle2" Class="mb-2">مبلغ استخر با تعداد فعال‌سازی‌های دیتابیس مطابقت ندارد</MudText>
<MudGrid Spacing="2">
<MudItem xs="12" sm="4">
<MudText Typo="Typo.caption">مبلغ ثبت‌شده</MudText>
<MudText Typo="Typo.body2"><strong>@_poolData.TotalPoolAmount.ToString("N0") تومان</strong></MudText>
</MudItem>
<MudItem xs="12" sm="4">
<MudText Typo="Typo.caption">مبلغ انتظاری (@_poolData.ValidationActivationCount فعال‌سازی)</MudText>
<MudText Typo="Typo.body2"><strong>@_poolData.ValidationExpectedAmount.ToString("N0") تومان</strong></MudText>
</MudItem>
<MudItem xs="12" sm="4">
<MudText Typo="Typo.caption">اختلاف</MudText>
<MudText Typo="Typo.body2" Style="color:var(--mud-palette-warning-lighten);">
<strong>@_poolData.ValidationDiscrepancy.ToString("N0") تومان</strong>
</MudText>
</MudItem>
</MudGrid>
</MudAlert>
}
else if (_poolData.ValidationActivationCount > 0)
{
<MudAlert Severity="Severity.Success" Variant="Variant.Text" Dense="true" Icon="@Icons.Material.Filled.VerifiedUser">
مبلغ استخر صحیح است — با @_poolData.ValidationActivationCount فعال‌سازی در این هفته مطابقت دارد.
</MudAlert>
}
}
@* ── جزئیات تکمیلی (بدون تکرار اطلاعات کارت‌ها) ── *@
@if (_poolData != null && _poolData.Id > 0)
{
<MudExpansionPanels Elevation="1">
<MudExpansionPanel Text="جزئیات بیشتر" Icon="@Icons.Material.Filled.ExpandMore">
<MudSimpleTable Dense="true" Hover="true" Style="max-width:540px;">
<tbody>
<tr>
<td style="width:200px;"><MudText Typo="Typo.body2" Color="Color.Secondary">شناسه استخر</MudText></td>
<td><MudText Typo="Typo.body2">#@_poolData.Id</MudText></td>
</tr>
<tr>
<td><MudText Typo="Typo.body2" Color="Color.Secondary">تاریخ ایجاد استخر</MudText></td>
<td><MudText Typo="Typo.body2">@_poolData.Created.ToDateTime().ToLocalTime().MiladiToJalaliWithTime()</MudText></td>
</tr>
@if (_poolData.CalculatedAt != null)
{
<tr>
<td><MudText Typo="Typo.body2" Color="Color.Secondary">تاریخ تسویه</MudText></td>
<td><MudText Typo="Typo.body2">@_poolData.CalculatedAt.ToDateTime().ToLocalTime().MiladiToJalaliWithTime()</MudText></td>
</tr>
}
</tbody>
</MudSimpleTable>
</MudExpansionPanel>
</MudExpansionPanels>
}
@* ── دکمه‌های عملیاتی ── *@
<MudPaper Elevation="0" Class="pa-3 mud-background-gray rounded">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Wrap="Wrap.Wrap">
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Payments"
Href="/commission/payouts"
Size="Size.Medium">
پرداخت‌های این هفته
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Default"
StartIcon="@Icons.Material.Filled.AccountBalance"
Href="/commission/withdrawals"
Size="Size.Medium">
درخواست‌های برداشت
</MudButton>
<MudSpacer />
@if (_poolData?.IsCalculated != true)
{
<MudButton Variant="Variant.Filled"
Color="Color.Warning"
StartIcon="@Icons.Material.Filled.Calculate"
OnClick="TriggerCalculation"
Size="Size.Medium">
اجرای تسویه دستی
</MudButton>
}
</MudStack>
</MudPaper>
}
</MudStack>
@@ -169,6 +169,42 @@
</CellTemplate>
</PropertyColumn>
<TemplateColumn Title="نوع فعالسازی" Sortable="false">
<CellTemplate>
@if (context.Item.IsNewActivation == null)
{
<span style="color: #bbb;">-</span>
}
else if (context.Item.IsNewActivation == true)
{
<MudChip T="string" Size="Size.Small" Color="Color.Success" Icon="@Icons.Material.Filled.FiberNew">
عضو جدید
</MudChip>
}
else
{
<MudChip T="string" Size="Size.Small" Color="Color.Secondary" Icon="@Icons.Material.Filled.Refresh">
خرید مجدد
</MudChip>
}
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="پکیج" Sortable="false">
<CellTemplate>
@if (!string.IsNullOrEmpty(context.Item.PackageName))
{
<MudChip T="string" Size="Size.Small" Color="Color.Dark">
@context.Item.PackageName
</MudChip>
}
else
{
<span style="color: #bbb;">-</span>
}
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="عملیات" Sortable="false">
<CellTemplate>
<MudButtonGroup Size="Size.Small" Variant="Variant.Text">
@@ -337,7 +373,9 @@
isClubActive = n.IsClubActive,
isActivatedInTargetWeek = n.IsActivatedInTargetWeek,
activationWeekDefinitionId = n.ActivationWeekDefinitionId,
clubActivatedAt = n.ClubActivatedAt?.ToDateTime().ToLocalTime().MiladiToJalali() ?? ""
clubActivatedAt = n.ClubActivatedAt?.ToDateTime().ToLocalTime().MiladiToJalali() ?? "",
isNewActivation = n.IsNewActivation,
packageName = n.PackageName ?? ""
}).ToArray();
var options = new { filterWeek = _activationWeekFilter };
+47 -4
View File
@@ -94,8 +94,9 @@
.admin-node-card .node-meta {
display: flex;
align-items: center;
gap: 6px;
margin-top: 2px;
gap: 4px;
margin-top: 3px;
flex-wrap: wrap;
}
.admin-node-card .level-badge {
@@ -124,8 +125,8 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 6px;
padding-top: 6px;
margin-top: 5px;
padding-top: 5px;
border-top: 1px solid #f0f0f0;
font-size: 10px;
}
@@ -204,6 +205,48 @@
animation: pulse-highlight 1.5s ease-in-out infinite;
}
/* نوع فعالسازی - inline pill */
.admin-node-card .activation-pill {
display: inline-flex;
align-items: center;
padding: 1px 6px;
border-radius: 8px;
font-size: 9px;
font-weight: 600;
white-space: nowrap;
line-height: 1.4;
}
.admin-node-card .new-member-pill {
background: #e8f5e9;
color: #2e7d32;
border: 1px solid #a5d6a7;
}
.admin-node-card .renewal-pill {
background: #ede7f6;
color: #5e35b1;
border: 1px solid #b39ddb;
}
/* نام پکیج badge */
.admin-node-card .package-name-badge {
display: inline-flex;
align-items: center;
height: 14px;
padding: 0 6px;
margin-top: 2px;
border-radius: 7px;
font-size: 9px;
color: #546e7a;
background: #eceff1;
border: 1px solid #b0bec5;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 120px;
}
@keyframes pulse-highlight {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.8; }
+23 -7
View File
@@ -39,8 +39,8 @@ window.AdminOrgChart = {
this.chart = new d3.OrgChart()
.container('#' + containerId)
.data(data)
.nodeWidth((d) => 160)
.nodeHeight((d) => 80)
.nodeWidth((d) => 178)
.nodeHeight((d) => 92)
.childrenMargin((d) => 60)
.compactMarginBetween((d) => 20)
.compactMarginPair((d) => 20)
@@ -83,12 +83,19 @@ window.AdminOrgChart = {
</div>`;
}
// Highlight badge for target week matches
let highlightBadge = '';
if (weekFilterActive && isTargetWeek) {
highlightBadge = '<div class="target-week-highlight">✨</div>';
// نوع فعالسازی - inline pill در meta row
let activationTypePill = '';
if (data.isNewActivation === true) {
activationTypePill = '<span class="activation-pill new-member-pill">عضو جدید</span>';
} else if (data.isNewActivation === false) {
activationTypePill = '<span class="activation-pill renewal-pill">تمدید</span>';
}
// نام پکیج badge
const packageBadge = data.packageName
? `<div class="package-name-badge">${data.packageName}</div>`
: '';
// Avatar - first letter of name
const firstChar = data.userName ? data.userName.charAt(0).toUpperCase() : '?';
@@ -114,6 +121,14 @@ window.AdminOrgChart = {
if (data.activationWeekDefinitionId) {
tooltipLines.push(`📆 هفته: ${data.activationWeekDefinitionId}`);
}
if (data.packageName) {
tooltipLines.push(`📦 پکیج: ${data.packageName}`);
}
if (data.isNewActivation === true) {
tooltipLines.push('🆕 نوع: اولین فعال‌سازی');
} else if (data.isNewActivation === false) {
tooltipLines.push('🔄 نوع: خرید مجدد');
}
if (weekFilterActive) {
tooltipLines.push(isTargetWeek ? '🎯 فعال در هفته انتخابی' : '⚪ خارج از هفته انتخابی');
}
@@ -125,14 +140,15 @@ window.AdminOrgChart = {
data-user-id="${data.userId}"
title="${tooltipText}">
${weekIndicator}
${highlightBadge}
<div class="node-header">
<div class="node-avatar">${firstChar}</div>
<div class="node-main-info">
<div class="node-name">${data.userName || 'کاربر ' + data.userId}</div>
${packageBadge}
<div class="node-meta">
${levelBadge}
${legText ? `<span class="${legClass}">${legText}</span>` : ''}
${activationTypePill}
</div>
</div>
</div>