Require auth on all work order QR codes and add top view QR
- StatusBump (GET + POST) now requires authentication; routes by job ID instead of anonymous ShopAccessCode GUID; records actual user name in status history instead of anonymous token string - WorkOrder action generates a second "View Job" QR in the header linking to the authenticated Details page (for verifying specs and seeing catalog images on mobile); status bump QR updated to ID-based URL - WorkOrder view: top QR added to header alongside job number; status bump label updated (removed "no login required" copy) - StatusBump view: updated form routing from asp-route-token to asp-route-id - HelpKnowledgeBase and Jobs help article updated with two-tier QR docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -576,6 +576,49 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="work-order-qr-codes" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-qr-code text-primary me-2"></i>Work Order QR Codes
|
||||
</h2>
|
||||
<p>
|
||||
Every printed job work order includes two tiers of QR codes — one for <strong>viewing</strong>
|
||||
the job and a separate set for <strong>acting</strong> on it. This gives shop workers everything
|
||||
they need from a printed sheet without touching the desktop app.
|
||||
All QR codes require a logged-in account.
|
||||
</p>
|
||||
|
||||
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-eye me-1"></i>Top QR — View Job</h3>
|
||||
<p>
|
||||
Located in the work order header, next to the job number. Scan it with your phone to open the
|
||||
full <strong>Job Details</strong> page — items, catalog product images, powder specs, coatings,
|
||||
prep services, and special instructions. Use it to verify you're working the right job or to
|
||||
see catalog item images on your phone without hunting through the app.
|
||||
</p>
|
||||
|
||||
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-arrow-right-circle me-1"></i>Bottom QR — Update Status</h3>
|
||||
<p>
|
||||
Scan to open a mobile-friendly status bump page for this job. Tap the button to advance to the
|
||||
next stage (or put the job on hold). The status change is recorded in history with your name —
|
||||
no anonymous bumps.
|
||||
</p>
|
||||
|
||||
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-box-seam me-1"></i>Bottom QR — Log Powder Usage</h3>
|
||||
<p>
|
||||
One QR per unique powder on the job. Scanning opens the inventory usage log page pre-filled
|
||||
with that powder and the job number, so you can record actual lbs used in seconds without
|
||||
navigating through the app.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
|
||||
<i class="bi bi-lock flex-shrink-0 mt-1"></i>
|
||||
<div>
|
||||
<strong>Login required:</strong> All three QR codes require workers to be logged in to their
|
||||
account. Logging in once on their phone is enough for the session. Make sure every shop
|
||||
floor worker has an account set up before handing out printed work orders.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="blank-work-order" class="mb-5">
|
||||
<h2 class="h5 fw-semibold mb-3">Blank Work Order</h2>
|
||||
<p>
|
||||
@@ -643,6 +686,7 @@
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#part-intake">Part Intake</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#shop-mobile">Shop Mobile</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#changing-customer">Changing the Customer</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#work-order-qr-codes">Work Order QR Codes</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#blank-work-order">Blank Work Order</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Layout = null;
|
||||
var job = ViewBag.Job as PowderCoating.Core.Entities.Job;
|
||||
var allStatuses = ViewBag.AllStatuses as List<PowderCoating.Core.Entities.JobStatusLookup>;
|
||||
var token = (Guid)ViewBag.Token;
|
||||
var jobId = (int)ViewBag.JobId;
|
||||
|
||||
// Determine next/previous status options
|
||||
var currentOrder = job!.JobStatus.DisplayOrder;
|
||||
@@ -240,7 +240,7 @@
|
||||
@* On hold — offer resume (next logical status after resume by advancing) *@
|
||||
@if (nextStatus != null)
|
||||
{
|
||||
<form method="post" asp-action="StatusBump" asp-route-token="@token">
|
||||
<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">
|
||||
@@ -254,7 +254,7 @@
|
||||
@* Advance to next step *@
|
||||
@if (nextStatus != null)
|
||||
{
|
||||
<form method="post" asp-action="StatusBump" asp-route-token="@token">
|
||||
<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-advance">
|
||||
@@ -270,7 +270,7 @@
|
||||
@* On Hold option *@
|
||||
@if (onHoldStatus != null)
|
||||
{
|
||||
<form method="post" asp-action="StatusBump" asp-route-token="@token">
|
||||
<form method="post" asp-action="StatusBump" asp-route-id="@jobId">
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="newStatusId" value="@onHoldStatus.Id" />
|
||||
<button type="submit" class="btn-hold">
|
||||
|
||||
@@ -292,22 +292,33 @@
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="work-order-title">WORK ORDER</div>
|
||||
<div class="text-center" style="font-size: 8pt; line-height: 1.6;">
|
||||
<div class="mb-2">
|
||||
<span class="text-muted">Job #:</span>
|
||||
<span style="font-size: 14pt;" class="fw-bold ms-1">@Model.JobNumber</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center gap-3 mb-1">
|
||||
<div>
|
||||
<span class="text-muted">Priority:</span>
|
||||
<span class="badge bg-@Model.PriorityColorClass ms-1">@Model.PriorityDisplayName</span>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 8px;">
|
||||
<div style="font-size: 8pt; line-height: 1.6; flex: 1; text-align: center;">
|
||||
<div class="mb-2">
|
||||
<span class="text-muted">Job #:</span>
|
||||
<span style="font-size: 14pt;" class="fw-bold ms-1">@Model.JobNumber</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted">Status:</span>
|
||||
<span class="badge bg-@Model.StatusColorClass ms-1">@Model.StatusDisplayName</span>
|
||||
<div class="d-flex justify-content-center align-items-center gap-3 mb-1">
|
||||
<div>
|
||||
<span class="text-muted">Priority:</span>
|
||||
<span class="badge bg-@Model.PriorityColorClass ms-1">@Model.PriorityDisplayName</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted">Status:</span>
|
||||
<span class="badge bg-@Model.StatusColorClass ms-1">@Model.StatusDisplayName</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center text-muted" style="font-size: 7pt;">Created: @Model.CreatedAt.ToString("MM/dd/yyyy")</div>
|
||||
</div>
|
||||
<div class="text-center text-muted" style="font-size: 7pt;">Created: @Model.CreatedAt.ToString("MM/dd/yyyy")</div>
|
||||
@if (ViewBag.ViewQrCodeBase64 != null)
|
||||
{
|
||||
<div style="text-align: center; flex-shrink: 0;">
|
||||
<img src="data:image/png;base64,@ViewBag.ViewQrCodeBase64"
|
||||
alt="View Job"
|
||||
style="width: 64px; height: 64px; image-rendering: pixelated; display: block;" />
|
||||
<div style="font-size: 6.5pt; color: #6c757d; margin-top: 2px;">View Job</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -605,7 +616,7 @@
|
||||
<i class="bi bi-arrow-right-circle me-1"></i>Update Status
|
||||
</div>
|
||||
<div style="font-size: 7.5pt; color: #6c757d; line-height: 1.5;">
|
||||
Advance job to next<br />status — no login required.
|
||||
Advance job to<br />next status.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user