262 lines
6.6 KiB
C#
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();
|
|
}
|
|
} |