@page "/projects/new" @page "/projects/{Id:int}" @inject ProjectService ProjectService @inject MaterialService MaterialService @inject NavigationManager Navigation @using CutList.Core.Formatting @(IsNew ? "New Project" : project.Name)

@(IsNew ? "New Project" : project.Name)

@if (!IsNew) { Run Optimization }
@if (loading) {

Loading...

} else if (IsNew) {
@RenderDetailsForm()
} else {
@if (activeTab == Tab.Details) {
@RenderDetailsForm()
} else if (activeTab == Tab.Parts) { @RenderPartsTab() }
} @code { private enum Tab { Details, Parts } [Parameter] public int? Id { get; set; } private Project project = new(); private List materials = new(); private List cuttingTools = new(); private bool loading = true; private bool savingProject; private string? projectErrorMessage; private Tab activeTab = Tab.Details; private void SetTab(Tab tab) => activeTab = tab; // Parts form private bool showPartForm; private ProjectPart newPart = new(); private ProjectPart? editingPart; private string? partErrorMessage; private string selectedShape = string.Empty; private IEnumerable DistinctShapes => materials.Select(m => m.Shape).Distinct().OrderBy(s => s); private IEnumerable FilteredMaterials => string.IsNullOrEmpty(selectedShape) ? Enumerable.Empty() : materials.Where(m => m.Shape == selectedShape).OrderBy(m => m.Size); private bool IsNew => !Id.HasValue; protected override async Task OnInitializedAsync() { materials = await MaterialService.GetAllAsync(); cuttingTools = await ProjectService.GetCuttingToolsAsync(); if (Id.HasValue) { var existing = await ProjectService.GetByIdAsync(Id.Value); if (existing == null) { Navigation.NavigateTo("projects"); return; } project = existing; } else { // Set default cutting tool for new projects var defaultTool = await ProjectService.GetDefaultCuttingToolAsync(); if (defaultTool != null) { project.CuttingToolId = defaultTool.Id; } } loading = false; } private RenderFragment RenderDetailsForm() => __builder => {
Project Details
@foreach (var tool in cuttingTools) { }
@if (!string.IsNullOrEmpty(projectErrorMessage)) {
@projectErrorMessage
}
Back
}; private RenderFragment RenderPartsTab() => __builder => {
Parts to Cut
@if (showPartForm) {
@(editingPart == null ? "Add Part" : "Edit Part")
@if (!string.IsNullOrEmpty(partErrorMessage)) {
@partErrorMessage
}
} @if (project.Parts.Count == 0) {

No parts added yet.

Add the parts you need to cut, selecting the material for each.

} else {
@foreach (var part in project.Parts) { }
Material Length Qty Name Actions
@part.Material.DisplayName @ArchUnits.FormatFromInches((double)part.LengthInches) @part.Quantity @(string.IsNullOrWhiteSpace(part.Name) ? "-" : part.Name)
Total: @project.Parts.Sum(p => p.Quantity) pieces
}
}; private async Task SaveProjectAsync() { projectErrorMessage = null; savingProject = true; try { if (string.IsNullOrWhiteSpace(project.Name)) { projectErrorMessage = "Project name is required"; return; } if (IsNew) { var created = await ProjectService.CreateAsync(project); Navigation.NavigateTo($"projects/{created.Id}"); } else { await ProjectService.UpdateAsync(project); } } finally { savingProject = false; } } // Parts methods private void ShowAddPartForm() { editingPart = null; newPart = new ProjectPart { ProjectId = Id!.Value, Quantity = 1 }; selectedShape = string.Empty; showPartForm = true; partErrorMessage = null; } private void OnShapeChanged() { newPart.MaterialId = 0; } private void EditPart(ProjectPart part) { editingPart = part; newPart = new ProjectPart { Id = part.Id, ProjectId = part.ProjectId, MaterialId = part.MaterialId, Name = part.Name, LengthInches = part.LengthInches, Quantity = part.Quantity, SortOrder = part.SortOrder }; selectedShape = part.Material?.Shape ?? string.Empty; showPartForm = true; partErrorMessage = null; } private void CancelPartForm() { showPartForm = false; editingPart = null; } private async Task SavePartAsync() { partErrorMessage = null; if (string.IsNullOrEmpty(selectedShape)) { partErrorMessage = "Please select a shape"; return; } if (newPart.MaterialId == 0) { partErrorMessage = "Please select a size"; return; } if (newPart.LengthInches <= 0) { partErrorMessage = "Length must be greater than zero"; return; } if (newPart.Quantity < 1) { partErrorMessage = "Quantity must be at least 1"; return; } if (editingPart == null) { await ProjectService.AddPartAsync(newPart); } else { await ProjectService.UpdatePartAsync(newPart); } project = (await ProjectService.GetByIdAsync(Id!.Value))!; showPartForm = false; editingPart = null; } private async Task DeletePart(ProjectPart part) { await ProjectService.DeletePartAsync(part.Id); project = (await ProjectService.GetByIdAsync(Id!.Value))!; } }