diff --git a/html/wp-creation-app.js b/html/wp-creation-app.js
index be40b13..35d2b05 100644
--- a/html/wp-creation-app.js
+++ b/html/wp-creation-app.js
@@ -844,6 +844,7 @@ function setFormChrome(on){
if(save) save.style.display = on ? 'flex' : 'none';
document.body.classList.toggle('has-sticky-save', !!on);
if(on){ buildSectionNav(); updateStickyStatus(); makeCollapsible(); initSectionNavAutoHide(); }
+ else positionSectionNav();
}
// Make each form card collapsible by clicking its heading (idempotent).
function makeCollapsible(){
@@ -881,6 +882,9 @@ function buildSectionNav(){
function positionSectionNav(){
const nav=document.getElementById('section-nav'), hdr=document.querySelector('.header');
if(nav && hdr) nav.style.top = hdr.offsetHeight + 'px';
+ // The WP navigator rail sticks below the header + section-nav chrome.
+ const top=(hdr?hdr.offsetHeight:0)+((nav && nav.style.display!=='none')?nav.offsetHeight:0);
+ document.documentElement.style.setProperty('--rail-top', top+'px');
}
let _snLastY=0, _snBound=false;
function initSectionNavAutoHide(){
@@ -912,6 +916,7 @@ function saveStore(){ try{ localStorage.setItem(wpKey(STORE_KEY), JSON.stringify
function loadStore(){ try{ const d=JSON.parse(localStorage.getItem(wpKey(STORE_KEY))); savedPackages=Array.isArray(d)?d:[]; }catch(e){ savedPackages=[]; } }
function renderSavedList(){
const card=document.getElementById('saved-card'), body=document.getElementById('saved-body');
+ renderWpNav();
document.getElementById('saved-count').textContent=savedPackages.length?`(${savedPackages.length})`:'';
if(!savedPackages.length){ card.style.display='none'; return; }
if(document.getElementById('pkg-output').style.display==='none' || document.getElementById('pkg-output').style.display==='') card.style.display='';
@@ -926,6 +931,58 @@ function renderSavedList(){
}
function viewPackage(i){ if(savedPackages[i]) renderPackage(savedPackages[i]); }
+// ── WP NAVIGATOR (left rail) ─────────────────────────────────────────────────
+// Every saved package on this project, grouped by status, filterable. Clicking a
+// row opens it in the form (same path as the Saved table's "edit").
+const WPNAV_ORDER=['Issue','In Progress','Issued','QC','Scheduled','Draft','Closed'];
+function toggleWpNav(){
+ document.body.classList.toggle('wp-nav-collapsed');
+ try{ localStorage.setItem('wp_nav_collapsed', document.body.classList.contains('wp-nav-collapsed')?'1':''); }catch(e){}
+}
+function renderWpNav(){
+ const list=document.getElementById('wp-nav-list'); if(!list) return;
+ const cnt=document.getElementById('wp-nav-count');
+ if(cnt) cnt.textContent = savedPackages.length ? '('+savedPackages.length+')' : '';
+ const q=((document.getElementById('wp-nav-search')||{}).value||'').trim().toLowerCase();
+ // Keep the original index — edit/view act on savedPackages by position.
+ const rows=savedPackages.map((p,i)=>({p:p,i:i})).filter(r=>{
+ if(!q) return true;
+ const p=r.p;
+ return [p.number,p.subject,p.type,p.location,p.system].some(v=>String(v||'').toLowerCase().includes(q));
+ });
+ if(!rows.length){
+ list.innerHTML='
'+(savedPackages.length?'No packages match “'+esc(q)+'”.':'No work packages saved yet. Fill the form and Save Draft.')+'
';
+ return;
+ }
+ const groups={};
+ rows.forEach(r=>{ const s=r.p.status||'Draft'; (groups[s]=groups[s]||[]).push(r); });
+ const keys=Object.keys(groups).sort((a,b)=>{
+ const ia=WPNAV_ORDER.indexOf(a), ib=WPNAV_ORDER.indexOf(b);
+ return (ia<0?99:ia)-(ib<0?99:ib);
+ });
+ list.innerHTML=keys.map(k=>{
+ const label = k==='Issue' ? 'Issue (Hold)' : k;
+ return ''+esc(label)+' · '+groups[k].length+'
'+groups[k].map(r=>{
+ const p=r.p, open=(p.constraints||[]).filter(c=>c.status==='open').length;
+ const dot = p.status==='Issue' ? 'hold' : (open===0 ? 'ok' : 'open');
+ const state = p.status==='Issue' ? 'on hold' : (open===0 ? 'ready' : open+' open');
+ const active = (editingId && p.id===editingId) ? ' active' : '';
+ return '';
+ }).join('');
+ }).join('');
+}
+function wpNavOpen(i){
+ const p=savedPackages[i]; if(!p) return;
+ editingId=p.id;
+ loadPackageIntoForm(p); // ends in showForm(), so this also leaves the dashboard / package view
+ renderWpNav();
+ track('wp_nav_open');
+}
+
// ── WP HISTORY (audit trail) ─────────────────────────────────────────────────
function showHistoryRow(i){ const p=savedPackages[i]; if(p) showHistory(p.id, p.number||p.subject); }
function showHistoryCurrent(){ showHistory(editingId, (document.getElementById('wp_number')||{}).value); }
@@ -960,7 +1017,7 @@ async function showHistory(wpId, label){
}
function deletePackage(i){ const p=savedPackages[i]; if(!p) return; if(!confirm('Delete work package "'+(p.number||p.subject||'untitled')+'"? This cannot be undone.')) return; const delId=p.id; savedPackages.splice(i,1); saveStore(); renderSavedList(); track('package_deleted'); if(typeof ProjectData!=='undefined' && ProjectData.removeWP) ProjectData.removeWP(delId); }
function clearSaved(){ if(!savedPackages.length) return; if(!confirm('Delete all '+savedPackages.length+' saved packages?')) return; const ids=savedPackages.map(p=>p.id); savedPackages=[]; saveStore(); renderSavedList(); if(typeof ProjectData!=='undefined' && ProjectData.removeWP) ids.forEach(id=>ProjectData.removeWP(id)); }
-function editPackage(i){ const p=savedPackages[i]; if(!p) return; editingId=p.id; loadPackageIntoForm(p); }
+function editPackage(i){ const p=savedPackages[i]; if(!p) return; editingId=p.id; loadPackageIntoForm(p); renderWpNav(); }
function loadExample(){ loadPackageIntoForm(JSON.parse(JSON.stringify(EXAMPLE_PKG))); editingId=null; toast('Example work package loaded'); track('example_loaded'); }
function loadPackageIntoForm(p){
pkgKind = (p.kind === 'ewp') ? 'ewp' : 'iwp'; // set before type/constraint pickers so they filter correctly
@@ -1055,7 +1112,7 @@ function newPackage(){
pkgHolds=[]; pkgOverrides={};
set_quality_from_sop(); lockQuality('wp_qc'); lockQuality('wp_photo'); lockQuality('wp_hold');
prevStatus='Draft';
- updateNumber(); updateReleaseBanner(); showForm(); track('new_package');
+ updateNumber(); updateReleaseBanner(); showForm(); renderWpNav(); track('new_package');
}
function set_quality_from_sop(){ const set=(id,v)=>{const el=document.getElementById(id); if(el) el.value=v||'';}; set('wp_qc',sopValueFor('wp_qc')); set('wp_photo',sopValueFor('wp_photo')); set('wp_hold',sopValueFor('wp_hold')); }
function exportPackages(){
@@ -1367,7 +1424,9 @@ function bootData(){
bootSOP();
setRadio('status','Draft');
loadMembers();
+ try{ if(localStorage.getItem('wp_nav_collapsed')) document.body.classList.add('wp-nav-collapsed'); }catch(e){}
renderSavedList();
+ positionSectionNav();
cmtInit();
// Deep-link: open straight to the dashboard when requested (?view=dashboard or #dashboard).
const p=new URLSearchParams(location.search); if(p.get('view')==='dashboard' || location.hash==='#dashboard'){ showDashboard(); }
diff --git a/html/wp-creation-index.html b/html/wp-creation-index.html
index 4d102ad..0f14173 100644
--- a/html/wp-creation-index.html
+++ b/html/wp-creation-index.html
@@ -45,6 +45,23 @@
+
+
+
+
+
+
@@ -268,6 +285,7 @@
+
diff --git a/html/wp-creation-styles.css b/html/wp-creation-styles.css
index 5854117..b757730 100644
--- a/html/wp-creation-styles.css
+++ b/html/wp-creation-styles.css
@@ -99,8 +99,11 @@
.step-tab.done { color: var(--accent-green); background: var(--accent-green-dim); }
.step-num { display: block; font-size: 9px; opacity: .65; margin-bottom: 2px; }
- /* ── MAIN ── */
- .main { max-width: 1000px; margin: 0 auto; padding: 28px 32px 64px; }
+ /* ── MAIN ──
+ The form sits in a wide two-column shell: a sticky work-package navigator on
+ the left and the form itself filling the rest of the screen. */
+ .wp-layout { display: flex; align-items: flex-start; gap: 0; max-width: 1760px; margin: 0 auto; }
+ .main { flex: 1 1 auto; min-width: 0; max-width: none; margin: 0; padding: 28px 32px 64px; }
.section { display: none; }
.section.active { display: block; animation: fade .25s ease; }
@@ -126,6 +129,11 @@
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 18px; }
.field-grid.col3 { grid-template-columns: 1fr 1fr 1fr; }
.field-grid.col1 { grid-template-columns: 1fr; }
+ /* On a wide screen let the two-up grids flow into 3–4 columns instead of
+ stretching two fields across the whole card. */
+ @media (min-width: 1200px) {
+ .field-grid:not(.col1):not(.col3) { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
+ }
.field { display: flex; flex-direction: column; gap: 6px; }
.field.span2 { grid-column: span 2; }
@@ -427,7 +435,7 @@
border-radius:var(--radius); padding:7px 10px; font-size:11px; }
/* ── CREATION TOOL ───────────────────────────────────────────────── */
- .ctx-bar { max-width:1080px; margin:0 auto; padding:12px 28px; display:flex; align-items:center; gap:20px;
+ .ctx-bar { max-width:1760px; margin:0 auto; padding:12px 28px; display:flex; align-items:center; gap:20px;
border-bottom:1px solid var(--border); background:var(--surface); flex-wrap:wrap; }
.ctx-empty { color:var(--text-muted); font-size:13px; }
.ctx-main .ctx-proj { font-weight:700; color:var(--text); font-size:14px; }
@@ -439,7 +447,7 @@
.ctx-meta code { background:var(--surface2); padding:1px 6px; border-radius:3px; color:var(--accent); }
.link-btn { background:none; border:none; color:var(--accent); cursor:pointer; font-size:inherit; padding:0; text-decoration:underline; }
- .mode-wrap { max-width:1080px; margin:0 auto; padding:16px 28px 0; display:flex; align-items:center; gap:16px; }
+ .mode-wrap { max-width:1760px; margin:0 auto; padding:16px 28px 0; display:flex; align-items:center; gap:16px; }
.mode-toggle { display:inline-flex; border:1px solid var(--border-strong); border-radius:6px; overflow:hidden; }
.mode-btn { padding:8px 18px; font-family:var(--sans); font-size:13px; font-weight:600; border:none; background:var(--surface);
color:var(--text-muted); cursor:pointer; }
@@ -463,7 +471,7 @@
/* ── WORK PACKAGE FORM ───────────────────────────────────────────── */
.sop-hint { color:var(--accent) !important; }
- .release-banner { max-width:1080px; margin:0 auto; padding:0 28px; }
+ .release-banner { max-width:1760px; margin:0 auto; padding:0 28px; }
.release-banner .rb-inner { margin-top:14px; border-radius:var(--radius); padding:11px 16px; font-size:13px; font-weight:600;
display:flex; align-items:center; gap:10px; }
.rb-ready { background:var(--accent-green-dim); color:var(--accent-green); border:1px solid #b6e3c6; }
@@ -580,6 +588,49 @@
border:1px solid var(--border); border-radius:14px; padding:4px 11px; cursor:pointer; white-space:nowrap; }
.sec-chip:hover{ border-color:var(--accent); color:var(--accent); }
+ /* ── WP NAVIGATOR (left rail) ─────────────────────────────────────────
+ Sticky list of every work package on the project. Click one to open it in
+ the form; the one being edited is highlighted. */
+ .wp-nav { flex:0 0 262px; width:262px; align-self:flex-start; position:sticky; top:var(--rail-top,0px);
+ height:calc(100vh - var(--rail-top,0px)); display:flex; flex-direction:column;
+ background:var(--surface); border-right:1px solid var(--border); }
+ .wp-nav-head { display:flex; align-items:center; gap:8px; padding:14px 14px 8px; }
+ .wp-nav-title { font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-muted); }
+ .wp-nav-count { color:var(--text-dim); font-weight:600; letter-spacing:0; }
+ .wp-nav-collapse, .wp-nav-reopen { background:transparent; border:1px solid var(--border); border-radius:4px;
+ color:var(--text-muted); cursor:pointer; font-size:14px; line-height:1; padding:2px 7px; margin-left:auto; }
+ .wp-nav-collapse:hover, .wp-nav-reopen:hover { border-color:var(--accent); color:var(--accent); }
+ .wp-nav-search { margin:0 14px 10px; padding:6px 9px; font-size:12px; font-family:inherit;
+ border:1px solid var(--border); border-radius:4px; background:var(--bg); color:var(--text); }
+ .wp-nav-search:focus { outline:none; border-color:var(--accent); }
+ .wp-nav-list { flex:1 1 auto; overflow-y:auto; padding:0 8px 8px; }
+ .wp-nav-group { font-size:10px; font-weight:700; letter-spacing:.09em; text-transform:uppercase;
+ color:var(--text-dim); padding:10px 6px 5px; }
+ .wp-nav-item { display:block; width:100%; text-align:left; background:transparent; border:0;
+ border-left:3px solid transparent; border-radius:4px; padding:6px 8px; cursor:pointer;
+ font-family:inherit; color:var(--text); }
+ .wp-nav-item:hover { background:var(--surface2); }
+ .wp-nav-item.active { background:var(--accent-dim); border-left-color:var(--accent); }
+ .wp-nav-num { display:block; font-family:var(--mono); font-size:11px; font-weight:600; color:var(--accent); }
+ .wp-nav-subj { display:block; font-size:12px; color:var(--text-muted); line-height:1.35;
+ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
+ .wp-nav-meta { display:flex; align-items:center; gap:6px; margin-top:3px; font-size:10px; color:var(--text-dim); }
+ .wp-nav-dot { width:7px; height:7px; border-radius:50%; background:var(--text-dim); flex:0 0 auto; }
+ .wp-nav-dot.ok { background:var(--accent-green); }
+ .wp-nav-dot.open { background:var(--accent-amber); }
+ .wp-nav-dot.hold { background:var(--red); }
+ .wp-nav-empty { padding:12px 8px; font-size:12px; color:var(--text-dim); }
+ .wp-nav-foot { border-top:1px solid var(--border); padding:9px 12px; display:flex; gap:8px; flex-wrap:wrap; }
+ .wp-nav-reopen { display:none; position:sticky; top:var(--rail-top,0px); margin:10px 0 0 8px; align-self:flex-start; z-index:20; }
+ /* Keep the rail's footer clear of the fixed save bar. */
+ body.has-sticky-save .wp-nav { height:calc(100vh - var(--rail-top,0px) - 56px); }
+ body.wp-nav-collapsed .wp-nav { display:none; }
+ body.wp-nav-collapsed .wp-nav-reopen { display:block; }
+ @media (max-width: 1100px) {
+ .wp-nav { display:none; }
+ .wp-nav-reopen { display:none !important; }
+ }
+
/* Sticky save bar */
.sticky-save{ position:fixed; left:0; right:0; bottom:0; z-index:40; display:flex; align-items:center;
justify-content:space-between; gap:14px; padding:10px 20px; background:#fff;