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>
221 lines
6.7 KiB
Plaintext
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.####}\")";
|
|
}
|
|
}
|