Add BIM/VDC work packages, platform links, and AWP traceability
SOP tool: - "Load BIM / VDC template" (Step 4): one click sets BIM deliverable types, install-phase disciplines, BIM release-gate constraints (with EN06 citations), the BIM process sequence, reference sources, BIM sign-off roles, and MWP##-[Area]-[PHASE] numbering. Tags the SOP mode as 'bim'. - Step 3 required sign-off role titles are now editable (default Superintendent/Foreman); the BIM template sets them to BIM Coordinator and Construction Lead (CRS). - Step 7: capture a project-homepage link for the chosen tracking and commissioning platforms. Work Package Creator: - BIM mode (SOP.mode==='bim'): hides controls.dev Assets, Material List, and Kitting/MIMO; shows a BIM Details card (LOD, model area, clash / coordination status, linked scan). - Project tracking/commissioning links are copied onto every WP and shown in the WP output. - "Enabled by - BIM package(s)" field links a field IWP back to the BIM package that enabled it (EWP -> IWP traceability). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ function onSizePresetChange(){
|
||||
}
|
||||
|
||||
let state = {
|
||||
mode: 'standard', // 'standard' (field IWP) | 'bim' (BIM/VDC EWP) — set by the BIM template
|
||||
project: {name:'', number:'', client:'', division:'', site:''},
|
||||
team: {pm:'', apm:'', cm:'', qm:''},
|
||||
teamMembers: [],
|
||||
@@ -31,7 +32,7 @@ let state = {
|
||||
wpTypes: [],
|
||||
governance: {woformat:'', wosize:'', issuance:[], disciplines:['Mechanical','Electrical','Tech'], discMode:'choice', instanceSuffix:'letter', sizeHoursMax:''},
|
||||
quality: {qcreq:'', photo:'', hold:''},
|
||||
platforms: {tracking:'CxAlloy', commissioning:'CxAlloy'},
|
||||
platforms: {tracking:'CxAlloy', commissioning:'CxAlloy', trackingUrl:'', commissioningUrl:''},
|
||||
sequence: [],
|
||||
constraints: [],
|
||||
sources: []
|
||||
@@ -92,7 +93,14 @@ const OPTIONAL_ROLES = [
|
||||
'Quality Representative',
|
||||
'Planner',
|
||||
'Safety Manager',
|
||||
'Project Controls Manager'
|
||||
'Project Controls Manager',
|
||||
// BIM / VDC roles (used by the BIM template; also selectable on any SOP)
|
||||
'BIM Coordinator',
|
||||
'BIM Modeler / Detailer',
|
||||
'VDC Manager',
|
||||
'Construction Lead (CRS)',
|
||||
'Field Lead',
|
||||
'General Contractor'
|
||||
];
|
||||
|
||||
const LABOR_COST_CODES = [
|
||||
@@ -137,6 +145,101 @@ const LABOR_COST_CODES = [
|
||||
'9000|Administration'
|
||||
];
|
||||
|
||||
// ── 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
|
||||
// remains editable afterward.
|
||||
const BIM_WP_TYPES = [
|
||||
'Model Area Package', 'Conduit Routing Package', 'Coordination / Clash Package',
|
||||
'First-of-Kind (FOK) Package', '2D Installation Sheet Set', 'Field Detailing / Markup Package',
|
||||
'Laser Scan Package', 'As-Built Model / Drawings'
|
||||
];
|
||||
// BIM "disciplines" are the install phases work is broken into (EN06-SOP §4.1.2.5).
|
||||
const BIM_PHASES = [
|
||||
'Cable Tray & Hangers', 'Conduits & Hangers', 'Wall & Slab Penetrations',
|
||||
'Panels & Instrument Racks', 'In-wall Instruments & Stud-ups'
|
||||
];
|
||||
const BIM_TEMPLATE_ROLES = [
|
||||
'BIM Coordinator', 'BIM Modeler / Detailer', 'VDC Manager',
|
||||
'Construction Lead (CRS)', 'Field Lead', 'General Contractor'
|
||||
];
|
||||
// Release-gate constraints for a BIM/model package (the BIM equivalent of the field's
|
||||
// AWP constraints). Citations point back to the EN06 documents.
|
||||
const BIM_CONSTRAINTS = [
|
||||
{name:'Required Docs Received (IO list, P&IDs, drawings, models, specs)', description:'Project-start inputs available — EN06-SOP §3.1'},
|
||||
{name:'Conduit Schedule & Schematic Redlines Received', description:'Hard gate: no conduit modeled without these — EN06-SOP §3'},
|
||||
{name:'LOD Defined & Agreed', description:'Level of detail set at kick-off — EN06-G-01'},
|
||||
{name:'Field Coordination / Laser Scan Complete', description:'Field walk or scan done — EN06-WI-01 / WI-03'},
|
||||
{name:'Clash-Free / Coordinated with GC & Trades', description:'Coordination complete — EN06-SOP §8.1'},
|
||||
{name:'Constructability Review (CRS) Signed', description:'Internal construction-lead sign-off before GC — EN06-SOP §8.4'},
|
||||
{name:'GC / Trade Sign-Off', description:'GC review and approval — EN06-SOP §8.2'},
|
||||
{name:'Issued-For-Fabrication (IFF) Granted', description:'Model approved for field use — EN06-SOP §9.4'}
|
||||
];
|
||||
const BIM_SEQUENCE = [
|
||||
'Project start — gather required docs', 'Kick-off (LOD, schedule, cost code)',
|
||||
'Field coordination / laser scan', 'Model racks, instruments & panels',
|
||||
'Model conduit (after schedule + redlines)', 'BIM coordination / clash with GC & trades',
|
||||
'Constructability review (CRS)', 'GC submission & sign-off (IFF)',
|
||||
'2D installation sheets', 'As-built (scan / redlines)'
|
||||
];
|
||||
const BIM_SOURCES = [
|
||||
{label:'IO List (Point Matrix DB)', ph:'controls.dev / SharePoint'},
|
||||
{label:'P&IDs', ph:'Procore / SharePoint'},
|
||||
{label:'Contract / Design Drawings', ph:'Procore / Bluebeam'},
|
||||
{label:'Navisworks / Revit Models', ph:'BIM360 / SharePoint'},
|
||||
{label:'Specs & Submittals', ph:'client portal'},
|
||||
{label:'Conduit Schedule', ph:'Excel on SharePoint'},
|
||||
{label:'Bluebeam Project', ph:'Bluebeam Studio'},
|
||||
{label:'Pre-Construction Tracker', ph:'SharePoint'},
|
||||
{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:''}));
|
||||
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}));
|
||||
_constraintsSeeded = true;
|
||||
renderStandardConstraints();
|
||||
|
||||
// Step 8 — BIM process sequence
|
||||
state.sequence = BIM_SEQUENCE.map(s => ({label:s, kind:'step'}));
|
||||
renderSequenceSteps();
|
||||
|
||||
// Step 10 — reference sources
|
||||
state.sources = BIM_SOURCES.map(s => ({label:s.label, system:'', notes:'', link:'', ph:s.ph, preset: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.');
|
||||
}
|
||||
|
||||
// ── INITIALIZATION ────────────────────────────────────────────────────────────
|
||||
window.addEventListener('DOMContentLoaded',()=>{
|
||||
initializeWPTypes();
|
||||
@@ -204,7 +307,12 @@ function loadSampleData(){
|
||||
document.getElementById('proj_cm').value = 'K. Boyd';
|
||||
document.getElementById('proj_qm').value = 'D. Nguyen';
|
||||
|
||||
// Step 3 already has defaults
|
||||
// Step 3 — standard required roles
|
||||
state.mode = 'standard';
|
||||
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';
|
||||
const ftEl = document.getElementById('role_foreman_title'); if(ftEl) ftEl.value = 'Foreman';
|
||||
document.getElementById('role_super_name').value = 'John Smith';
|
||||
document.getElementById('role_foreman_name').value = 'Mike Jones';
|
||||
|
||||
@@ -272,8 +380,8 @@ function repopulateForm(){
|
||||
set('proj_apm', state.team.apm);
|
||||
set('proj_cm', state.team.cm);
|
||||
set('proj_qm', state.team.qm);
|
||||
if(state.signoffRoles[0]) set('role_super_name', state.signoffRoles[0].name);
|
||||
if(state.signoffRoles[1]) set('role_foreman_name', state.signoffRoles[1].name);
|
||||
if(state.signoffRoles[0]){ set('role_super_title', state.signoffRoles[0].role); set('role_super_name', state.signoffRoles[0].name); }
|
||||
if(state.signoffRoles[1]){ set('role_foreman_title', state.signoffRoles[1].role); set('role_foreman_name', state.signoffRoles[1].name); }
|
||||
set('gov_woformat', state.governance.woformat);
|
||||
// gov_wosize is now a <select>; if a saved value isn't one of the presets
|
||||
// (e.g. legacy free text), add it as an option so the round-trip preserves it.
|
||||
@@ -290,6 +398,8 @@ function repopulateForm(){
|
||||
set('qual_hold', state.quality.hold);
|
||||
set('plat_tracking', state.platforms.tracking);
|
||||
set('plat_commissioning', state.platforms.commissioning);
|
||||
set('plat_tracking_url', state.platforms.trackingUrl);
|
||||
set('plat_commissioning_url', state.platforms.commissioningUrl);
|
||||
}
|
||||
|
||||
// ── TOOL SWITCHING ────────────────────────────────────────────────────────────
|
||||
@@ -412,8 +522,9 @@ function renderWPTypes(){
|
||||
container.appendChild(row);
|
||||
});
|
||||
const addRow = document.createElement('div');
|
||||
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>`;
|
||||
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>`;
|
||||
container.appendChild(addRow);
|
||||
}
|
||||
|
||||
@@ -461,7 +572,8 @@ function removeTeamMember(i){
|
||||
|
||||
function renderOptionalRoles(){
|
||||
const container = document.getElementById('optional-roles-list');
|
||||
const current = state.signoffRoles.filter(r=>r.role!=='Superintendent'&&r.role!=='Foreman');
|
||||
// The first two entries are the required (editable-title) roles; the rest are optional.
|
||||
const current = state.signoffRoles.slice(2);
|
||||
container.innerHTML = current.map((r,i)=>`
|
||||
<div style="display:grid; grid-template-columns:1fr 200px 30px; gap:1rem; align-items:center; padding:0.75rem; background:var(--bg); border-radius:6px; margin-bottom:0.5rem; border:1px solid var(--border);">
|
||||
<select onchange="state.signoffRoles[${state.signoffRoles.indexOf(r)}].role=this.value">
|
||||
@@ -732,7 +844,10 @@ function collectStepData(){
|
||||
state.team.qm = document.getElementById('proj_qm').value;
|
||||
break;
|
||||
case 3:
|
||||
// The two required roles now have editable titles (default Superintendent/Foreman).
|
||||
state.signoffRoles[0].role = (document.getElementById('role_super_title').value || 'Role 1').trim();
|
||||
state.signoffRoles[0].name = document.getElementById('role_super_name').value;
|
||||
state.signoffRoles[1].role = (document.getElementById('role_foreman_title').value || 'Role 2').trim();
|
||||
state.signoffRoles[1].name = document.getElementById('role_foreman_name').value;
|
||||
break;
|
||||
case 5:
|
||||
@@ -753,6 +868,8 @@ function collectStepData(){
|
||||
case 7:
|
||||
state.platforms.tracking = document.getElementById('plat_tracking').value;
|
||||
state.platforms.commissioning = document.getElementById('plat_commissioning').value;
|
||||
state.platforms.trackingUrl = (document.getElementById('plat_tracking_url').value || '').trim();
|
||||
state.platforms.commissioningUrl = (document.getElementById('plat_commissioning_url').value || '').trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -783,6 +900,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
|
||||
project: {
|
||||
name: state.project.name,
|
||||
number: state.project.number,
|
||||
@@ -813,8 +931,14 @@ function completeSOP(){
|
||||
approval: t.approval || ''
|
||||
})),
|
||||
sources: state.sources.filter(s=>s.label),
|
||||
field: {trackPlatform: state.platforms.tracking},
|
||||
commissioning: {tool: state.platforms.commissioning},
|
||||
field: {trackPlatform: state.platforms.tracking, trackPlatformUrl: state.platforms.trackingUrl || ''},
|
||||
commissioning: {tool: state.platforms.commissioning, toolUrl: state.platforms.commissioningUrl || ''},
|
||||
// Project homepage links in the tracking / commissioning systems. The Creator
|
||||
// copies these onto every Work Package created for this project.
|
||||
projectLinks: [
|
||||
state.platforms.trackingUrl ? {label:'Tracking — '+state.platforms.tracking, system:state.platforms.tracking, url:state.platforms.trackingUrl} : null,
|
||||
state.platforms.commissioningUrl ? {label:'Commissioning — '+state.platforms.commissioning, system:state.platforms.commissioning, url:state.platforms.commissioningUrl} : null
|
||||
].filter(Boolean),
|
||||
quality: {
|
||||
qcReq: state.quality.qcreq,
|
||||
photo: state.quality.photo,
|
||||
|
||||
@@ -130,19 +130,19 @@
|
||||
<!-- STEP 3: SIGN-OFF ROLES -->
|
||||
<div class="step" id="sop-step-3" style="display: none;">
|
||||
<h2>3. Required Sign-Off Roles</h2>
|
||||
<div class="notice">Superintendent and Foreman are required. Add other roles as needed for your project structure.</div>
|
||||
<div class="notice">Two roles are required on every package. They default to <strong>Superintendent</strong> and <strong>Foreman</strong> — rename either to fit your project (e.g. a BIM SOP uses <em>BIM Coordinator</em> and <em>Construction Lead</em>). Add more below.</div>
|
||||
<div class="required-roles">
|
||||
<div class="role-required">
|
||||
<div class="role-checkbox">
|
||||
<input type="checkbox" id="role_super" checked disabled>
|
||||
<label>Superintendent *</label>
|
||||
<input type="text" id="role_super_title" value="Superintendent" title="Required role title" style="font-weight:600; padding:0.4rem 0.5rem; border:1px solid var(--border); border-radius:4px; min-width:180px;"><span style="color:var(--danger); margin-left:4px;">*</span>
|
||||
</div>
|
||||
<input type="text" id="role_super_name" placeholder="Name (optional)" style="flex: 1; margin-left: 1rem;">
|
||||
</div>
|
||||
<div class="role-required">
|
||||
<div class="role-checkbox">
|
||||
<input type="checkbox" id="role_foreman" checked disabled>
|
||||
<label>Foreman *</label>
|
||||
<input type="text" id="role_foreman_title" value="Foreman" title="Required role title" style="font-weight:600; padding:0.4rem 0.5rem; border:1px solid var(--border); border-radius:4px; min-width:180px;"><span style="color:var(--danger); margin-left:4px;">*</span>
|
||||
</div>
|
||||
<input type="text" id="role_foreman_name" placeholder="Name (optional)" style="flex: 1; margin-left: 1rem;">
|
||||
</div>
|
||||
@@ -287,6 +287,16 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" style="margin-top:1rem;">
|
||||
<label>Tracking platform — project homepage link</label>
|
||||
<input type="url" id="plat_tracking_url" placeholder="Paste the project's URL in the tracking platform (e.g. its Procore / CxAlloy project home)">
|
||||
<small>Optional. Saved with every Work Package on this project for one-click access.</small>
|
||||
</div>
|
||||
<div class="field" style="margin-top:0.75rem;">
|
||||
<label>Commissioning tool — project homepage link</label>
|
||||
<input type="url" id="plat_commissioning_url" placeholder="Paste the project's URL in the commissioning tool">
|
||||
<small>Optional. Saved with every Work Package on this project for one-click access.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 8: SEQUENCE -->
|
||||
|
||||
@@ -92,12 +92,24 @@ function applySOP(){
|
||||
lockQuality('wp_qc'); lockQuality('wp_photo'); lockQuality('wp_hold');
|
||||
const hint=document.getElementById('wp_number_hint'); hint.textContent = SOP.governance&&SOP.governance.woFormat ? 'auto-built · format: '+SOP.governance.woFormat : '';
|
||||
buildConstraints(); buildSignoffs();
|
||||
applyBimMode();
|
||||
if(!pkgMaterials.length){ pkgMaterials=[{qty:'',unit:'',desc:''}]; buildMaterials(); }
|
||||
if(!pkgAttach.length){ pkgAttach=[{doc:'',rev:'',link:''}]; buildAttach(); }
|
||||
if(!pkgAssets.length){ pkgAssets=[{tag:'',desc:'',link:''}]; buildAssets(); }
|
||||
if(!pkgWorkSteps.length){ pkgWorkSteps=['']; buildWorkSteps(); }
|
||||
updateNumber(); updateReleaseBanner();
|
||||
}
|
||||
// In a BIM/VDC SOP, hide the field-only sections (controls.dev assets, materials,
|
||||
// kitting/MIMO) and reveal the BIM Details card. Driven by SOP.mode==='bim'.
|
||||
function isBimSOP(){ return !!(SOP && SOP.mode === 'bim'); }
|
||||
function applyBimMode(){
|
||||
const bim = isBimSOP();
|
||||
const show = (id, on) => { const el = document.getElementById(id); if(el) el.style.display = on ? '' : 'none'; };
|
||||
show('bim-card', bim);
|
||||
show('asset-card', !bim);
|
||||
show('material-card', !bim);
|
||||
show('mimo-card', !bim);
|
||||
}
|
||||
function buildCostCodes(){
|
||||
const sel=document.getElementById('wp_cost'); const cur=sel.value;
|
||||
sel.innerHTML=`<option value="">Select cost code…</option>`+COST_CODES.map(c=>{const [code,desc]=c.split('|'); return `<option value="${code}">${code} — ${esc(desc)}</option>`;}).join('');
|
||||
@@ -677,7 +689,14 @@ function collectPackage(){
|
||||
signoffs:pkgSignoffs.map(s=>({role:s.role,name:s.name,date:s.date,signed:s.signed,dateReason:s.dateReason||''})),
|
||||
holds:pkgHolds.map(h=>({...h})),
|
||||
actualHrs:gv('wp_actual_hrs'), installedQty:gv('wp_installed_qty'), redlines:gv('wp_redlines'), lessons:gv('wp_lessons'),
|
||||
// AWP traceability: which BIM/model package(s) enabled this install package.
|
||||
bimlink:gv('wp_bimlink'),
|
||||
// BIM/VDC package details (only meaningful on a BIM SOP).
|
||||
lod:gv('wp_lod'), modelArea:gv('wp_model_area'), clash:gv('wp_clash'), scanLink:gv('wp_scan_link'),
|
||||
project:(SOP&&SOP.project&&SOP.project.name)||'', track:(SOP&&SOP.field&&SOP.field.trackPlatform)||'',
|
||||
// Project homepage links in the tracking / commissioning systems, copied from the
|
||||
// SOP so they travel with every Work Package created for this project.
|
||||
projectLinks:(SOP&&SOP.projectLinks)?SOP.projectLinks.map(l=>({...l})):[],
|
||||
updatedAt:new Date().toISOString()
|
||||
};
|
||||
}
|
||||
@@ -696,6 +715,8 @@ function renderPackage(pkg){
|
||||
let h=`<h1>${esc(pkg.number||'(no number)')} — Work Package</h1>
|
||||
<div class="doc-subtitle">${esc(pkg.project)} · TYPE: ${esc(pkg.type).toUpperCase()} · STATUS: ${esc(pkg.status).toUpperCase()} · ${readyTxt}</div>`;
|
||||
const costDesc = (COST_CODES.find(c=>c.split('|')[0]===pkg.cost)||'').split('|')[1];
|
||||
// Project system links travel with the WP; fall back to the live SOP for older packages.
|
||||
const plinks = (pkg.projectLinks&&pkg.projectLinks.length) ? pkg.projectLinks : ((SOP&&SOP.projectLinks)||[]);
|
||||
h+=`<h2>1.0 General Information</h2><table><tbody>
|
||||
<tr><th style="width:200px">WP Number</th><td>${cell(pkg.number)}</td></tr>
|
||||
<tr><th>Subject</th><td>${cell(pkg.subject)}</td></tr>
|
||||
@@ -710,6 +731,9 @@ function renderPackage(pkg){
|
||||
<tr><th>Due Date</th><td>${cell(pkg.due)}</td></tr>
|
||||
<tr><th>Specification Section</th><td>${cell(pkg.spec)}</td></tr>
|
||||
<tr><th>Description</th><td>${cell(pkg.desc)}</td></tr>
|
||||
${plinks.length?`<tr><th>Project Systems</th><td>${plinks.map(l=>esc(l.label)+': '+linkify(l.url)).join('<br>')}</td></tr>`:''}
|
||||
${pkg.bimlink?`<tr><th>Enabled by (BIM)</th><td>${/^https?:\/\//i.test(pkg.bimlink)?linkify(pkg.bimlink):cell(pkg.bimlink)}</td></tr>`:''}
|
||||
${(pkg.lod||pkg.modelArea||pkg.clash||pkg.scanLink)?`<tr><th>BIM / Model</th><td>${[pkg.lod?'LOD: '+esc(pkg.lod):'', pkg.modelArea?'Area: '+esc(pkg.modelArea):'', pkg.clash?'Coordination: '+esc(pkg.clash):'', pkg.scanLink?'Scan: '+linkify(pkg.scanLink):''].filter(Boolean).join('<br>')}</td></tr>`:''}
|
||||
</tbody></table>`;
|
||||
if(pkg.assets&&pkg.assets.length){ h+=`<h2>2.0 Assets (controls.dev)</h2><table><thead><tr><th style="width:180px">Asset Tag / ID</th><th>Description</th><th>controls.dev Link</th></tr></thead><tbody>`;
|
||||
pkg.assets.forEach(a=>h+=`<tr><td>${cell(a.tag)}</td><td>${cell(a.desc)}</td><td>${a.link?linkify(a.link):ns()}</td></tr>`); h+=`</tbody></table>`; }
|
||||
@@ -857,6 +881,8 @@ function loadPackageIntoForm(p){
|
||||
set('wp_due',p.due); set('wp_spec',p.spec); set('wp_desc',p.desc); set('wp_hours',p.hours);
|
||||
set('wp_kit_owner',p.kitOwner); set('wp_kit_date',p.kitDate); set('wp_mimo_time',p.mimoTime); set('wp_mimo_loc',p.mimoLoc);
|
||||
set('wp_actual_hrs',p.actualHrs); set('wp_installed_qty',p.installedQty); set('wp_redlines',p.redlines); set('wp_lessons',p.lessons);
|
||||
set('wp_bimlink',p.bimlink); set('wp_lod',p.lod); set('wp_model_area',p.modelArea); set('wp_clash',p.clash); set('wp_scan_link',p.scanLink);
|
||||
applyBimMode();
|
||||
buildTypePicker(); document.getElementById('wp_type').value=p.type||'';
|
||||
buildCostCodes(); document.getElementById('wp_cost').value=p.cost||'';
|
||||
set('wp_wbs',p.wbs);
|
||||
@@ -923,8 +949,10 @@ function duplicateWP(){
|
||||
|
||||
function newPackage(){
|
||||
editingId=null;
|
||||
['wp_subject','wp_system','wp_location','wp_wbs','wp_assignees','wp_distribution','wp_due','wp_spec','wp_desc','wp_hours','wp_kit_owner','wp_kit_date','wp_mimo_time','wp_mimo_loc','wp_actual_hrs','wp_installed_qty','wp_redlines','wp_lessons'].forEach(id=>{const el=document.getElementById(id); if(el) el.value='';});
|
||||
['wp_subject','wp_system','wp_location','wp_wbs','wp_assignees','wp_distribution','wp_due','wp_spec','wp_desc','wp_hours','wp_kit_owner','wp_kit_date','wp_mimo_time','wp_mimo_loc','wp_actual_hrs','wp_installed_qty','wp_redlines','wp_lessons','wp_bimlink','wp_model_area','wp_scan_link'].forEach(id=>{const el=document.getElementById(id); if(el) el.value='';});
|
||||
document.getElementById('wp_type').value=''; document.getElementById('wp_kit_status').value=''; document.getElementById('wp_cost').value='';
|
||||
['wp_lod','wp_clash'].forEach(id=>{const el=document.getElementById(id); if(el) el.value='';});
|
||||
applyBimMode();
|
||||
setRadio('status','Draft');
|
||||
numberDims={}; buildNumberDims();
|
||||
pkgDisciplines=[]; pkgScope={}; pkgDiscStatus={}; buildDisciplinePicker(); renderScope(); onHoursChange();
|
||||
|
||||
@@ -80,10 +80,25 @@
|
||||
<div class="field"><label>Specification Section</label><input type="text" id="wp_spec" placeholder="e.g. 26_05_33_00 - Raceway and Boxes"><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"><div class="field"><label>Enabled by — BIM package(s)<span class="help-tip" data-tip="Advanced Work Packaging traceability: link the BIM / model package(s) that enabled this install package. Paste the MWP number(s) or a link to the model package.">i</span></label><input type="text" id="wp_bimlink" placeholder="e.g. MWP07-FAB-CONDUITS, or a link to the model package"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- BIM / MODEL DETAILS (shown for BIM/VDC SOPs) -->
|
||||
<div class="card" id="bim-card" style="display:none">
|
||||
<div class="sub-heading">BIM / Model Details</div>
|
||||
<div class="notice">For BIM/VDC work packages — the model deliverable's level of detail, area, source scan, and coordination status.</div>
|
||||
<div class="field-grid">
|
||||
<div class="field"><label>Level of Detail (LOD)</label>
|
||||
<select id="wp_lod"><option value="">—</option><option>LOD 100 — Conceptual</option><option>LOD 200 — Approximate</option><option>LOD 300 — Precise</option><option>LOD 350 — Precise + interfaces</option><option>LOD 400 — Fabrication</option><option>LOD 500 — As-built</option></select></div>
|
||||
<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"><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>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">
|
||||
<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>
|
||||
@@ -116,7 +131,7 @@
|
||||
</div>
|
||||
|
||||
<!-- MATERIAL LIST -->
|
||||
<div class="card">
|
||||
<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>
|
||||
@@ -147,7 +162,7 @@
|
||||
</div>
|
||||
|
||||
<!-- KITTING & MIMO -->
|
||||
<div class="card">
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user