Sweep all .cshtml files for encoding corruption; add pre-commit guard
Replace all corruption variants with HTML entities across 226 view files: - 3-char UTF-8-as-Win1252 sequences (ae-corruption) - Standalone smart/curly quotes that break C# Razor expressions - Partially re-corrupted variants where the 3rd byte was normalised to ASCII tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the script itself never contains a literal non-ASCII character; supports -DryRun .githooks/pre-commit: blocks commits containing the ae-corruption byte signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the hook is repo-committed and active for all future work on this machine. Build clean; 225 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -360,7 +360,7 @@
|
||||
|
||||
@if (!string.IsNullOrEmpty(card.Description))
|
||||
{
|
||||
<div class="card-desc">@(card.Description.Length > 60 ? card.Description[..60] + "…" : card.Description)</div>
|
||||
<div class="card-desc">@(card.Description.Length > 60 ? card.Description[..60] + "…" : card.Description)</div>
|
||||
}
|
||||
|
||||
<div class="card-footer-row">
|
||||
@@ -619,7 +619,7 @@
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Update card's priority border stays — status shown by column
|
||||
// Update card's priority border stays — status shown by column
|
||||
showToast(`Moved to ${data.newStatusDisplay}`, true);
|
||||
|
||||
if (data.guidedActivationNext && highlightJobId && jobId === highlightJobId) {
|
||||
@@ -641,7 +641,7 @@
|
||||
oldColEl.insertBefore(card, oldColEl.children[evt.oldIndex] ?? null);
|
||||
updateCount(oldColEl);
|
||||
updateCount(newColEl);
|
||||
showToast('Network error — move reverted', false);
|
||||
showToast('Network error — move reverted', false);
|
||||
})
|
||||
.finally(() => {
|
||||
card.style.opacity = '';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model PowderCoating.Application.DTOs.Job.CreateJobDto
|
||||
@model PowderCoating.Application.DTOs.Job.CreateJobDto
|
||||
@using PowderCoating.Core.Entities
|
||||
|
||||
@{
|
||||
@@ -19,7 +19,7 @@
|
||||
<i class="bi bi-layout-text-window-reverse fs-5"></i>
|
||||
<div>
|
||||
Pre-filled from template <strong>@ViewBag.TemplateName</strong>.
|
||||
Items and coatings have been loaded — review and adjust before saving.
|
||||
Items and coatings have been loaded — review and adjust before saving.
|
||||
</div>
|
||||
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@@ -50,7 +50,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Details"
|
||||
data-bs-content="Core job information. Priority and due date are visible on the shop floor board and affect how work is sorted. Customer PO is the customer's own reference number for their purchase order — include it so it appears on invoices. Special Instructions go directly to the shop floor worker.">
|
||||
data-bs-content="Core job information. Priority and due date are visible on the shop floor board and affect how work is sorted. Customer PO is the customer's own reference number for their purchase order — include it so it appears on invoices. Special Instructions go directly to the shop floor worker.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -70,7 +70,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Priority"
|
||||
data-bs-content="Controls sort order on the shop floor board and job list. Rush and Urgent jobs are highlighted in red/orange. Normal is the default. Raise priority only when the customer has an actual deadline constraint — overuse of Rush dilutes its meaning for the shop floor team.">
|
||||
data-bs-content="Controls sort order on the shop floor board and job list. Rush and Urgent jobs are highlighted in red/orange. Normal is the default. Raise priority only when the customer has an actual deadline constraint — overuse of Rush dilutes its meaning for the shop floor team.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -100,7 +100,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Due Date"
|
||||
data-bs-content="The customer's deadline — when the work must be ready for pickup or delivery. Overdue jobs (past due date and not yet completed) are highlighted in red on the job list.">
|
||||
data-bs-content="The customer's deadline — when the work must be ready for pickup or delivery. Overdue jobs (past due date and not yet completed) are highlighted in red on the job list.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -130,7 +130,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Special Instructions"
|
||||
data-bs-content="Free-text notes visible to the shop floor worker on the work order. Use this for masking requirements, handling notes, customer preferences, or anything that doesn't fit in the item-level notes — e.g., 'Keep brackets separated, customer allergic to zinc primer'.">
|
||||
data-bs-content="Free-text notes visible to the shop floor worker on the work order. Use this for masking requirements, handling notes, customer preferences, or anything that doesn't fit in the item-level notes — e.g., 'Keep brackets separated, customer allergic to zinc primer'.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -201,7 +201,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Items"
|
||||
data-bs-content="Each item represents a physical piece being coated. Use the wizard to pick from the catalog, enter custom dimensions, or upload a photo for AI analysis. Each item gets its own coating specification — color, powder, finish, and cure details. You can add multiple coating passes per item for multi-color or primer+topcoat work.">
|
||||
data-bs-content="Each item represents a physical piece being coated. Use the wizard to pick from the catalog, enter custom dimensions, or upload a photo for AI analysis. Each item gets its own coating specification — color, powder, finish, and cure details. You can add multiple coating passes per item for multi-color or primer+topcoat work.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model PowderCoating.Application.DTOs.Job.JobDto
|
||||
@model PowderCoating.Application.DTOs.Job.JobDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Job {Model.JobNumber}";
|
||||
@@ -2796,7 +2796,7 @@
|
||||
|
||||
document.getElementById('costingMargin').textContent = `${d.grossMargin}%`;
|
||||
document.getElementById('costingQuotedMargin').textContent =
|
||||
d.quotedPrice > 0 ? `${d.quotedMargin}% (quoted ${fmt(d.quotedPrice)})` : '—';
|
||||
d.quotedPrice > 0 ? `${d.quotedMargin}% (quoted ${fmt(d.quotedPrice)})` : '—';
|
||||
|
||||
// Powder detail lines
|
||||
const pBody = document.getElementById('powderLines');
|
||||
@@ -2900,9 +2900,9 @@
|
||||
}
|
||||
|
||||
function updateTotals(total) {
|
||||
const fmt = total > 0 ? total.toFixed(2) + ' hrs' : '—';
|
||||
const fmt = total > 0 ? total.toFixed(2) + ' hrs' : '—';
|
||||
document.getElementById('totalHoursDisplay').textContent = fmt;
|
||||
document.getElementById('timeEntriesTotalHours').textContent = total > 0 ? total.toFixed(2) : '—';
|
||||
document.getElementById('timeEntriesTotalHours').textContent = total > 0 ? total.toFixed(2) : '—';
|
||||
}
|
||||
|
||||
// -- Modal helpers -------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model PowderCoating.Application.DTOs.Job.UpdateJobDto
|
||||
@model PowderCoating.Application.DTOs.Job.UpdateJobDto
|
||||
@using PowderCoating.Core.Entities
|
||||
|
||||
@{
|
||||
@@ -26,7 +26,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Details"
|
||||
data-bs-content="Core job information. Priority and due date are visible on the shop floor board and job list. Customer PO is the customer's own reference number — it appears on invoices. Special Instructions go directly to the shop floor worker on the work order.">
|
||||
data-bs-content="Core job information. Priority and due date are visible on the shop floor board and job list. Customer PO is the customer's own reference number — it appears on invoices. Special Instructions go directly to the shop floor worker on the work order.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -45,7 +45,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Status"
|
||||
data-bs-content="Tracks where the job is in the workflow: Pending → Approved → Sandblasting → Cleaning → Coating → Curing → QualityCheck → Completed → ReadyForPickup → Delivered. Status changes trigger customer email notifications (if enabled). Use OnHold to pause work without losing progress.">
|
||||
data-bs-content="Tracks where the job is in the workflow: Pending â†' Approved â†' Sandblasting â†' Cleaning â†' Coating â†' Curing â†' QualityCheck â†' Completed â†' ReadyForPickup â†' Delivered. Status changes trigger customer email notifications (if enabled). Use OnHold to pause work without losing progress.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
@@ -57,7 +57,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Priority"
|
||||
data-bs-content="Controls sort order on the shop floor board and job list. Rush and Urgent jobs are highlighted in red/orange. Normal is the default. Raise priority only when the customer has an actual deadline constraint — overuse of Rush dilutes its meaning for the shop floor team.">
|
||||
data-bs-content="Controls sort order on the shop floor board and job list. Rush and Urgent jobs are highlighted in red/orange. Normal is the default. Raise priority only when the customer has an actual deadline constraint — overuse of Rush dilutes its meaning for the shop floor team.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
@@ -85,7 +85,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Due Date"
|
||||
data-bs-content="The customer's deadline — when the work must be ready for pickup or delivery. Overdue jobs (past due date and not yet completed) are highlighted in red on the job list.">
|
||||
data-bs-content="The customer's deadline — when the work must be ready for pickup or delivery. Overdue jobs (past due date and not yet completed) are highlighted in red on the job list.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
@@ -170,7 +170,7 @@
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Job Items"
|
||||
data-bs-content="Each item represents a physical piece being coated. Use the wizard to pick from the catalog, enter custom dimensions, or upload a photo for AI analysis. Each item gets its own coating specification — color, powder, finish, and cure details. You can add multiple coating passes per item for multi-color or primer+topcoat work.">
|
||||
data-bs-content="Each item represents a physical piece being coated. Use the wizard to pick from the catalog, enter custom dimensions, or upload a photo for AI analysis. Each item gets its own coating specification — color, powder, finish, and cure details. You can add multiple coating passes per item for multi-color or primer+topcoat work.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@model PowderCoating.Application.DTOs.Job.JobEditItemsViewModel
|
||||
@model PowderCoating.Application.DTOs.Job.JobEditItemsViewModel
|
||||
@using PowderCoating.Core.Entities
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Edit Items — {Model.JobNumber}";
|
||||
ViewData["Title"] = $"Edit Items — {Model.JobNumber}";
|
||||
ViewData["PageIcon"] = "bi-list-check";
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<span class="input-group-text border-end-0"><i class="bi bi-search text-muted"></i></span>
|
||||
<input type="text" name="searchTerm" class="form-control border-start-0"
|
||||
id="jobSearchInput"
|
||||
placeholder="Search jobs… /"
|
||||
placeholder="Search jobs… /"
|
||||
value="@ViewBag.SearchTerm"
|
||||
aria-label="Search jobs">
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@model (PowderCoating.Application.DTOs.Job.JobDto Job, PowderCoating.Application.DTOs.Job.IntakeJobDto Form)
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Part Intake — {Model.Job.JobNumber}";
|
||||
ViewData["Title"] = $"Part Intake — {Model.Job.JobNumber}";
|
||||
ViewData["PageIcon"] = "bi-box-seam";
|
||||
var job = Model.Job;
|
||||
var form = Model.Form;
|
||||
@@ -122,7 +122,7 @@
|
||||
{
|
||||
<div id="countMismatchAlert" class="alert alert-warning mt-2 py-2 d-none">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
Count doesn't match expected — note the discrepancy in condition notes.
|
||||
Count doesn't match expected — note the discrepancy in condition notes.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -246,7 +246,7 @@
|
||||
const formData = new FormData();
|
||||
formData.append('jobId', jobId);
|
||||
formData.append('photo', file);
|
||||
formData.append('caption', 'Intake — before');
|
||||
formData.append('caption', 'Intake — before');
|
||||
formData.append('photoType', '0'); // JobPhotoType.Before = 0
|
||||
|
||||
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* bg-info is a light cyan — use dark text so it's readable on TV */
|
||||
/* bg-info is a light cyan — use dark text so it's readable on TV */
|
||||
.badge-xl.bg-info { color: #000 !important; }
|
||||
|
||||
.worker-badge {
|
||||
@@ -457,7 +457,7 @@
|
||||
<span class="item-desc">@item.Description</span>
|
||||
@if (item.Colors.Any())
|
||||
{
|
||||
<span class="item-sep">—</span>
|
||||
<span class="item-sep">—</span>
|
||||
<i class="bi bi-circle-fill" style="color:#a371f7;font-size:0.55rem;flex-shrink:0"></i>
|
||||
<span class="item-color">@string.Join(" / ", item.Colors)</span>
|
||||
}
|
||||
@@ -626,7 +626,7 @@
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
// SignalR — real-time updates
|
||||
// SignalR — real-time updates
|
||||
(function () {
|
||||
const connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl('/hubs/shop')
|
||||
@@ -643,7 +643,7 @@
|
||||
connection.onreconnected(() => setStatus('Live', '#198754'));
|
||||
connection.onclose(() => setStatus('Disconnected', '#dc3545'));
|
||||
|
||||
// Daily Board changed order/priority/worker — reload job list
|
||||
// Daily Board changed order/priority/worker — reload job list
|
||||
connection.on('DailyBoardUpdated', function () {
|
||||
const url = new URL(window.location.href);
|
||||
fetch(url.toString(), { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
|
||||
@@ -651,14 +651,14 @@
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
// A job status was advanced — reload so progress strip updates
|
||||
// A job status was advanced — reload so progress strip updates
|
||||
connection.on('JobStatusChanged', function () {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
connection.start()
|
||||
.then(() => setStatus('Live', '#198754'))
|
||||
.catch(() => setStatus('Offline — refresh manually', '#dc3545'));
|
||||
.catch(() => setStatus('Offline — refresh manually', '#dc3545'));
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -445,7 +445,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="/Jobs/Intake/@job.JobId" class="meta-badge text-warning" title="Parts not yet checked in — tap to intake">
|
||||
<a href="/Jobs/Intake/@job.JobId" class="meta-badge text-warning" title="Parts not yet checked in — tap to intake">
|
||||
<i class="bi bi-box-seam"></i>
|
||||
</a>
|
||||
}
|
||||
@@ -608,7 +608,7 @@
|
||||
btn.disabled = false;
|
||||
}
|
||||
} catch (e) {
|
||||
showToast('Network error — try again', 'danger');
|
||||
showToast('Network error — try again', 'danger');
|
||||
btn.innerHTML = originalHtml;
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
Due: @job.DueDate.Value.ToString("MMM d, yyyy")
|
||||
@if (job.DueDate < DateTime.Today && !isTerminal)
|
||||
{
|
||||
<strong style="color: #dc2626;"> — OVERDUE</strong>
|
||||
<strong style="color: #dc2626;"> — OVERDUE</strong>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -237,14 +237,14 @@
|
||||
}
|
||||
else if (isOnHold)
|
||||
{
|
||||
@* On hold — offer resume (next logical status after resume by advancing) *@
|
||||
@* On hold — offer resume (next logical status after resume by advancing) *@
|
||||
@if (nextStatus != null)
|
||||
{
|
||||
<form method="post" asp-action="StatusBump" asp-route-id="@jobId">
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="newStatusId" value="@nextStatus.Id" />
|
||||
<button type="submit" class="btn-resume">
|
||||
▶ Resume — Move to @nextStatus.DisplayName
|
||||
▶ Resume — Move to @nextStatus.DisplayName
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -483,10 +483,10 @@
|
||||
<tr>
|
||||
<td style="text-align: center;">@coat.Sequence</td>
|
||||
<td><strong>@coat.CoatName</strong></td>
|
||||
<td>@(coat.ColorName ?? "—")</td>
|
||||
<td>@(coat.ColorCode ?? "—")</td>
|
||||
<td>@(coat.Finish ?? "—")</td>
|
||||
<td>@(coat.VendorName ?? "—")</td>
|
||||
<td>@(coat.ColorName ?? "—")</td>
|
||||
<td>@(coat.ColorCode ?? "—")</td>
|
||||
<td>@(coat.Finish ?? "—")</td>
|
||||
<td>@(coat.VendorName ?? "—")</td>
|
||||
<td>
|
||||
@if (coat.PowderToOrder.HasValue && coat.PowderToOrder.Value > 0)
|
||||
{
|
||||
@@ -501,7 +501,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">—</span>
|
||||
<span class="text-muted">—</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -622,7 +622,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Powder usage QRs — one per unique inventory item *@
|
||||
@* Powder usage QRs — one per unique inventory item *@
|
||||
@if (hasPowderQrs)
|
||||
{
|
||||
@foreach (var pqr in powderQrCodes!)
|
||||
|
||||
Reference in New Issue
Block a user