[verified] feat: add configurable results length units
Build CutList image / build-and-push (push) Successful in 31s
Build CutList image / build-and-push (push) Successful in 31s
Default cut results to total-inch mixed fractions, allow switching to feet and inches, and separate part names from lengths visually.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CutList.Core\CutList.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
using CutList.Core.Formatting;
|
||||
using Xunit;
|
||||
|
||||
namespace CutList.Core.Tests;
|
||||
|
||||
public class ResultsLengthDisplayTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(150, "150\"")]
|
||||
[InlineData(150.375, "150-3/8\"")]
|
||||
public void FormatInches_uses_total_inches_without_converting_to_feet(double inches, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ArchUnits.FormatInches(inches));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Results_markup_includes_unit_toggle_and_divider_between_part_and_length()
|
||||
{
|
||||
var sourcePath = Path.GetFullPath(
|
||||
Path.Combine(AppContext.BaseDirectory, "../../../../CutList.Web/Components/Pages/Jobs/Edit.razor"));
|
||||
var markup = File.ReadAllText(sourcePath);
|
||||
|
||||
Assert.Contains("FormatResultLength", markup);
|
||||
Assert.Contains("cut-part-badge-divider", markup);
|
||||
Assert.Contains("Show feet + inches", markup);
|
||||
}
|
||||
}
|
||||
@@ -80,5 +80,13 @@ namespace CutList.Core.Formatting
|
||||
return $"{inches}\"";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a measurement as a mixed fraction of total inches without converting to feet.
|
||||
/// </summary>
|
||||
public static string FormatInches(double totalInches)
|
||||
{
|
||||
return $"{FormatHelper.ConvertToMixedFraction(totalInches)}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,6 +503,7 @@ else
|
||||
private MultiMaterialPackingSummary? summary;
|
||||
private bool optimizing;
|
||||
private bool lockingJob;
|
||||
private bool showLengthsInInches = true;
|
||||
|
||||
private IEnumerable<MaterialShape> DistinctShapes => materials.Select(m => m.Shape).Distinct().OrderBy(s => s);
|
||||
private IEnumerable<Material> FilteredMaterials => !selectedShape.HasValue
|
||||
@@ -511,6 +512,9 @@ else
|
||||
|
||||
private bool IsNew => !Id.HasValue;
|
||||
private bool CanOptimize => job.Parts.Count > 0 && job.CuttingToolId != null;
|
||||
private string FormatResultLength(double inches) => showLengthsInInches
|
||||
? ArchUnits.FormatInches(inches)
|
||||
: ArchUnits.FormatFromInches(inches);
|
||||
|
||||
private async Task UnlockJob()
|
||||
{
|
||||
@@ -987,6 +991,10 @@ else
|
||||
<button class="btn btn-outline-secondary ms-2" @onclick="PrintReport">
|
||||
<i class="bi bi-printer me-1"></i> Print Report
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary ms-2" @onclick="() => showLengthsInInches = !showLengthsInInches"
|
||||
aria-pressed="@showLengthsInInches">
|
||||
@(showLengthsInInches ? "Show feet + inches" : "Show all inches")
|
||||
</button>
|
||||
@if (!job.IsLocked)
|
||||
{
|
||||
<button class="btn btn-warning ms-2" @onclick="LockJob" disabled="@lockingJob">
|
||||
@@ -1037,7 +1045,7 @@ else
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-0">@ArchUnits.FormatFromInches(summary.TotalWaste)</h2>
|
||||
<h2 class="card-title mb-0">@FormatResultLength(summary.TotalWaste)</h2>
|
||||
<p class="card-text text-muted">Total Waste</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1084,7 +1092,7 @@ else
|
||||
{
|
||||
<tr>
|
||||
<td>@materialResult.Material.DisplayName</td>
|
||||
<td>@ArchUnits.FormatFromInches(group.Key)</td>
|
||||
<td>@FormatResultLength(group.Key)</td>
|
||||
<td class="text-end">@group.Count()</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -1158,7 +1166,7 @@ else
|
||||
{
|
||||
<tr>
|
||||
<td>@binNum</td>
|
||||
<td style="white-space: nowrap;">@ArchUnits.FormatFromInches(entry.Bin.Length)</td>
|
||||
<td style="white-space: nowrap;">@FormatResultLength(entry.Bin.Length)</td>
|
||||
<td>
|
||||
@foreach (var item in entry.Bin.Items)
|
||||
{
|
||||
@@ -1166,12 +1174,13 @@ else
|
||||
@if (!string.IsNullOrWhiteSpace(item.Name))
|
||||
{
|
||||
<span class="cut-part-badge-name">@item.Name</span>
|
||||
<span class="cut-part-badge-divider" aria-hidden="true"></span>
|
||||
}
|
||||
<span class="cut-part-badge-length">@ArchUnits.FormatFromInches(item.Length)</span>
|
||||
<span class="cut-part-badge-length">@FormatResultLength(item.Length)</span>
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td style="white-space: nowrap;">@ArchUnits.FormatFromInches(entry.Bin.RemainingLength)</td>
|
||||
<td style="white-space: nowrap;">@FormatResultLength(entry.Bin.RemainingLength)</td>
|
||||
</tr>
|
||||
binNum++;
|
||||
}
|
||||
|
||||
@@ -275,6 +275,7 @@ a, .btn-link { color: var(--accent-dark); }
|
||||
/* Results cut badges keep the part number and its length visually distinct. */
|
||||
.cut-part-badge { background-color: #6c8ebf; display: inline-flex; font-weight: 500; overflow: hidden; padding: 0; }
|
||||
.cut-part-badge-name { padding: .35em .55em; }
|
||||
.cut-part-badge-divider { border-left: 1px solid rgba(255, 255, 255, .75); margin: .2em 0; }
|
||||
.cut-part-badge-length { background-color: #4c6680; padding: .35em .55em; }
|
||||
|
||||
/* Overview */
|
||||
|
||||
+14
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Mcp", "CutList.Mcp\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Web", "CutList.Web\CutList.Web.csproj", "{E3B33DE6-803C-4557-BF40-D8A5DB154144}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Core.Tests", "CutList.Core.Tests\CutList.Core.Tests.csproj", "{95E386F9-C906-423D-9869-BB1D2EA755E5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -69,6 +71,18 @@ Global
|
||||
{E3B33DE6-803C-4557-BF40-D8A5DB154144}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E3B33DE6-803C-4557-BF40-D8A5DB154144}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E3B33DE6-803C-4557-BF40-D8A5DB154144}.Release|x86.Build.0 = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{95E386F9-C906-423D-9869-BB1D2EA755E5}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user