Files
FrontOffice/src/FrontOffice.Main/Pages/Profile/Components/OrganizationChart.razor.cs
T

262 lines
6.6 KiB
C#

using FrontOffice.Main.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace FrontOffice.Main.Pages.Profile.Components;
public partial class OrganizationChart : IAsyncDisposable
{
private NetworkTreeDto? _networkTree;
private NetworkStatisticsDto? _statistics;
private bool _isLoading = true;
private bool _hasError = false;
private int _selectedDepthValue = 3;
private DotNetObjectReference<OrganizationChart>? _dotNetHelper;
private bool _chartNeedsInit = false;
private bool _jsReady = false;
// برای نگهداری شناسه کاربر فعلی که درختش نمایش داده شده
private long? _currentViewUserId = null;
// Stack برای بازگشت به عقب
private Stack<long> _navigationHistory = new();
private int _selectedDepth
{
get => _selectedDepthValue;
set
{
if (_selectedDepthValue != value)
{
_selectedDepthValue = value;
_ = OnDepthChanged();
}
}
}
[Inject] private NetworkMembershipService NetworkService { get; set; } = default!;
// JSRuntime is injected globally via _Imports.razor;
protected override async Task OnInitializedAsync()
{
await LoadData();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsReady = true;
}
// Initialize chart after JS is ready and data is loaded
if (_jsReady && _chartNeedsInit && _networkTree?.RootNode != null)
{
_chartNeedsInit = false;
await InitializeChart();
}
}
private async Task LoadData()
{
_isLoading = true;
_hasError = false;
StateHasChanged();
try
{
// Load tree and statistics in parallel
var treeTask = NetworkService.GetMyNetworkTreeAsync(_selectedDepth);
var statsTask = NetworkService.GetMyNetworkStatisticsAsync();
await Task.WhenAll(treeTask, statsTask);
_networkTree = await treeTask;
_statistics = await statsTask;
// Mark that chart needs to be initialized
_chartNeedsInit = true;
}
catch (Exception ex)
{
Console.WriteLine($"Error loading network data: {ex.Message}");
_hasError = true;
}
finally
{
_isLoading = false;
StateHasChanged();
}
}
private async Task InitializeChart()
{
if (_networkTree?.RootNode == null) return;
try
{
_dotNetHelper = DotNetObjectReference.Create(this);
var flatData = _networkTree.ToFlatArray();
await JSRuntime.InvokeVoidAsync("OrgChart.init", "org-chart-container", flatData, _dotNetHelper);
}
catch (Exception ex)
{
Console.WriteLine($"Error initializing chart: {ex.Message}");
}
}
private async Task RefreshData()
{
await LoadData();
if (_networkTree?.RootNode != null)
{
await InitializeChart();
}
}
private async Task OnDepthChanged()
{
await RefreshData();
}
private async Task ExpandAll()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.expandAll");
}
catch { }
}
private async Task CollapseAll()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.collapseAll");
}
catch { }
}
private async Task CenterChart()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.center");
}
catch { }
}
private async Task FitToScreen()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.fitToScreen");
}
catch { }
}
private async Task ExportPng()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.exportPng");
}
catch { }
}
/// <summary>
/// Called from JavaScript when a node is clicked
/// </summary>
[JSInvokable]
public async Task OnNodeClicked(long userId)
{
// اگر روی همان نود کلیک شده، کاری نکن
if (_currentViewUserId == userId) return;
// ذخیره نود فعلی در history برای بازگشت
if (_currentViewUserId.HasValue)
{
_navigationHistory.Push(_currentViewUserId.Value);
}
await LoadSubordinateTree(userId);
}
/// <summary>
/// بارگذاری درخت یک زیرمجموعه
/// </summary>
private async Task LoadSubordinateTree(long targetUserId)
{
_isLoading = true;
StateHasChanged();
try
{
_networkTree = await NetworkService.GetSubordinateTreeAsync(targetUserId, _selectedDepth);
_currentViewUserId = targetUserId;
_chartNeedsInit = true;
}
catch (UnauthorizedAccessException)
{
// نمایش پیام خطا - این کاربر زیرمجموعه نیست
Console.WriteLine("Unauthorized access to subordinate tree");
}
catch (Exception ex)
{
Console.WriteLine($"Error loading subordinate tree: {ex.Message}");
}
finally
{
_isLoading = false;
StateHasChanged();
}
}
/// <summary>
/// بازگشت به درخت قبلی
/// </summary>
private async Task GoBack()
{
if (_navigationHistory.Count > 0)
{
var previousUserId = _navigationHistory.Pop();
await LoadSubordinateTree(previousUserId);
}
else
{
// بازگشت به درخت خود کاربر
_currentViewUserId = null;
await LoadData();
if (_networkTree?.RootNode != null)
{
await InitializeChart();
}
}
}
/// <summary>
/// بازگشت به درخت اصلی (خود کاربر)
/// </summary>
private async Task GoToMyTree()
{
_navigationHistory.Clear();
_currentViewUserId = null;
await LoadData();
if (_networkTree?.RootNode != null)
{
await InitializeChart();
}
}
public async ValueTask DisposeAsync()
{
try
{
await JSRuntime.InvokeVoidAsync("OrgChart.dispose");
}
catch { }
_dotNetHelper?.Dispose();
}
}