BIM as a per-package kind; one project flows BIM -> construction

Replace the whole-project BIM 'mode' with an opt-in capability + a
per-package kind, so a single project can produce both model and
install packages (and install-only projects are unaffected).

SOP tool:
- Step 4 "Include BIM / VDC work packages" checkbox (state.bimEnabled).
  Enabling adds BIM package types + BIM release gates (flagged bim) plus
  BIM roles/sources/process steps alongside the construction defaults;
  disabling strips the bim-flagged items.
- Generated SOP carries bimEnabled and a per-type / per-constraint bim flag.
- Required sign-off role titles stay editable (no longer force-renamed).

Work Package Creator:
- Shows a Package Type selector (Install IWP / BIM EWP) only when the
  SOP has bimEnabled; kind is saved per package and labeled in the output.
- WP types and release gates are filtered by kind (BIM types+gates for
  EWP, install types+gates for IWP).
- EWP reveals the BIM Details card and hides controls.dev Assets /
  Materials / Kitting-MIMO; IWP shows those plus the "Enabled by - BIM
  package" traceability link.

Supersedes the earlier whole-project BIM mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 17:07:08 -07:00
parent 566ace9969
commit 0e698438a2
4 changed files with 107 additions and 64 deletions

View File

@@ -24,7 +24,7 @@ function onSizePresetChange(){
}
let state = {
mode: 'standard', // 'standard' (field IWP) | 'bim' (BIM/VDC EWP) — set by the BIM template
bimEnabled: false, // does this project also produce BIM/VDC packages? If so the Creator tags each WP Install (IWP) or BIM (EWP); if not, it's IWP-only.
project: {name:'', number:'', client:'', division:'', site:''},
team: {pm:'', apm:'', cm:'', qm:''},
teamMembers: [],
@@ -148,8 +148,9 @@ const LABOR_COST_CODES = [
// ── BIM / VDC TEMPLATE ──────────────────────────────────────────────────────────
// The BIM/VDC department also produces work packages under Advanced Work Packaging —
// model & engineering deliverables (EWPs) that feed the field's install packages.
// These defaults are drawn from the EN06 SOP + work instructions and are applied by
// loadBIMTemplate() (the "Load BIM / VDC template" button on Step 4). Everything
// These defaults are drawn from the EN06 SOP + work instructions and are added by
// enableBIM() when the "Include BIM / VDC work packages" box is ticked on Step 4
// (each is flagged bim so the Creator can offer them under the EWP kind). Everything
// remains editable afterward.
const BIM_WP_TYPES = [
'Model Area Package', 'Conduit Routing Package', 'Coordination / Clash Package',
@@ -196,48 +197,43 @@ const BIM_SOURCES = [
{label:'Constructability Review Sheet (CRS)', ph:'SharePoint'}
];
function loadBIMTemplate(){
if(!confirm('Load the BIM / VDC template?\n\nThis sets the WP types, release-gate constraints, sign-off roles, construction sequence, reference sources, disciplines (install phases) and numbering format to BIM/VDC defaults from the EN06 SOPs. Your project details (Step 1) and team (Step 2) are left alone, and you can edit everything afterward.')) return;
state.mode = 'bim'; // flags the Creator to show BIM fields / hide field-only sections
// Step 4 — deliverable package types
state.wpTypes = BIM_WP_TYPES.map(n => ({name:n, enabled:true, notes:'', approval:''}));
// Toggle BIM/VDC capability on the project. ON augments the SOP with BIM package
// types + release gates (flagged bim) plus BIM roles/sources/sequence steps, so the
// project produces both install (IWP) and BIM (EWP) packages. OFF strips the
// bim-flagged items. Everything stays editable.
function setBimEnabled(on){
state.bimEnabled = !!on;
if(on) enableBIM(); else disableBIM();
const cb = document.getElementById('bim_enabled'); if(cb) cb.checked = !!on;
track(on ? 'bim_enabled' : 'bim_disabled');
}
function enableBIM(){
// Package types (flagged bim so the Creator can offer them under "BIM (EWP)").
BIM_WP_TYPES.forEach(n => {
const t = state.wpTypes.find(x => x.name === n);
if(t){ t.bim = true; t.enabled = true; }
else state.wpTypes.push({name:n, enabled:true, notes:'', approval:'', bim:true});
});
renderWPTypes();
// Step 5 — disciplines become install phases; multi-discipline; model numbering
state.governance.disciplines = BIM_PHASES.slice();
state.governance.discMode = 'multi';
state.governance.woformat = 'MWP##-[Area]-[PHASE]';
const setVal = (id, v) => { const el = document.getElementById(id); if(el) el.value = v; };
setVal('gov_disciplines', BIM_PHASES.join(', '));
setVal('gov_discmode', 'multi');
setVal('gov_woformat', 'MWP##-[Area]-[PHASE]');
// Step 3 — set the two required roles to BIM titles, then add the rest as optional
state.signoffRoles[0] = {role:'BIM Coordinator', name: (state.signoffRoles[0] && state.signoffRoles[0].name) || ''};
state.signoffRoles[1] = {role:'Construction Lead (CRS)', name: (state.signoffRoles[1] && state.signoffRoles[1].name) || ''};
setVal('role_super_title', 'BIM Coordinator');
setVal('role_foreman_title', 'Construction Lead (CRS)');
BIM_TEMPLATE_ROLES.filter(r => r !== 'BIM Coordinator' && r !== 'Construction Lead (CRS)')
.forEach(r => { if(!state.signoffRoles.some(x => x.role === r)) state.signoffRoles.push({role:r, name:''}); });
renderOptionalRoles();
// Step 9 — BIM release-gate constraints (replaces the field's standard 10)
state.constraints = BIM_CONSTRAINTS.map(c => ({...c}));
// Release-gate constraints (seed standard 10 first if empty, then add BIM gates).
if(!state.constraints || !state.constraints.length) state.constraints = STANDARD_10_CONSTRAINTS.map(c => ({...c}));
_constraintsSeeded = true;
BIM_CONSTRAINTS.forEach(c => { if(!state.constraints.some(x => x.name === c.name)) state.constraints.push({...c, bim:true}); });
renderStandardConstraints();
// Step 8 — BIM process sequence
state.sequence = BIM_SEQUENCE.map(s => ({label:s, kind:'step'}));
// BIM sign-off roles (optional), reference sources, and process steps (idempotent).
BIM_TEMPLATE_ROLES.forEach(r => { if(!state.signoffRoles.some(x => x.role === r)) state.signoffRoles.push({role:r, name:'', bim:true}); });
renderOptionalRoles();
BIM_SEQUENCE.forEach(lbl => { if(!state.sequence.some(s => s.label === lbl)) state.sequence.push({label:lbl, kind:'step', bim:true}); });
renderSequenceSteps();
// Step 10 — reference sources
state.sources = BIM_SOURCES.map(s => ({label:s.label, system:'', notes:'', link:'', ph:s.ph, preset:true}));
BIM_SOURCES.forEach(s => { if(!state.sources.some(x => x.label === s.label)) state.sources.push({label:s.label, system:'', notes:'', link:'', ph:s.ph, preset:true, bim:true}); });
renderSources();
track('bim_template_loaded');
alert('BIM / VDC template loaded.\n\nReview Step 5 (disciplines are now the install phases; numbering = MWP##-[Area]-[PHASE]) and Step 3 (BIM Coordinator + Construction Lead were added; leave the built-in Superintendent/Foreman blank if not used). Adjust anything as needed, then finish the SOP.');
}
function disableBIM(){
state.wpTypes = state.wpTypes.filter(t => !t.bim); renderWPTypes();
state.constraints = (state.constraints || []).filter(c => !c.bim); renderStandardConstraints();
state.signoffRoles = state.signoffRoles.filter((r,i) => i < 2 || !r.bim); renderOptionalRoles();
state.sequence = (state.sequence || []).filter(s => !s.bim); renderSequenceSteps();
state.sources = (state.sources || []).filter(s => !s.bim); renderSources();
}
// ── INITIALIZATION ────────────────────────────────────────────────────────────
@@ -307,8 +303,9 @@ function loadSampleData(){
document.getElementById('proj_cm').value = 'K. Boyd';
document.getElementById('proj_qm').value = 'D. Nguyen';
// Step 3 — standard required roles
state.mode = 'standard';
// Step 3 — standard required roles (sample is an install-only project)
state.bimEnabled = false;
const beEl = document.getElementById('bim_enabled'); if(beEl) beEl.checked = false;
if(state.signoffRoles[0]) state.signoffRoles[0].role = 'Superintendent';
if(state.signoffRoles[1]) state.signoffRoles[1].role = 'Foreman';
const stEl = document.getElementById('role_super_title'); if(stEl) stEl.value = 'Superintendent';
@@ -400,6 +397,7 @@ function repopulateForm(){
set('plat_commissioning', state.platforms.commissioning);
set('plat_tracking_url', state.platforms.trackingUrl);
set('plat_commissioning_url', state.platforms.commissioningUrl);
const beEl = document.getElementById('bim_enabled'); if(beEl) beEl.checked = !!state.bimEnabled;
}
// ── TOOL SWITCHING ────────────────────────────────────────────────────────────
@@ -522,9 +520,8 @@ function renderWPTypes(){
container.appendChild(row);
});
const addRow = document.createElement('div');
addRow.style.cssText = 'margin-top:0.85rem; display:flex; gap:0.5rem; flex-wrap:wrap;';
addRow.innerHTML = `<button onclick="addCustomWPType()" style="background:var(--primary,#0f62fe); color:#fff; border:none; padding:0.55rem 1rem; border-radius:4px; font-weight:600; cursor:pointer; font-size:13px;">+ Add Custom Type</button>
<button onclick="loadBIMTemplate()" title="Configure this SOP for the BIM / VDC team's model & engineering work packages" style="background:var(--bg); color:var(--text); border:1px solid var(--border); padding:0.55rem 1rem; border-radius:4px; font-weight:600; cursor:pointer; font-size:13px;">Load BIM / VDC template</button>`;
addRow.style.cssText = 'margin-top:0.85rem;';
addRow.innerHTML = `<button onclick="addCustomWPType()" style="background:var(--primary,#0f62fe); color:#fff; border:none; padding:0.55rem 1rem; border-radius:4px; font-weight:600; cursor:pointer; font-size:13px;">+ Add Custom Type</button>`;
container.appendChild(addRow);
}
@@ -900,7 +897,7 @@ function completeSOP(){
sop = {
meta: {tool:'Work Package Configuration', sample:false},
mode: state.mode || 'standard', // 'bim' hides field-only sections + shows BIM fields in the Creator
bimEnabled: !!state.bimEnabled, // project also produces BIM (EWP) packages → Creator offers per-package IWP/EWP kind
project: {
name: state.project.name,
number: state.project.number,
@@ -928,7 +925,8 @@ function completeSOP(){
name: t.name.trim(),
enabled: true,
notes: t.notes || '',
approval: t.approval || ''
approval: t.approval || '',
bim: !!t.bim
})),
sources: state.sources.filter(s=>s.label),
field: {trackPlatform: state.platforms.tracking, trackPlatformUrl: state.platforms.trackingUrl || ''},
@@ -950,7 +948,7 @@ function completeSOP(){
kind: s.kind || 'step'
})),
costCodes: LABOR_COST_CODES,
constraints: state.constraints.map(c=>({name: c.name, description: c.description || ''}))
constraints: state.constraints.map(c=>({name: c.name, description: c.description || '', bim: !!c.bim}))
};
sopComplete = true;