Phase 5: Supervisor and Stall screens, weld map, heat table, clock and CO verification
The shop at parity with the old build. Supervisor screen (department chips, CO headline, numbers, Change orders
on hold, station grid with clocks and CO tags, boards) and Stall screen (station picker, hero, My packets,
Submitted). Shop steps on the packet page: assign with station, welder picker and stamp; pull back; Start, Pause,
Resume; Submit with a PIN signature and the missing steps as links; review with Send to Q.C., Return to station
and Skip Q.C. route; a lead's read-only review. Weld map (pdf.js viewer with pan, pinch, zoom, full screen; marks
with leader lines; weld sheet; weld record with field CAS; carried marks). Heat map (heat table placed, dragged,
sized, removed; legacy amber tags; borrowed heat map read-only). Checklist tab with templates from settings.
VOID stamps on voided pages. Change orders: CO-n labels, supervisor verify with signature, approval copies the
heat table to the new sheet and the BOM merge keeps the shop's values. Server clock of record with VISUAL ticket
hours beside it. Presence per packet over SSE. Migration 0006 (bfx, bfy, carried_from, checklist_templates).
New endpoints /api/shop/board, /api/shop/stations, /api/shop/stall, /api/departments/{code}/people.
Tests: policy oracle for every shop action, role and status; clock arithmetic; concurrent inserts; CO apply;
frontend geometry, heat table, clock, checklist, weld sheet, screens; seven Playwright scenarios. Docs updated.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -404,3 +404,152 @@ td.late { color: var(--red); }
|
||||
.mtrimg { max-width: 100%; height: auto; border-radius: 10px; background: var(--white); align-self: center; }
|
||||
.bar.usage { position: relative; height: 12px; border-radius: 6px; }
|
||||
.bar.usage .mark { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--amber); }
|
||||
|
||||
/* Phase 5: shop screens. From .stalls, .stall, .sn, .cotag, .cohot, .stallhero, .cobanner, .cob-h, .cob-t, .cob-row, .cosec. */
|
||||
.stalls { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; align-items: start; }
|
||||
.stall { background: var(--panel2); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; min-height: 112px; }
|
||||
.stall .sn { font-family: var(--f-disp); font-weight: 700; font-size: 18px; letter-spacing: .08em; color: var(--near); display: flex; justify-content: space-between; gap: 8px; }
|
||||
.stall.idle { opacity: .55; min-height: 0; padding-block: 8px; }
|
||||
.stall.idle .sn { font-size: 15px; }
|
||||
.stall .stjob { display: flex; flex-direction: column; gap: 4px; }
|
||||
.stall .stjob + .stjob { border-top: 1px solid var(--line); padding-top: 6px; }
|
||||
.stall button.stopen { all: unset; cursor: pointer; color: var(--cyan); font-weight: 600; font-size: 14px; }
|
||||
.stall button.stopen:focus-visible, .cotag:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
|
||||
.stall.cohot { border: 2px solid var(--pink); box-shadow: 0 0 0 3px rgba(229, 88, 159, .18); }
|
||||
.cotag, .stall button.cotag { all: unset; cursor: pointer; display: block; margin: 6px 0; background: var(--pink); color: var(--white); font-weight: 700; font-size: 11.5px; letter-spacing: .04em; padding: 5px 8px; border-radius: 6px; line-height: 1.25; text-transform: uppercase; }
|
||||
.stempty { margin-bottom: 22px; }
|
||||
.stclock { display: flex; align-items: center; flex-wrap: wrap; gap: 0 4px; }
|
||||
.sdot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--muted); margin-right: 6px; }
|
||||
.sdot.run { background: var(--green); animation: sdotpulse 1.6s infinite; }
|
||||
@keyframes sdotpulse { 0% { box-shadow: 0 0 0 0 rgba(95, 201, 138, .55); } 70% { box-shadow: 0 0 0 8px rgba(95, 201, 138, 0); } 100% { box-shadow: 0 0 0 0 rgba(95, 201, 138, 0); } }
|
||||
.stallhero { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-block: 6px 18px; }
|
||||
.stallhero .big { font-family: var(--f-disp); font-weight: 700; font-size: 64px; line-height: .9; color: var(--white); letter-spacing: .04em; }
|
||||
.stallhero .stpick { min-width: 220px; }
|
||||
.stnone { margin-top: 30px; }
|
||||
.cobanner { border: 3px solid var(--pink); background: linear-gradient(180deg, rgba(229, 88, 159, .22), rgba(229, 88, 159, .08)); border-radius: 14px; padding: 14px 16px; margin: 4px 0 18px; display: flex; flex-direction: column; gap: 10px; box-shadow: 0 0 0 4px rgba(229, 88, 159, .12); animation: copulse 2.2s ease-in-out infinite; }
|
||||
@keyframes copulse { 0%, 100% { box-shadow: 0 0 0 4px rgba(229, 88, 159, .12), 0 0 0 0 rgba(229, 88, 159, 0); border-color: var(--pink); } 50% { box-shadow: 0 0 0 6px rgba(229, 88, 159, .30), 0 0 34px 6px rgba(229, 88, 159, .38); border-color: #ff8cc4; } }
|
||||
.cob-h { display: flex; gap: 12px; align-items: center; color: var(--near); }
|
||||
.cob-h svg { color: var(--pink); flex: none; animation: cobpulse 1.6s ease-in-out infinite; }
|
||||
@keyframes cobpulse { 50% { opacity: .35; } }
|
||||
.cob-t { font-family: var(--f-disp); font-size: 26px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--white); line-height: 1.1; }
|
||||
.cob-row { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; background: rgba(13, 22, 27, .7); border-radius: 10px; padding: 10px 12px; }
|
||||
.cob-txt { min-width: 0; }
|
||||
.cosec { border-left: 4px solid var(--pink); padding-left: 12px; }
|
||||
.supchips { justify-content: flex-end; }
|
||||
@media (max-width: 640px) {
|
||||
.stalls { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.stall.busy { grid-column: 1 / -1; }
|
||||
.cob-t { font-size: 20px; }
|
||||
.stallhero .big { font-size: 48px; }
|
||||
}
|
||||
|
||||
/* Phase 5: packet page. From .dv, .viewer, .stage, .mk, .ld, .arw, .ht, .htbl, .htprev, .vtools, .fsbar, body.fs-view,
|
||||
.pagesel, .side, .hlist, .ichip, .ck, .ck-item, .cklay, .clock, .cdot. The drawing stays on white.
|
||||
Appended after the shop screens block because that block was written first; no selector overlaps it. */
|
||||
.dv { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 14px; align-items: start; }
|
||||
.dv.solo { grid-template-columns: minmax(0, 1fr); }
|
||||
@media (max-width: 980px) { .dv { grid-template-columns: minmax(0, 1fr); } }
|
||||
.viewer { position: relative; background: var(--white); border-radius: 10px; overflow: hidden; height: min(68vh, 720px); touch-action: none; border: 1px solid var(--line2); cursor: grab; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
|
||||
.viewer.panning { cursor: grabbing; }
|
||||
.viewer.marking { cursor: crosshair; outline: 3px solid var(--pink); outline-offset: -3px; }
|
||||
.viewer.placing { cursor: crosshair; outline: 3px solid var(--amber); outline-offset: -3px; }
|
||||
.stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
|
||||
.stage-canvas { display: block; max-width: none; pointer-events: none; }
|
||||
.vload { position: absolute; inset: 0; display: grid; place-items: center; color: #44535a; padding: 16px; pointer-events: none; }
|
||||
.vload .callout { pointer-events: auto; }
|
||||
.lead { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
|
||||
.ld { stroke-width: 2.5; vector-effect: non-scaling-stroke; fill: none; }
|
||||
.ld.h { stroke: #b07800; }
|
||||
.ld.w { stroke: #c2185b; }
|
||||
.ld.carried { stroke-dasharray: 6 4; }
|
||||
.mk { position: absolute; z-index: 2; transform: translate(-50%, -50%) scale(var(--inv, 1)); transform-origin: center; width: 44px; height: 44px; border-radius: 50%; background: #fff; color: #c2185b; border: 3px solid #c2185b; font-family: var(--f-mono); font-size: 13px; font-weight: 700; display: grid; place-items: center; box-shadow: 0 2px 6px rgba(0, 0, 0, .45); cursor: pointer; padding: 0; }
|
||||
.mk.drag { cursor: move; touch-action: none; }
|
||||
.mk.carried { border-style: dashed; opacity: .85; }
|
||||
.arw { position: absolute; z-index: 1; width: 20px; height: 14px; transform: translate(-100%, -50%) rotate(var(--rot, 0deg)) scale(var(--inv, 1)); transform-origin: 100% 50%; pointer-events: none; }
|
||||
.arw::before { content: ""; position: absolute; inset: 0; clip-path: polygon(0 0, 100% 50%, 0 100%); }
|
||||
.arw.h::before { background: #b07800; }
|
||||
.arw.w::before { background: #c2185b; }
|
||||
.arw.drag { pointer-events: auto; cursor: move; touch-action: none; width: 30px; height: 22px; }
|
||||
.ht { position: absolute; z-index: 2; transform: translate(-50%, -50%) scale(var(--inv, 1)); display: inline-flex; align-items: center; gap: 7px; background: #fff8e6; border: 2px solid #b07800; color: #3d2a00; border-radius: 999px; padding: 3px 12px 3px 3px; font-family: var(--f-mono); font-weight: 700; font-size: 13px; white-space: nowrap; box-shadow: 0 2px 6px rgba(0, 0, 0, .35); cursor: pointer; line-height: 1.2; min-height: 34px; }
|
||||
.ht .hn { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: #b07800; color: #fff; font-size: 12px; flex: none; }
|
||||
.ht.none { border-style: dashed; color: #7a5a20; }
|
||||
.ht.drag { cursor: move; touch-action: none; }
|
||||
.ht.carried { opacity: .85; }
|
||||
.htbl { position: absolute; z-index: 3; font-size: calc(var(--sw, 3000px) * 0.0075 * var(--hs, 1)); background: #fff; border: .16em solid #b07800; color: #2b1d00; font-family: Arial, Helvetica, sans-serif; line-height: 1.15; box-shadow: 0 .1em .4em rgba(0, 0, 0, .35); user-select: none; -webkit-user-select: none; }
|
||||
.htbl.drag { cursor: move; touch-action: none; }
|
||||
.htbl.drag:hover, .htbl.moving { outline: .12em dashed var(--cyan); outline-offset: .25em; }
|
||||
.htbl .htcap { background: #b07800; color: #fff; font-weight: 700; font-size: .85em; text-align: center; padding: .2em .5em; letter-spacing: .04em; }
|
||||
.htbl .htcols { display: flex; align-items: flex-start; }
|
||||
.htbl table { border-collapse: collapse; margin: 0; width: auto; font-size: inherit; }
|
||||
.htbl th, .htbl td { border: .08em solid #b07800; padding: .2em .5em; white-space: nowrap; background: #fff; color: #2b1d00; font-size: 1em; }
|
||||
.htbl th { background: #f3e2b8; color: #3d2a00; font-size: .8em; font-weight: 700; text-align: center; letter-spacing: .03em; text-transform: none; }
|
||||
.htbl td.i { text-align: center; font-weight: 700; }
|
||||
.htbl td.h { font-family: "Courier New", monospace; font-weight: 700; }
|
||||
.htbl td.h.none { color: #b00020; }
|
||||
.htbl td.h.na { color: #6b5a3a; }
|
||||
.htbl.static { position: static; font-size: 13px; box-shadow: none; display: inline-block; max-width: 100%; overflow: auto; }
|
||||
.htbl.htghost { position: fixed; z-index: 70; pointer-events: none; font-size: 13px; opacity: .92; transform: translate(-10px, -10px); }
|
||||
.htprev { border: 1px dashed var(--line2); border-radius: 10px; padding: 10px; }
|
||||
.htprev.htgrab { cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
|
||||
.htprev.htgrab:hover { border-color: var(--cyan); }
|
||||
.vtools { position: absolute; right: 10px; top: 10px; display: flex; flex-direction: column; gap: 6px; z-index: 4; }
|
||||
.vtools button { width: 44px; height: 44px; border-radius: 9px; border: 1px solid #c9d3d8; background: rgba(255, 255, 255, .94); color: #1a252b; font-size: 20px; font-weight: 700; cursor: pointer; display: grid; place-items: center; padding: 0; }
|
||||
.vtools button.fit { font-size: 13px; }
|
||||
.vtools button svg { display: block; margin: auto; }
|
||||
.fsbar { display: none; }
|
||||
body.fs-view { overflow: hidden; }
|
||||
body.fs-view .viewer { position: fixed; inset: 0; width: 100vw; height: 100vh; height: 100dvh; z-index: 80; border-radius: 0; border: 0; }
|
||||
body.fs-view .fsbar { display: flex; position: absolute; left: 10px; top: 10px; max-width: calc(100% - 80px); z-index: 4; gap: 6px; flex-wrap: wrap; align-items: center; background: rgba(13, 22, 27, .9); padding: 6px 8px; border-radius: 10px; color: var(--near); cursor: default; }
|
||||
.fsbar .fst { font-size: 13px; margin-right: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40vw; }
|
||||
body.fs-view .scrim { z-index: 95; }
|
||||
body.fs-view .toast { z-index: 99; }
|
||||
.pagesel { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
||||
.side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
|
||||
.hlist { display: flex; flex-direction: column; gap: 7px; font-size: 13.5px; }
|
||||
.hlist .row { display: grid; grid-template-columns: 28px 1fr auto; gap: 8px; align-items: center; }
|
||||
.hlist .row .d { min-width: 0; color: var(--near); overflow-wrap: anywhere; }
|
||||
.hlist .rowbtn { all: unset; display: grid; grid-template-columns: 28px 1fr auto; gap: 8px; align-items: center; cursor: pointer; min-height: 44px; border-radius: 8px; padding: 0 4px; }
|
||||
.hlist .rowbtn:hover { background: var(--panel2); }
|
||||
.hlist .rowbtn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
|
||||
.balloon.pinkb { border-color: var(--pink); color: var(--pink); }
|
||||
.balloon.carried { border-style: dashed; }
|
||||
.flabel { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
|
||||
.ichip { width: 56px; height: 56px; border-radius: 50%; border: 2px solid var(--line2); background: var(--panel); color: var(--near); font-family: var(--f-mono); font-size: 20px; font-weight: 700; cursor: pointer; transition: background .12s, border-color .12s, transform .12s; }
|
||||
.ichip:hover { border-color: var(--cyan); }
|
||||
.ichip[aria-pressed="true"] { background: var(--cyan); border-color: var(--cyan); color: var(--on-cyan); box-shadow: 0 0 0 4px rgba(73, 192, 222, .28); transform: scale(1.06); }
|
||||
.ichip:disabled { cursor: default; }
|
||||
.ck { display: flex; flex-direction: column; gap: 8px; }
|
||||
.ck-item { display: flex; gap: 14px; align-items: center; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; cursor: pointer; min-height: 58px; }
|
||||
.ck-item input { width: 28px; height: 28px; accent-color: var(--green); flex: none; }
|
||||
.ck-item .t { color: var(--near); font-weight: 500; flex: 1; min-width: 0; }
|
||||
.ck-item .by { font-size: 12px; color: var(--muted); text-align: right; }
|
||||
.ck-item.done { border-color: #2d5a41; }
|
||||
.ck-item[aria-disabled="true"] { cursor: default; }
|
||||
.cklay { display: grid; grid-template-columns: minmax(0, 1fr) 360px; grid-template-areas: "main side"; gap: 18px; align-items: start; }
|
||||
.cklay .ckmain { grid-area: main; min-width: 0; }
|
||||
.cklay .ckside { grid-area: side; position: sticky; top: calc(var(--toph, 64px) + 64px); }
|
||||
@media (max-width: 980px) { .cklay { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "side"; } .cklay .ckside { position: static; } }
|
||||
.clockrow { display: flex; gap: 16px; align-items: baseline; flex-wrap: wrap; margin-right: auto; }
|
||||
.clock { font-family: var(--f-disp); font-weight: 700; font-size: 40px; color: var(--white); line-height: 1; font-variant-numeric: tabular-nums; }
|
||||
.clock small { font-size: 18px; color: var(--muted); }
|
||||
.cdot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; background: var(--muted); margin-right: 10px; vertical-align: middle; }
|
||||
.cdot.run { background: var(--green); animation: cdotpulse 1.6s infinite; }
|
||||
@keyframes cdotpulse { 0% { box-shadow: 0 0 0 0 rgba(95, 201, 138, .6); } 70% { box-shadow: 0 0 0 10px rgba(95, 201, 138, 0); } 100% { box-shadow: 0 0 0 0 rgba(95, 201, 138, 0); } }
|
||||
.labor { color: var(--body); }
|
||||
.labor b { color: var(--amber); font-family: var(--f-mono); }
|
||||
.acts.center { align-items: center; }
|
||||
.revrow { display: flex; gap: 10px 16px; flex-wrap: wrap; align-items: flex-end; }
|
||||
.revrow.sep { border-top: 1px solid var(--line); padding-top: 10px; }
|
||||
.revrow .f { flex: 0 1 220px; }
|
||||
.revrow .f.grow2 { flex: 1 1 260px; }
|
||||
.presence { display: flex; gap: 8px; align-items: center; color: var(--body); margin: 6px 0; }
|
||||
.pdot { width: 9px; height: 9px; border-radius: 50%; background: var(--cyan); flex: none; }
|
||||
.verifybox { border-color: var(--pink); }
|
||||
.pinktxt { color: var(--pink); }
|
||||
.amber { color: var(--amber); }
|
||||
.near { color: var(--near); }
|
||||
.mb6 { margin-bottom: 6px; }
|
||||
.mb8 { margin-bottom: 8px; }
|
||||
.mb10 { margin-bottom: 10px; }
|
||||
.mt8 { margin-top: 8px; }
|
||||
.mt10 { margin-top: 10px; }
|
||||
|
||||
Reference in New Issue
Block a user