Auto — Work Order
An industrial service-bay work order screen for an auto repair shop. A vehicle and customer header carries the VIN, plate, and odometer beside a gradient truck photo, while editable labor and parts tables drive a live-updating summary with hours, shop fee, tax, and total due. Switch the job status across Waiting, In Progress, Done, and On Hold, add or remove line items, edit quantities and hours inline, jot technician notes, then save.
MCP
Code
:root {
--garage: #141518;
--garage-2: #1f2127;
--steel: #5b6470;
--steel-l: #8a929d;
--orange: #ff6a13;
--orange-d: #e2540a;
--orange-50: #fff0e6;
--ink: #16181c;
--ink-2: #3b4049;
--muted: #737a85;
--bg: #f3f4f6;
--surface: #ffffff;
--line: rgba(20, 21, 24, 0.1);
--line-2: rgba(20, 21, 24, 0.18);
--ok: #2f9e6f;
--warn: #e0962a;
--danger: #d4493e;
--waiting: #e0962a;
--inprogress: #2b7fff;
--done: #2f9e6f;
--hold: #d4493e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 18px;
--shadow: 0 1px 2px rgba(20, 21, 24, 0.06), 0 8px 24px rgba(20, 21, 24, 0.06);
--shadow-sm: 0 1px 2px rgba(20, 21, 24, 0.08);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
font-feature-settings: "cv05", "ss01";
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.mono { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.wo {
max-width: 1080px;
margin: 0 auto;
padding: 24px 18px 56px;
}
/* Top bar */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
background: var(--garage);
color: #fff;
border-radius: var(--r-lg);
padding: 16px 20px;
box-shadow: var(--shadow);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
display: grid;
place-items: center;
width: 40px;
height: 40px;
border-radius: var(--r-sm);
background: var(--orange);
color: #fff;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-text strong { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.brand-text span { font-size: 12px; color: var(--steel-l); }
.wo-id { text-align: right; display: flex; flex-direction: column; }
.wo-id-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--steel-l); }
.wo-id-num { font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums; }
/* Status strip */
.status-strip {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 14px 0 18px;
}
.status-pill {
display: inline-flex;
align-items: center;
gap: 8px;
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--ink-2);
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
padding: 8px 14px;
cursor: pointer;
transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.status-pill .dot {
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--steel-l);
transition: background 0.12s ease, box-shadow 0.12s ease;
}
.status-pill:hover { border-color: var(--line-2); transform: translateY(-1px); }
.status-pill:active { transform: translateY(0); }
.status-pill:focus-visible { outline: 2px solid var(--inprogress); outline-offset: 2px; }
.status-pill[data-status="waiting"] .dot { background: var(--waiting); }
.status-pill[data-status="inprogress"] .dot { background: var(--inprogress); }
.status-pill[data-status="done"] .dot { background: var(--done); }
.status-pill[data-status="hold"] .dot { background: var(--hold); }
.status-pill.is-active {
color: #fff;
border-color: transparent;
box-shadow: var(--shadow-sm);
}
.status-pill.is-active .dot { background: #fff; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3); }
.status-pill.is-active[data-status="waiting"] { background: var(--waiting); }
.status-pill.is-active[data-status="inprogress"] { background: var(--inprogress); }
.status-pill.is-active[data-status="done"] { background: var(--done); }
.status-pill.is-active[data-status="hold"] { background: var(--hold); }
.bay-tag {
margin-left: auto;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--steel);
background: var(--surface);
border: 1px dashed var(--line-2);
border-radius: var(--r-sm);
padding: 7px 12px;
}
.bay-tag strong { color: var(--ink); font-variant-numeric: tabular-nums; }
/* Layout */
.grid {
display: grid;
grid-template-columns: 1fr 320px;
gap: 16px;
align-items: start;
}
.col { display: flex; flex-direction: column; gap: 16px; }
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
box-shadow: var(--shadow);
overflow: hidden;
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid var(--line);
}
.card-head h2 { margin: 0; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--steel); }
.btn-add {
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--orange-d);
background: var(--orange-50);
border: 1px solid rgba(255, 106, 19, 0.25);
border-radius: var(--r-sm);
padding: 6px 11px;
cursor: pointer;
transition: background 0.12s ease, transform 0.12s ease;
}
.btn-add:hover { background: #ffe2ce; }
.btn-add:active { transform: scale(0.97); }
.btn-add:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
/* Vehicle card */
.vehicle-photo {
position: relative;
height: 152px;
background:
radial-gradient(120% 80% at 80% 0%, rgba(255, 106, 19, 0.22), transparent 60%),
linear-gradient(135deg, var(--garage) 0%, var(--garage-2) 55%, #2c3038 100%);
}
.vehicle-photo::after {
content: "";
position: absolute;
inset: 0;
background:
repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.04) 0 2px, transparent 2px 22px);
}
.vehicle-photo-tag {
position: absolute;
bottom: 12px;
left: 14px;
z-index: 1;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
color: #fff;
background: rgba(20, 21, 24, 0.55);
border: 1px solid rgba(255, 255, 255, 0.16);
border-radius: 999px;
padding: 4px 10px;
backdrop-filter: blur(4px);
}
.vehicle-body { padding: 16px; }
.vehicle-title { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -0.02em; }
.vehicle-sub { margin: 3px 0 14px; font-size: 13px; color: var(--muted); }
.spec-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
margin: 0 0 14px;
background: var(--line);
border: 1px solid var(--line);
border-radius: var(--r-sm);
overflow: hidden;
}
.spec { background: var(--surface); padding: 9px 12px; }
.spec dt { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.spec dd { margin: 2px 0 0; font-size: 14px; font-weight: 600; }
.customer {
display: flex;
align-items: center;
gap: 11px;
padding: 11px;
border: 1px solid var(--line);
border-radius: var(--r-sm);
background: #fafbfc;
}
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--garage);
color: #fff;
display: grid;
place-items: center;
font-size: 13px;
font-weight: 700;
flex: none;
}
.customer-info { display: flex; flex-direction: column; line-height: 1.3; }
.customer-info strong { font-size: 14px; }
.customer-info span { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.badge {
margin-left: auto;
font-size: 11px;
font-weight: 600;
color: var(--ink-2);
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 4px 9px;
white-space: nowrap;
}
/* Lines */
.lines { display: flex; flex-direction: column; }
.line {
display: grid;
align-items: center;
gap: 10px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
}
.line:last-child { border-bottom: 0; }
.line.labor { grid-template-columns: 1fr 78px 92px 34px; }
.line.part { grid-template-columns: 1fr 56px 92px 92px 34px; }
.line-desc {
font: inherit;
font-size: 14px;
font-weight: 500;
color: var(--ink);
border: 1px solid transparent;
background: transparent;
border-radius: var(--r-sm);
padding: 7px 9px;
width: 100%;
min-width: 0;
transition: border-color 0.12s ease, background 0.12s ease;
}
.line-desc:hover { background: #f7f8fa; }
.line-desc:focus { outline: none; border-color: var(--inprogress); background: #fff; }
.line-meta { font-size: 11px; color: var(--muted); }
.num-field {
display: flex;
flex-direction: column;
gap: 2px;
}
.num-field label {
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
}
.num-field input {
font: inherit;
font-size: 13px;
font-weight: 600;
font-variant-numeric: tabular-nums;
text-align: right;
width: 100%;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 6px 8px;
background: var(--surface);
color: var(--ink);
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.num-field input:focus { outline: none; border-color: var(--inprogress); box-shadow: 0 0 0 3px rgba(43, 127, 255, 0.15); }
.line-total {
font-size: 14px;
font-weight: 700;
font-variant-numeric: tabular-nums;
text-align: right;
align-self: end;
padding-bottom: 6px;
}
.btn-del {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: var(--r-sm);
border: 1px solid transparent;
background: transparent;
color: var(--steel-l);
cursor: pointer;
align-self: end;
margin-bottom: 2px;
transition: background 0.12s ease, color 0.12s ease;
}
.btn-del:hover { background: rgba(212, 73, 62, 0.1); color: var(--danger); }
.btn-del:focus-visible { outline: 2px solid var(--danger); outline-offset: 1px; }
.btn-del svg { width: 15px; height: 15px; }
.empty { margin: 0; padding: 18px 16px; font-size: 13px; color: var(--muted); text-align: center; }
/* Diagnostics + notes */
.dtc-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 14px 16px 4px; }
.dtc {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
font-weight: 600;
color: var(--ink);
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 5px 9px;
letter-spacing: 0.02em;
}
.dtc.warn { color: var(--warn); border-color: rgba(224, 150, 42, 0.4); background: rgba(224, 150, 42, 0.08); }
.notes-label { display: block; padding: 12px 16px 0; font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); }
.notes {
display: block;
width: calc(100% - 32px);
margin: 6px 16px 16px;
font: inherit;
font-size: 13.5px;
color: var(--ink);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 10px 12px;
resize: vertical;
background: #fafbfc;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.notes:focus { outline: none; border-color: var(--inprogress); box-shadow: 0 0 0 3px rgba(43, 127, 255, 0.13); background: #fff; }
/* Summary */
.col-side { position: sticky; top: 18px; }
.summary .card-head { border-bottom: 1px solid var(--line); }
.totals { margin: 0; padding: 8px 16px 4px; }
.t-row { display: flex; align-items: baseline; justify-content: space-between; padding: 7px 0; }
.t-row dt { margin: 0; font-size: 13px; color: var(--ink-2); }
.t-row dd { margin: 0; font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }
.t-row.grand {
margin-top: 4px;
padding-top: 12px;
border-top: 1px solid var(--line-2);
}
.t-row.grand dt { font-size: 14px; font-weight: 700; color: var(--ink); }
.t-row.grand dd { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; }
.rate-note {
margin: 4px 16px 0;
padding: 10px 12px;
font-size: 12px;
color: var(--muted);
background: var(--bg);
border-radius: var(--r-sm);
}
.rate-note strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.actions { display: flex; gap: 8px; padding: 14px 16px 6px; }
.btn-primary {
flex: 1;
font: inherit;
font-size: 14px;
font-weight: 700;
color: #fff;
background: var(--orange);
border: 0;
border-radius: var(--r-sm);
padding: 11px 14px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(255, 106, 19, 0.3);
transition: background 0.12s ease, transform 0.12s ease, box-shadow 0.12s ease;
}
.btn-primary:hover { background: var(--orange-d); }
.btn-primary:active { transform: translateY(1px); box-shadow: 0 1px 4px rgba(255, 106, 19, 0.3); }
.btn-primary:focus-visible { outline: 2px solid var(--garage); outline-offset: 2px; }
.btn-ghost {
font: inherit;
font-size: 14px;
font-weight: 600;
color: var(--ink-2);
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 11px 16px;
cursor: pointer;
transition: background 0.12s ease, border-color 0.12s ease;
}
.btn-ghost:hover { background: var(--bg); border-color: var(--steel-l); }
.btn-ghost:focus-visible { outline: 2px solid var(--inprogress); outline-offset: 2px; }
.saved-line { margin: 2px 16px 12px; min-height: 16px; font-size: 12px; font-weight: 600; color: var(--done); }
/* Toast */
.toast-wrap {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
display: flex;
flex-direction: column;
gap: 8px;
z-index: 50;
pointer-events: none;
}
.toast {
display: flex;
align-items: center;
gap: 9px;
background: var(--garage);
color: #fff;
font-size: 13.5px;
font-weight: 500;
padding: 11px 16px;
border-radius: var(--r-sm);
box-shadow: 0 10px 30px rgba(20, 21, 24, 0.32);
transform: translateY(12px);
opacity: 0;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast .t-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--orange); flex: none; }
/* Responsive */
@media (max-width: 820px) {
.grid { grid-template-columns: 1fr; }
.col-side { position: static; }
}
@media (max-width: 520px) {
.wo { padding: 16px 12px 48px; }
.topbar { padding: 13px 15px; }
.brand-text strong { font-size: 14px; }
.wo-id-num { font-size: 16px; }
.bay-tag { margin-left: 0; }
.spec-grid { grid-template-columns: 1fr; }
.line.labor { grid-template-columns: 1fr 72px 34px; }
.line.part { grid-template-columns: 1fr 56px 80px 34px; }
.line.labor .line-total,
.line.part .line-total { grid-column: 1 / -1; text-align: left; padding-bottom: 0; font-size: 13px; }
.vehicle-photo { height: 124px; }
}(function () {
"use strict";
var LABOR_RATE = 145.0;
var SHOP_FEE = 24.5;
var TAX_RATE = 0.0825;
var labor = [
{ id: lid(), desc: "Diagnose cylinder 1 misfire", hours: 1.5 },
{ id: lid(), desc: "DPF differential pressure test & regen", hours: 1.0 },
{ id: lid(), desc: "Replace glow plug harness", hours: 0.8 }
];
var parts = [
{ id: lid(), desc: "Glow plug harness — OEM 68211010AA", qty: 1, price: 184.6 },
{ id: lid(), desc: "Engine oil 15W-40 (gal)", qty: 3, price: 21.4 },
{ id: lid(), desc: "Fuel filter kit", qty: 1, price: 96.8 }
];
var status = "inprogress";
function lid() {
return "x" + Math.random().toString(36).slice(2, 9);
}
function money(n) {
return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
var laborWrap = document.getElementById("labor-lines");
var partWrap = document.getElementById("part-lines");
var laborEmpty = document.getElementById("labor-empty");
var partEmpty = document.getElementById("part-empty");
var delIcon =
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>';
function renderLabor() {
laborWrap.innerHTML = "";
labor.forEach(function (l) {
var row = document.createElement("div");
row.className = "line labor";
row.setAttribute("role", "listitem");
row.dataset.id = l.id;
row.innerHTML =
'<input class="line-desc" type="text" value="" aria-label="Labor description" />' +
'<div class="num-field"><label>Hours</label><input class="f-hours" type="number" min="0" step="0.1" inputmode="decimal" value="' +
l.hours +
'" aria-label="Labor hours" /></div>' +
'<div class="line-total">' +
money(l.hours * LABOR_RATE) +
"</div>" +
'<button class="btn-del" type="button" aria-label="Remove labor line">' +
delIcon +
"</button>";
row.querySelector(".line-desc").value = l.desc;
laborWrap.appendChild(row);
});
laborEmpty.hidden = labor.length > 0;
}
function renderParts() {
partWrap.innerHTML = "";
parts.forEach(function (p) {
var row = document.createElement("div");
row.className = "line part";
row.setAttribute("role", "listitem");
row.dataset.id = p.id;
row.innerHTML =
'<input class="line-desc" type="text" value="" aria-label="Part description" />' +
'<div class="num-field"><label>Qty</label><input class="f-qty" type="number" min="0" step="1" inputmode="numeric" value="' +
p.qty +
'" aria-label="Quantity" /></div>' +
'<div class="num-field"><label>Unit $</label><input class="f-price" type="number" min="0" step="0.01" inputmode="decimal" value="' +
p.price +
'" aria-label="Unit price" /></div>' +
'<div class="line-total">' +
money(p.qty * p.price) +
"</div>" +
'<button class="btn-del" type="button" aria-label="Remove part line">' +
delIcon +
"</button>";
row.querySelector(".line-desc").value = p.desc;
partWrap.appendChild(row);
});
partEmpty.hidden = parts.length > 0;
}
function totals() {
var laborHours = labor.reduce(function (s, l) {
return s + (parseFloat(l.hours) || 0);
}, 0);
var laborSum = laborHours * LABOR_RATE;
var partsSum = parts.reduce(function (s, p) {
return s + (parseFloat(p.qty) || 0) * (parseFloat(p.price) || 0);
}, 0);
var fee = labor.length || parts.length ? SHOP_FEE : 0;
var taxable = laborSum + partsSum + fee;
var tax = taxable * TAX_RATE;
var grand = taxable + tax;
document.getElementById("labor-hours").textContent = laborHours.toLocaleString("en-US", {
maximumFractionDigits: 1
});
document.getElementById("t-labor").textContent = money(laborSum);
document.getElementById("t-parts").textContent = money(partsSum);
document.getElementById("t-fee").textContent = money(fee);
document.getElementById("t-tax").textContent = money(tax);
document.getElementById("t-grand").textContent = money(grand);
}
function refreshLineTotals() {
Array.prototype.forEach.call(laborWrap.children, function (row) {
var l = labor.find(function (x) {
return x.id === row.dataset.id;
});
if (l) row.querySelector(".line-total").textContent = money((parseFloat(l.hours) || 0) * LABOR_RATE);
});
Array.prototype.forEach.call(partWrap.children, function (row) {
var p = parts.find(function (x) {
return x.id === row.dataset.id;
});
if (p)
row.querySelector(".line-total").textContent = money(
(parseFloat(p.qty) || 0) * (parseFloat(p.price) || 0)
);
});
totals();
}
/* Labor events */
laborWrap.addEventListener("input", function (e) {
var row = e.target.closest(".line");
if (!row) return;
var l = labor.find(function (x) {
return x.id === row.dataset.id;
});
if (!l) return;
if (e.target.classList.contains("line-desc")) l.desc = e.target.value;
if (e.target.classList.contains("f-hours")) l.hours = e.target.value;
refreshLineTotals();
});
laborWrap.addEventListener("click", function (e) {
var btn = e.target.closest(".btn-del");
if (!btn) return;
var row = btn.closest(".line");
var id = row.dataset.id;
labor = labor.filter(function (x) {
return x.id !== id;
});
renderLabor();
totals();
toast("Labor line removed");
});
/* Part events */
partWrap.addEventListener("input", function (e) {
var row = e.target.closest(".line");
if (!row) return;
var p = parts.find(function (x) {
return x.id === row.dataset.id;
});
if (!p) return;
if (e.target.classList.contains("line-desc")) p.desc = e.target.value;
if (e.target.classList.contains("f-qty")) p.qty = e.target.value;
if (e.target.classList.contains("f-price")) p.price = e.target.value;
refreshLineTotals();
});
partWrap.addEventListener("click", function (e) {
var btn = e.target.closest(".btn-del");
if (!btn) return;
var row = btn.closest(".line");
var id = row.dataset.id;
parts = parts.filter(function (x) {
return x.id !== id;
});
renderParts();
totals();
toast("Part removed");
});
/* Add buttons */
document.querySelectorAll("[data-add]").forEach(function (btn) {
btn.addEventListener("click", function () {
if (btn.dataset.add === "labor") {
labor.push({ id: lid(), desc: "New labor operation", hours: 0.5 });
renderLabor();
focusLast(laborWrap);
toast("Labor line added");
} else {
parts.push({ id: lid(), desc: "New part", qty: 1, price: 0 });
renderParts();
focusLast(partWrap);
toast("Part line added");
}
totals();
});
});
function focusLast(wrap) {
var last = wrap.lastElementChild;
if (last) {
var input = last.querySelector(".line-desc");
if (input) {
input.focus();
input.select();
}
}
}
/* Status */
var pills = document.querySelectorAll(".status-pill");
pills.forEach(function (pill) {
pill.addEventListener("click", function () {
pills.forEach(function (p) {
p.classList.remove("is-active");
p.setAttribute("aria-pressed", "false");
});
pill.classList.add("is-active");
pill.setAttribute("aria-pressed", "true");
status = pill.dataset.status;
var labels = { waiting: "Waiting", inprogress: "In Progress", done: "Done", hold: "On Hold" };
toast("Status → " + labels[status]);
});
});
/* Save / print */
document.getElementById("save-btn").addEventListener("click", function () {
var stamp = new Date().toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
document.getElementById("saved-line").textContent = "Saved at " + stamp + " · " + labor.length + " labor, " + parts.length + " parts";
toast("Work order #WO-48217 saved");
});
document.getElementById("print-btn").addEventListener("click", function () {
toast("Sending to shop printer…");
});
/* Toast */
var toastWrap = document.getElementById("toast-wrap");
function toast(msg) {
var el = document.createElement("div");
el.className = "toast";
el.innerHTML = '<span class="t-dot"></span><span></span>';
el.querySelector("span:last-child").textContent = msg;
toastWrap.appendChild(el);
requestAnimationFrame(function () {
el.classList.add("show");
});
setTimeout(function () {
el.classList.remove("show");
setTimeout(function () {
el.remove();
}, 220);
}, 2400);
}
/* Init */
renderLabor();
renderParts();
totals();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Auto — Work Order</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wo" aria-label="Service work order">
<!-- Top bar -->
<header class="topbar">
<div class="brand">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13l2-5a2 2 0 0 1 1.9-1.3h10.2A2 2 0 0 1 19 8l2 5"/><path d="M3 13h18v4a1 1 0 0 1-1 1h-1a2 2 0 1 1-4 0H9a2 2 0 1 1-4 0H4a1 1 0 0 1-1-1z"/><circle cx="7.5" cy="17" r="1.2"/><circle cx="16.5" cy="17" r="1.2"/></svg>
</span>
<div class="brand-text">
<strong>Ironwood Auto & Diesel</strong>
<span>Service Department</span>
</div>
</div>
<div class="wo-id">
<span class="wo-id-label">Work Order</span>
<span class="wo-id-num">#WO-48217</span>
</div>
</header>
<!-- Status strip -->
<section class="status-strip" aria-label="Work order status">
<button class="status-pill" data-status="waiting" type="button" aria-pressed="false">
<span class="dot"></span> Waiting
</button>
<button class="status-pill is-active" data-status="inprogress" type="button" aria-pressed="true">
<span class="dot"></span> In Progress
</button>
<button class="status-pill" data-status="done" type="button" aria-pressed="false">
<span class="dot"></span> Done
</button>
<button class="status-pill" data-status="hold" type="button" aria-pressed="false">
<span class="dot"></span> On Hold
</button>
<div class="bay-tag">Bay <strong>04</strong></div>
</section>
<div class="grid">
<!-- Left column -->
<div class="col">
<!-- Vehicle + customer header -->
<section class="card vehicle">
<div class="vehicle-photo" aria-hidden="true">
<span class="vehicle-photo-tag">2019 · 4WD</span>
</div>
<div class="vehicle-body">
<h1 class="vehicle-title">2019 Ram 2500 Laramie</h1>
<p class="vehicle-sub">6.7L Cummins Turbo Diesel · Bright White</p>
<dl class="spec-grid">
<div class="spec"><dt>VIN</dt><dd class="mono">3C6UR5FL4KG587120</dd></div>
<div class="spec"><dt>Plate</dt><dd class="mono">TX · DRV-8841</dd></div>
<div class="spec"><dt>Odometer</dt><dd class="mono">118,402 mi</dd></div>
<div class="spec"><dt>Engine hrs</dt><dd class="mono">3,210 h</dd></div>
</dl>
<div class="customer">
<div class="avatar" aria-hidden="true">MR</div>
<div class="customer-info">
<strong>Marcus Reyes</strong>
<span>(512) 555-0173 · Fleet #07</span>
</div>
<span class="badge">Drop-off 7:40a</span>
</div>
</div>
</section>
<!-- Labor -->
<section class="card" aria-labelledby="labor-h">
<div class="card-head">
<h2 id="labor-h">Labor</h2>
<button class="btn-add" type="button" data-add="labor">+ Add labor</button>
</div>
<div class="lines" id="labor-lines" role="list"></div>
<p class="empty" id="labor-empty" hidden>No labor lines yet.</p>
</section>
<!-- Parts -->
<section class="card" aria-labelledby="parts-h">
<div class="card-head">
<h2 id="parts-h">Parts</h2>
<button class="btn-add" type="button" data-add="part">+ Add part</button>
</div>
<div class="lines" id="part-lines" role="list"></div>
<p class="empty" id="part-empty" hidden>No parts added.</p>
</section>
<!-- Diagnostics + notes -->
<section class="card">
<div class="card-head"><h2>Diagnostics & notes</h2></div>
<div class="dtc-row" aria-label="Diagnostic trouble codes">
<span class="dtc">P0301</span>
<span class="dtc">P2459</span>
<span class="dtc warn">P0401</span>
</div>
<label class="notes-label" for="notes">Technician notes</label>
<textarea id="notes" class="notes" rows="3" placeholder="Findings, recommendations, customer authorization…">Cyl 1 misfire under load. DPF regen incomplete — verified differential pressure. Recommend EGR cleaning at next service.</textarea>
</section>
</div>
<!-- Right column / summary -->
<aside class="col col-side">
<section class="card summary" aria-labelledby="sum-h">
<div class="card-head"><h2 id="sum-h">Summary</h2></div>
<dl class="totals">
<div class="t-row"><dt>Labor (<span id="labor-hours">0</span> h)</dt><dd id="t-labor">$0.00</dd></div>
<div class="t-row"><dt>Parts</dt><dd id="t-parts">$0.00</dd></div>
<div class="t-row"><dt>Shop fee</dt><dd id="t-fee">$0.00</dd></div>
<div class="t-row"><dt>Tax (8.25%)</dt><dd id="t-tax">$0.00</dd></div>
<div class="t-row grand"><dt>Total due</dt><dd id="t-grand">$0.00</dd></div>
</dl>
<div class="rate-note">Labor rate <strong>$145.00</strong>/hr · Est. ready <strong>Today 4:30p</strong></div>
<div class="actions">
<button class="btn-primary" type="button" id="save-btn">Save work order</button>
<button class="btn-ghost" type="button" id="print-btn">Print</button>
</div>
<p class="saved-line" id="saved-line" aria-live="polite"></p>
</section>
</aside>
</div>
</main>
<div class="toast-wrap" id="toast-wrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Work Order
A full service-desk work order for an auto repair and diesel shop, built in the industrial, status-forward style of the bay floor. The header pairs a gradient vehicle photo with the customer record and the technical identifiers a tech actually reads off the dash — VIN, plate, odometer, and engine hours — all set in tabular figures. A status strip up top lets you move the job between Waiting, In Progress, Done, and On Hold, with the active panel glowing in its own status color, and a bay tag pins the truck to a stall.
The labor and parts tables are fully editable. Type into any description, nudge the hours or quantity steppers, or change a unit price and every figure recalculates instantly — per-line totals, labor hours, the shop fee, 8.25% tax, and the grand total due. Add new labor operations or parts with one tap (the new row auto-focuses for fast entry) and remove lines with the trash control. Diagnostic trouble codes, technician notes, a print action, and a save button that stamps the time round out a screen that feels lifted straight from a working service department.
Everything is vanilla HTML, CSS, and JavaScript — no frameworks, no build step. A small toast()
helper surfaces confirmations, the layout collapses to a single mobile-first column under ~520px,
and controls stay keyboard-usable with visible focus rings.
Illustrative UI only — fictional shop/dealership, not a real service system.