[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}\"";
|
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 MultiMaterialPackingSummary? summary;
|
||||||
private bool optimizing;
|
private bool optimizing;
|
||||||
private bool lockingJob;
|
private bool lockingJob;
|
||||||
|
private bool showLengthsInInches = true;
|
||||||
|
|
||||||
private IEnumerable<MaterialShape> DistinctShapes => materials.Select(m => m.Shape).Distinct().OrderBy(s => s);
|
private IEnumerable<MaterialShape> DistinctShapes => materials.Select(m => m.Shape).Distinct().OrderBy(s => s);
|
||||||
private IEnumerable<Material> FilteredMaterials => !selectedShape.HasValue
|
private IEnumerable<Material> FilteredMaterials => !selectedShape.HasValue
|
||||||
@@ -511,6 +512,9 @@ else
|
|||||||
|
|
||||||
private bool IsNew => !Id.HasValue;
|
private bool IsNew => !Id.HasValue;
|
||||||
private bool CanOptimize => job.Parts.Count > 0 && job.CuttingToolId != null;
|
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()
|
private async Task UnlockJob()
|
||||||
{
|
{
|
||||||
@@ -987,6 +991,10 @@ else
|
|||||||
<button class="btn btn-outline-secondary ms-2" @onclick="PrintReport">
|
<button class="btn btn-outline-secondary ms-2" @onclick="PrintReport">
|
||||||
<i class="bi bi-printer me-1"></i> Print Report
|
<i class="bi bi-printer me-1"></i> Print Report
|
||||||
</button>
|
</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)
|
@if (!job.IsLocked)
|
||||||
{
|
{
|
||||||
<button class="btn btn-warning ms-2" @onclick="LockJob" disabled="@lockingJob">
|
<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="col-md-3 col-6 mb-3">
|
||||||
<div class="card text-center">
|
<div class="card text-center">
|
||||||
<div class="card-body">
|
<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>
|
<p class="card-text text-muted">Total Waste</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1084,7 +1092,7 @@ else
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@materialResult.Material.DisplayName</td>
|
<td>@materialResult.Material.DisplayName</td>
|
||||||
<td>@ArchUnits.FormatFromInches(group.Key)</td>
|
<td>@FormatResultLength(group.Key)</td>
|
||||||
<td class="text-end">@group.Count()</td>
|
<td class="text-end">@group.Count()</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@@ -1158,7 +1166,7 @@ else
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@binNum</td>
|
<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>
|
<td>
|
||||||
@foreach (var item in entry.Bin.Items)
|
@foreach (var item in entry.Bin.Items)
|
||||||
{
|
{
|
||||||
@@ -1166,12 +1174,13 @@ else
|
|||||||
@if (!string.IsNullOrWhiteSpace(item.Name))
|
@if (!string.IsNullOrWhiteSpace(item.Name))
|
||||||
{
|
{
|
||||||
<span class="cut-part-badge-name">@item.Name</span>
|
<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>
|
</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td style="white-space: nowrap;">@ArchUnits.FormatFromInches(entry.Bin.RemainingLength)</td>
|
<td style="white-space: nowrap;">@FormatResultLength(entry.Bin.RemainingLength)</td>
|
||||||
</tr>
|
</tr>
|
||||||
binNum++;
|
binNum++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ a, .btn-link { color: var(--accent-dark); }
|
|||||||
/* Results cut badges keep the part number and its length visually distinct. */
|
/* 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 { background-color: #6c8ebf; display: inline-flex; font-weight: 500; overflow: hidden; padding: 0; }
|
||||||
.cut-part-badge-name { padding: .35em .55em; }
|
.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; }
|
.cut-part-badge-length { background-color: #4c6680; padding: .35em .55em; }
|
||||||
|
|
||||||
/* Overview */
|
/* Overview */
|
||||||
|
|||||||
+14
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Mcp", "CutList.Mcp\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Web", "CutList.Web\CutList.Web.csproj", "{E3B33DE6-803C-4557-BF40-D8A5DB154144}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Web", "CutList.Web\CutList.Web.csproj", "{E3B33DE6-803C-4557-BF40-D8A5DB154144}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CutList.Core.Tests", "CutList.Core.Tests\CutList.Core.Tests.csproj", "{95E386F9-C906-423D-9869-BB1D2EA755E5}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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|x64.Build.0 = Release|Any CPU
|
||||||
{E3B33DE6-803C-4557-BF40-D8A5DB154144}.Release|x86.ActiveCfg = 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
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user