@using MudBlazor @using Tasks.Module.Models @using Tasks.Module.Services TASKS SHEETS
@if (_loading) { } else if (_treeItems.Any()) { @foreach (var item in _treeItems) { } } else { No tasks sheets yet }
@if (_contextMenuOpen) {
New Subfolder New Task List Delete
} @code { private bool _loading = true; private List _treeItems = new(); private bool _contextMenuOpen = false; private string? _contextItemId = null; private TreeItemType _contextItemType = TreeItemType.Folder; private double _contextMenuX = 0; private double _contextMenuY = 0; private bool _rootDragOver = false; private TreeItemDto? _draggingItem = null; private bool _isMoving = false; private int _dragSessionId = 0; [Inject] private IFolderService FolderService { get; set; } = null!; [Inject] private ISnackbar Snackbar { get; set; } = null!; [Inject] private IDialogService DialogService { get; set; } = null!; [Inject] private NavigationManager NavigationManager { get; set; } = null!; protected override async Task OnInitializedAsync() { await RefreshTree(); } private async Task RefreshTree() { _loading = true; try { var newTreeItems = new List(); var rootFolders = await FolderService.GetAllRootFoldersAsync(); foreach (var folder in rootFolders) { var treeItem = await BuildTreeItem(folder); newTreeItems.Add(treeItem); } var rootTasksSheets = await FolderService.GetRootTasksSheetsAsync(); foreach (var ts in rootTasksSheets) { newTreeItems.Add(new TreeItemDto { Id = ts.Id, Name = ts.Title, Type = TreeItemType.TasksSheet, ParentId = null, IsExpanded = false, Children = new List() }); } _treeItems = newTreeItems; } catch (Exception ex) { Snackbar.Add($"Error: {ex.Message}", Severity.Error); } finally { _loading = false; } } private async Task BuildTreeItem(FolderDto folder) { var item = new TreeItemDto { Id = folder.Id, Name = folder.Name, Type = TreeItemType.Folder, ParentId = folder.ParentId, IsExpanded = false, Children = new List() }; foreach (var child in folder.Children) { var childItem = await BuildTreeItem(child); item.Children.Add(childItem); } try { var tasksSheets = await FolderService.GetTasksSheetsInFolderAsync(folder.Id); foreach (var ts in tasksSheets) { item.Children.Add(new TreeItemDto { Id = ts.Id, Name = ts.Title, Type = TreeItemType.TasksSheet, ParentId = folder.Id, IsExpanded = false, Children = new List() }); } } catch { } return item; } private async Task OnItemClick(TreeItemDto item) { if (item.Type == TreeItemType.Folder) { item.IsExpanded = !item.IsExpanded; if (item.IsExpanded && !item.Children.Any()) { await LoadFolderChildren(item); } } else if (item.Type == TreeItemType.TasksSheet) { NavigationManager.NavigateTo($"/tasks/taskssheet/{item.Id}"); } } private async Task OnItemExpand(TreeItemDto item) { if (item.Type != TreeItemType.Folder) return; item.IsExpanded = !item.IsExpanded; if (item.IsExpanded && !item.Children.Any()) { await LoadFolderChildren(item); } } private async Task LoadFolderChildren(TreeItemDto item) { try { var folder = await FolderService.GetFolderByIdAsync(item.Id); if (folder != null) { item.Children.Clear(); foreach (var child in folder.Children) { var childItem = await BuildTreeItem(child); item.Children.Add(childItem); } var tasksSheets = await FolderService.GetTasksSheetsInFolderAsync(item.Id); foreach (var ts in tasksSheets) { item.Children.Add(new TreeItemDto { Id = ts.Id, Name = ts.Title, Type = TreeItemType.TasksSheet, ParentId = item.Id, IsExpanded = false, Children = new List() }); } } } catch { } } private void OnContextMenuWithArgs((TreeItemDto, MouseEventArgs) args) { _contextItemId = args.Item1.Id; _contextItemType = args.Item1.Type; _contextMenuX = args.Item2.ClientX; _contextMenuY = args.Item2.ClientY; _contextMenuOpen = true; } private void CloseContextMenu() { _contextMenuOpen = false; _contextItemId = null; } private string GetContextMenuStyle() { return $"position: fixed; left: {_contextMenuX}px; top: {_contextMenuY}px; z-index: 1100;"; } private async Task OnCreateFolder() { var parameters = new DialogParameters { { "Title", "New Folder" }, { "Label", "Folder Name" } }; var dialog = await DialogService.ShowAsync("New Folder", parameters); var result = await dialog.Result; if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name)) { try { await FolderService.CreateFolderAsync(new CreateFolderDto { Name = name }); Snackbar.Add($"Created folder '{name}'", Severity.Success); await RefreshTree(); } catch (Exception ex) { Snackbar.Add($"Error creating folder: {ex.Message}", Severity.Error); } } } private async Task OnCreateFile() { var parameters = new DialogParameters { { "Title", "New Tasks Sheet" }, { "Label", "Tasks Sheet Name" } }; var dialog = await DialogService.ShowAsync("New Tasks Sheet", parameters); var result = await dialog.Result; if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name)) { try { await FolderService.CreateTasksSheetAsync(new CreateTasksSheetDto { Title = name }); Snackbar.Add($"Created tasks sheet '{name}'", Severity.Success); await RefreshTree(); } catch (Exception ex) { Snackbar.Add($"Error creating tasks sheet: {ex.Message}", Severity.Error); } } } private async Task CreateSubFolder() { if (_contextItemId == null) return; var parentId = _contextItemId; CloseContextMenu(); var parameters = new DialogParameters { { "Title", "New Subfolder" }, { "Label", "Folder Name" } }; var dialog = await DialogService.ShowAsync("New Subfolder", parameters); var result = await dialog.Result; if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name)) { try { await FolderService.CreateFolderAsync(new CreateFolderDto { Name = name, ParentId = parentId }); Snackbar.Add($"Created subfolder '{name}'", Severity.Success); await RefreshTree(); } catch (Exception ex) { Snackbar.Add($"Error: {ex.Message}", Severity.Error); } } } private async Task CreateSubFile() { if (_contextItemId == null) return; var parentId = _contextItemId; CloseContextMenu(); var parameters = new DialogParameters { { "Title", "New Tasks Sheet" }, { "Label", "Tasks Sheet Name" } }; var dialog = await DialogService.ShowAsync("New Tasks Sheet", parameters); var result = await dialog.Result; if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name)) { try { await FolderService.CreateTasksSheetAsync(new CreateTasksSheetDto { Title = name, ParentFolderId = parentId }); Snackbar.Add($"Created tasks sheet '{name}'", Severity.Success); await RefreshTree(); } catch (Exception ex) { Snackbar.Add($"Error: {ex.Message}", Severity.Error); } } } private async Task DeleteSelected() { if (_contextItemId == null) return; var itemId = _contextItemId; var itemType = _contextItemType; CloseContextMenu(); try { if (itemType == TreeItemType.Folder) { await FolderService.DeleteFolderAsync(itemId); Snackbar.Add("Folder deleted", Severity.Success); } await RefreshTree(); } catch (Exception ex) { Snackbar.Add($"Error: {ex.Message}", Severity.Error); } } private void OnDragStartHandler(TreeItemDto item) { _dragSessionId++; _draggingItem = item; item.IsDragSource = true; } private void OnDragEndHandler(TreeItemDto item) { _dragSessionId++; _isMoving = false; ClearDragState(_treeItems); _rootDragOver = false; _draggingItem = null; } private void ClearDragState(List items) { foreach (var item in items) { item.IsDragSource = false; item.IsDragOver = false; if (item.Children.Any()) ClearDragState(item.Children); } } private async Task OnItemDropHandler(TreeItemDto target) { var source = _draggingItem; if (source == null) return; var sessionId = _dragSessionId; await MoveItem(source, target.Id, sessionId); _draggingItem = null; } private void OnRootDragEnter() { if (_draggingItem != null) _rootDragOver = true; } private void OnRootDragOver() { if (_draggingItem != null) _rootDragOver = true; } private void OnRootDragLeave() { _rootDragOver = false; } private async Task OnRootDrop() { _rootDragOver = false; var source = _draggingItem; if (source == null) return; var sessionId = _dragSessionId; await MoveItem(source, null, sessionId); _draggingItem = null; } private async Task MoveItem(TreeItemDto source, string? targetParentId, int sessionId) { if (_isMoving || sessionId != _dragSessionId) return; _isMoving = true; if (source.Id == targetParentId) { _isMoving = false; return; } if (source.ParentId == targetParentId && targetParentId != null) { _isMoving = false; return; } if (source.Type == TreeItemType.Folder && targetParentId != null) { if (await IsDescendantOf(source.Id, targetParentId)) { Snackbar.Add("Cannot move a folder into one of its children.", Severity.Error); _isMoving = false; return; } } try { bool success; if (source.Type == TreeItemType.Folder) { success = await FolderService.MoveFolderAsync(source.Id, targetParentId); } else { success = await FolderService.MoveTasksSheetAsync(source.Id, targetParentId); } if (success) { Snackbar.Add($"Moved '{source.Name}'", Severity.Success); await RefreshTree(); } else { Snackbar.Add($"Could not move '{source.Name}' — item not found.", Severity.Warning); } } catch (Exception ex) { Snackbar.Add($"Error moving item: {ex.Message}", Severity.Error); } finally { _isMoving = false; } } private async Task IsDescendantOf(string ancestorId, string candidateId) { var queue = new Queue(); queue.Enqueue(ancestorId); var visited = new HashSet(); while (queue.Count > 0) { var current = queue.Dequeue(); if (!visited.Add(current)) continue; if (current == candidateId) return true; var children = await FolderService.GetFolderByIdAsync(current); if (children == null) continue; foreach (var child in children.Children) { queue.Enqueue(child.Id); } } return false; } }