feat: Implement Commission and Network Statistics Pages with DTOs and Services

- Added CommissionDashboardPage and CommissionHistoryPage for displaying commission payouts and history.
- Implemented WeeklyBalancePage to show weekly balance details.
- Created NetworkStatisticsPage to display network statistics and tree structure.
- Developed corresponding services (CommissionService, NetworkMembershipService) for data retrieval.
- Introduced DTOs for Commission and Network data structures (CommissionPayoutDto, WeeklyBalanceDto, NetworkStatisticsDto).
- Added mock data generation for testing purposes in services.
- Enhanced UI with MudBlazor components for better user experience.
This commit is contained in:
masoodafar-web
2025-12-04 17:29:16 +03:30
parent a8e6693c70
commit 95c6bf5efa
25 changed files with 1882 additions and 26 deletions
@@ -0,0 +1,148 @@
@attribute [Route(RouteConstants.Network.Statistics)]
@using FrontOffice.Main.Utilities
@using MudBlazor
<PageTitle>آمار شبکه</PageTitle>
<MudContainer MaxWidth="MaxWidth.Large" Class="py-6">
<MudStack Spacing="3">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudText Typo="Typo.h5">آمار شبکه من</MudText>
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack" Href="@RouteConstants.Profile.Index">بازگشت</MudButton>
</MudStack>
@if (_isLoading)
{
<MudProgressLinear Color="Color.Primary" Indeterminate="true" />
}
else if (_statistics is not null)
{
<!-- کارت‌های آماری -->
<MudGrid Spacing="2">
<MudItem xs="12" sm="6" md="3">
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudStack Spacing="1">
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary">کل اعضای شبکه</MudText>
<MudText Typo="Typo.h4" Color="Color.Primary">@_statistics.TotalMembers</MudText>
<MudIcon Icon="@Icons.Material.Filled.Groups" Color="Color.Primary" />
</MudStack>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudStack Spacing="1">
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary">شاخه چپ</MudText>
<MudText Typo="Typo.h4" Color="Color.Info">@_statistics.LeftLegCount</MudText>
<MudIcon Icon="@Icons.Material.Filled.ChevronLeft" Color="Color.Info" />
</MudStack>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudStack Spacing="1">
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary">شاخه راست</MudText>
<MudText Typo="Typo.h4" Color="Color.Success">@_statistics.RightLegCount</MudText>
<MudIcon Icon="@Icons.Material.Filled.ChevronRight" Color="Color.Success" />
</MudStack>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudStack Spacing="1">
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary">عمق درخت</MudText>
<MudText Typo="Typo.h4" Color="Color.Secondary">@_statistics.TreeDepth</MudText>
<MudIcon Icon="@Icons.Material.Filled.AccountTree" Color="Color.Secondary" />
</MudStack>
</MudPaper>
</MudItem>
</MudGrid>
<!-- تعادل شاخه‌ها -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudText Typo="Typo.h6" Class="mb-3">تعادل شاخه‌ها</MudText>
<MudStack Spacing="2">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudText Typo="Typo.body1">شاخه چپ: <strong>@_statistics.LeftLegCount</strong> عضو</MudText>
<MudProgressLinear Color="Color.Info"
Value="@GetLeftPercentage()"
Size="Size.Medium"
Class="rounded-lg"
Style="width: 200px;" />
</MudStack>
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudText Typo="Typo.body1">شاخه راست: <strong>@_statistics.RightLegCount</strong> عضو</MudText>
<MudProgressLinear Color="Color.Success"
Value="@GetRightPercentage()"
Size="Size.Medium"
Class="rounded-lg"
Style="width: 200px;" />
</MudStack>
@if (!string.IsNullOrEmpty(_statistics.WeakerLeg))
{
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined">
شاخه ضعیف‌تر: <strong>@GetLegText(_statistics.WeakerLeg)</strong>
</MudAlert>
}
</MudStack>
</MudPaper>
<!-- نمودار دایره‌ای -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudText Typo="Typo.h6" Class="mb-3">توزیع اعضا</MudText>
<MudChart ChartType="ChartType.Donut"
Width="300px"
Height="300px"
InputData="@(new double[] { _statistics.LeftLegCount, _statistics.RightLegCount })"
InputLabels="@(new[] { "شاخه چپ", "شاخه راست" })"
ChartOptions="@_chartOptions" />
</MudPaper>
<!-- آخرین عضو -->
@if (_statistics.LastMember is not null)
{
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudText Typo="Typo.h6" Class="mb-3">آخرین عضو پیوسته</MudText>
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Center">
<MudAvatar Color="Color.Primary" Size="Size.Large">
@GetInitials(_statistics.LastMember.FullName)
</MudAvatar>
<MudStack Spacing="0">
<MudText Typo="Typo.body1"><strong>@_statistics.LastMember.FullName</strong></MudText>
<MudText Typo="Typo.caption" Class="mud-text-secondary">
موقعیت: @GetPositionText(_statistics.LastMember.Position)
</MudText>
<MudText Typo="Typo.caption" Class="mud-text-secondary">
تاریخ عضویت: @_statistics.LastMember.JoinedAt.ToString("yyyy/MM/dd HH:mm")
</MudText>
</MudStack>
</MudStack>
</MudPaper>
}
<!-- دکمه‌های عملیات -->
<MudStack Row="true" Spacing="2">
<MudButton Variant="Variant.Outlined"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.AccountTree"
Href="@RouteConstants.Profile.Tree"
FullWidth="true">
مشاهده درخت کامل
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.Refresh"
OnClick="LoadStatisticsAsync"
FullWidth="true">
بروزرسانی آمار
</MudButton>
</MudStack>
}
else if (_hasError)
{
<MudAlert Severity="Severity.Error" Variant="Variant.Filled">
خطا در دریافت آمار شبکه. لطفا دوباره تلاش کنید.
</MudAlert>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="LoadStatisticsAsync">تلاش مجدد</MudButton>
}
</MudStack>
</MudContainer>
@@ -0,0 +1,86 @@
using FrontOffice.Main.Utilities;
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace FrontOffice.Main.Pages.Network;
public partial class NetworkStatisticsPage : ComponentBase
{
[Inject] private NetworkMembershipService NetworkService { get; set; } = default!;
private NetworkStatisticsDto? _statistics;
private bool _isLoading = true;
private bool _hasError = false;
private readonly ChartOptions _chartOptions = new()
{
ChartPalette = new[]
{
"#2196F3", // Info (Left)
"#4CAF50" // Success (Right)
}
};
protected override async Task OnInitializedAsync()
{
await LoadStatisticsAsync();
}
private async Task LoadStatisticsAsync()
{
try
{
_isLoading = true;
_hasError = false;
_statistics = await NetworkService.GetMyNetworkStatisticsAsync();
}
catch (Exception ex)
{
_hasError = true;
Snackbar.Add($"خطا در دریافت آمار: {ex.Message}", Severity.Error);
}
finally
{
_isLoading = false;
}
}
private double GetLeftPercentage()
{
if (_statistics is null || _statistics.TotalMembers == 0)
return 0;
return (_statistics.LeftLegCount / (double)_statistics.TotalMembers) * 100;
}
private double GetRightPercentage()
{
if (_statistics is null || _statistics.TotalMembers == 0)
return 0;
return (_statistics.RightLegCount / (double)_statistics.TotalMembers) * 100;
}
private string GetLegText(string leg) => leg.ToLower() switch
{
"left" => "شاخه چپ",
"right" => "شاخه راست",
_ => leg
};
private string GetPositionText(string position) => position.ToLower() switch
{
"left" => "چپ",
"right" => "راست",
_ => position
};
private string GetInitials(string fullName)
{
if (string.IsNullOrWhiteSpace(fullName))
return "؟";
var parts = fullName.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2)
return $"{parts[0][0]}{parts[1][0]}";
return fullName.Length > 0 ? fullName[0].ToString() : "؟";
}
}