feat: Add reusable MaterialFilter component

Provides shape, type, grade dropdowns and text search
for filtering material-based list pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 14:36:58 -05:00
parent 141176cc5d
commit f932e8ba13
2 changed files with 90 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
@using CutList.Web.Data.Entities
<div class="row g-2 mb-3">
<div class="col-auto">
<select class="form-select form-select-sm" value="@Value.Shape" @onchange="OnShapeChanged">
<option value="">All Shapes</option>
@foreach (var shape in Enum.GetValues<MaterialShape>())
{
<option value="@shape">@shape.GetDisplayName()</option>
}
</select>
</div>
<div class="col-auto">
<select class="form-select form-select-sm" value="@Value.Type" @onchange="OnTypeChanged">
<option value="">All Types</option>
@foreach (var type in Enum.GetValues<MaterialType>())
{
<option value="@type">@type</option>
}
</select>
</div>
<div class="col-auto">
<select class="form-select form-select-sm" value="@(Value.Grade ?? "")" @onchange="OnGradeChanged">
<option value="">All Grades</option>
@foreach (var grade in AvailableGrades)
{
<option value="@grade">@grade</option>
}
</select>
</div>
<div class="col-auto">
<input type="text" class="form-control form-control-sm" placeholder="Search..." value="@Value.SearchText" @oninput="OnSearchInput" />
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary" @onclick="OnClear" title="Clear filters">
<i class="bi bi-x-lg"></i> Clear
</button>
</div>
</div>
@code {
[Parameter] public IEnumerable<string> AvailableGrades { get; set; } = Enumerable.Empty<string>();
[Parameter] public MaterialFilterState Value { get; set; } = new();
[Parameter] public EventCallback<MaterialFilterState> ValueChanged { get; set; }
private async Task OnShapeChanged(ChangeEventArgs e)
{
Value.Shape = Enum.TryParse<MaterialShape>(e.Value?.ToString(), out var shape) ? shape : null;
await ValueChanged.InvokeAsync(Value);
}
private async Task OnTypeChanged(ChangeEventArgs e)
{
Value.Type = Enum.TryParse<MaterialType>(e.Value?.ToString(), out var type) ? type : null;
await ValueChanged.InvokeAsync(Value);
}
private async Task OnGradeChanged(ChangeEventArgs e)
{
var val = e.Value?.ToString();
Value.Grade = string.IsNullOrEmpty(val) ? null : val;
await ValueChanged.InvokeAsync(Value);
}
private async Task OnSearchInput(ChangeEventArgs e)
{
Value.SearchText = e.Value?.ToString();
await ValueChanged.InvokeAsync(Value);
}
private async Task OnClear()
{
Value.Shape = null;
Value.Type = null;
Value.Grade = null;
Value.SearchText = null;
await ValueChanged.InvokeAsync(Value);
}
}

View File

@@ -0,0 +1,11 @@
using CutList.Web.Data.Entities;
namespace CutList.Web.Components.Shared;
public class MaterialFilterState
{
public MaterialShape? Shape { get; set; }
public MaterialType? Type { get; set; }
public string? Grade { get; set; }
public string? SearchText { get; set; }
}