There is no iframe in html/ any more. The creator is a top-level document with
the same app bar and the same tab strip as the SOP wizard; the two tabs that
used to swap a frame are links between them.
DEVIATION, stated rather than smuggled. The wave file says "remove the iframe
boundary so the creator renders in the parent document". It renders as its own
document instead. Every done-when is met - no iframe, no cross-frame messaging,
F4 resolved structurally, CR-006 toggles with no special-casing, back and
forward intact with T4.2's URL state - but the route is the other one, and the
reason is in creator-frame.md's own numbers:
merge into parent make it a page
selector collisions to resolve 21 0
script global collisions 9 0
cross-frame call sites to remove 28 28
probe entry points needing rework ~29 2
The 21 and the 9 were never the cost of dissolving the boundary. They are the
cost of MERGING TWO DOCUMENTS, which is a different change the boundary was
hiding. And 29 probe call sites address wp-creation-index.html directly, so a
route that keeps that address keeps all of them. creator-frame.md section 5
records this in full.
What went, and what replaced it:
#wp-frame, applyEmbedLayout, sizeWPFrame, viewportMinusChrome, chromeHeight,
renderWPTab, the resize handler, the ResizeObserver, --wp-chrome-h,
.content-area.embed-full, body.embed-full -> the window sizes the page
?embedded=1, body.embedded, .embed-hide, .embed-first -> nothing. An old
link carrying the param is ignored rather than half-obeyed.
openWpById / showDashboard / showForm / dashApplyFlag / applySopSections
called across the frame -> the URL. ?project= ?view= ?wp= ?flag= were
already read at the creator's own boot (T4.2), which is exactly why those four
could be DELETED rather than migrated. X4 is closed: the surviving path is the
one T5.5 built and proved.
inIframe in auth-guard.js, wp-chrome.js, wp-sidenav.js, help.js and _isTop in
project-data.js -> gone. help.js now reads the explicit WP_HELP_NO_FAB flag
both tool pages set, instead of inferring intent from where it is rendered.
.main-nav / .nav-tab in work-package-suite-styles.css -> wp-chrome.css,
because a tab row only one of two documents can style is the shape that put
the tabs in the parent and the toolbar in the child to begin with.
The three questions creator-frame.md section 4 said no count could answer:
1. The creator gets the app bar. It was the only page loading neither
wp-chrome file. Its header is now the .header-left / .header-right pair the
wizard uses, so the switcher lands in the same place on both.
2. Two sequence components, scoped not merged - confirmed Aug 18 that the
sequence is authored in the SOP and adjustable per package. BL-015 stays.
3. body.embedded is gone. The header it hid is replaced by the app bar; the
sample controls are visible in a new package toolbar (D1); the analytics
button is visible there until T7.10 moves it. The Dashboard BUTTON in that
row became a TAB, which is the one place B7's "fold the toolbar into the
tab row" actually happened.
Old addresses still resolve. ?tab=wp, ?view=dashboard and ?wp=<id> are in
bookmarks, in wp-sidenav's link map, and they are the shape CR-011 and CR-014
were specified against (X1). The wizard forwards them with replace(), so Back
does not bounce. Breaking these silently was the one regression this task could
have shipped that nobody would notice for weeks. frame_check.py section 4 pins
all three.
BEHAVIOUR CHANGE, deliberate. The live cross-frame hand-off showed the creator a
section toggle that had NOT been saved: flip it, look, reload, and the section
came back. What the creator shows now is the SOP that is stored. sections_check
5b pins both halves - an unsaved toggle does not travel, a saved one does.
BEHAVIOUR CHANGE, not deliberate, logged as BL-020. A tab switch is a page exit
now, so leaving the wizard with unsaved SOP edits fires T4.3's unsaved-work
guard. Nothing is lost - the guard writes the draft first and T4.3 recovers it -
but it is friction that did not exist, and suppressing a deliberate guard is a
product decision with its own downside. Logged, not quietly handled here.
tests/frame_check.py, 39 checks, new. Two of them exist because of failures
during this task rather than in it:
- "both documents parse and boot". A const shadowing a function parameter is a
SyntaxError, and work-package-suite-app.js did not parse at all for one run.
Four checks in url_state_check went red and not one said "the script did not
load". Asserting a page's own entry points exist costs nothing.
- "focus emulation is on, so a focus reading means something". An earlier draft
called page.call instead of page.ws.call inside a try/except and measured
nothing, reporting no focus ring anywhere - which looks exactly like a
finding. Trap 5 in reverse, for the second time in this project.
The four backlog entries logged against this file, re-measured rather than
assumed:
BL-001 still reproduces (485px in a 390px viewport) but its RECORDED CAUSE IS
WRONG. --nav-w now computes to 56px, so the injected-style explanation
is spent. The overflow is the creator's data tables - #asset-body's
lays out at 520px with no scroll container. frame_check reports the
offending boxes by selector and skips position:fixed subtrees, because
the comments drawer parked off-screen at right:844 made the first
measurement blame the drawer. Pinned, not fixed: T7.2 lays out the form.
BL-013 CLOSED. It was fixed by S12 in WAVE 4 - wp-creation-styles.css:209
carries the comment naming this entry - and nobody updated it. It was
quoted as a live CLAUDE.md violation while planning wave 7 and had not
been true for four waves. a11y_check walks 120 focusable elements on
the creator and every one rings at >= 3:1.
BL-006 15 by the probe's measure, unchanged; different denominator, stated.
BL-007 68 raw radii by the probe's measure. Nothing has reduced it in four
waves; it is measured every run now instead of once.
BL-018 cost a FOURTH probe. frame_check imports set_sop from sections_check
rather than writing a fifth copy of the workaround. T9.9 owns it.
Probes re-pointed, with reasons in the files: sections_check 5b (drove the live
hand-off), pipeline_check check 2 (read through contentDocument), f_items F4
(drove standalone and embedded; there is one mode now), validation_check
(lost "the wrong tab", gained the SOP gate).
Verified: frame_check 39/39, sections_check 95/95, pipeline_check 44/44,
url_state_check 23/23, validation_check 83/83, a11y_check 22/22,
autosave_check 34/34, aggregates_check 16/16, stepper_check 71/71,
browser_check 71/71, launcher_check 58/58, generalinfo_check 49/49,
rollup_check 63/63, cards_check 44/44, locations_check 58/58.
f_items: F1-F5 fixed, F6 reproduces (T7.2).
Metrics: iframes 1 -> 0, colour literals in rules outside theme-light.css 0,
dialogs 64, <div onclick> 2, .help-tip 18.
Items: B7 D1
Task: T7.1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
485 lines
35 KiB
JavaScript
485 lines
35 KiB
JavaScript
/* Shared Help center + tooltip module for the Work Package Suite.
|
||
Included by the home page, the SOP wizard and the work package creator. It injects:
|
||
- tooltip styles for the .help-tip (ⓘ) component and [data-tip] hovers
|
||
- a searchable, multi-topic Help center modal opened via window.openHelp()
|
||
- a floating "?" launcher on any page that doesn't already have a Help button
|
||
|
||
API (unchanged + extended):
|
||
openHelp() open the help center
|
||
openHelp('topicId') open and jump to a topic (e.g. openHelp('constraints'))
|
||
closeHelp() close it
|
||
Add a Help button anywhere with onclick="openHelp()". */
|
||
(function (global) {
|
||
'use strict';
|
||
|
||
// ── styles ────────────────────────────────────────────────────────────────
|
||
var css = `
|
||
.help-tip{ display:inline-flex; align-items:center; justify-content:center; width:15px; height:15px;
|
||
margin-left:5px; border-radius:50%; background:#525252; color:#fff; font-size:10px; font-weight:700;
|
||
font-family:ui-sans-serif,system-ui,sans-serif; cursor:help; vertical-align:middle; position:relative; }
|
||
.help-tip::after{ content:attr(data-tip); position:absolute; bottom:130%; left:50%; transform:translateX(-50%);
|
||
background:#161616; color:#fff; padding:7px 10px; border-radius:0; font-size:12px; font-weight:400;
|
||
line-height:1.4; white-space:normal; width:max-content; max-width:260px; text-align:left; z-index:9999;
|
||
opacity:0; pointer-events:none; transition:opacity .12s; box-shadow:0 4px 14px rgba(20,30,50,.22); }
|
||
.help-tip::before{ content:''; position:absolute; bottom:130%; left:50%; transform:translate(-50%,95%);
|
||
border:5px solid transparent; border-top-color:#161616; opacity:0; transition:opacity .12s; z-index:9999; }
|
||
.help-tip:hover::after, .help-tip:hover::before, .help-tip:focus::after, .help-tip:focus::before{ opacity:1; }
|
||
|
||
.ui-help-overlay{ position:fixed; inset:0; background:rgba(20,30,50,.5); display:none; align-items:center;
|
||
justify-content:center; z-index:10000; padding:4vh 16px; }
|
||
.ui-help-overlay.open{ display:flex; }
|
||
.ui-help-modal{ background:#fff; color:#161616; max-width:980px; width:100%; height:88vh; max-height:880px;
|
||
border-radius:0; box-shadow:0 12px 40px rgba(20,30,50,.3); display:flex; flex-direction:column; overflow:hidden;
|
||
font-family:ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif; }
|
||
.ui-help-head{ display:flex; align-items:center; gap:14px; padding:13px 18px; border-bottom:1px solid #e0e0e0; flex:none; }
|
||
.ui-help-head .ui-help-title{ font-size:15px; font-weight:700; white-space:nowrap; }
|
||
.ui-help-search{ flex:1; position:relative; max-width:420px; }
|
||
.ui-help-search input{ width:100%; padding:8px 12px; border:1px solid #8d8d8d; border-radius:0;
|
||
font-size:13px; outline:none; background:#f7f8fa; }
|
||
.ui-help-search input:focus{ border-color:#0f62fe; background:#fff; box-shadow:0 0 0 2px rgba(37,99,214,.15); }
|
||
.ui-help-head .ui-help-x{ margin-left:auto; background:none; border:none; font-size:20px; cursor:pointer; color:#525252; line-height:1; }
|
||
.ui-help-wrap{ display:flex; flex:1; min-height:0; }
|
||
.ui-help-nav{ width:230px; flex:none; border-right:1px solid #e0e0e0; overflow:auto; padding:10px 8px; background:#fafbfc; }
|
||
.ui-help-nav a{ display:block; padding:7px 10px; border-radius:0; color:#27313f; text-decoration:none; font-size:13px;
|
||
cursor:pointer; margin-bottom:1px; }
|
||
.ui-help-nav a:hover{ background:#eef1f6; }
|
||
.ui-help-nav a.active{ background:#edf5ff; color:#0353e9; font-weight:600; }
|
||
.ui-help-nav a.nohit{ display:none; }
|
||
.ui-help-content{ flex:1; overflow:auto; padding:22px 28px; scroll-behavior:smooth; }
|
||
.ui-help-sec{ margin-bottom:30px; }
|
||
.ui-help-sec.hide{ display:none; }
|
||
.ui-help-sec h3{ font-size:18px; margin:0 0 10px; color:#161616; scroll-margin-top:10px; }
|
||
.ui-help-sec h4{ margin:18px 0 6px; font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:#0f62fe; }
|
||
.ui-help-content p{ font-size:13.5px; line-height:1.62; margin:0 0 9px; color:#27313f; }
|
||
.ui-help-content ol, .ui-help-content ul{ margin:0 0 10px; padding-left:20px; font-size:13.5px; line-height:1.6; }
|
||
.ui-help-content li{ margin-bottom:5px; }
|
||
.ui-help-content code{ background:#eef1f6; padding:1px 5px; border-radius:4px; font-size:12px; }
|
||
.ui-help-content table{ border-collapse:collapse; width:100%; font-size:12.5px; margin:6px 0 12px; }
|
||
.ui-help-content th, .ui-help-content td{ border:1px solid #e0e0e0; padding:6px 9px; text-align:left; vertical-align:top; }
|
||
.ui-help-content th{ background:#f4f6f9; font-weight:600; }
|
||
.ui-help-pill{ display:inline-block; padding:1px 8px; border-radius:11px; font-size:11px; font-weight:600; }
|
||
.pill-draft{ background:#eef1f6; color:#525252; } .pill-sched{ background:#edf5ff; color:#0353e9; }
|
||
.pill-prog{ background:#fef3e0; color:#b45309; } .pill-issued{ background:#e4f6ec; color:#15924f; }
|
||
.pill-qc{ background:#f3e8ff; color:#7c3aed; } .pill-closed{ background:#e2e8f0; color:#334155; }
|
||
.pill-hold{ background:#fde8e8; color:#c0392b; }
|
||
.ui-help-callout{ background:#f4f8ff; border-left:3px solid #0f62fe; padding:10px 14px; border-radius:0;
|
||
font-size:13px; line-height:1.55; margin:10px 0; }
|
||
.ui-help-noresult{ display:none; color:#525252; font-size:14px; padding:10px 2px; }
|
||
.ui-help-content mark{ background:#fff1a8; color:inherit; border-radius:2px; padding:0 1px; }
|
||
.ui-help-fab{ position:fixed; bottom:12px; left:12px; z-index:9998; width:38px; height:38px; border-radius:50%;
|
||
border:none; background:#0f62fe; color:#fff; font-size:18px; font-weight:700; cursor:pointer;
|
||
box-shadow:0 2px 10px rgba(20,30,50,.28); }
|
||
.ui-help-fab:hover{ background:#0353e9; }
|
||
@media (max-width:760px){
|
||
.ui-help-modal{ height:92vh; } .ui-help-wrap{ flex-direction:column; }
|
||
.ui-help-nav{ width:auto; display:flex; flex-wrap:wrap; gap:4px; border-right:none; border-bottom:1px solid #e0e0e0; }
|
||
.ui-help-nav a{ margin:0; font-size:12px; padding:5px 9px; }
|
||
.ui-help-head{ flex-wrap:wrap; }
|
||
}`;
|
||
var style = document.createElement('style');
|
||
style.textContent = css;
|
||
(document.head || document.documentElement).appendChild(style);
|
||
|
||
// ── content ─────────────────────────────────────────────────────────────────
|
||
// Each topic: { id, title, body(HTML) }. Order here is the nav order.
|
||
var TOPICS = [
|
||
{ id: 'overview', title: 'Getting started', body: `
|
||
<h3>Getting started</h3>
|
||
<p>The Work Package Suite turns a project's standard procedure into release-ready <strong>Installation Work Packages (IWPs)</strong>. You work in three stages, always in the same order:</p>
|
||
<ol>
|
||
<li><strong>Pick or create a Project</strong> on the home page. Each project keeps its own SOP and its own Work Packages, so you can run many jobs at once.</li>
|
||
<li><strong>SOP Configuration</strong> — set the project baseline in 10 steps (team, sign-offs, WP types, governance & sizing, quality, platforms, sequence, constraints, sources). Every Work Package inherits these defaults. The Creator stays locked until the SOP is marked complete.</li>
|
||
<li><strong>Work Package Creation</strong> — author individual IWPs against the SOP, clear their constraints, and issue them to the field.</li>
|
||
<li><strong>Dashboard</strong> — track status, hours, due dates, and what's gating each package across the project.</li>
|
||
</ol>
|
||
<h4>Moving around</h4>
|
||
<p>From the home page, open <strong>SOP Configuration</strong>, the <strong>Work Package Creator</strong>, or the <strong>Dashboard</strong>. Inside the suite, switch any time using the top tabs: <strong>⚙️ SOP Configuration</strong>, <strong>📋 Work Package Creation</strong>, and <strong>📊 Dashboard</strong>. The active project and SOP follow you across all of them.</p>
|
||
<h4>Quick start</h4>
|
||
<ol>
|
||
<li><strong>Open “SOP Configuration”</strong> and complete the 10 steps for your project (~15 minutes).</li>
|
||
<li><strong>Finish the SOP</strong> — its home-page card turns green and unlocks the Work Package Creator.</li>
|
||
<li><strong>Open “Work Package Creation”</strong> to author packages with your SOP defaults pre-populated.</li>
|
||
<li><strong>Update from the field</strong> using the <strong>Field View</strong>, and <strong>leave feedback</strong> on any page with the Feedback button.</li>
|
||
</ol>
|
||
<div class="ui-help-callout">New here? On the home page choose the <strong>Sample Project</strong>, then click <strong>⭐ Load sample</strong> in the suite to see a fully filled-out SOP and an example Work Package.</div>` },
|
||
|
||
{ id: 'projects', title: 'Projects', body: `
|
||
<h3>Projects</h3>
|
||
<p>A <strong>project</strong> is the top-level container — every SOP and Work Package belongs to one. Create or select projects on the home page.</p>
|
||
<h4>Project fields</h4>
|
||
<ul>
|
||
<li><strong>Project Name</strong> (required)</li>
|
||
<li><strong>Project Number</strong></li>
|
||
<li><strong>Client</strong></li>
|
||
<li><strong>Division / Sector</strong></li>
|
||
<li><strong>Site / Location</strong></li>
|
||
</ul>
|
||
<h4>The active project</h4>
|
||
<p>The <strong>active project</strong> is the one you're currently working in. All SOP and Work Package data is scoped (namespaced) to it, so switching projects loads that project's own configuration and packages — nothing leaks between jobs. Use the <em>change</em> link next to the active project name to switch.</p>
|
||
<div class="ui-help-callout">Projects are stored centrally via the API and mirrored to your browser, so the suite still works offline; it re-syncs when the connection returns.</div>` },
|
||
|
||
{ id: 'sop', title: 'SOP Configuration', body: `
|
||
<h3>SOP Configuration (10 steps)</h3>
|
||
<p>The SOP is the project baseline. Walk the 10 steps with <strong>← Back</strong> / <strong>Next →</strong>, or jump using the step indicators. The final step is <strong>✓ SOP complete</strong> — saving it unlocks the Work Package Creator and turns the home-page card green.</p>
|
||
<ol>
|
||
<li><strong>Project Basics</strong> — name, number, client, division/sector, site. Inherited by every WP.</li>
|
||
<li><strong>Project Team Leadership</strong> — PM, APM, CM, QM, plus any additional members (<em>+ Add Team Member</em>).</li>
|
||
<li><strong>Required Sign-Off Roles</strong> — Superintendent and Foreman are always required; add optional roles (HSE, Quality Rep, Planner, etc.) with <em>+ Add Role</em>.</li>
|
||
<li><strong>Work Package Types</strong> — enable the install types this project uses (Conduit Install, Wire Pull, Terminations, …). Enabled types populate the WP type picker.</li>
|
||
<li><strong>Governance & WP Numbering</strong> — the WP <strong>number format</strong> (e.g. <code>WP##-[Sector]-[TYPE]</code>), issuance strategy, the project's <strong>disciplines</strong>, the <strong>discipline strategy</strong>, and <strong>WP sizing</strong> (see <a data-help-jump="sizing">Sizing</a> and <a data-help-jump="disciplines">Disciplines</a>).</li>
|
||
<li><strong>Quality & Inspection Strategy</strong> — QC requirement, photo/documentation standard, and hold/witness points.</li>
|
||
<li><strong>Tracking & Commissioning Platforms</strong> — e.g. CxAlloy, Procore, ACC.</li>
|
||
<li><strong>Construction Sequence</strong> — the install flow; reorder by dragging (⠿), edit labels, add <em>◆ QC Hold</em> gates or custom steps. These feed the WP "predecessor" picker.</li>
|
||
<li><strong>Release Gate Constraints</strong> — choose which standard AWP constraints apply and add custom ones (see <a data-help-jump="constraints">Constraints</a>).</li>
|
||
<li><strong>Engineering Sources & References</strong> — labelled links (Design Drawings, Specs, …) that appear as quick-access buttons in the WP Creator's <em>Drawings & Attachments</em>.</li>
|
||
</ol>
|
||
<div class="ui-help-callout">Fields a WP inherits from the SOP show a <strong>"from SOP"</strong> tag and are locked. You can override a locked field with <strong>🔒 Edit</strong>, which requires a logged reason.</div>` },
|
||
|
||
{ id: 'wps', title: 'Work Packages', body: `
|
||
<h3>Creating Work Packages</h3>
|
||
<p>In the Creator, start a package with <strong>+ New</strong> (blank, auto-numbered) or <strong>⧉ Duplicate</strong> (copies a saved package and increments the number). The <strong>WP Number</strong> is built automatically from the SOP number format plus your scope fields, the WP type, and a counter — it's read-only.</p>
|
||
<h4>Key fields</h4>
|
||
<ul>
|
||
<li><strong>Subject / Title</strong> (required) and <strong>WP Type</strong> (required, from the SOP).</li>
|
||
<li><strong>Assets</strong> — link each controls.dev asset the package covers.</li>
|
||
<li><strong>Disciplines</strong> — which trades the package covers (see <a data-help-jump="disciplines">Disciplines & Split</a>).</li>
|
||
<li><strong>Scope & Work</strong> — the sequenced steps the crew performs (per-discipline in multi-discipline mode).</li>
|
||
<li><strong>Labor – Est. Hrs.</strong> — drives the sizing check (see <a data-help-jump="sizing">Sizing</a>).</li>
|
||
<li><strong>Material List</strong> — the bill of materials; import from CSV/Excel or add lines manually.</li>
|
||
<li><strong>Drawings & Attachments</strong> — documents and SOP source-folder links.</li>
|
||
<li><strong>Kitting & Material Movement (MIMO)</strong> — kitting status, warehouse owner, move date/location.</li>
|
||
<li><strong>Constraints</strong> — the release gate (see <a data-help-jump="constraints">Constraints</a>).</li>
|
||
<li><strong>Quality / Hold Points</strong>, <strong>Approvals & Sign-offs</strong>, and <strong>Closeout</strong> (actual hours, as-builts, lessons learned — shown at QC/Closed).</li>
|
||
</ul>
|
||
<h4>Saving</h4>
|
||
<p><strong>Save Draft</strong> stores the package; <strong>⚡ Save & View</strong> saves and renders the print-ready output. Drafts auto-save to your browser as you type, so nothing is lost if you close the tab.</p>` },
|
||
|
||
{ id: 'statuses', title: 'Statuses', body: `
|
||
<h3>Work Package statuses</h3>
|
||
<table>
|
||
<tr><th>Status</th><th>Meaning</th></tr>
|
||
<tr><td><span class="ui-help-pill pill-draft">Draft</span></td><td>Work in progress; not yet released.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-sched">Scheduled</span></td><td>Planned and scheduled; upcoming.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-issued">Issued</span></td><td>Released to the field. Requires <em>all constraints Cleared or N/A</em>.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-prog">In Progress</span></td><td>Actively being worked.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-qc">QC</span></td><td>In quality check / inspection.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-closed">Closed</span></td><td>Completed.</td></tr>
|
||
<tr><td><span class="ui-help-pill pill-hold">Issue (Hold)</span></td><td>A constraint reopened after release — work is paused until it's resolved.</td></tr>
|
||
</table>
|
||
<div class="ui-help-callout">A package <strong>cannot move to Issued</strong> while any constraint is Open. If a constraint reopens after a package is Issued, its status automatically drops to <strong>Issue (Hold)</strong> and the suite makes you log what happened.</div>
|
||
<p>On a multi-discipline package, each discipline carries its own status and the overall status <strong>rolls up to the least-advanced discipline</strong> — so a package is never "Closed" while one trade still lags.</p>` },
|
||
|
||
{ id: 'constraints', title: 'Constraints & release', body: `
|
||
<h3>Constraints & release readiness</h3>
|
||
<p>Constraints are the readiness checklist that gates a package's release to the field. They follow Advanced Work Packaging (AWP Vol II §2.3.2). The standard set:</p>
|
||
<ol>
|
||
<li>Safety & Permitting</li><li>Quality Control / Inspection</li><li>IFC Drawings & Specs</li>
|
||
<li>Schedule</li><li>Materials (on site, bagged & tagged)</li><li>Prefabrication</li>
|
||
<li>Work Access & Laydown</li><li>Craft Availability</li><li>Construction Equipment & Tools</li>
|
||
<li>Scaffolding / Access Equipment</li>
|
||
</ol>
|
||
<p>Pick which apply (and add custom ones) in <strong>SOP Step 9</strong>. Each constraint on a package has one of three states:</p>
|
||
<table>
|
||
<tr><th>State</th><th>Effect</th></tr>
|
||
<tr><td><strong>Open</strong></td><td>Not yet cleared — <em>blocks release</em>.</td></tr>
|
||
<tr><td><strong>Cleared</strong></td><td>Requirement met — counts toward release-ready.</td></tr>
|
||
<tr><td><strong>N/A</strong></td><td>Not applicable to this package — counts as cleared.</td></tr>
|
||
</table>
|
||
<h4>The release gate</h4>
|
||
<ul>
|
||
<li>A package is <strong>release-ready</strong> when every constraint is Cleared or N/A. The sticky banner shows green when ready, amber when constraints are still open, and red when on hold.</li>
|
||
<li>When the last open constraint clears, the suite offers to mark the package <strong>Issued</strong>.</li>
|
||
<li>If a constraint reopens after the package is Issued, you log the hold (what reopened, details, optional doc link & photo) and the status drops to <strong>Issue (Hold)</strong>.</li>
|
||
</ul>` },
|
||
|
||
{ id: 'disciplines', title: 'Disciplines & Split', body: `
|
||
<h3>Disciplines & Split by Discipline</h3>
|
||
<p>Disciplines are trades (Mechanical, Electrical, Tech, …) set in <strong>SOP Step 5</strong>. The <strong>discipline strategy</strong> controls how packages handle them:</p>
|
||
<ul>
|
||
<li><strong>Let the planner choose per package</strong> (recommended) — pick one discipline (flat scope) or several (per-discipline scope + the <em>Split</em> option).</li>
|
||
<li><strong>One discipline per package</strong> — each WP is single-discipline.</li>
|
||
<li><strong>Multiple disciplines per package</strong> — scope is always split by discipline.</li>
|
||
</ul>
|
||
<h4>Split by Discipline</h4>
|
||
<p>When a package covers 2+ disciplines, the <strong>⎘ Split by Discipline</strong> button breaks it into one numbered instance per discipline — <code>WP01A</code>, <code>WP01B</code>, <code>WP01C</code> (or <code>_MECH</code>/<code>_ELEC</code> suffixes, set in the SOP). The original is kept as a <strong>master / roll-up</strong>; each instance:</p>
|
||
<ul>
|
||
<li>becomes its own single-discipline package, issued independently;</li>
|
||
<li>receives only the <strong>scope steps</strong> and <strong>materials tagged to that discipline</strong>;</li>
|
||
<li>stays linked back to the master.</li>
|
||
</ul>
|
||
<div class="ui-help-callout">Tag material rows to a discipline <em>before</em> splitting. <strong>Untagged rows stay on the master only</strong> and won't be routed to any instance. Masters are excluded from dashboard counts so hours aren't double-counted.</div>` },
|
||
|
||
{ id: 'sizing', title: 'Sizing', body: `
|
||
<h3>Work Package sizing</h3>
|
||
<p>In <strong>SOP Step 5</strong> you set a typical WP <strong>size band</strong>, which sets a <strong>split threshold</strong> (max labor hours):</p>
|
||
<table>
|
||
<tr><th>Size band</th><th>Split threshold</th></tr>
|
||
<tr><td>Small — 1–2 days (≈8–24 hrs)</td><td>24 hrs</td></tr>
|
||
<tr><td>Standard — 3–5 days (≈40–80 hrs)</td><td>80 hrs</td></tr>
|
||
<tr><td>Large — 1–2 weeks (≈80–160 hrs)</td><td>160 hrs</td></tr>
|
||
<tr><td>Custom…</td><td>you set it</td></tr>
|
||
</table>
|
||
<p>In the Creator, the <strong>Est. Hrs.</strong> field is checked live against the threshold. Within range you see the target band; over it you get an amber warning — <em>"⚠ … exceeds the …-hr split threshold — consider breaking this package down"</em> — and a nudge to split by discipline where that applies. It's a guide, not a hard block: you can proceed if it's intentional.</p>` },
|
||
|
||
{ id: 'dashboard', title: 'Dashboard', body: `
|
||
<h3>Dashboard & metrics</h3>
|
||
<p>The dashboard aggregates every (non-master) package in the active project. Open it from the home page, the suite's <strong>📊 Dashboard</strong> tab, or the Creator header.</p>
|
||
<h4>Metric cards (click to filter)</h4>
|
||
<ul>
|
||
<li><strong>Total WPs</strong>, <strong>Release-ready</strong>, <strong>On hold</strong>, <strong>Overdue</strong></li>
|
||
<li><strong>Est. hrs</strong> and <strong>Actual hrs</strong> (summed)</li>
|
||
</ul>
|
||
<h4>Breakdowns & gates</h4>
|
||
<ul>
|
||
<li><strong>By status</strong> and <strong>by discipline</strong> chips.</li>
|
||
<li><strong>⛔ Gating constraints</strong> — lists every blocked package and exactly which constraints are holding it.</li>
|
||
</ul>
|
||
<h4>The table</h4>
|
||
<p>Shows WP #, subject, type, discipline, status, <strong>Gates</strong> (<em>clear</em>, <em>n open</em>, or <em>master</em>), due date (red if overdue), and hours. Row actions: <strong>issue</strong> (when release-ready), <strong>view</strong>, and <strong>edit</strong>. Filter with the search box and the status / discipline dropdowns.</p>
|
||
<div class="ui-help-callout">Split <strong>masters</strong> are labelled and excluded from the counts; you issue their instances one at a time as each becomes release-ready.</div>` },
|
||
|
||
{ id: 'data', title: 'Samples, sharing & comments', body: `
|
||
<h3>Samples, import / export & comments</h3>
|
||
<h4>Load sample</h4>
|
||
<p><strong>⭐ Load sample</strong> is context-aware: on the SOP tab it loads a complete sample SOP; on the WP tab it loads an example Work Package. Great for learning the tool or demoing.</p>
|
||
<h4>Import / Export</h4>
|
||
<ul>
|
||
<li><strong>Work Packages</strong> — <em>⤓ Export (JSON)</em> downloads all saved packages; import restores them.</li>
|
||
<li><strong>SOP</strong> — the Creator can import a SOP <code>.json</code> (via <em>⤒ Import SOP</em>) or load the sample SOP.</li>
|
||
<li><strong>Materials</strong> — import a bill of materials from Excel/CSV, or download a template.</li>
|
||
</ul>
|
||
<h4>Comments & feedback</h4>
|
||
<p>Leave feedback from the home page, per-step comments in the SOP tool (<strong>💬 Step Comments</strong>), or package comments in the Creator's <strong>💬 Comments</strong> drawer. Comments are saved and can be exported/imported as <code>.json</code> so reviewers can share them — and, when the API is reachable, they're collected centrally too.</p>
|
||
<h4>Usage logs</h4>
|
||
<p><strong>📊 Usage Logs</strong> / <strong>▤ Usage Data</strong> shows session and event counts and can export the full log. A <strong>dev-mode</strong> toggle pauses tracking during demos.</p>` },
|
||
|
||
{ id: 'shortcuts', title: 'Tips & shortcuts', body: `
|
||
<h3>Tips & keyboard shortcuts</h3>
|
||
<ul>
|
||
<li><strong>Enter</strong> in a sequence, constraint, or material input adds/saves that row.</li>
|
||
<li><strong>Esc</strong> closes any modal — this help center, comments, the constraint library, and the hold-log dialog.</li>
|
||
<li>Hover any <span class="help-tip" data-tip="Like this one — hover any ⓘ for a hint.">i</span> icon for an inline hint.</li>
|
||
<li>Data is kept <strong>per project</strong> — switch projects from the home page.</li>
|
||
<li>Your work <strong>auto-saves</strong> to the browser as you type; <em>Save & View</em> produces the print-ready output.</li>
|
||
<li>Click a metric card or status chip on the <strong>Dashboard</strong> to filter the table.</li>
|
||
</ul>` },
|
||
|
||
{ id: 'glossary', title: 'Glossary', body: `
|
||
<h3>Glossary</h3>
|
||
<table>
|
||
<tr><th>Term</th><th>Meaning</th></tr>
|
||
<tr><td><strong>IWP</strong></td><td>Installation Work Package — the field-level package this tool produces.</td></tr>
|
||
<tr><td><strong>AWP</strong></td><td>Advanced Work Packaging — the methodology behind the constraint set and release gate.</td></tr>
|
||
<tr><td><strong>SOP</strong></td><td>Standard Operating Procedure — the project baseline every WP inherits.</td></tr>
|
||
<tr><td><strong>Constraint</strong></td><td>A readiness item (Open / Cleared / N/A) that gates release.</td></tr>
|
||
<tr><td><strong>Release-ready</strong></td><td>All constraints Cleared or N/A — the package can be Issued.</td></tr>
|
||
<tr><td><strong>Issued</strong></td><td>Released to the field.</td></tr>
|
||
<tr><td><strong>Issue (Hold)</strong></td><td>A released package paused because a constraint reopened.</td></tr>
|
||
<tr><td><strong>Discipline</strong></td><td>A trade (Mechanical, Electrical, Tech, …).</td></tr>
|
||
<tr><td><strong>Split / Master / Instance</strong></td><td>Breaking a multi-discipline package (master/roll-up) into single-discipline instances (WP01A/B/C).</td></tr>
|
||
<tr><td><strong>Scope</strong></td><td>The sequenced steps the crew performs.</td></tr>
|
||
<tr><td><strong>Sequence</strong></td><td>SOP-defined construction phases; a WP can name a predecessor step.</td></tr>
|
||
<tr><td><strong>Bagged & tagged</strong></td><td>Materials on site, kitted, and labelled — part of the Materials constraint.</td></tr>
|
||
<tr><td><strong>MIMO</strong></td><td>Material In / Material Out — kitting and staging logistics.</td></tr>
|
||
<tr><td><strong>Asset</strong></td><td>A controls.dev record (equipment/system) a package is built around.</td></tr>
|
||
<tr><td><strong>Hold / Witness point</strong></td><td>Hold = work stops until inspection sign-off; Witness = inspection offered but work may proceed.</td></tr>
|
||
<tr><td><strong>Active project</strong></td><td>The currently selected project; all data is scoped to it.</td></tr>
|
||
</table>` },
|
||
|
||
{ id: 'faq', title: 'FAQ', body: `
|
||
<h3>Frequently asked questions</h3>
|
||
<h4>The Work Package Creator is locked — why?</h4>
|
||
<p>The SOP for the active project isn't complete yet. Finish SOP Configuration and click <strong>✓ SOP complete</strong> on the last step; the Creator unlocks and the home card turns green.</p>
|
||
<h4>Why can't I set a package to Issued?</h4>
|
||
<p>At least one constraint is still <strong>Open</strong>. Clear or mark N/A every constraint — the release banner turns green — and the suite will offer to issue it.</p>
|
||
<h4>My package's materials didn't all carry over when I split it.</h4>
|
||
<p>Only material rows <strong>tagged to a discipline</strong> are routed to that instance. Untagged rows stay on the master. Tag them before splitting.</p>
|
||
<h4>Why don't split masters show in the dashboard totals?</h4>
|
||
<p>Masters are roll-ups; counting them would double-count their hours and packages. The individual instances are counted instead.</p>
|
||
<h4>Will I lose my work if I close the browser?</h4>
|
||
<p>No — drafts auto-save locally per project and reload next time. Use <em>Export (JSON)</em> for a backup or to share with a teammate.</p>
|
||
<h4>Does each project keep its own data?</h4>
|
||
<p>Yes. SOP and Work Packages are scoped to the active project; switching projects loads that project's own set.</p>
|
||
<h4>How do I report a problem or suggestion?</h4>
|
||
<p>Use the feedback / comments features (home page, SOP <em>Step Comments</em>, or the Creator's <em>Comments</em> drawer).</p>` }
|
||
];
|
||
|
||
// ── build ─────────────────────────────────────────────────────────────────
|
||
function buildModal() {
|
||
if (document.getElementById('ui-help-overlay')) return;
|
||
|
||
var overlay = document.createElement('div');
|
||
overlay.className = 'ui-help-overlay';
|
||
overlay.id = 'ui-help-overlay';
|
||
|
||
var nav = TOPICS.map(function (t) {
|
||
return '<a data-help-target="' + t.id + '">' + t.title + '</a>';
|
||
}).join('');
|
||
|
||
var sections = TOPICS.map(function (t) {
|
||
return '<section class="ui-help-sec" id="ui-help-sec-' + t.id + '">' + t.body + '</section>';
|
||
}).join('');
|
||
|
||
overlay.innerHTML =
|
||
'<div class="ui-help-modal" role="dialog" aria-modal="true" aria-label="Help center">' +
|
||
'<div class="ui-help-head">' +
|
||
'<span class="ui-help-title">Help — Work Package Suite</span>' +
|
||
'<span class="ui-help-search"><input id="ui-help-q" type="search" placeholder="Search help…" aria-label="Search help"></span>' +
|
||
'<button type="button" class="ui-help-x" onclick="closeHelp()" aria-label="Close help">✕</button>' +
|
||
'</div>' +
|
||
'<div class="ui-help-wrap">' +
|
||
'<nav class="ui-help-nav" id="ui-help-nav">' + nav + '</nav>' +
|
||
'<div class="ui-help-content" id="ui-help-content">' +
|
||
'<p class="ui-help-noresult" id="ui-help-noresult">No matches. Try another word.</p>' +
|
||
sections +
|
||
'</div>' +
|
||
'</div>' +
|
||
'</div>';
|
||
|
||
overlay.addEventListener('click', function (e) { if (e.target === overlay) closeHelp(); });
|
||
document.body.appendChild(overlay);
|
||
|
||
// nav clicks + in-content cross-links jump to a section
|
||
overlay.addEventListener('click', function (e) {
|
||
var t = e.target.closest('[data-help-target],[data-help-jump]');
|
||
if (!t) return;
|
||
e.preventDefault();
|
||
jumpTo(t.getAttribute('data-help-target') || t.getAttribute('data-help-jump'));
|
||
});
|
||
|
||
// search
|
||
var q = overlay.querySelector('#ui-help-q');
|
||
q.addEventListener('input', function () { runSearch(q.value); });
|
||
|
||
// highlight nav as you scroll
|
||
var content = overlay.querySelector('#ui-help-content');
|
||
content.addEventListener('scroll', syncActiveNav, { passive: true });
|
||
}
|
||
|
||
function jumpTo(id) {
|
||
var sec = document.getElementById('ui-help-sec-' + id);
|
||
if (!sec) return;
|
||
// Clear any active search filter so the target is visible.
|
||
var q = document.getElementById('ui-help-q');
|
||
if (q && q.value) { q.value = ''; runSearch(''); }
|
||
sec.scrollIntoView({ block: 'start' });
|
||
setActiveNav(id);
|
||
}
|
||
|
||
function setActiveNav(id) {
|
||
var nav = document.getElementById('ui-help-nav');
|
||
if (!nav) return;
|
||
nav.querySelectorAll('a').forEach(function (a) {
|
||
a.classList.toggle('active', a.getAttribute('data-help-target') === id);
|
||
});
|
||
}
|
||
|
||
function syncActiveNav() {
|
||
var content = document.getElementById('ui-help-content');
|
||
if (!content) return;
|
||
var top = content.scrollTop, best = null, bestDist = Infinity;
|
||
TOPICS.forEach(function (t) {
|
||
var sec = document.getElementById('ui-help-sec-' + t.id);
|
||
if (!sec || sec.classList.contains('hide')) return;
|
||
var d = Math.abs(sec.offsetTop - top);
|
||
if (sec.offsetTop - top <= 40 && d < bestDist) { bestDist = d; best = t.id; }
|
||
});
|
||
if (best) setActiveNav(best);
|
||
}
|
||
|
||
// ── search: filter sections + highlight matches ───────────────────────────
|
||
function clearMarks(root) {
|
||
root.querySelectorAll('mark').forEach(function (m) {
|
||
var txt = document.createTextNode(m.textContent);
|
||
m.parentNode.replaceChild(txt, m);
|
||
});
|
||
root.normalize();
|
||
}
|
||
|
||
function markMatches(el, query) {
|
||
var lower = query.toLowerCase();
|
||
var walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
|
||
acceptNode: function (node) {
|
||
if (!node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
|
||
var p = node.parentNode.nodeName;
|
||
if (p === 'MARK' || p === 'STYLE' || p === 'SCRIPT') return NodeFilter.FILTER_REJECT;
|
||
return node.nodeValue.toLowerCase().indexOf(lower) >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
||
}
|
||
});
|
||
var nodes = [], n;
|
||
while ((n = walker.nextNode())) nodes.push(n);
|
||
nodes.forEach(function (node) {
|
||
var val = node.nodeValue, low = val.toLowerCase(), frag = document.createDocumentFragment(), i = 0, idx;
|
||
while ((idx = low.indexOf(lower, i)) >= 0) {
|
||
if (idx > i) frag.appendChild(document.createTextNode(val.slice(i, idx)));
|
||
var mk = document.createElement('mark');
|
||
mk.textContent = val.slice(idx, idx + query.length);
|
||
frag.appendChild(mk);
|
||
i = idx + query.length;
|
||
}
|
||
if (i < val.length) frag.appendChild(document.createTextNode(val.slice(i)));
|
||
node.parentNode.replaceChild(frag, node);
|
||
});
|
||
}
|
||
|
||
function runSearch(query) {
|
||
var content = document.getElementById('ui-help-content');
|
||
var nav = document.getElementById('ui-help-nav');
|
||
var noresult = document.getElementById('ui-help-noresult');
|
||
if (!content) return;
|
||
query = (query || '').trim();
|
||
var hits = 0;
|
||
|
||
TOPICS.forEach(function (t) {
|
||
var sec = document.getElementById('ui-help-sec-' + t.id);
|
||
var navItem = nav.querySelector('[data-help-target="' + t.id + '"]');
|
||
clearMarks(sec);
|
||
var match = !query || sec.textContent.toLowerCase().indexOf(query.toLowerCase()) >= 0;
|
||
sec.classList.toggle('hide', !match);
|
||
if (navItem) navItem.classList.toggle('nohit', !!query && !match);
|
||
if (match) {
|
||
hits++;
|
||
if (query) markMatches(sec, query);
|
||
}
|
||
});
|
||
|
||
noresult.style.display = (query && hits === 0) ? 'block' : 'none';
|
||
if (query) { content.scrollTop = 0; }
|
||
else { syncActiveNav(); }
|
||
}
|
||
|
||
// ── public API ──────────────────────────────────────────────────────────────
|
||
global.openHelp = function (topicId) {
|
||
buildModal();
|
||
document.getElementById('ui-help-overlay').classList.add('open');
|
||
var q = document.getElementById('ui-help-q');
|
||
if (topicId && typeof topicId === 'string') jumpTo(topicId);
|
||
else { setActiveNav(TOPICS[0].id); if (q) setTimeout(function () { q.focus(); }, 30); }
|
||
};
|
||
global.closeHelp = function () {
|
||
var o = document.getElementById('ui-help-overlay');
|
||
if (o) o.classList.remove('open');
|
||
};
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape') global.closeHelp();
|
||
});
|
||
|
||
// ── floating launcher on pages without their own Help button ────────────────
|
||
function maybeAddFab() {
|
||
// Both tool pages set WP_HELP_NO_FAB and carry a Help button in the app bar
|
||
// instead. The creator used to be covered by an iframe test rather than the
|
||
// flag, which stopped working the moment it became a page (B7/T7.1) - so it
|
||
// sets the flag now, and says so, rather than relying on where it is rendered.
|
||
if (global.WP_HELP_NO_FAB) return;
|
||
if (document.querySelector('[onclick*="openHelp"]')) return; // page already has a Help trigger
|
||
if (document.getElementById('ui-help-fab')) return;
|
||
var b = document.createElement('button');
|
||
b.id = 'ui-help-fab'; b.className = 'ui-help-fab'; b.type = 'button';
|
||
b.title = 'Help'; b.setAttribute('aria-label', 'Open help'); b.textContent = '?';
|
||
b.addEventListener('click', function () { global.openHelp(); });
|
||
document.body.appendChild(b);
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', maybeAddFab);
|
||
else maybeAddFab();
|
||
})(window);
|