251 buttons across the 7 pages, counted in the browser with every wizard step,
creator section and tool panel forced visible. Two thirds of them are
display:none at load, so a static grep sees about eighty and misses the rest.
FOUR ROLES, defined once in theme-light.css as --wp-btn-*, and no fifth:
primary the one action the screen exists for. Filled accent.
secondary every other real action. White, --border-strong, accent on hover.
tertiary navigating or undoing. No fill, no border, accent text.
danger destructive. Outlined red; filled red only where the control is too
small for an outline to read - the 28px x on a sequence row.
Every button class is mapped to a role in docs/reference/tokens.md section 12.
No value is new: these are the fills the sheets already rendered, given one
definition so that "primary" means one thing.
GREEN IS A STATUS COLOUR AND NO LONGER FILLS A BUTTON. A5 names two green action
buttons; there are four. .use-btn and the launcher's completed-SOP card button
never render green in the default fixture, so the review could not have seen
them - the SOP has to be finished and a suggested value has to be offered first.
.nav-btn.primary "SOP complete" wizard
.btn.btn-generate "Save & view" creator
.use-btn creator
.card.complete .card-button launcher
The green did not go anywhere. .cstatus button.on-cleared, .toggle-btn.enabled,
.wp-nav-dot.ok, .rb-ready, .badge-R and the launcher card's own left border and
status line all still carry it, and every one of those is a state rather than an
action. The launcher card in particular still says "complete" twice after this
change; it just no longer says it on the button.
SENTENCE CASE, applied to buttons and field labels only, which is the scope A5
sets. First word capitalised, the rest lowercased, acronyms and external proper
nouns left alone (SOP, QC, WP, UPN, PM/APM/CM/QM, PDF, JSON, CSV, BIM, MIMO,
Excel, Acumatica).
~30 button labels across launcher, wizard, creator, admin and two scripts
46 field labels
text-transform:uppercase removed from 4 rules - .btn and .add-btn (creator
buttons), label and .cmt-namebar label (creator field labels)
Labels carrying markup - a .req asterisk, a .help-tip chip - had only their text
nodes transformed, so the markup survives and "first word" means the first word
of the label rather than of each fragment. The creator's mono face, 10px size and
tracking are its idiom and are untouched; only the forced uppercase goes.
help.js was updated too. It names "Load Sample" and "SOP Complete" in prose, so
renaming the buttons without it would have left the help centre describing
controls that no longer exist. That coupling is the only place in the app where
button text is referenced by name.
Verified by re-running the inventory: 0 green action buttons, 0 uppercase button
labels, 251 buttons still present - nothing was lost in the rename.
console.css card headers are unchanged, confirmed by diff: the only six lines
this task touches in that file are token substitutions on button/button.primary/
button.danger, none of them within twenty lines of .card h2.
f_items 5 FIXED / F6 REPRODUCES. browser_check 71/71.
Left alone and logged: .step-tab is still uppercase (BL-015) - it is a stepper
tab, neither a button nor a field label, and A4/S9 rebuild the stepper. Table
headers, section eyebrows and headings keep their case throughout. BL-008 and
BL-009 were re-targeted from T3.5 to wave 9: both are colour merges on a field
fill and a status pill, and this task is scoped to buttons.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
482 lines
35 KiB
JavaScript
482 lines
35 KiB
JavaScript
/* Shared Help center + tooltip module for the Work Package Suite.
|
||
Included by the home page, the suite, and the embedded 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() {
|
||
var inIframe = (function () { try { return window.top !== window.self; } catch (e) { return true; } })();
|
||
if (inIframe || global.WP_HELP_NO_FAB) return; // suite shows the parent's button
|
||
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);
|