@using System.ComponentModel.DataAnnotations @using RackPeek.Domain.Persistence @using RackPeek.Domain.Resources.Hardware @using RackPeek.Domain.Resources.SystemResources @inject IResourceCollection Repo @if (IsOpen) {
@Title
@* ReSharper disable once CSharpWarnings::CS8619 *@
@foreach (var group in FilteredItems) { @foreach (var item in group.Value) { } } @* Optional helper text *@ @if (!string.IsNullOrWhiteSpace(_model.Value) && _lookup.TryGetValue(_model.Value!, out var selected)) {
Selected: @selected.Type @selected.Group
}
} @code { /* ---------- Parameters ---------- */ [Parameter] public bool IsOpen { get; set; } [Parameter] public EventCallback IsOpenChanged { get; set; } [Parameter] public string Title { get; set; } = "Select hardware or system"; [Parameter] public string? Value { get; set; } [Parameter] public EventCallback OnAccept { get; set; } private SelectionFormModel _model = new(); private string _search = string.Empty; private List _items = new(); private Dictionary _lookup = new(); private bool CanAccept => !string.IsNullOrWhiteSpace(_model.Value); /* ---------- Lifecycle ---------- */ protected override async Task OnParametersSetAsync() { if (!IsOpen) return; // Load both sets var hardware = (await Repo.GetAllOfTypeAsync()) .Select(h => SelectionItem.Hardware(h.Name, h.Kind)) .ToList(); var systems = (await Repo.GetAllOfTypeAsync()) .Select(s => SelectionItem.System(s.Name, string.IsNullOrWhiteSpace(s.Type) ? "System" : s.Type!)) .ToList(); _items = hardware .Concat(systems) .OrderBy(i => i.TypeSort) .ThenBy(i => i.Group) .ThenBy(i => i.Label) .ToList(); _lookup = _items.ToDictionary(i => i.Value, i => i); _model = new SelectionFormModel { Value = Value }; _search = string.Empty; } private IReadOnlyDictionary> FilteredItems => _items .Where(i => string.IsNullOrWhiteSpace(_search) || i.Label.Contains(_search.Trim(), StringComparison.OrdinalIgnoreCase) || i.Group.Contains(_search.Trim(), StringComparison.OrdinalIgnoreCase)) .GroupBy(i => $"{i.Type} — {i.Group}") .ToDictionary( g => g.Key, g => g.OrderBy(i => i.Label).ToList()); private async Task HandleAccept() { await OnAccept.InvokeAsync(_model.Value); await Close(); } private async Task Cancel() { await Close(); } private async Task Close() { _model = new SelectionFormModel(); _search = string.Empty; await IsOpenChanged.InvokeAsync(false); } private void OnSearchChanged(string? value) { _search = value ?? string.Empty; } private void OnSearchInput(ChangeEventArgs e) { _search = e.Value?.ToString() ?? string.Empty; if (FilteredItems.SelectMany(g => g.Value).All(i => i.Value != _model.Value)) _model.Value = null; } private sealed class SelectionFormModel { [Required] public string? Value { get; set; } } private sealed record SelectionItem( string Type, string Group, string Label, string Value, int TypeSort) { public static SelectionItem Hardware(string name, string kind) { return new SelectionItem("Hardware", kind, name, $"{name}", 0); } public static SelectionItem System(string name, string category) { return new SelectionItem("System", category, name, $"{name}", 1); } } }