Wave 0 counted pushState across html/ and found 0. Every page read its query
string once at boot and never wrote one again, so you could not send anyone a
link to WP07, a refresh dropped you back at the default view, and Back left the
app entirely because the app had never added a history entry.
CR-011 and CR-014 both promise an email carrying a direct link to a work package.
That is X1, and it was blocked on this. It is not blocked now.
html/wp-url.js is the whole mechanism, and it is deliberately NOT a router.
Nothing in it intercepts navigation or renders anything; it is the query string
treated as state that can be read, merged, written and subscribed to. Pages keep
their own rendering. Query parameters rather than a hash, because the server
already serves these paths and a hash is never sent to the server - which matters
the day a link has to be resolved before the page boots.
The merge behaviour is the part that earns its place: WPUrl.push({wp:id}) keeps
the active project, and WPUrl.push({wp:''}) clears one key without needing to know
what else is in the URL. Hand-built URLs losing ?project= is the usual way this
goes wrong.
WIRED: the creator (open package, dashboard view), the SOP wizard (tool, step),
the launcher (project). Each records a history entry only when the user chose the
change - restoring from the URL uses replace, or Back would immediately add an
entry and appear to do nothing.
WPUrl.absolute() is what CR-011/CR-014 will paste into an email in wave 8.
TWO BUGS THIS TASK CREATED AND FIXED, both found by the probe rather than by
reading:
- bootSOP() calls newPackage() during boot, and newPackage() cleared ?wp=. A
deep link therefore worked and then erased its own parameter, leaving Back
with nothing to return to. Now guarded on wpCreatorReady.
- goToStep() runs validateStep(), which ends in alert() when a required field
is empty - always true on a freshly loaded page. So restoring ?step=3 from a
shared link opened a modal dialog mid-boot, and hung the browser under CDP.
Restoring a view is not a forward navigation and no longer runs the
forward-navigation guard.
The second one is worth keeping in mind for the rest of wave 4: this app has 79
native dialogs, and any of them firing during a restore path will hang a headless
browser rather than fail visibly.
VERIFICATION. tests/url_state_check.py, 23 checks, all passing, covering every
done-when on the task:
- a URL identifying a work package opens that package
- the same URL for a SIGNED-OUT user goes to login, carries the target through
?next=, and lands on the work package itself after signing in
- refresh preserves project, package, tab and view
- Back and Forward move through states, verified as still-initialised rather
than reloaded, and with the dashboard actually rendered rather than only the
URL changed
- a different user opening the same URL reaches the same view
- nothing credential-shaped appears in the query string
Metric 8, pushState: was 0 at wave 0, now 2 in html/ (one pushState and one
replaceState, both in wp-url.js) behind 6 call sites across 4 files. The raw
count stays low by design - one place writes history, which is the same reason
the token work put one place in charge of colour.
browser_check 71/71, f_items 5 FIXED / F6 REPRODUCES, aggregates 16/16.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
412 lines
30 KiB
HTML
412 lines
30 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Work Package (IWP) — Prime Controls</title>
|
||
<script src="auth-guard.js"></script>
|
||
<!-- Date/number formatting. Must parse BEFORE the app scripts: they format
|
||
timestamps during their own boot. -->
|
||
<script src="wp-format.js"></script>
|
||
<!-- Addressable state (S3). Parses before the app scripts, which read the URL
|
||
during their own boot. -->
|
||
<script src="wp-url.js"></script>
|
||
<link rel="icon" href="favicon.ico" sizes="any">
|
||
<link rel="manifest" href="manifest.webmanifest">
|
||
<meta name="theme-color" content="#161616">
|
||
<link rel="stylesheet" href="theme-light.css">
|
||
<link rel="stylesheet" href="wp-creation-styles.css">
|
||
<link rel="stylesheet" href="wp-sidenav.css">
|
||
</head>
|
||
<body>
|
||
|
||
<div class="loading-overlay" id="loadingOverlay"><div class="spinner"></div><div class="loading-text">Saving work package…</div></div>
|
||
|
||
<div class="header">
|
||
<!-- The logo chip, not the mono wordmark this page used to carry: every other page
|
||
in the suite shows the chip, and the creator was the only one spelling the brand
|
||
out in monospace (T2.2). -->
|
||
<div class="logo-wrap embed-hide">
|
||
<a href="index.html" class="header-logo-chip" title="Work Package Suite home">
|
||
<span class="wp-logo-chip"><img src="prime-controls-logo.jpg" alt="Prime Controls"></span>
|
||
</a>
|
||
<button id="dev-toggle" class="dev-toggle" onclick="toggleDevMode()" title="dev mode" aria-label="dev mode"></button>
|
||
</div>
|
||
<div class="header-sep embed-hide">|</div>
|
||
<div class="header-title">Work Package (IWP)</div>
|
||
<button class="btn btn-ghost embed-hide" style="margin-left:auto;padding:7px 16px" onclick="document.getElementById('sop-import').click()">⤒ Import SOP</button>
|
||
<input type="file" id="sop-import" accept="application/json" style="display:none" onchange="importSOP(event)">
|
||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="loadSampleSOP()">⤓ Sample SOP</button>
|
||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="openSopModal()">👁 View SOP</button>
|
||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="loadExample()">★ Load example</button>
|
||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="showDashboard()">📊 Dashboard</button>
|
||
<button class="btn btn-ghost embed-first" style="padding:7px 16px" onclick="newPackage()">+ New</button>
|
||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="duplicateWP()">⧉ Duplicate</button>
|
||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="showHistoryCurrent()" title="Change history for this work package">🕘 History</button>
|
||
<button class="btn btn-ghost embed-hide" id="comments-btn" style="padding:7px 16px" onclick="toggleComments()">💬 Comments <span class="cbadge-total" id="cbadge-total" style="display:none">0</span></button>
|
||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="showAnalytics()">▤ Usage data</button>
|
||
</div>
|
||
|
||
<div class="dev-banner" id="dev-banner" style="display:none">⚙ DEV MODE — usage tracking paused. This session's actions are not being recorded.</div>
|
||
|
||
<div class="ctx-bar" id="ctx-bar"></div>
|
||
|
||
<!-- RELEASE READINESS BANNER -->
|
||
<div class="release-banner" id="release-banner"></div>
|
||
|
||
<!-- SECTION NAV (jump links, built from the form cards) -->
|
||
<div class="section-nav-bar" id="section-nav"></div>
|
||
|
||
<div class="wp-layout">
|
||
|
||
<!-- WORK PACKAGE NAVIGATOR
|
||
A persistent side panel (not a hover drawer): collapse toggle, a primary action,
|
||
icon nav, then the project's packages as rows with colour-coded initial badges.
|
||
Collapsing leaves a narrow icon rail so you can still see and switch packages. -->
|
||
<aside class="wp-nav" id="wp-nav" aria-label="Work packages">
|
||
<div class="wp-nav-top">
|
||
<button class="wp-nav-toggle" id="wp-nav-toggle" onclick="toggleWpNav()"
|
||
title="Collapse the panel" aria-label="Collapse the panel" aria-expanded="true">
|
||
<svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true">
|
||
<rect x="2.5" y="3.5" width="15" height="13" rx="1.5" fill="none" stroke="currentColor" stroke-width="1.4"/>
|
||
<line x1="7.5" y1="3.5" x2="7.5" y2="16.5" stroke="currentColor" stroke-width="1.4"/>
|
||
<path class="wp-nav-toggle-arrow" d="M14 10 H10 M11.6 8.2 L9.8 10 L11.6 11.8"
|
||
fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="wp-nav-primary">
|
||
<button class="wp-nav-cta" onclick="newPackage()" title="Start a new work package">
|
||
<span class="wp-nav-cta-plus" aria-hidden="true">+</span><span class="wp-nav-cta-label">New work package</span>
|
||
</button>
|
||
<button class="wp-nav-cta-more" id="wp-nav-more-btn" onclick="toggleWpNavMore(event)"
|
||
title="More actions" aria-label="More actions" aria-haspopup="true" aria-expanded="false">▾</button>
|
||
<div class="wp-nav-menu" id="wp-nav-more" hidden>
|
||
<button type="button" onclick="wpNavAction('duplicate')">Duplicate this package</button>
|
||
<button type="button" onclick="wpNavAction('split')">Split by discipline</button>
|
||
<button type="button" onclick="wpNavAction('export')">Export all (JSON)</button>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="wp-nav-links" aria-label="Views">
|
||
<button type="button" class="wp-nav-link" data-view="mine" onclick="setWpNavView('mine')" title="Packages you own">
|
||
<span class="wp-nav-ico" aria-hidden="true">◔</span><span class="wp-nav-link-label">My packages</span>
|
||
<span class="wp-nav-link-n" id="wp-nav-n-mine"></span>
|
||
</button>
|
||
<button type="button" class="wp-nav-link is-current" data-view="all" onclick="setWpNavView('all')" title="Every package on this project">
|
||
<span class="wp-nav-ico" aria-hidden="true">▤</span><span class="wp-nav-link-label">All packages</span>
|
||
<span class="wp-nav-link-n" id="wp-nav-n-all"></span>
|
||
</button>
|
||
<button type="button" class="wp-nav-link" data-view="open" onclick="setWpNavView('open')" title="Not release-ready yet">
|
||
<span class="wp-nav-ico" aria-hidden="true">⚠</span><span class="wp-nav-link-label">Needs attention</span>
|
||
<span class="wp-nav-link-n" id="wp-nav-n-open"></span>
|
||
</button>
|
||
<button type="button" class="wp-nav-link" onclick="showDashboard()" title="Status and gating across the project">
|
||
<span class="wp-nav-ico" aria-hidden="true">▦</span><span class="wp-nav-link-label">Dashboard</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="wp-nav-sect">
|
||
<span class="wp-nav-sect-label" id="wp-nav-sect-label">Work packages</span>
|
||
<span class="wp-nav-count" id="wp-nav-count"></span>
|
||
</div>
|
||
<div class="wp-nav-filter">
|
||
<input type="search" class="wp-nav-search" id="wp-nav-search" placeholder="Filter packages…" oninput="renderWpNav()">
|
||
</div>
|
||
<div class="wp-nav-list" id="wp-nav-list"></div>
|
||
</aside>
|
||
|
||
<div class="main">
|
||
|
||
<!-- PACKAGE KIND (only shown when the project's SOP includes BIM/VDC) -->
|
||
<div class="card" id="kind-row" style="display:none">
|
||
<div class="sub-heading">Package Type</div>
|
||
<div class="notice">This project includes BIM/VDC packages. Choose what this one is — it tailors the fields below and the WP types / release gates offered.</div>
|
||
<div class="radio-group" id="kind-group" style="margin-bottom:0">
|
||
<label class="radio-pill" data-val="iwp"><input type="radio" name="pkgkind" onclick="setKind('iwp')"><span class="dot"></span>Install package (IWP)</label>
|
||
<label class="radio-pill" data-val="ewp"><input type="radio" name="pkgkind" onclick="setKind('ewp')"><span class="dot"></span>BIM package (EWP)</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- GENERAL INFORMATION -->
|
||
<div class="card">
|
||
<div class="section-header"><div class="section-title">General Information</div>
|
||
<div class="section-desc">Parameters in <span style="color:var(--accent)">blue</span> are inherited from the project SOP. Fill the rest for this package.</div></div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>WP number <span class="auto-tag">auto</span><span class="help-tip" data-tip="Built automatically from the SOP number format — the scope fields below (e.g. Sector) plus the WP type and a sequence counter.">i</span></label><input type="text" id="wp_number" readonly class="locked-field" placeholder="auto-built"><div class="field-hint sop-hint" id="wp_number_hint"></div></div>
|
||
<div class="field"><label>Status</label>
|
||
<div class="radio-group" id="status-group" style="margin-bottom:0">
|
||
<label class="radio-pill" data-val="Draft"><input type="radio" name="status"><span class="dot"></span>Draft</label>
|
||
<label class="radio-pill" data-val="Scheduled"><input type="radio" name="status"><span class="dot"></span>Scheduled</label>
|
||
<label class="radio-pill" data-val="Issued"><input type="radio" name="status"><span class="dot"></span>Issued</label>
|
||
<label class="radio-pill" data-val="In Progress"><input type="radio" name="status"><span class="dot"></span>In progress</label>
|
||
<label class="radio-pill pill-hold" data-val="Issue"><input type="radio" name="status"><span class="dot"></span>Issue (hold)</label>
|
||
<label class="radio-pill" data-val="QC"><input type="radio" name="status"><span class="dot"></span>QC</label>
|
||
<label class="radio-pill" data-val="Closed"><input type="radio" name="status"><span class="dot"></span>Closed</label>
|
||
</div>
|
||
<div class="field-hint">Cannot move to <strong>Issued</strong> or beyond until all constraints are cleared.</div>
|
||
</div>
|
||
</div>
|
||
<div class="notice">WP number builds automatically from these scope fields + the WP type (per the SOP naming format):</div>
|
||
<div class="field-grid" id="number-dims"></div>
|
||
<div class="field field-grid col1"><div class="field"><label>Subject / title <span class="req">*</span></label><input type="text" id="wp_subject" placeholder="e.g. Utility Level 2P Inert Gas Room Wall Mount Midas/ Rack"></div></div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>WP type <span class="req">*</span></label><select id="wp_type" onchange="onTypeChange()"></select><div class="field-hint sop-hint">from SOP types</div></div>
|
||
<div class="field"><label>System / facility code / UPN</label><input type="text" id="wp_system" placeholder="ties to controls.dev / COIN"></div>
|
||
<div class="field"><label>Location</label><input type="text" id="wp_location" placeholder="building / level / sector / room"></div>
|
||
<div class="field"><label>Cost code</label><select id="wp_cost"></select><div class="field-hint sop-hint">Acumatica cost codes</div></div>
|
||
<div class="field"><label>Acumatica task</label><input type="text" id="wp_wbs" placeholder="Acumatica task no."></div>
|
||
</div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>Owner <span class="help-tip" data-tip="The accountable owner (a user account on this project). Assigning notifies them by email if email notifications are enabled in the admin console.">i</span></label><select id="wp_assignee"><option value="">— Unassigned —</option></select></div>
|
||
<div class="field"><label>Assignees<span class="help-tip" data-tip="The crew and staff working this package. Pick from the project team named on the SOP; anyone without a user account can still be added by name.">i</span></label>
|
||
<div class="people-pick" id="pick_assignees"></div>
|
||
<input type="hidden" id="wp_assignees"></div>
|
||
<div class="field"><label>Distribution<span class="help-tip" data-tip="Who gets notified about this package. The project's Construction Manager is included by default and can be removed per package.">i</span></label>
|
||
<div class="people-pick" id="pick_distribution"></div>
|
||
<input type="hidden" id="wp_distribution"></div>
|
||
<div class="field"><label>Due date</label><input type="date" id="wp_due"></div>
|
||
<div class="field"><label>Specification section</label>
|
||
<input type="text" id="wp_spec" readonly class="locked-field" placeholder="set on the WP type in the SOP">
|
||
<div class="field-hint" id="spec-folder-link"></div></div>
|
||
</div>
|
||
<div class="field field-grid col1"><div class="field"><label>Description</label><textarea id="wp_desc" rows="2" placeholder="Short summary of the package"></textarea></div></div>
|
||
<div class="field field-grid col1" id="bimlink-wrap"><div class="field"><label>Enabled by — BIM package(s)<span class="help-tip" data-tip="Advanced Work Packaging traceability: link the BIM / model package(s) that enabled this install package. Paste the MWP number(s) or a link to the model package.">i</span></label><input type="text" id="wp_bimlink" placeholder="e.g. MWP07-FAB-CONDUITS, or a link to the model package"></div></div>
|
||
</div>
|
||
|
||
<!-- BIM / MODEL DETAILS (shown for BIM/VDC SOPs) -->
|
||
<div class="card" id="bim-card" style="display:none">
|
||
<div class="sub-heading">BIM / Model Details</div>
|
||
<div class="notice">For BIM/VDC work packages — the model deliverable's level of detail, area, source scan, and coordination status.</div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>Model area / zone</label><input type="text" id="wp_model_area" placeholder="e.g. Fab 09 Subfab — Level 2"></div>
|
||
<div class="field"><label>Clash / coordination status</label>
|
||
<select id="wp_clash" onchange="onClashChange()"><option value="">—</option><option>Not started</option><option>In coordination</option><option>Clashes open</option><option>Clash-free</option><option>Signed off (IFF)</option></select></div>
|
||
<div class="field"><label>IFF #<span class="help-tip" data-tip="Issued-For-Fabrication/Field number — the GC sign-off reference for this model package. Required once the coordination status is Signed off (IFF).">i</span></label>
|
||
<input type="text" id="wp_iff" placeholder="e.g. IFF-2026-0142" oninput="onClashChange()">
|
||
<div class="field-hint" id="iff-hint"></div></div>
|
||
<div class="field"><label>Linked scan / point cloud</label><input type="url" id="wp_scan_link" placeholder="WebShare / BIM360 / SharePoint link"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ASSETS (controls.dev) -->
|
||
<div class="card" id="asset-card">
|
||
<div class="sub-heading">Assets</div>
|
||
<div class="notice">Every work package is based on one or more assets managed in <strong>controls.dev</strong>. Paste the controls.dev link for each asset this package covers. <span style="color:var(--text-dim)">A direct integration to pick assets from a list is planned — for now, link them manually.</span></div>
|
||
<div class="table-wrap"><table><thead><tr><th style="width:200px">Asset Tag / ID</th><th>Description</th><th>controls.dev Link <span class="req">*</span></th><th style="width:44px"></th></tr></thead><tbody id="asset-body"></tbody></table></div>
|
||
<button class="add-btn" onclick="addAsset()">+ Add asset</button>
|
||
</div>
|
||
|
||
<!-- DISCIPLINES -->
|
||
<div class="card" id="discipline-card" style="display:none">
|
||
<div class="sub-heading">Disciplines<span class="help-tip" data-tip="Pick every discipline this package covers. Choosing two or more turns Scope into per-discipline sections and enables Split by Discipline.">i</span></div>
|
||
<div class="notice" id="discipline-note"></div>
|
||
<div class="disc-picker" id="discipline-picker"></div>
|
||
</div>
|
||
|
||
<!-- SCOPE & WORK -->
|
||
<div class="card">
|
||
<div class="sub-heading">Scope & Work<span class="help-tip" data-tip="Ordered steps the crew performs. With multiple disciplines selected, each gets its own scope section and status. Use Split by Discipline to break a large package into WP01A / WP01B / WP01C instances.">i</span></div>
|
||
<div id="flat-scope">
|
||
<div class="field"><label>Description of work (sequenced steps)</label>
|
||
<div class="notice">Enter the work as ordered steps — added in sequence, the way the crew performs them.</div>
|
||
<div id="worksteps-body"></div>
|
||
<button class="add-btn" onclick="addWorkStep()">+ Add step</button>
|
||
</div>
|
||
</div>
|
||
<div id="scope-by-discipline" style="display:none"></div>
|
||
<button class="btn btn-ghost" id="split-disc-btn" style="display:none;margin-top:10px" onclick="splitByDiscipline()" title="Break this multi-discipline package into one numbered instance per discipline">⎘ Split by discipline</button>
|
||
<div class="field-grid" style="margin-top:14px">
|
||
<div class="field"><label>Labor – est. hrs.</label><input type="number" id="wp_hours" min="0" step="1" placeholder="e.g. 20" oninput="onHoursChange()"><div class="field-hint" id="size-check"></div></div>
|
||
<div class="field"><label>Predecessor work packages<span class="help-tip" data-tip="The packages that must be Closed before this one can be released. A package with an open predecessor is not release-ready — you can still release it, but the override is logged.">i</span></label>
|
||
<div class="people-pick" id="pick_predecessors"></div>
|
||
<div class="field-hint" id="pred-hint"></div></div>
|
||
<div class="field"><label>Sequence phase <span class="help-tip" data-tip="Which phase of the SOP's construction sequence this package belongs to. Descriptive — it does not gate release; predecessor packages do.">i</span></label>
|
||
<select id="wp_seq"></select><div class="field-hint sop-hint">from the SOP construction sequence</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MATERIAL LIST -->
|
||
<div class="card" id="material-card">
|
||
<div class="sub-heading">Material List<span class="help-tip" data-tip="Bill of materials — feeds kitting. On a multi-discipline package each line can be tagged to a discipline so a split routes each instance only its own materials. Import from CSV is supported.">i</span></div>
|
||
<div class="notice">Structured bill of materials. Feeds kitting and the delivery forecast. Unit is from the Acumatica unit list.</div>
|
||
<div class="table-wrap"><table><thead><tr><th style="width:90px">Qty</th><th style="width:120px">Unit</th><th>Description</th><th id="mat-disc-th" style="width:140px;display:none">Discipline</th><th style="width:44px"></th></tr></thead><tbody id="material-body"></tbody></table></div>
|
||
<div class="material-actions">
|
||
<button class="add-btn" onclick="addMaterial()">+ Add material line</button>
|
||
<button class="add-btn" onclick="document.getElementById('material-import').click()">⤒ Import from Excel/CSV</button>
|
||
<button class="add-btn" onclick="downloadMaterialTemplate()">⤓ Download template</button>
|
||
<input type="file" id="material-import" accept=".csv,.xlsx,.xls" style="display:none" onchange="importMaterials(event)">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DRAWINGS / ATTACHMENTS -->
|
||
<div class="card">
|
||
<div class="sub-heading">Drawings & Attachments</div>
|
||
<div id="sop-ref-links" class="sop-ref-links"></div>
|
||
<div class="table-wrap"><table><thead><tr><th>Document / Drawing</th><th style="width:90px">Rev</th><th>Link / Note</th><th style="width:44px"></th></tr></thead><tbody id="attach-body"></tbody></table></div>
|
||
<button class="add-btn" onclick="addAttach()">+ Add document</button>
|
||
<button class="add-btn" onclick="toggleSopFilePanel()">+ Add files from SOP folder</button>
|
||
<div id="sop-file-panel" style="display:none; margin-top:0.75rem; padding:0.75rem; border:1px dashed var(--border); border-radius:6px; background:var(--bg);">
|
||
<div id="sop-file-folders"></div>
|
||
<div class="field-hint" style="margin-top:0.5rem;">2) Paste the file links here, one per line:</div>
|
||
<textarea id="sop-file-links" rows="4" placeholder="https://primecontrolsdallas.sharepoint.com/:b:/r/.../Drawings/EE-1YA-2P.pdf?csf=1&web=1&e=..." style="width:100%; font-size:12px; padding:0.5rem; border:1px solid var(--border); border-radius:4px; box-sizing:border-box;"></textarea>
|
||
<div style="margin-top:0.5rem; display:flex; gap:0.5rem;">
|
||
<button class="add-btn" onclick="addPastedFileLinks()">Add to attachments</button>
|
||
<button class="add-btn" onclick="toggleSopFilePanel()" style="background:transparent;">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- KITTING & MIMO -->
|
||
<div class="card" id="mimo-card">
|
||
<div class="sub-heading">Kitting & Material Movement (MIMO)</div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>Kitting status</label>
|
||
<select id="wp_kit_status"><option value="">—</option><option>Open</option><option>In Progress</option><option>Kitted</option><option>Delivered</option></select></div>
|
||
<div class="field"><label>Warehouse owner</label><input type="text" id="wp_kit_owner" placeholder="name"></div>
|
||
<div class="field"><label>Kitting need date</label><input type="date" id="wp_kit_date"></div>
|
||
<div class="field"><label>MIMO sch. time</label><input type="datetime-local" id="wp_mimo_time"><div class="field-hint">scheduled material-move date & time</div></div>
|
||
<div class="field"><label>MIMO location</label><input type="text" id="wp_mimo_loc" placeholder="staging / move location"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CONSTRAINTS / RELEASE READINESS -->
|
||
<div class="card" id="constraint-card">
|
||
<div class="sub-heading">Constraints — Release Readiness<span class="help-tip" data-tip="A package can't be Issued until every constraint is Cleared or N/A. If one reopens after release, the package drops to Issue (Hold).">i</span></div>
|
||
<div class="notice">Per AWP, a package is not released to the field until every constraint is <strong>Cleared</strong> or <strong>N/A</strong>. If a constraint reopens after release, status drops to <strong>Issue (Hold)</strong>.</div>
|
||
<div class="table-wrap"><table><thead><tr><th>Constraint</th><th style="width:230px">Status</th><th>Comment</th></tr></thead><tbody id="constraint-body"></tbody></table></div>
|
||
</div>
|
||
|
||
<!-- QUALITY & HOLD POINTS -->
|
||
<div class="card">
|
||
<div class="sub-heading">Quality, Inspection & Hold Points</div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>QC required</label><input type="text" id="wp_qc" placeholder="from SOP" readonly>
|
||
<div class="lock-row"><button type="button" class="lock-edit" onclick="editQuality('wp_qc')">🔒 Edit (reason required)</button><span class="override-note"></span></div></div>
|
||
<div class="field"><label>Photo documentation</label><input type="text" id="wp_photo" placeholder="from SOP" readonly>
|
||
<div class="lock-row"><button type="button" class="lock-edit" onclick="editQuality('wp_photo')">🔒 Edit (reason required)</button><span class="override-note"></span></div></div>
|
||
</div>
|
||
<div class="field field-grid col1"><div class="field"><label>Witness / hold points</label><textarea id="wp_hold" rows="2" readonly placeholder="from SOP"></textarea>
|
||
<div class="lock-row"><button type="button" class="lock-edit" onclick="editQuality('wp_hold')">🔒 Edit (reason required)</button><span class="override-note"></span></div>
|
||
<div class="field-hint">Inherited from the SOP. A <strong>Hold Point</strong> stops work until inspection sign-off; a <strong>Witness Point</strong> is offered for inspection but work may proceed if declined.</div></div></div>
|
||
</div>
|
||
|
||
<!-- SIGN-OFFS -->
|
||
<div class="card">
|
||
<div class="sub-heading">Approvals & Sign-offs</div>
|
||
<div class="notice">Per the AWP IWP checklist. A package should be signed by these roles before release.</div>
|
||
<div class="table-wrap"><table><thead><tr><th style="width:220px">Role</th><th>Name</th><th style="width:150px">Date</th><th style="width:80px;text-align:center">Signed</th></tr></thead><tbody id="signoff-body"></tbody></table></div>
|
||
</div>
|
||
|
||
<!-- CLOSEOUT -->
|
||
<div class="card" id="closeout-card">
|
||
<div class="sub-heading">Closeout</div>
|
||
<div class="notice">Completed at QC / Closed — captures as-built reality and lessons learned.</div>
|
||
<div class="field-grid">
|
||
<div class="field"><label>Actual hrs.</label><input type="number" id="wp_actual_hrs" min="0" step="1"></div>
|
||
<div class="field"><label>Installed quantity</label><input type="text" id="wp_installed_qty" placeholder="e.g. 42 of 42 tags"></div>
|
||
</div>
|
||
<div class="field field-grid col1"><div class="field"><label>Redlines / as-built notes</label><textarea id="wp_redlines" rows="2"></textarea></div></div>
|
||
<div class="field field-grid col1"><div class="field"><label>Lessons learned</label><textarea id="wp_lessons" rows="2"></textarea></div></div>
|
||
</div>
|
||
|
||
<div class="nav-row"><button class="btn btn-ghost" onclick="newPackage()">↺ Clear</button>
|
||
<div style="display:flex;gap:10px">
|
||
<button class="btn btn-ghost" onclick="savePackage(false)">Save draft</button>
|
||
<button class="btn btn-generate" onclick="savePackage(true)">⚡ Save & view</button>
|
||
</div></div>
|
||
|
||
<!-- DASHBOARD -->
|
||
<div id="dashboard-view" style="display:none">
|
||
<div class="output-toolbar">
|
||
<button class="btn btn-ghost" onclick="showForm()">← Back to form</button>
|
||
<div style="font-weight:700;font-size:15px">Work Package Dashboard</div>
|
||
<div style="display:flex;gap:10px;margin-left:auto">
|
||
<button class="btn btn-ghost" onclick="newPackage()">+ New WP</button>
|
||
<button class="btn btn-ghost" onclick="renderDashboard()">↻ Refresh</button>
|
||
<button class="btn btn-ghost" onclick="exportPackages()">⤓ Export (JSON)</button>
|
||
</div>
|
||
</div>
|
||
<div class="ctx-bar" style="margin:0 0 14px"><div class="field-hint">Status, metrics and gating across all saved work packages on this device. <span style="color:var(--text-dim)">Reads local data now; wires to the shared SQL database in Phase 2.</span></div></div>
|
||
<div id="dash-body"></div>
|
||
</div>
|
||
|
||
<!-- OUTPUT -->
|
||
<div id="pkg-output" style="display:none">
|
||
<div class="output-toolbar">
|
||
<button class="btn btn-ghost" onclick="showForm()">← Back to form</button>
|
||
<button class="btn btn-primary" onclick="printPackage()">⎙ Print / save PDF</button>
|
||
<button class="btn btn-ghost" onclick="exportPackages()">⤓ Export (JSON)</button>
|
||
</div>
|
||
<div class="output-doc" id="pkg-doc"></div>
|
||
</div>
|
||
|
||
<!-- SAVED -->
|
||
<div class="card" id="saved-card" style="display:none">
|
||
<div class="sub-heading">Saved Work Packages <span id="saved-count"></span></div>
|
||
<div class="table-wrap"><table><thead><tr><th>WP #</th><th>Type</th><th>Subject</th><th>Status</th><th style="width:110px">Ready?</th><th style="width:120px"></th></tr></thead><tbody id="saved-body"></tbody></table></div>
|
||
<button class="add-btn" onclick="exportPackages()">⤓ Export all (JSON)</button>
|
||
<button class="add-btn" onclick="clearSaved()">Clear all</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HOLD LOG MODAL (comment 7) -->
|
||
<div class="modal-overlay" id="hold-modal">
|
||
<div class="modal">
|
||
<div class="modal-head"><div class="modal-title">Log Hold — On-Hold Constraint</div><button class="cmt-x" onclick="cancelHold()" title="Cancel">✕</button></div>
|
||
<div class="modal-body">
|
||
<div class="notice">Moving a package to <strong>Issue (Hold)</strong> requires logging the constraint that blocked it.</div>
|
||
<div class="field"><label>Constraint type <span class="req">*</span></label><select id="hold-constraint"></select></div>
|
||
<div class="field"><label>Details <span class="req">*</span></label><textarea id="hold-details" rows="3" placeholder="What reopened / blocked this package?"></textarea></div>
|
||
<div class="field"><label>Supporting document link</label><input type="text" id="hold-doclink" placeholder="link to RFI, photo, email, etc. (optional)"></div>
|
||
<div class="field"><label>Supporting photo</label><input type="file" id="hold-photo" accept="image/*" onchange="holdPhotoChange(event)"><div class="hold-photo-preview" id="hold-photo-preview"></div></div>
|
||
</div>
|
||
<div class="modal-foot"><button class="btn btn-ghost" onclick="cancelHold()">Cancel</button><button class="btn btn-generate" onclick="submitHold()">Log hold</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- VIEW SOP MODAL (comment 2) -->
|
||
<div class="modal-overlay" id="sop-modal">
|
||
<div class="modal" style="max-width:640px">
|
||
<div class="modal-head"><div class="modal-title">Project SOP — Reference</div><button class="cmt-x" onclick="closeSopModal()" title="Close">✕</button></div>
|
||
<div class="modal-body" id="sop-modal-body"></div>
|
||
<div class="modal-foot"><button class="btn btn-primary" onclick="closeSopModal()">Close</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- COMMENTS DRAWER -->
|
||
<div class="cmt-overlay" id="cmt-overlay" onclick="toggleComments()"></div>
|
||
<aside class="cmt-drawer" id="cmt-drawer" aria-hidden="true">
|
||
<div class="cmt-head"><div class="cmt-title">Review Comments</div><button class="cmt-x" onclick="toggleComments()" title="Close">✕</button></div>
|
||
<div class="cmt-namebar"><label>Your name</label><input type="text" id="cmt-author" placeholder="e.g. J. Park" oninput="cmtSaveAuthor(this.value)"></div>
|
||
<div class="cmt-compose"><div class="cmt-compose-label">Comment on <strong id="cmt-cur-step">this form</strong></div>
|
||
<textarea id="cmt-input" rows="3" placeholder="Add feedback…"></textarea>
|
||
<button class="btn btn-primary cmt-add" onclick="addComment()">Add comment</button></div>
|
||
<div class="cmt-list" id="cmt-list"></div>
|
||
<div class="cmt-foot"><div class="cmt-note">Comments are saved in your browser. Use <strong>Export</strong> to send feedback back; the owner can <strong>Import</strong> each file.</div>
|
||
<div class="cmt-foot-btns"><button class="btn btn-ghost" onclick="exportComments()">⤓ Export</button>
|
||
<button class="btn btn-ghost" onclick="document.getElementById('cmt-import').click()">⤒ Import</button>
|
||
<button class="btn btn-ghost cmt-clear" onclick="clearMyComments()">Clear mine</button>
|
||
<input type="file" id="cmt-import" accept="application/json" style="display:none" onchange="importComments(event)"></div></div>
|
||
</aside>
|
||
|
||
<!-- STICKY SAVE BAR (always-visible save + release status) -->
|
||
<div class="sticky-save" id="sticky-save" style="display:none">
|
||
<span class="sticky-status" id="sticky-status"></span>
|
||
<div class="sticky-actions">
|
||
<button class="btn btn-ghost" onclick="savePackage(false)">Save draft</button>
|
||
<button class="btn btn-generate" onclick="savePackage(true)">⚡ Save & view</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="feedback-config.js"></script>
|
||
<script src="project-data.js"></script>
|
||
<script src="help.js"></script>
|
||
<script src="wp-creation-app.js"></script>
|
||
<script src="wp-sidenav.js"></script>
|
||
</body>
|
||
</html>
|