A named person owns fulfillment of the kit - today that is Paul Coonrod, informally, and everything bottlenecks through him. The package now records it explicitly: - The Warehouse owner control is a dropdown of project members (the same roster the Owner picker reads). Picking someone stores BOTH the display name (kitOwner - exports and old renderers keep working) and the account id (kitOwnerId - the routing CR-011's notifications will read at T8.3). - Confirmed Aug 18: the field lives ON the work package, not the project - a package retargeted to a different warehouse notifies the right person without touching the project. The wizard gets no field. - A stored name with no matching account - typed before the field was account-backed, or someone since removed from the project - is KEPT as a selected "(no account)" option and round-trips unchanged. Removing someone from the project breaks nothing. - The dashboard filters by warehouse owner, options drawn from the owners actually present in the data - a filter offering people with nothing to fulfill is noise. Verification (each probe run alone): kitting_check.py extended to 21/21 (T8.2 section: picker, id+name persistence, orphan survival, board filter). Regression: generalinfo_check 49/49. Items: CR-010 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
601 lines
43 KiB
HTML
601 lines
43 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>
|
||
<script src="wp-usage.js"></script>
|
||
<!-- Autosave, unsaved-work guard, draft recovery (S2). -->
|
||
<script src="wp-autosave.js"></script>
|
||
<!-- Which sections this project uses (CR-006). The same file the SOP wizard
|
||
reads, so "Assets is off" means one thing in both. -->
|
||
<script src="wp-sections.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">
|
||
<!-- B7/T7.1: the app bar. This page used to load neither of wp-chrome's two
|
||
files because it was an iframe child and wp-chrome.js returned early inside
|
||
one. It is a page now. -->
|
||
<link rel="stylesheet" href="wp-chrome.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>
|
||
|
||
<!-- APP BAR (B7 / T7.1)
|
||
This page loaded neither wp-chrome.css nor wp-chrome.js while it was an
|
||
iframe child - it was the only page in html/ without an app bar, which is
|
||
what made it read as part of the wizard rather than as a page. It has one
|
||
now, in the same .header-left / .header-right shape the suite page uses, so
|
||
wp-chrome.js inserts the project switcher and search at the same place on
|
||
both. -->
|
||
<div class="header">
|
||
<div class="header-left">
|
||
<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>
|
||
<div class="header-title">Work Package Suite</div>
|
||
<button id="dev-toggle" class="dev-toggle" onclick="toggleDevMode()" title="dev mode" aria-label="dev mode"></button>
|
||
</div>
|
||
<!-- wp-chrome.js inserts the project switcher and global search here. -->
|
||
<div class="header-right">
|
||
<button class="btn btn-ghost" id="comments-btn" style="padding:7px 16px" onclick="toggleComments()">Feedback <span class="cbadge-total" id="cbadge-total" style="display:none">0</span></button>
|
||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="openHelp()">Help</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TOOL TABS (B7 / T7.1)
|
||
The tab row used to live in the parent document and the toolbar in this one,
|
||
which is the thing B7 says makes folding either into the other impossible.
|
||
Both are here now. `Dashboard` was a toolbar button; it is a tab, because
|
||
that is what it always was.
|
||
|
||
SOP Configuration is an <a> because it goes to another document; the other
|
||
two are <button>s because they switch view inside this one. wp-chrome.js
|
||
stamps ?project= onto the link once the active project is known. -->
|
||
<div class="main-nav" role="navigation" aria-label="Work package tools">
|
||
<a class="nav-tab" data-nav-href="work-package-suite.html?tab=sop">SOP Configuration</a>
|
||
<button class="nav-tab" data-tab="wp" onclick="showForm()">Work Package Creation</button>
|
||
<button class="nav-tab" data-tab="dashboard" onclick="showDashboard()">Dashboard</button>
|
||
</div>
|
||
|
||
<!-- PACKAGE TOOLBAR
|
||
What is left of the old header once the brand, the feedback panel and the
|
||
Dashboard button have found their proper homes: actions on the package or on
|
||
the SOP behind it. `Sample SOP` and `Load example` are visible here rather
|
||
than hidden by body.embedded (D1). They are still two affordances under two
|
||
names, which is S7 and is T9.4's to reduce - this task makes them reachable,
|
||
not fewer. -->
|
||
<div class="wp-toolbar" id="wp-toolbar">
|
||
<button class="btn btn-ghost" onclick="newPackage()">+ New</button>
|
||
<button class="btn btn-ghost" onclick="duplicateWP()">Duplicate</button>
|
||
<button class="btn btn-ghost" onclick="showHistoryCurrent()" title="Change history for this work package">History</button>
|
||
<span class="wp-toolbar-sep" aria-hidden="true"></span>
|
||
<button class="btn btn-ghost" onclick="openSopModal()">View SOP</button>
|
||
<button class="btn btn-ghost" 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" onclick="loadSampleSOP()">Sample SOP</button>
|
||
<button class="btn btn-ghost" onclick="loadExample()">Load example</button>
|
||
<span class="wp-toolbar-sep" aria-hidden="true"></span>
|
||
<!-- T7.10 moves this to the admin console and deletes one of the two
|
||
implementations. It stays visible here until then rather than being
|
||
deleted by the task that dissolved the frame. -->
|
||
</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 - the ONE readiness warning on the page (A2).
|
||
role="status" is a polite live region, the login.html pattern: a change in
|
||
the open-constraint count is announced without stealing focus. The two
|
||
duplicates this page used to render (the sticky bar's copy and the static
|
||
hint under the status control) are gone; the rail badge carries the count. -->
|
||
<div class="release-banner" id="release-banner" role="status"></div>
|
||
|
||
<!-- F6/D3: the jump-link strip stood here. It was a row of `<span onclick>` chips
|
||
that scrolled you somewhere inside a 5,399px page and then told you nothing
|
||
about where you had landed. The replacement is a real rail, declared below the
|
||
form so it can be a sticky column beside it at desk width. -->
|
||
|
||
<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 -->
|
||
<!-- CR-006 gives every toggleable section a stable id. The map lives in
|
||
wp-creation-app.js (WP_SECTION_NODES); the ids are what it addresses. -->
|
||
<div class="card" id="general-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" data-val="Ready for QA"><input type="radio" name="status"><span class="dot"></span>Ready for QA</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>
|
||
</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>
|
||
<!-- Location is its own CR-006 section while still living inside General
|
||
Information's grid. CR-004 gives it structured building/floor/sector
|
||
fields of its own in wave 6; only this wrapper's contents change then. -->
|
||
<!-- CR-004. Three dependent dropdowns off the project's own taxonomy
|
||
(CR-005 / T5.4), not free text: the values are what CR-018 rolls cost
|
||
up by, and a rollup keyed on what somebody typed is not a rollup.
|
||
wp_location survives as a hidden field so a package written before
|
||
this keeps what it said — CLAUDE.md, removals are hidden not deleted. -->
|
||
<div class="field" id="location-card">
|
||
<label for="wp_building">Location<span class="help-tip" data-tip="Building, floor and sector come from this project's own location list, configured on step 11 of the SOP. Cost and progress roll up by these, which is why they are picked rather than typed.">i</span></label>
|
||
<div class="loc-picker">
|
||
<select id="wp_building" aria-label="Building"></select>
|
||
<select id="wp_floor" aria-label="Floor"></select>
|
||
<select id="wp_sector" aria-label="Sector"></select>
|
||
</div>
|
||
<div class="field-hint" id="wp_location_note"></div>
|
||
<input type="hidden" id="wp_location">
|
||
</div>
|
||
<!-- CR-002 hides these two rather than deleting them: the columns, the model
|
||
and every value already captured stay exactly as they are, and another
|
||
project can turn them back on without a code change. The ids are what
|
||
WP_FIELD_NODES addresses. -->
|
||
<div class="field" id="field-costCode"><label>Cost code</label><select id="wp_cost"></select><div class="field-hint sop-hint">Acumatica cost codes</div></div>
|
||
<div class="field" id="field-acumaticaTask"><label>Acumatica task</label><input type="text" id="wp_wbs" placeholder="Acumatica task no."></div>
|
||
<!-- Moved up from the second grid by T7.2. The specification section is
|
||
classification, not assignment: it is read-only and filled from the WP
|
||
type on the SOP, so it belongs beside Subject and Type. -->
|
||
<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>
|
||
|
||
<!-- ASSIGNMENT & SCHEDULE (F6 / T7.2)
|
||
Split out of General Information, which was 1,288px on its own and the
|
||
whole of the gap between the page at rest and F6's two-screen bar. The
|
||
split is presentational: BOTH cards are the CR-006 section `general`
|
||
(WP_SECTION_NODES.general lists both), so the shared section registry in
|
||
wp-sections.js is untouched, the SOP wizard still shows one toggle, and
|
||
turning General Information off still hides every field it hid before.
|
||
|
||
CR-001 requires the P6 activity to sit beside the due date. Both are here,
|
||
adjacent, and tests/generalinfo_check.py asserts the adjacency. -->
|
||
<div class="card" id="assign-card">
|
||
<div class="sub-heading">Assignment & Schedule</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>
|
||
<!-- CR-003. Three levels, agreed live in the meeting, and there is no fourth.
|
||
Independent of status: a package can become Urgent after it is issued
|
||
without its status moving. -->
|
||
<div class="field"><label>Priority <span class="req">*</span><span class="help-tip" data-tip="How urgent this package is, independently of its status and its due date. Normal is the baseline; High and Urgent are exceptions and are meant to stay rare.">i</span></label>
|
||
<select id="wp_priority">
|
||
<option value="Normal" selected>Normal</option>
|
||
<option value="High">High</option>
|
||
<option value="Urgent">Urgent</option>
|
||
</select></div>
|
||
<div class="field"><label>Due date</label><input type="date" id="wp_due"></div>
|
||
<!-- CR-001. Beside the due date on purpose: a date on a work package that
|
||
is not anchored to a schedule activity is a date floating on its own,
|
||
and the meeting placed the activity next to it for exactly that reason.
|
||
Free text — a validated lookup against an imported P6 activity list is
|
||
deferred (BL-000a) partly because the Micron schedule is being reworked,
|
||
and importing it now would import churn. -->
|
||
<div class="field"><label>P6 activity ID<span class="help-tip" data-tip="The Primavera P6 schedule activity this package delivers. Free text for now — a validated lookup against an imported activity list is deferred while the schedule is being reworked.">i</span></label>
|
||
<input type="text" id="wp_p6_id" placeholder="e.g. A1234"></div>
|
||
<div class="field"><label>P6 activity description</label>
|
||
<input type="text" id="wp_p6_desc" placeholder="what that activity covers"></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" id="scope-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" id="drawings-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>
|
||
<!-- CR-007/D8: uploads live beside the links, never instead of them. The
|
||
limits are stated HERE, before anyone picks a file, and the running
|
||
project total is on the same line (warning style from 80%). -->
|
||
<div class="file-rules" id="file-rules">
|
||
Uploads: <strong>PDF or image, up to 5MB a file.</strong>
|
||
<span id="file-usage" aria-live="polite"></span>
|
||
</div>
|
||
<div id="wp-file-list" class="wp-file-list"></div>
|
||
<div class="wp-file-row">
|
||
<label class="add-btn wp-file-pick">⇪ Upload drawing
|
||
<input type="file" id="wp-file-input" accept="application/pdf,image/png,image/jpeg,image/gif,image/webp" style="display:none" onchange="wpFileUpload(event)">
|
||
</label>
|
||
<input type="text" id="wp-file-desc" placeholder="focus area, e.g. Tray section, Level 3 east only" aria-label="Description for the next upload">
|
||
</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></select>
|
||
<div class="field-hint">Options come from KIT_STATUSES (CR-009): an explicit set, never free text. A pre-CR-009 value on a saved package is kept as a legacy option.</div></div>
|
||
<div class="field"><label>Warehouse owner <span class="help-tip" data-tip="Who owns fulfillment of this kit — a user account on this project. They are a default recipient of kitting notifications (CR-011). Recorded ON the package: retargeting one package notifies the right warehouse without touching the project.">i</span></label><select id="wp_kit_owner_sel"><option value="">— not assigned —</option></select><input type="hidden" id="wp_kit_owner"></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" id="quality-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 -->
|
||
<!-- The only .main > .card that had no id. buildSectionRail() filled the gap
|
||
positionally - `card.id = "sec-" + i` - and that id then rode into ?section=
|
||
as a shareable address. It moved every time the set of VISIBLE sections
|
||
changed: a CR-006 toggle, the BIM flag, or T7.2 inserting #assign-card ahead
|
||
of it. A link somebody sent then opened a different section, silently. -->
|
||
<div class="card" id="signoff-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>
|
||
|
||
<!-- SECTION RAIL (F6 / D3)
|
||
Built by buildSectionRail() from the cards themselves, so a section added or
|
||
suppressed by a CR-006 toggle changes the rail without anybody maintaining a
|
||
second list. Every entry is a real <button>, the current one carries
|
||
aria-current, and each card's heading became a disclosure button with
|
||
aria-expanded - the two accessibility defects F6's old chip strip and
|
||
makeCollapsible() were carrying.
|
||
|
||
A sticky column beside the form at 1200px and up; a horizontal strip above it
|
||
below that, which is what fits at 390px. -->
|
||
<nav class="sec-rail" id="section-rail" aria-label="Form sections" hidden>
|
||
<div class="sec-rail-head">
|
||
<span class="sec-rail-title">Sections</span>
|
||
<button type="button" class="sec-rail-all" id="sec-expand-all" aria-pressed="false"
|
||
onclick="toggleExpandAll()">Expand all</button>
|
||
</div>
|
||
<ul class="sec-rail-list" id="sec-rail-list"></ul>
|
||
</nav>
|
||
</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?" aria-describedby="hold-details_err"></textarea><div class="field-error" id="hold-details_err" role="alert"></div></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>
|
||
|
||
<!-- DIALOG (S1 / T7.9). The one replacement for the creator's 43 native
|
||
dialogs: a modal with a message, an optional input with an inline error,
|
||
and real buttons. Promise-based - wpConfirmDialog()/wpPromptDialog(). -->
|
||
<div class="modal-overlay" id="wp-dialog" role="dialog" aria-modal="true" aria-labelledby="wp-dialog-title">
|
||
<div class="modal" style="max-width:480px">
|
||
<div class="modal-head"><div class="modal-title" id="wp-dialog-title"></div><button class="cmt-x" onclick="wpDialogCancel()" title="Cancel">✕</button></div>
|
||
<div class="modal-body">
|
||
<div id="wp-dialog-msg" style="white-space:pre-wrap"></div>
|
||
<div class="field" id="wp-dialog-input-wrap" style="margin-top:10px">
|
||
<label id="wp-dialog-label" for="wp-dialog-input"></label>
|
||
<input type="text" id="wp-dialog-input" onkeydown="if(event.key==='Enter'){wpDialogOk();}">
|
||
<div class="field-error" id="wp-dialog-err" role="alert"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot"><button class="btn btn-ghost" id="wp-dialog-cancel" onclick="wpDialogCancel()">Cancel</button><button class="btn btn-generate" id="wp-dialog-ok" onclick="wpDialogOk()">OK</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- QA REJECT MODAL (CR-014). The comment is not optional: a rejection with no
|
||
reason is the after-the-fact surprise this gate exists to end, and the server
|
||
refuses the transition without one. -->
|
||
<div class="modal-overlay" id="qa-reject-modal">
|
||
<div class="modal">
|
||
<div class="modal-head"><div class="modal-title">Return to the crew — QA rejection</div><button class="cmt-x" onclick="qaRejectCancel()" title="Cancel">✕</button></div>
|
||
<div class="modal-body">
|
||
<div class="notice">The package goes back to <strong>In Progress</strong>. The owner and the QA group are notified, and the comment stays on the package.</div>
|
||
<div class="field"><label>What needs fixing <span class="req">*</span></label><textarea id="qa-reject-comment" rows="3" placeholder="What QA found — required" aria-describedby="qa-reject-comment_err"></textarea><div class="field-error" id="qa-reject-comment_err" role="alert"></div></div>
|
||
</div>
|
||
<div class="modal-foot"><button class="btn btn-ghost" onclick="qaRejectCancel()">Cancel</button><button class="btn btn-generate" onclick="qaRejectSubmit()">Reject — back to In Progress</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. The status span belongs to the B5 autosave indicator and
|
||
nothing else: the readiness text that used to be written here was duplicate
|
||
two of the A2 warning, and writing it via textContent destroyed the mounted
|
||
indicator every time the count changed. -->
|
||
<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>
|
||
<!-- The Help button in the app bar is this page's entry point, the same as the
|
||
suite page. help.js used to skip its floating button here because it saw an
|
||
iframe; that test is gone, so say so explicitly instead. -->
|
||
<script>window.WP_HELP_NO_FAB = true;</script>
|
||
<script src="help.js"></script>
|
||
<script src="wp-creation-app.js"></script>
|
||
<script src="wp-chrome.js"></script>
|
||
<script src="wp-sidenav.js"></script>
|
||
</body>
|
||
</html>
|