/* Al Hajar Construction — executive and role dashboards */
function DDonut({data,label='TOTAL'}){const sum=data.reduce((a,x)=>a+x.n,0)||1,R=42,C=2*Math.PI*R;let off=0;return <div className="row" style={{gap:16}}><svg viewBox="0 0 110 110" style={{width:120}}><circle cx="55" cy="55" r={R} fill="none" stroke="var(--bg)" strokeWidth="14"/>{data.map((d,i)=>{const n=d.n/sum*C,e=<circle key={i} cx="55" cy="55" r={R} fill="none" stroke={d.c} strokeWidth="14" strokeDasharray={`${n} ${C-n}`} strokeDashoffset={-off} transform="rotate(-90 55 55)"/>;off+=n;return e;})}<text x="55" y="54" textAnchor="middle" style={{fontSize:18,fontWeight:800,fill:'var(--ink)'}}>{sum}</text><text x="55" y="68" textAnchor="middle" style={{fontSize:8,fill:'var(--text-3)'}}>{label}</text></svg><div style={{flex:1}}>{data.map(d=><div className="row" key={d.k} style={{fontSize:11.5,marginBottom:6}}><i style={{width:8,height:8,borderRadius:3,background:d.c}}/><span style={{flex:1}}>{d.k}</span><b>{d.n}</b></div>)}</div></div>;}
function DHBars({data,value}){const max=Math.max(...data.map(x=>x.n),1);return <div>{data.map((d,i)=><div key={i} style={{marginBottom:10}}><div className="row" style={{fontSize:11.5,marginBottom:4}}><span style={{width:145,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{d.k}</span><div className="progress" style={{flex:1,height:13}}><i style={{width:d.n/max*100+'%',background:d.c||'var(--primary-600)'}}/></div><b className="mono" style={{minWidth:65,textAlign:'right'}}>{value?value(d):d.n}</b></div>{d.sub&&<div className="faint" style={{fontSize:10,marginLeft:145}}>{d.sub}</div>}</div>)}</div>;}
function DTrend({data,color='var(--primary-600)'}){const max=Math.max(...data.map(x=>x.n),1),pts=data.map((x,i)=>`${i/(Math.max(1,data.length-1))*100},${36-x.n/max*30}`).join(' ');return <div><svg viewBox="0 0 100 42" preserveAspectRatio="none" style={{height:110,width:'100%'}}><line x1="0" y1="36" x2="100" y2="36" stroke="var(--border)"/><polyline points={pts} fill="none" stroke={color} strokeWidth="2" vectorEffect="non-scaling-stroke"/>{data.map((x,i)=><circle key={i} cx={i/(Math.max(1,data.length-1))*100} cy={36-x.n/max*30} r="1.3" fill={color}/>)}</svg><div className="row" style={{justifyContent:'space-between'}}>{data.map(x=><small className="faint" key={x.k}>{x.k}</small>)}</div></div>;}
function CardChart({icon,title,children,right,onClick,footer,target,onNav}){const routes={'Project portfolio · planned vs actual':'projects','Revenue & profitability by project':'cost-control','HSE performance':'hse-incidents','Injury by body part':'body-map','HSE observations':'observations','Environmental performance':'environment','Quality performance':'ncr','Contracts overview':'contracts','Supply chain overview':'purchase-orders'},module=target||routes[title],go=onClick||((module&&onNav)?()=>onNav(module):null);return <div className={'card exec-card'+(go?' kpi-click':'')} style={go?{cursor:'pointer'}:null}><div className="card-head"><Icon name={icon} size={16} style={{color:'var(--primary-600)'}}/><div className="h-sec">{title}</div><span className="spacer"/>{right}</div><div className="card-body">{children}{module&&<div className="card-link" onClick={e=>{e.stopPropagation();(onNav?onNav(module):go&&go());}}>{footer||`View ${title.split(' · ')[0]}`} →</div>}</div></div>;}
function SparkBars({data}){const max=Math.max(...data,1);return <div className="kpi-spark">{data.map((n,i)=><i key={i} className={i===data.length-1?'last':''} style={{height:`${Math.max(8,n/max*100)}%`}}/>)}</div>;}
function ExecHero({brief,onNav}){const pulse=window.sparkFor('pulse'),max=Math.max(...pulse,1);return <section className="exec-hero"><div className="row" style={{alignItems:'flex-start',gap:22,flexWrap:'wrap'}}><div style={{flex:'1 1 620px'}}><div className="row"><img src="assets/aicos-mark.png" alt="AI-COS" style={{height:25}}/><span className="eyebrow" style={{color:'#b9c9e7'}}>AI-COS EXECUTIVE BRIEF</span><span className="row exec-live"><i className="pulse-dot"/>Computed live</span></div><div style={{fontSize:24,fontWeight:780,lineHeight:1.28,margin:'13px 0 16px',maxWidth:950}}>{brief.headline}</div><div className="wrap" style={{gap:8}}>{brief.points.map((x,i)=><button className="exec-chip" key={i} onClick={()=>onNav&&onNav(x.module)}><Icon name={x.icon} size={14}/>{x.text}<Icon name="chevR" size={12}/></button>)}</div></div><div style={{width:230}}><div className="eyebrow" style={{color:'#b9c9e7'}}>12-point business pulse</div><div className="kpi-spark" style={{height:70,marginTop:12}}>{pulse.map((n,i)=><i key={i} className={i===pulse.length-1?'last':''} style={{height:`${n/max*100}%`,background:i===pulse.length-1?'#a78bfa':'rgba(255,255,255,.34)'}}/>)}</div><div style={{fontSize:10,color:'#aebed8',marginTop:8}}>Governed from live ERP registers · role controls enforced · human review required</div></div></div></section>;}
function HealthMatrix({rows,onNav}){const counts={ok:rows.filter(x=>x.band==='ok').length,warn:rows.filter(x=>x.band==='warn').length,bad:rows.filter(x=>x.band==='bad').length},color=t=>t==='ok'?'var(--ok)':t==='warn'?'var(--warn)':'var(--bad)';return <div className="card card-pad" style={{marginBottom:16}}><div className="row" style={{marginBottom:12}}><div><div className="h-sec">Business health matrix</div><div className="faint" style={{fontSize:11}}>Cross-functional control score · live</div></div><span className="spacer"/><Badge kind={counts.bad?'bad':counts.warn?'warn':'ok'}>ALL SYSTEMS · {counts.ok} green · {counts.warn} amber · {counts.bad} red</Badge></div><div className="exec-health-grid">{rows.map(x=><div className="card card-pad health-card" key={x.domain} onClick={()=>onNav&&onNav(x.module)} style={{boxShadow:'none',padding:12}}><Ring value={x.score} size={72} stroke={7} color={color(x.band)}><b>{x.score}</b></Ring><b style={{display:'block',marginTop:7}}>{x.domain}</b><small className="faint" style={{display:'block',minHeight:48,lineHeight:1.3,marginTop:3}}>{x.issue}</small><div className="card-link">Open <Icon name="chevR" size={11}/></div></div>)}</div></div>;}
function ConstructionFilters({forcedProject}){const [,setTick]=useState(0),commit=patch=>{Object.assign(window.DASH,patch);try{localStorage.setItem('aicos-dash',JSON.stringify(window.DASH));}catch(e){}setTick(x=>x+1);window.__bump&&window.__bump();},presets=[['Today','Today'],['7D','Last 7 Days'],['MTD','MTD'],['QTD','QTD'],['YTD','YTD'],['12M','Last 12 Months'],['Custom','Custom']],governorates=['All governorates',...new Set(DB.projects.map(x=>x.governorate))];useEffect(()=>{if(forcedProject&&window.DASH.project!==forcedProject)commit({project:forcedProject});},[forcedProject]);return <div className="card" style={{padding:'10px 12px',marginBottom:16}}><div className="row" style={{gap:10,flexWrap:'wrap'}}><div className="seg">{presets.map(([s,v])=><button key={v} className={window.DASH.preset===v?'active':''} onClick={()=>commit({preset:v})}>{s}</button>)}</div>{window.DASH.preset==='Custom'&&<><input className="input" type="date" value={window.DASH.from||''} onChange={e=>commit({from:e.target.value})}/><input className="input" type="date" value={window.DASH.to||''} onChange={e=>commit({to:e.target.value})}/></>}<select className="input" value={forcedProject||window.DASH.project} disabled={!!forcedProject} onChange={e=>commit({project:e.target.value})} style={{width:215}}><option>All projects</option>{DB.projects.map(x=><option value={x.id} key={x.id}>{x.name}</option>)}</select><select className="input" value={window.DASH.governorate} onChange={e=>commit({governorate:e.target.value})} style={{width:180}}>{governorates.map(x=><option key={x}>{x}</option>)}</select><Btn size="sm" icon="refresh" onClick={()=>commit({})}>Refresh</Btn><Btn size="sm" onClick={()=>commit({preset:'YTD',from:null,to:null,project:forcedProject||'All projects',governorate:'All governorates'})}>Reset</Btn></div></div>;}
const bodyFallback={Head:[169,70],Eyes:[169,90],Shoulder:[120,170],Hand:[65,390],Fingers:[60,410],Back:[169,300],Leg:[145,680],Knee:[145,590],Foot:[145,800]};
function InjuryBodyMap({project}){const inj=window.injuryAgg({project,window:'12m'}),max=Math.max(...inj.rows.map(x=>x.n),1),anchor=x=>bodyFallback[x.part]||[169,420];return <div className="exec-body-map"><svg viewBox={window.BODY_FRONT_VB} preserveAspectRatio="xMidYMid meet"><g dangerouslySetInnerHTML={{__html:window.BODY_FRONT}}/>{inj.rows.map((x,i)=>{const p=anchor(x);return <circle key={i} cx={p[0]+(x.side==='R'?16:x.side==='L'?-16:0)} cy={p[1]} r={8+x.worst*2} fill={x.worst>=4?'#df463c':'#2f6fed'} stroke="#fff" strokeWidth="3"/>;})}</svg><div>{inj.rows.sort((a,b)=>b.n-a.n).map(x=><div className="exec-body-row" key={x.k}><span>{x.part}{x.side?` ${x.side}`:''}</span><div className="progress"><i style={{width:x.n/max*100+'%',background:x.worst>=4?'var(--bad)':'var(--primary-600)'}}/></div><b>{x.n}</b><small>{inj.total?Math.round(x.n/inj.total*100):0}%</small></div>)}</div></div>;}
function ExecutiveDashboard({onNav,forcedProject,scopeNote,mode='executive'}){const go=id=>onNav&&onNav(id),project=forcedProject||window.DASH.project||'All projects',gov=window.DASH.governorate||'All governorates',allowedProject=p=>(project==='All projects'||p.id===project)&&(gov==='All governorates'||p.governorate===gov),portfolio=window.portfolioSummary().filter(x=>allowedProject(DB.projectById(x.project))),projectIds=new Set(portfolio.map(x=>x.project)),within=d=>window.inRange(d),inc=DB.hseIncidents.filter(x=>projectIds.has(x.project)&&within(x.date)),obs=DB.hseObservations.filter(x=>projectIds.has(x.project)&&within(x.date)),ncrs=DB.ncrs.filter(x=>projectIds.has(x.project)&&within(x.date)),env=DB.envMetrics.filter(x=>projectIds.has(x.project)&&window.inRange(x.period+'-01')),ips=DB.invoices.filter(x=>projectIds.has(x.project)&&within(x.submittedDate)),costs=DB.projectCosts.filter(x=>projectIds.has(x.project)),contracts=DB.contracts.filter(x=>projectIds.has(x.project)),pos=DB.purchaseOrders.filter(x=>projectIds.has(x.project)&&within(x.orderDate)),mh=DB.manhours.filter(x=>projectIds.has(x.project)&&window.inRange(x.period+'-01')),manHours=mh.reduce((a,x)=>a+x.hours,0),lti=inc.filter(x=>x.type==='LTI').length,recordable=inc.filter(x=>x.recordable).length,lastLti=DB.hseIncidents.filter(x=>projectIds.has(x.project)&&x.type==='LTI').sort((a,b)=>new Date(b.date)-new Date(a.date))[0],hse={manHours,lti,mtc:inc.filter(x=>x.type==='MTC').length,fac:inc.filter(x=>x.type==='FAC').length,rta:inc.filter(x=>x.type==='RTA').length,recordable,trir:Number((recordable*200000/Math.max(1,manHours)).toFixed(2)),ltifr:Number((lti*1000000/Math.max(1,manHours)).toFixed(2)),ltiFreeDays:lastLti?Math.floor((DB.TODAY-new Date(lastLti.date))/DB.day):0},revenue=ips.filter(x=>['Certified','Paid'].includes(x.status)).reduce((a,x)=>a+x.netCertified,0),cost=costs.reduce((a,x)=>a+x.actual,0),margin=(revenue-cost)/Math.max(1,revenue)*100,contractValue=portfolio.reduce((a,x)=>a+x.value,0),active=portfolio.filter(x=>DB.projectById(x.project).status==='Active').length,months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],trend=months.map((k,i)=>({k,n:inc.filter(x=>new Date(x.date).getMonth()===i&&x.recordable).length})),safe=obs.filter(window.obsIsSafe).length,unsafe=obs.length-safe,categories=[...new Set(obs.map(x=>x.category))].map(k=>({k,n:obs.filter(x=>x.category===k).length})),envSum=k=>env.reduce((a,x)=>a+Number(x[k]||0),0),q={open:ncrs.filter(x=>x.status!=='Closed').length,closed:ncrs.filter(x=>x.status==='Closed').length,major:ncrs.filter(x=>x.severity==='Major').length,minor:ncrs.filter(x=>x.severity==='Minor').length},surveys=DB.customerSurveys.filter(x=>projectIds.has(x.project)),csat=surveys.reduce((a,x)=>a+x.score,0)/Math.max(1,surveys.length),complaints=DB.complaints.filter(x=>projectIds.has(x.project)&&x.status!=='Closed').length,retention=contracts.reduce((a,x)=>a+x.retentionHeld,0),milestones=contracts.flatMap(c=>c.milestones.map(m=>({...m,contract:c.id,project:c.project}))).filter(x=>new Date(x.date)>=DB.TODAY).sort((a,b)=>new Date(a.date)-new Date(b.date)).slice(0,5),inventory=DB.inventoryItems.filter(x=>!x.project||projectIds.has(x.project)).reduce((a,x)=>a+x.value,0),supplierOnTime=DB.suppliers.reduce((a,x)=>a+x.onTimePct,0)/DB.suppliers.length,refresh=()=>{window.__bump();window.toast('Dashboard snapshot refreshed','ok');},exportPdf=()=>{window.toast('Preparing print layout','info');setTimeout(()=>window.print(),80);},stats=[['Contract value',window.money(contractValue),'building','grad-blue','projects'],['Revenue certified YTD',window.money(revenue),'receipt','grad-green','billing'],['Gross margin',`${margin.toFixed(1)}%`,'chart','grad-purple','cost-control'],['Active projects',active,'grid','grad-teal','projects'],['Man-hours YTD',manHours.toLocaleString(),'clock','grad-amber','attendance'],['LTI-free days',hse.ltiFreeDays,'helmet','grad-red','hse-incidents']];return <div className="page page-wide rise exec-dashboard"><div className="row exec-page-head"><div><div className="h-page">Executive Dashboard</div><div className="h-sub">Live project, financial, HSE, quality and supply chain performance across Oman</div>{scopeNote&&<Badge kind="info" style={{marginTop:7}}>{scopeNote}</Badge>}</div><span className="spacer"/><span className="tag"><Icon name="calendar" size={13}/>{DB.fmt(DB.TODAY)}</span><span className="row exec-live"><i className="pulse-dot"/>Live</span><Btn icon="refresh" onClick={refresh}>Refresh</Btn><Btn icon="download" onClick={exportPdf}>Export</Btn></div><ConstructionFilters forcedProject={forcedProject}/>
<ExecHero brief={window.execBrief()} onNav={go}/><div className="exec-stat-grid">{stats.map((x,i)=><div key={x[0]} className={`exec-stat ${x[3]}`} onClick={()=>go(x[4])}><div className="eyebrow">{x[0]}</div><div className="exec-stat-value" style={{fontSize:String(x[1]).length>14?20:28}}>{x[1]}</div><Icon name={x[2]} size={18}/><SparkBars data={window.sparkFor(['revenue','revenue','revenue','po','manhours','incidents'][i])}/></div>)}</div>
	<div className="card card-pad" style={{marginBottom:16}}><div className="row" style={{marginBottom:10}}><div><div className="h-sec">Project delivery flow</div><div className="faint" style={{fontSize:11}}>Tender through substantial completion</div></div><span className="spacer"/><Badge kind="info">Live pipeline</Badge></div><div className="exec-flowbar">{window.projectFlow().map(x=><div className="exec-flowseg" key={x.label} style={{background:x.color,flex:Math.max(1,x.count)}} onClick={()=>go(x.module)}><b>{x.count}</b><span>{x.label}</span></div>)}</div><div className="card-link" onClick={()=>go('projects')}>View project pipeline →</div></div>
	<HealthMatrix rows={window.domainHealth()} onNav={go}/>
<CardChart icon="building" title="Project portfolio · planned vs actual" right={<Badge kind="neutral">ED-01</Badge>}><div style={{overflowX:'auto'}}><table className="tbl"><thead><tr><th>Project</th><th>Client</th><th>Value</th><th style={{minWidth:230}}>Progress</th><th>Variance</th><th>Project status</th></tr></thead><tbody>{portfolio.map(x=>{const p=DB.projectById(x.project),client=DB.custById(p.client);return <tr key={x.project} onClick={()=>go('projects')} style={{cursor:'pointer'}}><td><b>{x.name}</b><div className="faint">{p.location} · {p.governorate}</div></td><td>{client.name}</td><td>{window.money(x.value)}</td><td><div className="row" style={{fontSize:10}}><span style={{width:48}}>Plan {x.plannedPct}%</span><Bar pct={x.plannedPct} color="#94a3b8"/></div><div className="row" style={{fontSize:10,marginTop:4}}><span style={{width:48}}>Actual {x.actualPct}%</span><Bar pct={x.actualPct} color={x.status==='Behind'?'var(--bad)':'var(--ok)'}/></div></td><td><Badge kind={x.status==='Behind'?'bad':x.status==='Ahead'?'ok':'neutral'}>{x.variance>0?'+':''}{x.variance}% · {x.status}</Badge></td><td><StatusBadge status={p.status}/></td></tr>;})}</tbody></table></div></CardChart>
<div style={{display:'grid',gridTemplateColumns:'1.2fr 1fr',gap:16,marginTop:16}}><CardChart icon="chart" title="Revenue & profitability by project" right={<Badge kind="neutral">ED-02</Badge>}><DHBars data={portfolio.map(x=>{const f=window.financeSummary({project:x.project});return {k:x.name,n:f.revenue,c:f.marginPct<0?'var(--bad)':'var(--ok)',sub:`Margin ${f.marginPct}% · Budget utilization ${f.utilizationPct}%`};})} value={d=>window.money(d.n)}/></CardChart><CardChart icon="helmet" title="HSE performance" right={<Badge kind="bad">{hse.ltiFreeDays} LTI-free days</Badge>}><div className="exec-four-cells">{[['TRIR',hse.trir],['LTIFR',hse.ltifr],['LTI',hse.lti],['MTC',hse.mtc],['FAC',hse.fac],['RTA',hse.rta]].map(x=><div className="stat-cell" key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><small>ED-03/04</small></div>)}</div><DTrend data={trend} color="var(--bad)"/></CardChart></div>
<div style={{display:'grid',gridTemplateColumns:'1.1fr .9fr',gap:16,marginTop:16}}><CardChart icon="helmet" title="Injury by body part" right={<Badge kind="neutral">ED-05</Badge>} onClick={()=>go('body-map')}><InjuryBodyMap project={project}/></CardChart><CardChart icon="search" title="HSE observations" right={<Badge kind="neutral">ED-07</Badge>}><DDonut data={[{k:'Safe',n:safe,c:'#15a06b'},{k:'Unsafe act / condition',n:unsafe,c:'#df463c'}]} label="OBS"/><DHBars data={categories}/><div className="row"><Badge kind="warn">{obs.filter(x=>x.status==='Open').length} open</Badge><Badge kind="ok">{obs.filter(x=>x.status==='Closed').length} closed</Badge></div></CardChart></div>
<div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:16,marginTop:16}}><CardChart icon="thermometer" title="Environmental performance" right={<Badge kind="neutral">ED-06</Badge>}><div className="exec-four-cells">{[['Waste',envSum('wasteTonnes').toFixed(1),'t','↓'],['Water',envSum('waterM3').toLocaleString(),'m³','↓'],['Diesel',envSum('dieselLitres').toLocaleString(),'L','↑'],['CO₂',envSum('co2Tonnes').toFixed(1),'t','↓']].map(x=><div className="stat-cell" key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><small>{x[2]}</small><em style={{color:x[3]==='↓'?'var(--ok)':'var(--warn)'}}>{x[3]} trend</em></div>)}</div><div className="lrow"><Icon name="alert"/><b>{env.reduce((a,x)=>a+x.incidents,0)} environmental incidents</b></div></CardChart><CardChart icon="check-shield" title="Quality performance" right={<Badge kind="neutral">ED-08</Badge>}><div className="exec-four-cells">{[['Open NCR',q.open],['Closed NCR',q.closed],['Major',q.major],['Minor',q.minor],['Audits due',DB.audits.filter(x=>projectIds.has(x.project)&&['Scheduled','Actions open'].includes(x.status)).length],['Complaints',complaints]].map(x=><div className="stat-cell" key={x[0]}><span>{x[0]}</span><b>{x[1]}</b></div>)}</div><div className="row" style={{justifyContent:'center',marginTop:12}}><Ring value={csat/5*100} size={105} stroke={10} color="var(--ok)"><div><b>{csat.toFixed(1)}</b><small>/5 CSAT</small></div></Ring></div></CardChart></div>
<div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:16,marginTop:16}}><CardChart icon="documents" title="Contracts overview" right={<Badge kind="neutral">ED-09</Badge>}><div className="exec-four-cells">{[['Active',contracts.filter(x=>x.status==='Active').length],['Awarded',contracts.length],['Expiring ≤180d',contracts.filter(x=>{const d=(new Date(x.completionDate)-DB.TODAY)/DB.day;return d>=0&&d<=180;}).length],['Total value',window.money(contracts.reduce((a,x)=>a+x.value,0))],['Retention',window.money(retention)]].map(x=><div className="stat-cell" key={x[0]}><span>{x[0]}</span><b style={{fontSize:String(x[1]).length>14?13:20}}>{x[1]}</b></div>)}</div><div className="eyebrow" style={{marginTop:12}}>Upcoming milestones</div>{milestones.map((x,i)=><div className="lrow" key={i}><Icon name="calendar" size={14}/><div style={{flex:1}}><b>{x.name}</b><div className="faint">{DB.projectById(x.project).name}</div></div><span>{DB.fmt(x.date)}</span></div>)}</CardChart><CardChart icon="truck" title="Supply chain overview" right={<Badge kind="neutral">ED-10</Badge>}><div className="exec-four-cells">{[['Open POs',pos.filter(x=>x.status==='Open').length],['PO value',window.money(pos.reduce((a,x)=>a+x.value,0))],['Delayed',pos.filter(x=>x.deliveryStatus==='Delayed').length],['Supplier on-time',`${supplierOnTime.toFixed(1)}%`],['Inventory value',window.money(inventory)]].map(x=><div className="stat-cell" key={x[0]}><span>{x[0]}</span><b style={{fontSize:String(x[1]).length>14?13:20}}>{x[1]}</b></div>)}</div>{pos.filter(x=>x.deliveryStatus==='Delayed').map(x=><div className="lrow" key={x.id}><Badge kind="bad">Delayed</Badge><div style={{flex:1}}><b>{x.id}</b><div className="faint">{x.description}</div></div><span>{DB.fmt(x.requiredDate)}</span></div>)}</CardChart></div>
<div className="exec-grid exec-row-three" style={{marginTop:16}}><CardChart icon="bell" title="Alerts & notifications" target="aicos" onNav={go} right={<Badge kind="bad">{window.execAlerts().length}</Badge>}>{window.execAlerts().map((x,i)=><div className="exec-alert-row" key={i} onClick={()=>go(x.module)} style={{cursor:'pointer'}}><span className="kpi-ico" style={{background:`var(--${x.tone}-bg)`,color:`var(--${x.tone})`}}><Icon name={x.icon} size={14}/></span><span>{x.text}</span><small>{x.when}</small></div>)}</CardChart><CardChart icon="check" title="Today's key tasks" target="dashboard" onNav={go}>{window.execTasks().map((x,i)=><div className="exec-task-row" key={i} onClick={()=>go(x.module)}><span className="exec-check"><Icon name="check" size={11}/></span><span>{x.label}</span><Badge kind={x.n?'warn':'ok'}>{x.n}</Badge></div>)}</CardChart></div>{(()=>{const c=window.complianceStrip(),rows=[['ISO 9001',c.iso9001,'check-shield','ncr'],['ISO 45001',c.iso45001,'helmet','hse-incidents'],['ISO 14001',c.iso14001,'droplet','environment'],['Omanisation',c.omanisationPct,'building','omanisation'],['Training',c.trainingCompliancePct,'certificate','training'],['PPM',c.ppmCompliancePct,'wrench','ppm']];return <div className="card exec-compliance-strip" style={{marginTop:16}}>{rows.map(x=><div key={x[0]} onClick={()=>go(x[3])}><span className="kpi-ico"><Icon name={x[2]} size={16}/></span><span>{x[0]}</span><b>{x[1]}%</b><small>{x[1]>=70?'↑ controlled':'↓ action due'}</small></div>)}<div onClick={()=>go('aicos')}><Ring value={c.aiHealth} size={44} stroke={5} color="var(--ai)"><b>{c.aiHealth}</b></Ring><span>AI-COS health</span><small>/100</small></div></div>;})()}<div className="card card-pad" style={{marginTop:16,background:'linear-gradient(120deg,var(--ai-bg),#fff)',borderColor:'var(--ai-line)'}}><div className="row"><span className="ai-badge-ico"><Icon name="sparkles" fill size={15}/></span><div><div className="ai-label">AI-COS executive briefing · live</div><b>Priority management signals</b></div></div>{DB.aicosInsights.map((x,i)=><div className="exec-brief-row" key={i} onClick={()=>go(x.module)}><span className="kpi-ico"><Icon name={x.icon} size={14}/></span><span><b>{x.title}</b> — {x.detail}</span></div>)}<div className="card-link" onClick={()=>go('aicos')}>View full AI briefing →</div><div className="faint" style={{fontSize:10.5}}>Advisory only. Human review is required before action.</div></div></div>;}
function ProjectManagerDashboard({onNav}){return <ExecutiveDashboard onNav={onNav} forcedProject="PRJ-001" scopeNote="Project Manager · assigned project progress, CPI/SPI, NCR, permits, manpower, IPC and requisition control" mode="project"/>;}
function FocusDashboard({onNav,title,sub,kpis,cards}){return <div className="page page-wide rise"><div className="row exec-page-head"><div><div className="h-page">{title}</div><div className="h-sub">{sub}</div></div><span className="spacer"/><Badge kind="info"><i className="pulse-dot"/>Live role view</Badge></div><ConstructionFilters/><div className="kpi-grid" style={{gridTemplateColumns:`repeat(${Math.min(6,kpis.length)},1fr)`,marginBottom:16}}>{kpis.map(x=><Kpi key={x.label} label={x.label} value={x.value} unit={x.unit} icon={x.icon} tone={x.tone} onClick={()=>onNav&&onNav(x.module)}/>)}</div><div className="exec-grid exec-row-three">{cards.map((c,i)=><CardChart key={i} icon={c.icon} title={c.title} target={c.module} onNav={onNav} right={c.badge&&<Badge kind={c.tone||'neutral'}>{c.badge}</Badge>}>{c.rows.map((r,j)=><div className="lrow" key={j}><span className="kpi-ico"><Icon name={r.icon||c.icon} size={14}/></span><div style={{flex:1}}><b>{r.label}</b>{r.sub&&<div className="faint">{r.sub}</div>}</div><b>{r.value}</b></div>)}</CardChart>)}</div></div>;}
function HSEDashboard({onNav}){const h=window.hseStats({}),p=window.permitStatus(),env=window.envSummary({}),obs=window.hseObsTrend();return <FocusDashboard onNav={onNav} title="HSE Manager Dashboard" sub="Lagging and leading indicators, permits, JSA and environmental assurance" kpis={[{label:'TRIR',value:h.trir,icon:'helmet',tone:'warn',module:'hse-incidents'},{label:'LTIFR',value:h.ltifr,icon:'alert',tone:'bad',module:'hse-incidents'},{label:'LTI-free',value:h.ltiFreeDays,unit:'days',icon:'shield',module:'hse-incidents'},{label:'Permits expiring',value:p.expiring,icon:'clock',tone:'warn',module:'permits'},{label:'High-risk JSA',value:DB.jsas.reduce((a,x)=>a+window.jsaRisk(x.id).highRiskSteps,0),icon:'alert',tone:'bad',module:'jsa'},{label:'Open environment',value:env.openEnvIncidents,icon:'droplet',tone:'warn',module:'environment'}]} cards={[{icon:'trend',title:'Incident trend',module:'hse-incidents',rows:[{label:'Recordable incidents',value:h.recordable},{label:'Lost days',value:h.lostDays},{label:'Near misses',value:h.nearMiss}]},{icon:'search',title:'Safe / unsafe observations',module:'observations',rows:obs.slice(-5).map(x=>({label:x.period,sub:`${x.safe} safe`,value:`${x.unsafe} unsafe`}))},{icon:'clock',title:'Permit watch',module:'permits',rows:DB.permits.filter(x=>x.status==='Active').map(x=>({label:x.id,sub:x.type,value:DB.rel(x.validTo)}))}]}/>;}
function QualityDashboard({onNav}){const q=window.qualityKpis({});return <FocusDashboard onNav={onNav} title="Quality Manager Dashboard" sub="NCR ageing, CAPA effectiveness, inspection and document assurance" kpis={[{label:'Open NCR',value:q.openNcr,icon:'flag',tone:'bad',module:'ncr'},{label:'CAPA open',value:q.capaOpen,icon:'refresh',tone:'warn',module:'ncr'},{label:'ITP pass',value:q.itpPassRate,unit:'%',icon:'check',module:'itp'},{label:'IR approval',value:q.irApprovalRate,unit:'%',icon:'check-shield',module:'itp'},{label:'Docs overdue',value:q.docsOverdueReview,icon:'documents',tone:'warn',module:'documents'},{label:'Calibration overdue',value:q.calOverdue,icon:'gauge',tone:'bad',module:'calibration'}]} cards={[{icon:'flag',title:'NCR ageing by discipline',module:'ncr',rows:[...new Set(DB.ncrs.map(x=>x.discipline))].map(k=>({label:k,value:DB.ncrs.filter(x=>x.discipline===k&&x.status!=='Closed').length}))},{icon:'refresh',title:'CAPA status',module:'ncr',rows:[...new Set(DB.capas.map(x=>x.status))].map(k=>({label:k,value:DB.capas.filter(x=>x.status===k).length}))},{icon:'star',title:'Customer quality',module:'projects',rows:[{label:'CSAT',value:`${q.csatAvg}/5`},{label:'Complaints open',value:DB.complaints.filter(x=>x.status!=='Closed').length}]}]}/>;}
function FinanceDashboard({onNav}){const f=window.financeSummary({}),i=window.ipcSummary({}),c=window.cashPosition();return <FocusDashboard onNav={onNav} title="Finance Manager Dashboard" sub="Earned, billed, cost, cash, ageing, retention, guarantees and WIP" kpis={[{label:'Earned revenue',value:window.money(f.revenueEarned),icon:'trend',module:'billing'},{label:'Billed',value:window.money(f.billed),icon:'receipt',module:'billing'},{label:'Cost',value:window.money(f.cost),icon:'chart',module:'cost-control'},{label:'WIP',value:window.money(i.wip),icon:'chart',tone:'warn',module:'cost-control'},{label:'Retention',value:window.money(i.retentionHeld),icon:'lock',module:'billing'},{label:'Net cash',value:window.money(c.netPosition),icon:'receipt',tone:c.netPosition<0?'bad':'ok',module:'receivables'}]} cards={[{icon:'receipt',title:'IPC position',module:'billing',rows:[{label:'Submitted',value:window.money(i.submitted)},{label:'Certified',value:window.money(i.certified)},{label:'Paid',value:window.money(i.paid)}]},{icon:'trend',title:'Cash position',module:'receivables',rows:[{label:'Receivables',value:window.money(c.receivables)},{label:'Payables',value:window.money(c.payables)},{label:'Guarantees',value:window.money(c.guarantees)}]},{icon:'building',title:'Project margin',module:'cost-control',rows:DB.projects.map(p=>({label:p.name,value:`${window.financeSummary({project:p.id}).marginPct}%`}))}]}/>;}
function HRDashboard({onNav}){const o=window.omanisationStats(),active=DB.employees.filter(x=>x.status!=='Demobilized'),exp=active.filter(e=>[e.visaExpiry,e.labourCardExpiry].filter(Boolean).some(d=>(new Date(d)-DB.TODAY)/DB.day<=60)),tr=DB.trainingRecords.filter(x=>x.status!=='Valid');return <FocusDashboard onNav={onNav} title="HR Manager Dashboard" sub="Headcount, Omanisation, documents, leave, training and EOSB" kpis={[{label:'Headcount',value:active.length,icon:'users',module:'employees'},{label:'Omanisation',value:o.achievedPct,unit:'%',icon:'building',tone:o.status==='Compliant'?'ok':'bad',module:'omanisation'},{label:'Documents due',value:exp.length,icon:'documents',tone:'warn',module:'employees'},{label:'Leave pending',value:DB.leaveRequests.filter(x=>x.status==='Pending').length,icon:'calendar',module:'leave'},{label:'Training attention',value:tr.length,icon:'certificate',tone:'warn',module:'training'},{label:'EOSB liability',value:window.money(window.eosbLiability()),icon:'receipt',module:'payroll'}]} cards={[{icon:'building',title:'Omanisation by category',module:'omanisation',rows:o.byCategory.map(x=>({label:x.category,sub:`Target ${x.target}%`,value:`${x.actual}%`}))},{icon:'documents',title:'Expiring documents',module:'employees',rows:exp.slice(0,6).map(x=>({label:x.name,sub:x.designation,value:'≤60d'}))},{icon:'certificate',title:'Training compliance',module:'training',rows:tr.slice(0,6).map(x=>({label:x.course,sub:DB.empById(x.employee)?.name,value:x.status}))}]}/>;}
function SupplyChainDashboard({onNav}){const s=window.scmSummary(),low=DB.inventoryItems.filter(x=>x.qtyOnHand<x.minLevel);return <FocusDashboard onNav={onNav} title="Supply Chain Dashboard" sub="PR/RFQ/PO conversion, delivery performance, suppliers and stock" kpis={[{label:'PR pending',value:DB.requisitions.filter(x=>x.status==='Pending approval').length,icon:'receipt',module:'requisitions'},{label:'RFQs active',value:DB.rfqs.filter(x=>x.status!=='Awarded').length,icon:'chat',module:'rfq'},{label:'Open POs',value:s.openPOs,icon:'box',module:'purchase-orders'},{label:'Delayed',value:s.delayed,icon:'truck',tone:'bad',module:'purchase-orders'},{label:'Supplier on-time',value:s.supplierOnTimePct,unit:'%',icon:'truck',module:'suppliers'},{label:'Below minimum',value:low.length,icon:'alert',tone:'warn',module:'inventory'}]} cards={[{icon:'workflow',title:'PR / RFQ / PO funnel',module:'requisitions',rows:[{label:'Requisitions',value:DB.requisitions.length},{label:'RFQs',value:DB.rfqs.length},{label:'POs',value:DB.purchaseOrders.length}]},{icon:'truck',title:'Delayed deliveries',module:'purchase-orders',rows:DB.purchaseOrders.filter(x=>x.deliveryStatus==='Delayed').map(x=>({label:x.id,sub:x.description,value:DB.fmt(x.requiredDate)}))},{icon:'box',title:'Stock below minimum',module:'inventory',rows:low.slice(0,6).map(x=>({label:x.description,sub:`Min ${x.minLevel}`,value:x.qtyOnHand}))}]}/>;}
function MaintenanceDashboard({onNav}){const m=window.maintenanceKpis(),u=window.equipmentUtilisation();return <FocusDashboard onNav={onNav} title="Maintenance Dashboard" sub="Availability, PPM, breakdown, ageing and operating cost" kpis={[{label:'Availability',value:Number((u.reduce((a,x)=>a+x.availability,0)/u.length).toFixed(1)),unit:'%',icon:'gauge',module:'equipment'},{label:'PPM compliance',value:m.ppmCompliance,unit:'%',icon:'refresh',tone:'warn',module:'ppm'},{label:'PPM overdue',value:m.overdue,icon:'alert',tone:'bad',module:'ppm'},{label:'Open job cards',value:m.open,icon:'wrench',module:'job-cards'},{label:'Downtime',value:m.downtimeHours,unit:'h',icon:'clock',module:'job-cards'},{label:'MTBF',value:m.mtbf,unit:'h',icon:'trend',module:'equipment'}]} cards={[{icon:'gauge',title:'Equipment availability',module:'equipment',rows:u.slice(0,6).map(x=>({label:DB.equipment.find(e=>e.id===x.equipment)?.name,value:`${x.availability}%`}))},{icon:'clock',title:'Job-card ageing',module:'job-cards',rows:DB.jobCards.filter(x=>x.status!=='Closed').slice(0,6).map(x=>({label:x.id,sub:x.status,value:`${Math.ceil((DB.TODAY-new Date(x.reportedDate))/DB.day)}d`}))},{icon:'receipt',title:'Cost per hour',module:'equipment',rows:u.slice(0,6).map(x=>({label:x.equipment,value:window.money(x.costPerHour)}))}]}/>;}
function SiteEngineerDashboard({onNav}){return <ProjectManagerDashboard onNav={onNav}/>;}
function WarehouseDashboard({onNav}){const low=DB.inventoryItems.filter(x=>x.qtyOnHand<x.minLevel);return <FocusDashboard onNav={onNav} title="Stores Dashboard" sub="Stock health, receipts, issues and project material availability" kpis={[{label:'Stock lines',value:DB.inventoryItems.length,icon:'box',module:'inventory'},{label:'Below minimum',value:low.length,icon:'alert',tone:'warn',module:'inventory'},{label:'GRNs pending',value:DB.grns.filter(x=>x.inspection==='Pending').length,icon:'truck',module:'purchase-orders'},{label:'Movements',value:DB.stockMovements.length,icon:'refresh',module:'inventory'}]} cards={[{icon:'box',title:'Low stock',module:'inventory',rows:low.map(x=>({label:x.description,sub:x.store,value:`${x.qtyOnHand}/${x.minLevel}`}))},{icon:'truck',title:'Recent receipts',module:'purchase-orders',rows:DB.grns.slice(-6).map(x=>({label:x.id,sub:x.items,value:x.condition}))},{icon:'refresh',title:'Stock movements',module:'inventory',rows:DB.stockMovements.slice(-6).map(x=>({label:x.kind,sub:x.item,value:x.qty}))}]}/>;}
function SalesDashboard({onNav}){const p=window.tenderPipeline();return <FocusDashboard onNav={onNav} title="Sales & BD Dashboard" sub="Opportunity pipeline, tender deadlines, conversion and variation exposure" kpis={[{label:'Opportunities',value:p.count,icon:'trend',module:'leads'},{label:'Pipeline',value:window.money(p.value),icon:'chart',module:'leads'},{label:'Win rate',value:p.winRate,unit:'%',icon:'star',module:'leads'},{label:'Due ≤10d',value:DB.tenders.filter(x=>{const n=(new Date(x.submissionDate)-DB.TODAY)/DB.day;return n>=0&&n<=10}).length,icon:'clock',tone:'warn',module:'estimation'}]} cards={[{icon:'workflow',title:'Pipeline by stage',module:'leads',rows:Object.entries(p.stages).map(([k,v])=>({label:k,sub:window.money(v.value),value:v.count}))},{icon:'clock',title:'Tender deadlines',module:'estimation',rows:DB.tenders.filter(x=>new Date(x.submissionDate)>=DB.TODAY).slice(0,6).map(x=>({label:x.id,sub:x.title,value:DB.rel(x.submissionDate)}))},{icon:'refresh',title:'Variation exposure',module:'variations',rows:DB.variations.filter(x=>['Submitted','Under review'].includes(x.status)).slice(0,6).map(x=>({label:x.id,sub:x.title,value:window.money(x.submittedValue)}))}]}/>;}
function RoleDashboard({onNav}){if(['Executive Management','System Administrator'].includes(window.ROLE))return <ExecutiveDashboard onNav={onNav}/>;if(window.ROLE==='Project Manager')return <ProjectManagerDashboard onNav={onNav}/>;if(window.ROLE==='Site Engineer')return <SiteEngineerDashboard onNav={onNav}/>;if(window.ROLE==='HSE Manager')return <HSEDashboard onNav={onNav}/>;if(window.ROLE==='Quality Manager')return <QualityDashboard onNav={onNav}/>;if(window.ROLE==='Finance Manager')return <FinanceDashboard onNav={onNav}/>;if(window.ROLE==='HR Manager')return <HRDashboard onNav={onNav}/>;if(window.ROLE==='Procurement Manager')return <SupplyChainDashboard onNav={onNav}/>;if(window.ROLE==='Maintenance Manager')return <MaintenanceDashboard onNav={onNav}/>;if(window.ROLE==='Warehouse Keeper')return <WarehouseDashboard onNav={onNav}/>;if(window.ROLE==='Sales & BD Manager')return <SalesDashboard onNav={onNav}/>;return <ExecutiveDashboard onNav={onNav} scopeNote={`${window.ROLE} view · permitted portfolio subset`}/>;}
Object.assign(window,{DDonut,DHBars,DTrend,CardChart,ConstructionFilters,InjuryBodyMap,ExecutiveDashboard,ProjectManagerDashboard,HSEDashboard,QualityDashboard,FinanceDashboard,HRDashboard,SupplyChainDashboard,MaintenanceDashboard,SiteEngineerDashboard,WarehouseDashboard,SalesDashboard,RoleDashboard});
