Menu/UX cleanup: cost codes, Duplicate WP, condensed toolbars, Dashboard tab
- Cost codes: drop everything after 4990 (material/quality/admin codes removed). - WP creator: add "Duplicate" (asks how many copies; each a clean Draft with unique number/subject, approvals & closeout cleared). When embedded the WP toolbar now shows only New + Duplicate. - Condense duplicated toolbars: the embedded creator's Usage Data, Load Example/ Sample SOP, Comments, View SOP and Dashboard buttons are hidden; the suite's top-right "Usage Logs" and "Load Sample" are the single instances. - Load Sample is context-aware: SOP tab loads the sample SOP, WP/Dashboard tab loads the example Work Package in the creator. - Dashboard moved to a nav-tab next to SOP Configuration / Work Package Creation. - Remove "Prime Controls" branding from the embedded WP menu. - Remove "Bill Clarida" from the example WP distribution and the step-comments name placeholder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -155,7 +155,7 @@ window.addEventListener('DOMContentLoaded',()=>{
|
|||||||
|
|
||||||
// Deep-link: ?tab=sop | ?tab=wp | ?view=dashboard from the home page.
|
// Deep-link: ?tab=sop | ?tab=wp | ?view=dashboard from the home page.
|
||||||
const tab = params.get('tab');
|
const tab = params.get('tab');
|
||||||
if(params.get('view') === 'dashboard') switchTool('wp');
|
if(params.get('view') === 'dashboard') switchTool('dashboard');
|
||||||
else if(tab === 'wp' || tab === 'sop') switchTool(tab);
|
else if(tab === 'wp' || tab === 'sop') switchTool(tab);
|
||||||
|
|
||||||
track('app_open');
|
track('app_open');
|
||||||
@@ -176,7 +176,15 @@ function initializeWPTypes(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── LOAD SAMPLE DATA ──────────────────────────────────────────────────────────
|
// ── LOAD SAMPLE DATA ──────────────────────────────────────────────────────────
|
||||||
|
// Context-aware: on the SOP tab it loads the sample SOP; on the WP / Dashboard
|
||||||
|
// tab it loads the example Work Package inside the embedded creator.
|
||||||
function loadSampleData(){
|
function loadSampleData(){
|
||||||
|
if(currentTool && currentTool !== 'sop'){
|
||||||
|
const f = document.getElementById('wp-frame');
|
||||||
|
if(f && f.contentWindow && typeof f.contentWindow.loadExample === 'function'){ f.contentWindow.loadExample(); }
|
||||||
|
else { alert('Open the Work Package Creation tab first, then load the sample.'); }
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Populate Step 1
|
// Populate Step 1
|
||||||
document.getElementById('proj_name').value = 'MICRON_PH1_CUP_HPM_FMCS INSTALL';
|
document.getElementById('proj_name').value = 'MICRON_PH1_CUP_HPM_FMCS INSTALL';
|
||||||
document.getElementById('proj_number').value = '26-67-008';
|
document.getElementById('proj_number').value = '26-67-008';
|
||||||
@@ -281,30 +289,32 @@ function repopulateForm(){
|
|||||||
// ── TOOL SWITCHING ────────────────────────────────────────────────────────────
|
// ── TOOL SWITCHING ────────────────────────────────────────────────────────────
|
||||||
function switchTool(tool){
|
function switchTool(tool){
|
||||||
currentTool = tool;
|
currentTool = tool;
|
||||||
|
// 'dashboard' is a pseudo-tab: it reuses the WP tool's content (the embedded
|
||||||
|
// creator) but opens it straight to the dashboard view.
|
||||||
|
const isDash = (tool === 'dashboard');
|
||||||
|
const contentTool = isDash ? 'wp' : tool;
|
||||||
|
|
||||||
// Update nav tabs
|
// Update nav tabs
|
||||||
document.querySelectorAll('.nav-tab').forEach(t=>t.classList.remove('active'));
|
document.querySelectorAll('.nav-tab').forEach(t=>t.classList.remove('active'));
|
||||||
document.querySelector(`[data-tab="${tool}"]`).classList.add('active');
|
const tabBtn = document.querySelector(`[data-tab="${tool}"]`);
|
||||||
|
if(tabBtn) tabBtn.classList.add('active');
|
||||||
|
|
||||||
// Update content
|
// Update content
|
||||||
document.querySelectorAll('.tool').forEach(t=>t.classList.remove('active'));
|
document.querySelectorAll('.tool').forEach(t=>t.classList.remove('active'));
|
||||||
document.getElementById(`tool-${tool}`).classList.add('active');
|
document.getElementById(`tool-${contentTool}`).classList.add('active');
|
||||||
|
|
||||||
// Reset step counter
|
|
||||||
if(tool === 'sop'){
|
|
||||||
document.getElementById('total-steps').textContent = '10';
|
|
||||||
}else{
|
|
||||||
document.getElementById('total-steps').textContent = '—';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tool === 'wp') renderWPTab();
|
// Reset step counter
|
||||||
|
document.getElementById('total-steps').textContent = (tool === 'sop') ? '10' : '—';
|
||||||
|
|
||||||
|
if(contentTool === 'wp') renderWPTab(isDash);
|
||||||
|
|
||||||
updateStepUI();
|
updateStepUI();
|
||||||
updateProjectDisplay();
|
updateProjectDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the gate or the embedded Work Package Creator depending on SOP status.
|
// Show the gate or the embedded Work Package Creator depending on SOP status.
|
||||||
function renderWPTab(){
|
// wantDash=true opens the creator straight to the dashboard view.
|
||||||
|
function renderWPTab(wantDash){
|
||||||
const gate = document.getElementById('wp-gate');
|
const gate = document.getElementById('wp-gate');
|
||||||
const frame = document.getElementById('wp-frame');
|
const frame = document.getElementById('wp-frame');
|
||||||
if(!gate || !frame) return;
|
if(!gate || !frame) return;
|
||||||
@@ -313,9 +323,9 @@ function renderWPTab(){
|
|||||||
frame.style.display = 'block';
|
frame.style.display = 'block';
|
||||||
// Reload each time so the creator picks up the latest SOP from localStorage.
|
// Reload each time so the creator picks up the latest SOP from localStorage.
|
||||||
const sp = new URLSearchParams(window.location.search);
|
const sp = new URLSearchParams(window.location.search);
|
||||||
const wantDash = sp.get('view') === 'dashboard';
|
const dash = wantDash || sp.get('view') === 'dashboard';
|
||||||
const projId = sp.get('project') || (activeProject && activeProject.id) || '';
|
const projId = sp.get('project') || (activeProject && activeProject.id) || '';
|
||||||
frame.src = 'wp-creation-index.html?embedded=1' + (wantDash ? '&view=dashboard' : '')
|
frame.src = 'wp-creation-index.html?embedded=1' + (dash ? '&view=dashboard' : '')
|
||||||
+ (projId ? '&project=' + encodeURIComponent(projId) : '') + '&t=' + Date.now();
|
+ (projId ? '&project=' + encodeURIComponent(projId) : '') + '&t=' + Date.now();
|
||||||
}else{
|
}else{
|
||||||
gate.style.display = 'block';
|
gate.style.display = 'block';
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="load-sample-btn" class="header-button" onclick="loadSampleData()" title="Load example SOP data">⭐ Load Sample</button>
|
<button id="load-sample-btn" class="header-button" onclick="loadSampleData()" title="Load sample data for the current tool (SOP or Work Package)">⭐ Load Sample</button>
|
||||||
<button class="header-button" onclick="toggleComments()" title="View and add comments for the current step">💬 Step Comments</button>
|
<button class="header-button" onclick="toggleComments()" title="View and add comments for the current step">💬 Step Comments</button>
|
||||||
<button class="header-button" onclick="showAnalytics()" title="Review usage logs for this tool">📊 Usage Logs</button>
|
<button class="header-button" onclick="showAnalytics()" title="Review usage logs for this tool">📊 Usage Logs</button>
|
||||||
<span class="step-counter"><span id="current-step">1</span> / <span id="total-steps">10</span></span>
|
<span class="step-counter"><span id="current-step">1</span> / <span id="total-steps">10</span></span>
|
||||||
@@ -37,6 +37,9 @@
|
|||||||
<button class="nav-tab" data-tab="wp" onclick="switchTool('wp')">
|
<button class="nav-tab" data-tab="wp" onclick="switchTool('wp')">
|
||||||
<span class="tab-icon">📋</span> Work Package Creation
|
<span class="tab-icon">📋</span> Work Package Creation
|
||||||
</button>
|
</button>
|
||||||
|
<button class="nav-tab" data-tab="dashboard" onclick="switchTool('dashboard')">
|
||||||
|
<span class="tab-icon">📊</span> Dashboard
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CONTENT AREA -->
|
<!-- CONTENT AREA -->
|
||||||
@@ -348,7 +351,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom: 1rem;">
|
<div style="margin-bottom: 1rem;">
|
||||||
<label style="font-weight: 600; font-size: 13px;">Your Name (optional)</label>
|
<label style="font-weight: 600; font-size: 13px;">Your Name (optional)</label>
|
||||||
<input type="text" id="commenter-name" placeholder="e.g., Bill Clarida" style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; margin-top: 0.25rem;">
|
<input type="text" id="commenter-name" placeholder="e.g., your name" style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; margin-top: 0.25rem;">
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom: 1rem;">
|
<div style="margin-bottom: 1rem;">
|
||||||
<label style="font-weight: 600; font-size: 13px;">Feedback</label>
|
<label style="font-weight: 600; font-size: 13px;">Feedback</label>
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ const STATUS_ORDER = ['Draft','Scheduled','Issued','In Progress','QC','Closed'];
|
|||||||
const ISSUED_IDX = STATUS_ORDER.indexOf('Issued');
|
const ISSUED_IDX = STATUS_ORDER.indexOf('Issued');
|
||||||
|
|
||||||
// Acumatica cost codes (comment 10) — code|description
|
// Acumatica cost codes (comment 10) — code|description
|
||||||
const COST_CODES = ['1000|Project Management','2000|Design and Development','2100|Design','2110|Control System Design','2120|Instrument Design','2130|Electrical Design','2140|Panel Design','2141|Panel Design Rework','2150|BIM','2151|BIM Rework','2160|Documentation','2200|Development','2210|PLC Programming','2220|OIT Programming','2230|SCADA Programming','2240|Simulation Development','2290|Programming Subcontract','2300|Customer Training','3000|Operational Technology','3100|OT Design','3200|Rack Assembly','3300|Network Configuration','3400|Computer Configuration','4000|Construction','4010|Instruments Install','4020|Network & Computers Install','4040|PLC Install','4050|Panel Install','4060|Electrical Install','4070|Mechanical Install','4080|Security Install','4090|Radio Install','4100|Commissioning','4940|Contract Labor','4960|Electrical Subcontract','4970|Mechanical Subcontract','4980|Security Subcontract','4990|Other/Radio Subcontract','5010|Instrument Material','5020|Network & Computers Material','5030|Software Material','5040|PLC Material','5050|Panel Material','5060|Electrical Material','5070|Mechanical Material','5080|Security Material','5090|Radio Material','6000|Production','7000|Quality','7100|Panel Quality Control','7200|Factory Acceptance Testing','7300|Site Acceptance Testing','8000|Safety','9000|Administration','9100|Warranty','9200|Freight','9300|Travel','9350|Jobsite Costs/Consumable/Other Direct Costs','9400|Contingency','9450|Other','9500|Accrued Incentive Compensation','9600|Sales Tax','9650|Job Cost Labor Burden','9700|Bonding','9800|Non-Billable Compensation'];
|
const COST_CODES = ['1000|Project Management','2000|Design and Development','2100|Design','2110|Control System Design','2120|Instrument Design','2130|Electrical Design','2140|Panel Design','2141|Panel Design Rework','2150|BIM','2151|BIM Rework','2160|Documentation','2200|Development','2210|PLC Programming','2220|OIT Programming','2230|SCADA Programming','2240|Simulation Development','2290|Programming Subcontract','2300|Customer Training','3000|Operational Technology','3100|OT Design','3200|Rack Assembly','3300|Network Configuration','3400|Computer Configuration','4000|Construction','4010|Instruments Install','4020|Network & Computers Install','4040|PLC Install','4050|Panel Install','4060|Electrical Install','4070|Mechanical Install','4080|Security Install','4090|Radio Install','4100|Commissioning','4940|Contract Labor','4960|Electrical Subcontract','4970|Mechanical Subcontract','4980|Security Subcontract','4990|Other/Radio Subcontract'];
|
||||||
// Acumatica allowed units of measure (comment 15) — common first
|
// Acumatica allowed units of measure (comment 15) — common first
|
||||||
const ACU_UNITS = ['EA','EACH','FT','M','METER','HR','DAYS','MINUTE','KG','LITER','CASE','LOT','LS','PK','PACK','PALLET','PIECE','BOTTLE','CAN'];
|
const ACU_UNITS = ['EA','EACH','FT','M','METER','HR','DAYS','MINUTE','KG','LITER','CASE','LOT','LS','PK','PACK','PALLET','PIECE','BOTTLE','CAN'];
|
||||||
|
|
||||||
// Example built work package (comment 4 / "Load Example") — WP02 export
|
// Example built work package (comment 4 / "Load Example") — WP02 export
|
||||||
const EXAMPLE_PKG = {"number":"WP02-1P-ELEC-Conduit","status":"Issue","subject":"FAB 1P Horn Strobe Conduit","type":"Conduit Install","system":"Chilled Water","location":"FAB / LVL 1 / Sect P","cost":"4060","wbs":"2001","assignees":"David Velazquez (Catapult Solutions Group), Jesus Casiano-Figueroa (Prime Controls)","distribution":"Bill Clarida (Prime Controls), Sean Tolley (Prime Controls), Jefferson Dufriend (Prime Controls)","due":"2026-06-18","spec":"26_05_33_31 - Conduit","desc":"1P Conduit run for horns and strobes","assets":[{"tag":"1P-HS-NORTH","desc":"1P North horn/strobe circuit","link":"https://controls.dev/assets/1P-HS-NORTH"}],"work":"Layout conduit route\nUsing ladders and MEWP install conduit and boxes with proper strapping, supports, and pull points according to 2D drawings and 3D model. Reference package drawings included in attachments.\nWhen complete initiate inspection with Prime QAQC","workSteps":["Layout conduit route","Using ladders and MEWP install conduit and boxes with proper strapping, supports, and pull points according to 2D drawings and 3D model. Reference package drawings included in attachments.","When complete initiate inspection with Prime QAQC"],"numberDims":{"Sector":"1P","Discipline":"ELEC"},"hours":"60","seq":"Layout","materials":[{"qty":"400","unit":"FT","desc":"3/4\" EMT"},{"qty":"300","unit":"FT","desc":"1\" EMT"},{"qty":"50","unit":"FT","desc":"7/8\" strut"},{"qty":"50","unit":"FT","desc":"1-5/8\" strut"},{"qty":"8","unit":"EA","desc":"4x4x4 NEMA 3 Box"},{"qty":"2","unit":"EA","desc":"1\" C-Type Conduit Body"},{"qty":"1","unit":"EA","desc":"1\" T-Type Conduit Body"},{"qty":"2","unit":"EA","desc":"3/4\" C-Type Conduit Body"},{"qty":"2","unit":"EA","desc":"3/4\" T-Type Conduit Body"},{"qty":"3","unit":"EA","desc":"3/4\" EMT LB & Cover"},{"qty":"6","unit":"EA","desc":"Hoffman F44GCPNK Horn Strobe Box"},{"qty":"2","unit":"EA","desc":"EMO 2x4 Box"},{"qty":"4","unit":"EA","desc":"1\" Bond Bushing w/ Lug"},{"qty":"4","unit":"EA","desc":"3/4\" Bond Bushing w/ Lug"},{"qty":"50","unit":"EA","desc":"1/4\"x3\" Toggle Bolt"},{"qty":"100","unit":"EA","desc":"Drywall Anchor"},{"qty":"5","unit":"EA","desc":"1\" to 3/4\" threaded reducer"}],"attachments":[{"doc":"EE-1YA-2P-5_ HPM PANEL CALLOUT 05","rev":"0","link":"https://us02.procore.com/webclients/host/companies/562949953431440/projects/562949954073428/tools/document-viewer/prostore/562950644886790"}],"kitStatus":"Open","kitOwner":"Ian Spielburg","kitDate":"2026-06-15","mimoTime":"2026-06-11T10:30","mimoLoc":"04","constraints":[{"name":"Safety & Permitting","status":"cleared","comment":""},{"name":"Quality Control / Inspection","status":"cleared","comment":""},{"name":"IFC Drawings & Specs","status":"cleared","comment":""},{"name":"Schedule","status":"cleared","comment":""},{"name":"Materials (on site, bagged & tagged)","status":"cleared","comment":""},{"name":"Prefabrication","status":"cleared","comment":""},{"name":"Work Access & Laydown","status":"cleared","comment":""},{"name":"Craft Availability","status":"cleared","comment":""},{"name":"Construction Equipment & Tools","status":"open","comment":"Boom lift not yet on site"},{"name":"Scaffolding / Access Equipment","status":"cleared","comment":""}],"qc":"Yes — Detailed inspection items","photo":"Key checkpoints only","hold":"HOLD: Prime QAQC to inspect rough-in before cover/cover-up. WITNESS: client QC to observe megger / insulation-resistance test before energization.","overrides":{"wp_photo":"Change Order"},"signoffs":[{"role":"Planner","name":"L. Graver","date":"2026-06-10","signed":true,"dateReason":""},{"role":"Superintendent","name":"K. Boyd","date":"2026-06-10","signed":true,"dateReason":""},{"role":"HSE Professional","name":"A. Reyes","date":"","signed":false,"dateReason":""},{"role":"Quality Representative","name":"D. Nguyen","date":"","signed":false,"dateReason":""},{"role":"Work Foreman","name":"M. Torres","date":"","signed":false,"dateReason":""}],"holds":[],"actualHrs":"54","installedQty":"200 ft","redlines":"Conduit size changed. need to update model","lessons":"Prepping trapeze hangers with conduit straps saved time","project":"Micron — INC Construction Work Packages","track":"CxAlloy"};
|
const EXAMPLE_PKG = {"number":"WP02-1P-ELEC-Conduit","status":"Issue","subject":"FAB 1P Horn Strobe Conduit","type":"Conduit Install","system":"Chilled Water","location":"FAB / LVL 1 / Sect P","cost":"4060","wbs":"2001","assignees":"David Velazquez (Catapult Solutions Group), Jesus Casiano-Figueroa (Prime Controls)","distribution":"Sean Tolley (Prime Controls), Jefferson Dufriend (Prime Controls)","due":"2026-06-18","spec":"26_05_33_31 - Conduit","desc":"1P Conduit run for horns and strobes","assets":[{"tag":"1P-HS-NORTH","desc":"1P North horn/strobe circuit","link":"https://controls.dev/assets/1P-HS-NORTH"}],"work":"Layout conduit route\nUsing ladders and MEWP install conduit and boxes with proper strapping, supports, and pull points according to 2D drawings and 3D model. Reference package drawings included in attachments.\nWhen complete initiate inspection with Prime QAQC","workSteps":["Layout conduit route","Using ladders and MEWP install conduit and boxes with proper strapping, supports, and pull points according to 2D drawings and 3D model. Reference package drawings included in attachments.","When complete initiate inspection with Prime QAQC"],"numberDims":{"Sector":"1P","Discipline":"ELEC"},"hours":"60","seq":"Layout","materials":[{"qty":"400","unit":"FT","desc":"3/4\" EMT"},{"qty":"300","unit":"FT","desc":"1\" EMT"},{"qty":"50","unit":"FT","desc":"7/8\" strut"},{"qty":"50","unit":"FT","desc":"1-5/8\" strut"},{"qty":"8","unit":"EA","desc":"4x4x4 NEMA 3 Box"},{"qty":"2","unit":"EA","desc":"1\" C-Type Conduit Body"},{"qty":"1","unit":"EA","desc":"1\" T-Type Conduit Body"},{"qty":"2","unit":"EA","desc":"3/4\" C-Type Conduit Body"},{"qty":"2","unit":"EA","desc":"3/4\" T-Type Conduit Body"},{"qty":"3","unit":"EA","desc":"3/4\" EMT LB & Cover"},{"qty":"6","unit":"EA","desc":"Hoffman F44GCPNK Horn Strobe Box"},{"qty":"2","unit":"EA","desc":"EMO 2x4 Box"},{"qty":"4","unit":"EA","desc":"1\" Bond Bushing w/ Lug"},{"qty":"4","unit":"EA","desc":"3/4\" Bond Bushing w/ Lug"},{"qty":"50","unit":"EA","desc":"1/4\"x3\" Toggle Bolt"},{"qty":"100","unit":"EA","desc":"Drywall Anchor"},{"qty":"5","unit":"EA","desc":"1\" to 3/4\" threaded reducer"}],"attachments":[{"doc":"EE-1YA-2P-5_ HPM PANEL CALLOUT 05","rev":"0","link":"https://us02.procore.com/webclients/host/companies/562949953431440/projects/562949954073428/tools/document-viewer/prostore/562950644886790"}],"kitStatus":"Open","kitOwner":"Ian Spielburg","kitDate":"2026-06-15","mimoTime":"2026-06-11T10:30","mimoLoc":"04","constraints":[{"name":"Safety & Permitting","status":"cleared","comment":""},{"name":"Quality Control / Inspection","status":"cleared","comment":""},{"name":"IFC Drawings & Specs","status":"cleared","comment":""},{"name":"Schedule","status":"cleared","comment":""},{"name":"Materials (on site, bagged & tagged)","status":"cleared","comment":""},{"name":"Prefabrication","status":"cleared","comment":""},{"name":"Work Access & Laydown","status":"cleared","comment":""},{"name":"Craft Availability","status":"cleared","comment":""},{"name":"Construction Equipment & Tools","status":"open","comment":"Boom lift not yet on site"},{"name":"Scaffolding / Access Equipment","status":"cleared","comment":""}],"qc":"Yes — Detailed inspection items","photo":"Key checkpoints only","hold":"HOLD: Prime QAQC to inspect rough-in before cover/cover-up. WITNESS: client QC to observe megger / insulation-resistance test before energization.","overrides":{"wp_photo":"Change Order"},"signoffs":[{"role":"Planner","name":"L. Graver","date":"2026-06-10","signed":true,"dateReason":""},{"role":"Superintendent","name":"K. Boyd","date":"2026-06-10","signed":true,"dateReason":""},{"role":"HSE Professional","name":"A. Reyes","date":"","signed":false,"dateReason":""},{"role":"Quality Representative","name":"D. Nguyen","date":"","signed":false,"dateReason":""},{"role":"Work Foreman","name":"M. Torres","date":"","signed":false,"dateReason":""}],"holds":[],"actualHrs":"54","installedQty":"200 ft","redlines":"Conduit size changed. need to update model","lessons":"Prepping trapeze hangers with conduit straps saved time","project":"Micron — INC Construction Work Packages","track":"CxAlloy"};
|
||||||
|
|
||||||
// ── STATE ────────────────────────────────────────────────────────────────────
|
// ── STATE ────────────────────────────────────────────────────────────────────
|
||||||
let SOP=null, editingId=null, numberDirty=false;
|
let SOP=null, editingId=null, numberDirty=false;
|
||||||
@@ -830,6 +830,36 @@ function renderConstraintRows(){ const tmp=pkgConstraints; pkgConstraints=[]; bu
|
|||||||
buildConstraints(); }
|
buildConstraints(); }
|
||||||
function renderSignoffRows(){ const tmp=pkgSignoffs; pkgSignoffs=[]; buildSignoffs(); tmp.forEach(s=>{ const c=pkgSignoffs.find(x=>x.role===s.role); if(c){ c.name=s.name; c.date=s.date; c.signed=s.signed; c.dateReason=s.dateReason||''; }}); buildSignoffs(); }
|
function renderSignoffRows(){ const tmp=pkgSignoffs; pkgSignoffs=[]; buildSignoffs(); tmp.forEach(s=>{ const c=pkgSignoffs.find(x=>x.role===s.role); if(c){ c.name=s.name; c.date=s.date; c.signed=s.signed; c.dateReason=s.dateReason||''; }}); buildSignoffs(); }
|
||||||
|
|
||||||
|
// Duplicate the current work package N times (asks how many). Each copy is a
|
||||||
|
// fresh Draft with a unique number/subject and approvals/closeout cleared.
|
||||||
|
function duplicateWP(){
|
||||||
|
if(!gv('wp_subject')){ alert('Open or fill in a work package first, then Duplicate.'); return; }
|
||||||
|
const ans=prompt('How many copies of this work package do you want to create?','1');
|
||||||
|
if(ans===null) return;
|
||||||
|
const n=parseInt(ans,10);
|
||||||
|
if(!n || n<1 || n>50){ alert('Enter a whole number between 1 and 50.'); return; }
|
||||||
|
const base=collectPackage();
|
||||||
|
const baseNum = base.number || ('WP'+pad2(editingSeq()));
|
||||||
|
const made=[];
|
||||||
|
for(let i=1;i<=n;i++){
|
||||||
|
const c=JSON.parse(JSON.stringify(base));
|
||||||
|
c.id='wp_'+Date.now().toString(36)+Math.random().toString(36).slice(2,5)+i;
|
||||||
|
c.number = baseNum + '-C' + i;
|
||||||
|
c.subject = base.subject + (n>1 ? ' (copy '+i+')' : ' (copy)');
|
||||||
|
c.status='Draft';
|
||||||
|
c.instanceOf=undefined; c.instanceLabel=undefined; c.parentNumber=undefined; c.split=undefined; c.children=undefined;
|
||||||
|
if(Array.isArray(c.signoffs)) c.signoffs=c.signoffs.map(s=>({...s, signed:false, date:'', dateReason:''}));
|
||||||
|
c.holds=[]; c.actualHrs=''; c.installedQty=''; c.redlines=''; c.lessons='';
|
||||||
|
c.projectId = base.projectId || activeProjectId || '';
|
||||||
|
c.updatedAt=new Date().toISOString();
|
||||||
|
savedPackages.push(c); made.push(c);
|
||||||
|
}
|
||||||
|
editingId=null; saveStore(); renderSavedList();
|
||||||
|
toast('Created '+n+' duplicate'+(n>1?'s':''));
|
||||||
|
track('wp_duplicated',{count:n});
|
||||||
|
alert('Created '+n+' duplicate'+(n>1?'s':'')+':\n\n• '+made.map(m=>m.number).join('\n• ')+'\n\nThey are in the Saved Work Packages list — edit each as needed.');
|
||||||
|
}
|
||||||
|
|
||||||
function newPackage(){
|
function newPackage(){
|
||||||
editingId=null;
|
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'].forEach(id=>{const el=document.getElementById(id); if(el) el.value='';});
|
||||||
|
|||||||
@@ -13,21 +13,22 @@
|
|||||||
<div class="loading-overlay" id="loadingOverlay"><div class="spinner"></div><div class="loading-text">Saving work package…</div></div>
|
<div class="loading-overlay" id="loadingOverlay"><div class="spinner"></div><div class="loading-text">Saving work package…</div></div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="logo-wrap">
|
<div class="logo-wrap embed-hide">
|
||||||
<div class="header-logo">Prime Controls</div>
|
<div class="header-logo">Prime Controls</div>
|
||||||
<button id="dev-toggle" class="dev-toggle" onclick="toggleDevMode()" title="dev mode" aria-label="dev mode"></button>
|
<button id="dev-toggle" class="dev-toggle" onclick="toggleDevMode()" title="dev mode" aria-label="dev mode"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-sep">|</div>
|
<div class="header-sep embed-hide">|</div>
|
||||||
<div class="header-title">Work Package (IWP)</div>
|
<div class="header-title">Work Package (IWP)</div>
|
||||||
<button class="btn btn-ghost embed-hide" style="margin-left:auto;padding:7px 16px" onclick="document.getElementById('sop-import').click()">⤒ Import SOP</button>
|
<button class="btn btn-ghost embed-hide" style="margin-left:auto;padding:7px 16px" onclick="document.getElementById('sop-import').click()">⤒ Import SOP</button>
|
||||||
<input type="file" id="sop-import" accept="application/json" style="display:none" onchange="importSOP(event)">
|
<input type="file" id="sop-import" accept="application/json" style="display:none" onchange="importSOP(event)">
|
||||||
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="loadSampleSOP()">⤓ Sample SOP</button>
|
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="loadSampleSOP()">⤓ Sample SOP</button>
|
||||||
<button class="btn btn-ghost embed-first" style="padding:7px 16px" onclick="openSopModal()">👁 View SOP</button>
|
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="openSopModal()">👁 View SOP</button>
|
||||||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="loadExample()">★ Load Example</button>
|
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="loadExample()">★ Load Example</button>
|
||||||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="showDashboard()">📊 Dashboard</button>
|
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="showDashboard()">📊 Dashboard</button>
|
||||||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="newPackage()">+ New</button>
|
<button class="btn btn-ghost embed-first" style="padding:7px 16px" onclick="newPackage()">+ New</button>
|
||||||
<button class="btn btn-ghost" id="comments-btn" style="padding:7px 16px" onclick="toggleComments()">💬 Comments <span class="cbadge-total" id="cbadge-total" style="display:none">0</span></button>
|
<button class="btn btn-ghost" style="padding:7px 16px" onclick="duplicateWP()">⧉ Duplicate</button>
|
||||||
<button class="btn btn-ghost" style="padding:7px 16px" onclick="showAnalytics()">▤ Usage Data</button>
|
<button class="btn btn-ghost embed-hide" id="comments-btn" style="padding:7px 16px" onclick="toggleComments()">💬 Comments <span class="cbadge-total" id="cbadge-total" style="display:none">0</span></button>
|
||||||
|
<button class="btn btn-ghost embed-hide" style="padding:7px 16px" onclick="showAnalytics()">▤ Usage Data</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dev-banner" id="dev-banner" style="display:none">⚙ DEV MODE — usage tracking paused. This session's actions are not being recorded.</div>
|
<div class="dev-banner" id="dev-banner" style="display:none">⚙ DEV MODE — usage tracking paused. This session's actions are not being recorded.</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user