@using Commerce.Contracts.DTOs @inject CategoryAPIConsumer CategoryService
@if (_isOpen) {
@for (int i = 0; i < _path.Count; i++) { var index = i; var step = _path[i];
}
@if (_loading) {
Loading categories...
} else if (!string.IsNullOrEmpty(_error)) {
@_error
} else if (_activeCategories == null || !_activeCategories.Any()) {
No categories found in this level
} else {
@foreach (var cat in _activeCategories) {
@cat.Translations?.FirstOrDefault()?.Info?.Name ?? string.Empty) @if (cat.Id == Value) { Selected }
@if (cat.ChildCategories?.Any() == true) { }
}
}
Total: @(_tree.Count) roots
}
@code { [Parameter] public Guid? Value { get; set; } [Parameter] public EventCallback ValueChanged { get; set; } [Parameter] public string Placeholder { get; set; } = "Select Category"; [Parameter] public List? DisallowedIds { get; set; } [Parameter] public bool ShowBundles { get; set; } = true; private List _tree = new(); private List _path = new(); private int _activeLevel = 0; private bool _isOpen = false; private bool _loading = true; private string? _error; private List _activeCategories => GetCategoriesForLevel(_activeLevel); private CategoryVM? SelectedCategory; protected override async Task OnInitializedAsync() { await LoadTree(); } protected override async Task OnParametersSetAsync() { await UpdateSelectedCategory(); } private async Task LoadTree() { _loading = true; _error = null; try { _tree = await CategoryService.FetchCategoryTree(); await UpdateSelectedCategory(); } catch (Exception ex) { _error = "Failed to load categories hierarchy."; Console.WriteLine($"Error loading category tree: {ex.Message}"); } finally { _loading = false; } } private async Task UpdateSelectedCategory() { if (Value.HasValue) { SelectedCategory = FindInCategoryTree(_tree, Value.Value); } else { SelectedCategory = null; } } private CategoryVM? FindInCategoryTree(List categories, Guid id) { foreach (var cat in categories) { if (cat.Id == id) return cat; if (cat.ChildCategories != null) { var found = FindInCategoryTree(cat.ChildCategories.ToList(), id); if (found != null) return found; } } return null; } private List GetCategoriesForLevel(int level) { var cats = level == 0 ? _tree : _path[level - 1].ChildCategories?.ToList(); if (cats != null) { if (DisallowedIds != null) { cats = cats.Where(x => !DisallowedIds.Contains(x.Id ?? Guid.Empty)).ToList(); } if (!ShowBundles) { cats = cats.Where(x => !x.IsBundle).ToList(); } } return cats ?? new List(); } private void ToggleDropdown() => _isOpen = !_isOpen; private void SetLevel(int level) { _activeLevel = level; if (level <= _path.Count) { _path = _path.Take(level).ToList(); } } private async Task SelectValue(CategoryVM category) { Value = category.Id; await ValueChanged.InvokeAsync(Value); SelectedCategory = category; StateHasChanged(); } private void DrillDown(CategoryVM category) { if (category.ChildCategories?.Any() == true) { _path.Add(category); _activeLevel = _path.Count; } } private async Task ClearSelection() { Value = null; await ValueChanged.InvokeAsync(Value); SelectedCategory = null; _path.Clear(); _activeLevel = 0; } }