Files
CutList/CutList.Web/Components/Pages/Tools/Index.razor
AJ Isaacs 9868df162d feat: Add CutList.Web Blazor Server application
Add a new web-based frontend for cut list optimization using:
- Blazor Server with .NET 8
- Entity Framework Core with MSSQL LocalDB
- Full CRUD for Materials, Suppliers, Projects, and Cutting Tools
- Supplier stock length management for quick project setup
- Integration with CutList.Core for bin packing optimization
- Print-friendly HTML reports with efficiency statistics

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:56:21 -05:00

221 lines
6.7 KiB
Plaintext

@page "/tools"
@inject ProjectService ProjectService
@using CutList.Core.Formatting
<PageTitle>Cutting Tools</PageTitle>
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Cutting Tools</h1>
<button class="btn btn-primary" @onclick="ShowAddForm">Add Tool</button>
</div>
@if (loading)
{
<p><em>Loading...</em></p>
}
else
{
@if (showForm)
{
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">@(editingTool == null ? "Add Cutting Tool" : "Edit Cutting Tool")</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Name</label>
<input type="text" class="form-control" @bind="formTool.Name" placeholder="e.g., Bandsaw" />
</div>
<div class="col-md-4">
<label class="form-label">Kerf Width (inches)</label>
<input type="number" step="0.0001" class="form-control" @bind="formTool.KerfInches" />
<div class="form-text">Common: 1/16" = 0.0625, 1/8" = 0.125</div>
</div>
<div class="col-md-4">
<label class="form-label">Default</label>
<div class="form-check mt-2">
<input type="checkbox" class="form-check-input" id="isDefault" @bind="formTool.IsDefault" />
<label class="form-check-label" for="isDefault">Set as default tool</label>
</div>
</div>
</div>
@if (!string.IsNullOrEmpty(errorMessage))
{
<div class="alert alert-danger mt-3">@errorMessage</div>
}
<div class="mt-3 d-flex gap-2">
<button class="btn btn-primary" @onclick="SaveAsync" disabled="@saving">
@if (saving)
{
<span class="spinner-border spinner-border-sm me-1"></span>
}
@(editingTool == null ? "Add Tool" : "Save Changes")
</button>
<button class="btn btn-outline-secondary" @onclick="CancelForm">Cancel</button>
</div>
</div>
</div>
}
@if (tools.Count == 0)
{
<div class="alert alert-info">
No cutting tools found. Add your first cutting tool to get started.
</div>
}
else
{
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>Kerf Width</th>
<th>Default</th>
<th style="width: 140px;">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var tool in tools)
{
<tr>
<td>@tool.Name</td>
<td>@FormatKerf(tool.KerfInches)</td>
<td>
@if (tool.IsDefault)
{
<span class="badge bg-primary">Default</span>
}
</td>
<td>
<button class="btn btn-sm btn-outline-primary" @onclick="() => Edit(tool)">Edit</button>
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(tool)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
}
}
<ConfirmDialog @ref="deleteDialog"
Title="Delete Cutting Tool"
Message="@deleteMessage"
ConfirmText="Delete"
OnConfirm="DeleteConfirmed" />
@code {
private List<CuttingTool> tools = new();
private bool loading = true;
private bool showForm;
private bool saving;
private string? errorMessage;
private CuttingTool formTool = new();
private CuttingTool? editingTool;
private ConfirmDialog deleteDialog = null!;
private CuttingTool? toolToDelete;
private string deleteMessage = "";
protected override async Task OnInitializedAsync()
{
tools = await ProjectService.GetCuttingToolsAsync();
loading = false;
}
private void ShowAddForm()
{
editingTool = null;
formTool = new CuttingTool { KerfInches = 0.125m };
showForm = true;
errorMessage = null;
}
private void Edit(CuttingTool tool)
{
editingTool = tool;
formTool = new CuttingTool
{
Id = tool.Id,
Name = tool.Name,
KerfInches = tool.KerfInches,
IsDefault = tool.IsDefault,
IsActive = tool.IsActive
};
showForm = true;
errorMessage = null;
}
private void CancelForm()
{
showForm = false;
editingTool = null;
errorMessage = null;
}
private async Task SaveAsync()
{
errorMessage = null;
saving = true;
try
{
if (string.IsNullOrWhiteSpace(formTool.Name))
{
errorMessage = "Name is required";
return;
}
if (formTool.KerfInches < 0)
{
errorMessage = "Kerf width cannot be negative";
return;
}
if (editingTool == null)
{
await ProjectService.CreateCuttingToolAsync(formTool);
}
else
{
await ProjectService.UpdateCuttingToolAsync(formTool);
}
tools = await ProjectService.GetCuttingToolsAsync();
showForm = false;
editingTool = null;
}
finally
{
saving = false;
}
}
private void ConfirmDelete(CuttingTool tool)
{
toolToDelete = tool;
deleteMessage = $"Are you sure you want to delete \"{tool.Name}\"?";
deleteDialog.Show();
}
private async Task DeleteConfirmed()
{
if (toolToDelete != null)
{
await ProjectService.DeleteCuttingToolAsync(toolToDelete.Id);
tools = await ProjectService.GetCuttingToolsAsync();
}
}
private string FormatKerf(decimal kerf)
{
// Show as fraction if it's a common value
var inches = (double)kerf;
var formatted = FormatHelper.ConvertToMixedFraction(inches);
return $"{formatted}\" ({kerf:0.####}\")";
}
}