const {useEffect,useMemo,useState}=React;

const NAV=[
  {g:'Command',items:[['dashboard','fa-chart-pie','Executive Dashboard'],['projects','fa-folder-tree','Projects']]},
  {g:'HSE Assurance',items:[['docs','fa-file-signature','HSE Documents'],['hemp','fa-triangle-exclamation','HEMP / Risk Register'],['mersa','fa-heart-pulse','MERSA'],['nerf','fa-truck-medical','NERF'],['vessel','fa-ship','Vessel Assurance']]},
  {g:'Operations',items:[['daily_hse','fa-clipboard-check','Daily HSE'],['simops','fa-tower-observation','SIMOPS'],['incidents','fa-bolt','Incidents'],['actions','fa-list-check','Actions & Close-Out']]},
  {g:'Resources',items:[['personnel','fa-users','Personnel'],['equipment','fa-plane-up','UAV & Equipment'],['audit','fa-clock-rotate-left','Audit Trail']]},
  {g:'Administration',items:[['client_admin','fa-user-shield','Client Portal']]},
];

const statusType=(s='')=>{s=s.toLowerCase();if(s.includes('approv')||s.includes('verified')||s.includes('closed')||s.includes('ready')||s.includes('active'))return'safe';if(s.includes('critical')||s.includes('reject')||s.includes('overdue')||s.includes('open incident'))return'crit';if(s.includes('review')||s.includes('progress')||s.includes('conditional'))return'info';if(s.includes('pending')||s.includes('draft')||s.includes('tbc')||s.includes('not started'))return'warn';return'wait'};
const typeClass={safe:'text-status-safe border-status-safe/50 bg-status-safe/10',crit:'text-status-crit border-status-crit/50 bg-status-crit/10',info:'text-status-info border-status-info/50 bg-status-info/10',warn:'text-status-warn border-status-warn/50 bg-status-warn/10',wait:'text-status-wait border-status-wait/50 bg-status-wait/10'};
const Badge=({children,type})=><span className={`status-chip ${typeClass[type||statusType(children)]}`}>{children||'—'}</span>;
const fmtDate=v=>v?new Date(v.replace(' ','T')).toLocaleDateString('en-MY',{day:'2-digit',month:'short',year:'numeric'}):'—';
const today=()=>new Date().toISOString().slice(0,10);

async function api(action,payload,opts={}){
  const res=await fetch(`api.php?action=${encodeURIComponent(action)}`,{method:payload?'POST':'GET',headers:payload&&!opts.form?{'Content-Type':'application/json'}:undefined,body:payload?(opts.form?payload:JSON.stringify(payload)):undefined});
  let data;try{data=await res.json()}catch{data={ok:false,error:'Invalid server response'}}
  if(!res.ok||data.ok===false)throw new Error(data.error||`Request failed (${res.status})`);return data;
}

function IconButton({icon,title,onClick,danger=false}){return <button title={title} onClick={onClick} className={`w-8 h-8 rounded-lg border flex items-center justify-center transition ${danger?'border-status-crit/40 text-status-crit hover:bg-status-crit/10':'border-marine-border text-text-muted hover:text-white hover:border-marine-accent'}`}><i className={`fa-solid ${icon}`}></i></button>}
function Card({title,sub,action,children,className=''}){return <section className={`panel rounded-2xl shadow-soft overflow-hidden ${className}`}><div className="px-4 py-3 border-b border-marine-border flex items-center justify-between gap-3"><div><h3 className="font-bold text-white">{title}</h3>{sub&&<p className="text-xs text-text-muted mt-1">{sub}</p>}</div>{action}</div><div className="p-4">{children}</div></section>}
function KPI({label,value,icon,sub,tone='pgr'}){const bg=tone==='crit'?'bg-status-crit/10 text-status-crit':tone==='warn'?'bg-status-warn/10 text-status-warn':tone==='info'?'bg-status-info/10 text-status-info':'bg-pgr/10 text-pgr';return <div className="panel rounded-2xl p-4 relative overflow-hidden min-h-[112px]"><div className={`w-10 h-10 ${bg} rounded-xl flex items-center justify-center mb-3`}><i className={`fa-solid ${icon}`}></i></div><div className="text-2xl font-extrabold text-white">{value}</div><div className="text-xs font-bold uppercase tracking-wider text-text-muted mt-1">{label}</div>{sub&&<div className="text-[11px] text-text-muted mt-2">{sub}</div>}</div>}

function Modal({title,onClose,children,wide=false}){return <div className="fixed inset-0 z-[100] modal-backdrop flex items-center justify-center p-4" onMouseDown={e=>{if(e.target===e.currentTarget)onClose()}}><div className={`panel rounded-2xl shadow-2xl w-full ${wide?'max-w-6xl':'max-w-2xl'} max-h-[92vh] flex flex-col`}><div className="px-5 py-4 border-b border-marine-border flex items-center justify-between"><h2 className="text-lg font-extrabold text-white">{title}</h2><button onClick={onClose} className="w-9 h-9 rounded-lg border border-marine-border hover:bg-marine-accent/20"><i className="fa-solid fa-xmark"></i></button></div><div className="p-5 overflow-auto">{children}</div></div></div>}

function FormModal({title,fields,initial={},onClose,onSave}){
  const [form,setForm]=useState({...initial});const [busy,setBusy]=useState(false);const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const submit=async e=>{e.preventDefault();setBusy(true);try{await onSave(form);onClose()}finally{setBusy(false)}};
  return <Modal title={title} onClose={onClose}><form onSubmit={submit} className="grid grid-cols-1 md:grid-cols-2 gap-4">{fields.map(f=><div key={f.name} className={f.full?'md:col-span-2':''}><label className="label">{f.label}</label>{f.type==='textarea'?<textarea className="input min-h-[100px]" value={form[f.name]??''} onChange={e=>set(f.name,e.target.value)} required={f.required}/>:f.type==='select'?<select className="input" value={form[f.name]??''} onChange={e=>set(f.name,e.target.value)} required={f.required}><option value="">Select…</option>{(f.options||[]).map(o=>{const v=Array.isArray(o)?o[0]:o;const l=Array.isArray(o)?o[1]:o;return <option value={v} key={v}>{l}</option>})}</select>:f.type==='checkbox'?<label className="flex gap-3 items-center h-11"><input type="checkbox" checked={!!Number(form[f.name]??0)} onChange={e=>set(f.name,e.target.checked?1:0)}/><span className="text-sm text-text-muted">{f.help||'Enabled'}</span></label>:<input className="input" type={f.type||'text'} value={form[f.name]??''} onChange={e=>set(f.name,e.target.value)} required={f.required} min={f.min} max={f.max} placeholder={f.placeholder||''}/>} {f.help&&f.type!=='checkbox'&&<div className="text-[11px] text-text-muted mt-1">{f.help}</div>}</div>)}<div className="md:col-span-2 flex justify-end gap-2 pt-2"><button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button><button className="btn btn-primary" disabled={busy}>{busy?<><i className="fa-solid fa-spinner fa-spin"></i> Saving</>:<><i className="fa-solid fa-floppy-disk"></i> Save</>}</button></div></form></Modal>}

function Confirm({text,onYes,onClose}){return <Modal title="Confirm action" onClose={onClose}><p className="text-text-muted mb-5">{text}</p><div className="flex justify-end gap-2"><button className="btn btn-ghost" onClick={onClose}>Cancel</button><button className="btn btn-danger" onClick={async()=>{await onYes();onClose()}}><i className="fa-solid fa-trash"></i> Delete</button></div></Modal>}

function Login({onLogin}){const[email,setEmail]=useState('admin@pgrgeospatial.com');const[pw,setPw]=useState('');const[show,setShow]=useState(false);const[err,setErr]=useState('');const[busy,setBusy]=useState(false);const submit=async e=>{e.preventDefault();setBusy(true);setErr('');try{const r=await api('login',{email,password:pw});onLogin(r.user)}catch(e){setErr(e.message)}finally{setBusy(false)}};return <div className="h-full grid-bg contour-bg flex"><div className="hidden lg:flex w-[58%] p-12 flex-col justify-between border-r border-marine-border bg-[#081b2b]/90"><div><div className="flex items-center gap-5"><img src="assets/pgr-logo.png" className="w-20 h-20 object-contain"/><div><div className="text-2xl font-extrabold tracking-wider">PRESTIGE GEOMATIC RESOURCES</div><div className="text-xs text-pgr font-bold tracking-[.22em] mt-1">HSE ASSURANCE PLATFORM</div></div></div><div className="mt-24"><h1 className="text-6xl font-extrabold leading-[1.05]">HSE COMMAND<br/><span className="text-pgr">CENTRE</span></h1><p className="text-xl text-text-muted max-w-xl mt-5">Health, Safety, Environment, marine assurance and mobilisation readiness for PGR UAV LiDAR operations.</p><div className="flex flex-wrap gap-2 mt-8 text-xs font-bold uppercase tracking-wider">{['Plan','Verify','Approve','Mobilise Safely'].map(x=><span key={x} className="px-3 py-2 rounded-full border border-marine-border bg-marine-panel">{x}</span>)}</div></div></div><div className="text-xs text-text-muted"><i className="fa-solid fa-shield-halved text-pgr mr-2"></i> Controlled access • MySQL-backed • Hostinger deployment</div></div><div className="w-full lg:w-[42%] p-7 flex items-center justify-center"><form onSubmit={submit} className="w-full max-w-md panel shadow-soft rounded-3xl p-7"><div className="lg:hidden flex justify-center mb-6"><img src="assets/pgr-logo.png" className="w-24 h-24 object-contain"/></div><p className="text-xs text-pgr font-extrabold tracking-[.2em]">SECURE ACCESS</p><h2 className="text-3xl font-extrabold mt-2">Sign in to HSE Command Centre</h2><p className="text-sm text-text-muted mt-2 mb-7">Use the administrator account created during installation.</p>{err&&<div className="p-3 rounded-xl bg-status-crit/10 border border-status-crit/40 text-status-crit text-sm mb-4">{err}</div>}<label className="label">Email</label><input className="input mb-4" type="email" value={email} onChange={e=>setEmail(e.target.value)} required/><label className="label">Password</label><div className="relative mb-5"><input className="input pr-12" type={show?'text':'password'} value={pw} onChange={e=>setPw(e.target.value)} required/><button type="button" onClick={()=>setShow(!show)} className="absolute right-3 top-1/2 -translate-y-1/2 text-text-muted hover:text-white"><i className={`fa-regular ${show?'fa-eye-slash':'fa-eye'}`}></i></button></div><button className="btn btn-primary w-full py-3" disabled={busy}>{busy?<><i className="fa-solid fa-spinner fa-spin"></i> Signing in</>:<><i className="fa-solid fa-right-to-bracket"></i> Sign In</>}</button><div className="text-center text-[11px] text-text-muted mt-5">hse.pgrgeospatial.com</div></form></div></div>}

function Sidebar({view,setView,open,setOpen}){return <aside className={`${open?'translate-x-0':'-translate-x-full lg:translate-x-0'} sidebar-fixed lg:relative w-64 flex-shrink-0 bg-[#061521] border-r border-marine-border flex flex-col transition-transform duration-200`}><div className="h-16 flex items-center gap-3 px-4 border-b border-marine-border"><img src="assets/pgr-logo.png" className="w-10 h-10 object-contain"/><div><div className="font-extrabold tracking-wide">HSE COMMAND</div><div className="text-[10px] text-pgr font-bold tracking-[.2em]">CENTRE</div></div><button className="lg:hidden ml-auto text-text-muted" onClick={()=>setOpen(false)}><i className="fa-solid fa-xmark"></i></button></div><div className="flex-1 overflow-y-auto py-4">{NAV.map(g=><div key={g.g} className="mb-5"><div className="px-4 text-[10px] font-extrabold uppercase tracking-[.15em] text-marine-accent mb-2">{g.g}</div>{g.items.map(([id,ic,label])=><button key={id} onClick={()=>{setView(id);setOpen(false)}} className={`w-full px-4 py-2.5 flex gap-3 items-center border-l-2 text-sm transition ${view===id?'border-pgr bg-pgr/10 text-white':'border-transparent text-text-muted hover:text-white hover:bg-marine-panel/60'}`}><i className={`fa-solid ${ic} w-5 text-center ${view===id?'text-pgr':''}`}></i><span className="font-semibold">{label}</span></button>)}</div>)}</div><div className="p-4 border-t border-marine-border text-[10px] text-text-muted"><div>v4.7 • Equipment Register • Hostinger + MySQL</div><div className="text-pgr mt-1">hse.pgrgeospatial.com</div></div></aside>}

function Topbar({data,activeProjectId,setActiveProjectId,setEmergency,query,setQuery,onLogout,onPassword,setSidebarOpen}){const [menu,setMenu]=useState(false);return <header className="h-16 glass flex items-center px-4 md:px-6 gap-3 flex-shrink-0 z-40"><button onClick={()=>setSidebarOpen(true)} className="lg:hidden w-9 h-9 rounded-lg border border-marine-border"><i className="fa-solid fa-bars"></i></button><select className="input max-w-[330px] h-10 py-1" value={activeProjectId||''} onChange={e=>setActiveProjectId(Number(e.target.value))}>{data.projects.map(p=><option key={p.id} value={p.id}>{p.name}</option>)}</select><div className="relative hidden md:block flex-1 max-w-md"><i className="fa-solid fa-magnifying-glass absolute left-3 top-1/2 -translate-y-1/2 text-text-muted"></i><input className="input pl-9 h-10 py-1" placeholder="Search current module…" value={query} onChange={e=>setQuery(e.target.value)}/></div><div className="ml-auto flex items-center gap-3"><button onClick={()=>setEmergency(true)} className="btn bg-status-crit hover:bg-red-600 text-white pulse-crit text-xs"><i className="fa-solid fa-triangle-exclamation"></i><span className="hidden sm:inline">EMERGENCY</span></button><div className="relative"><button onClick={()=>setMenu(!menu)} className="flex items-center gap-2 rounded-xl border border-marine-border px-2 py-1.5 hover:bg-marine-panel"><div className="hidden md:block text-right"><div className="text-xs font-bold">{data.user.name}</div><div className="text-[10px] text-pgr">{data.user.role}</div></div><div className="w-8 h-8 rounded-lg bg-marine-accent flex items-center justify-center font-extrabold">{(data.user.name||'P').split(/\s+/).map(x=>x[0]).slice(0,2).join('').toUpperCase()}</div></button>{menu&&<div className="absolute right-0 top-12 w-52 panel rounded-xl overflow-hidden shadow-2xl z-50"><button className="w-full px-4 py-3 text-left text-sm hover:bg-marine-accent/20" onClick={()=>{setMenu(false);onPassword()}}><i className="fa-solid fa-key w-5 text-pgr"></i> Change password</button><button className="w-full px-4 py-3 text-left text-sm hover:bg-status-crit/10 text-status-crit" onClick={onLogout}><i className="fa-solid fa-right-from-bracket w-5"></i> Logout</button></div>}</div></div></header>}

function Dashboard({data,project,setView}){const projectRisks=data.risks.filter(x=>!project||!x.project_id||Number(x.project_id)===project.id);const readiness=project?.readiness??0;const criticalRisks=projectRisks.filter(x=>(x.initial_rating||'').includes(' H')).length;const tbcDocs=data.documents.filter(d=>(d.status||'').toLowerCase().includes('review')).length;const gate=[['HSE Case (PGR-HSE-001)','For Client Review'],['MERSA (PGR-HSE-002)','For Client Review'],['NERF (PGR-HSE-003)','For Client Review'],['HEMP (PGR-HSE-004)','For Client Review'],['Mode 3 Boat Acceptance','For Client Review'],['Vessel particulars / registration',data.vessels.length?'In Progress':'TBC'],['Personnel competency',data.personnel.length?'In Progress':'TBC'],['Open critical actions',data.stats.critical_actions?`${data.stats.critical_actions} Open`:'None']];return <div className="max-w-7xl mx-auto space-y-5"><div className="flex flex-col md:flex-row justify-between gap-4"><div><div className="text-xs text-pgr font-extrabold tracking-[.16em] uppercase">Operational HSE Assurance</div><h1 className="text-3xl font-extrabold mt-1">Executive Dashboard</h1><p className="text-sm text-text-muted mt-1">One command centre for controlled documents, risk, marine assurance and operational readiness.</p></div><div className="panel rounded-2xl px-4 py-3 min-w-[300px]"><div className="text-[10px] uppercase tracking-wider text-text-muted">Active Project</div><div className="font-extrabold text-pgr mt-1">{project?.name||'No project selected'}</div><div className="text-xs text-text-muted mt-1">{project?.project_code||'—'} • {project?.location||'Location TBC'}</div></div></div><div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3"><KPI label="Active Projects" value={data.stats.active_projects} icon="fa-diagram-project"/><KPI label="Controlled Docs" value={data.stats.documents_total} icon="fa-file-shield" sub={`${tbcDocs} for client review`} tone="info"/><KPI label="HEMP Hazards" value={projectRisks.length} icon="fa-triangle-exclamation" sub={`${criticalRisks} initial high risk`} tone="warn"/><KPI label="Open Actions" value={data.stats.open_actions} icon="fa-list-check" tone={data.stats.open_actions?'warn':'pgr'}/><KPI label="Critical Actions" value={data.stats.critical_actions} icon="fa-circle-exclamation" tone={data.stats.critical_actions?'crit':'pgr'}/><KPI label="Open Incidents" value={data.stats.open_incidents} icon="fa-bolt" tone={data.stats.open_incidents?'crit':'pgr'}/></div><div className="grid xl:grid-cols-3 gap-5"><Card title="Mobilisation Readiness" sub="Project-level readiness gate" action={<Badge type={readiness>=80?'safe':readiness>=50?'warn':'crit'}>{readiness}%</Badge>}><div className="h-3 rounded-full bg-marine-bg overflow-hidden"><div className="h-full bg-pgr transition-all" style={{width:`${readiness}%`}}></div></div><div className="grid grid-cols-2 gap-3 mt-5 text-sm"><div><div className="text-text-muted text-xs">Client</div><div className="font-semibold mt-1">{project?.client||'TBC'}</div></div><div><div className="text-text-muted text-xs">Project Manager</div><div className="font-semibold mt-1">{project?.project_manager||'TBC'}</div></div><div><div className="text-text-muted text-xs">HSE Lead</div><div className="font-semibold mt-1">{project?.hse_lead||'TBC'}</div></div><div><div className="text-text-muted text-xs">Mobilisation</div><div className="font-semibold mt-1">{fmtDate(project?.mobilisation_date)}</div></div></div><button className="btn btn-secondary w-full mt-5" onClick={()=>setView('projects')}><i className="fa-solid fa-pen-to-square"></i> Update Project Readiness</button></Card><Card title="Readiness Gate Checklist" sub="Unknown project information remains TBC until confirmed" className="xl:col-span-2"><div className="grid md:grid-cols-2 gap-2">{gate.map(([a,b])=><div key={a} className="flex items-center justify-between gap-3 border border-marine-border rounded-xl p-3 bg-marine-bg/30"><div className="text-sm font-semibold">{a}</div><Badge>{b}</Badge></div>)}</div></Card></div><div className="grid lg:grid-cols-2 gap-5"><Card title="Latest HSE Documents" action={<button className="btn btn-ghost text-xs" onClick={()=>setView('docs')}>Open register</button>}><div className="space-y-2">{data.documents.slice(0,5).map(d=><div key={d.id} className="flex gap-3 items-center border border-marine-border rounded-xl p-3"><div className="w-10 h-10 rounded-lg bg-pgr/10 text-pgr flex items-center justify-center"><i className="fa-solid fa-file-word"></i></div><div className="min-w-0 flex-1"><div className="font-bold text-sm">{d.document_no}</div><div className="text-xs text-text-muted truncate">{d.title}</div></div><Badge>{d.status}</Badge></div>)}</div></Card><Card title="Priority Risk Snapshot" action={<button className="btn btn-ghost text-xs" onClick={()=>setView('hemp')}>Open HEMP</button>}><div className="space-y-2">{projectRisks.slice(0,5).map(r=><div key={r.id} className="border border-marine-border rounded-xl p-3"><div className="flex justify-between gap-3"><div><div className="text-xs text-pgr font-bold">{r.risk_code}</div><div className="font-bold text-sm mt-1">{r.hazard}</div></div><Badge type={(r.initial_rating||'').includes(' H')?'crit':'warn'}>{r.initial_rating||'TBC'}</Badge></div><div className="text-xs text-text-muted truncate-2 mt-2">{r.top_event}</div></div>)}</div></Card></div></div>}

const projectFields=[{name:'project_code',label:'Project Code',required:true},{name:'name',label:'Project Name',required:true},{name:'client',label:'Client'},{name:'location',label:'Location / Field'},{name:'project_manager',label:'Project Manager'},{name:'hse_lead',label:'HSE Lead'},{name:'start_date',label:'Start Date',type:'date'},{name:'mobilisation_date',label:'Mobilisation Date',type:'date'},{name:'status',label:'Status',type:'select',options:['Planning','HSE Preparation','For Client Review','Mobilisation Ready','Active','On Hold','Closed']},{name:'readiness',label:'Readiness %',type:'number',min:0,max:100},{name:'is_active',label:'Active Project',type:'checkbox',help:'Include in active project list'}];

function CrudTable({rows,columns,onEdit,onDelete,empty='No records yet'}){return <div className="table-wrap">{rows.length?<table className="data-table"><thead><tr>{columns.map(c=><th key={c.key}>{c.label}</th>)}{(onEdit||onDelete)&&<th>Actions</th>}</tr></thead><tbody>{rows.map(r=><tr key={r.id}>{columns.map(c=><td key={c.key}>{c.render?c.render(r):r[c.key]||'—'}</td>)}{(onEdit||onDelete)&&<td><div className="flex gap-2">{onEdit&&<IconButton icon="fa-pen" title="Edit" onClick={()=>onEdit(r)}/>} {onDelete&&<IconButton icon="fa-trash" title="Delete" danger onClick={()=>onDelete(r)}/>}</div></td>}</tr>)}</tbody></table>:<div className="p-12 text-center text-text-muted"><i className="fa-regular fa-folder-open text-3xl mb-3"></i><div>{empty}</div></div>}</div>}

function EntityModule({title,sub,entity,rows,columns,fields,onRefresh,query,defaults={},addLabel='Add Record'}){const[edit,setEdit]=useState(null);const[del,setDel]=useState(null);const filtered=useMemo(()=>{const q=query.toLowerCase().trim();if(!q)return rows;return rows.filter(r=>JSON.stringify(r).toLowerCase().includes(q))},[rows,query]);const save=async form=>{await api('save',{entity,data:{...defaults,...form}});await onRefresh()};const remove=async()=>{await api('delete',{entity,id:del.id});await onRefresh()};return <div className="max-w-7xl mx-auto"><div className="flex flex-col md:flex-row justify-between md:items-end gap-3 mb-5"><div><h1 className="text-3xl font-extrabold">{title}</h1><p className="text-sm text-text-muted mt-1">{sub}</p></div><button className="btn btn-primary" onClick={()=>setEdit({})}><i className="fa-solid fa-plus"></i>{addLabel}</button></div><CrudTable rows={filtered} columns={columns} onEdit={r=>setEdit(r)} onDelete={r=>setDel(r)}/>{edit!==null&&<FormModal title={edit.id?`Edit ${title}`:`New ${title}`} fields={fields} initial={edit} onClose={()=>setEdit(null)} onSave={save}/>} {del&&<Confirm text={`Delete this ${title.toLowerCase()} record?`} onClose={()=>setDel(null)} onYes={remove}/>}</div>}

function Projects({data,onRefresh,query}){return <EntityModule title="Projects" sub="Manage project particulars, TBC fields and mobilisation readiness." entity="projects" rows={data.projects} fields={projectFields} onRefresh={onRefresh} query={query} columns={[{key:'project_code',label:'Code',render:r=><span className="font-mono text-pgr">{r.project_code}</span>},{key:'name',label:'Project'},{key:'client',label:'Client',render:r=>r.client||<span className="text-status-warn">TBC</span>},{key:'location',label:'Location'},{key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>},{key:'readiness',label:'Readiness',render:r=><div className="w-32"><div className="flex justify-between text-xs"><span>{r.readiness}%</span></div><div className="h-1.5 bg-marine-bg rounded-full mt-1"><div className="h-full bg-pgr rounded-full" style={{width:`${r.readiness}%`}}></div></div></div>} ]}/>} 

function DocumentPreview({doc,onClose}){const[html,setHtml]=useState('');const[err,setErr]=useState('');const[busy,setBusy]=useState(true);useEffect(()=>{(async()=>{try{if((doc.file_path||'').toLowerCase().endsWith('.pdf')){setHtml('PDF')}else{const r=await fetch(`document.php?id=${doc.id}`);if(!r.ok)throw new Error('Unable to load document file');const ab=await r.arrayBuffer();const out=await mammoth.convertToHtml({arrayBuffer:ab});setHtml(out.value)}}catch(e){setErr(e.message)}finally{setBusy(false)}})()},[doc]);return <Modal title={`${doc.document_no} • Rev ${doc.rev}`} onClose={onClose} wide><div className="flex flex-wrap items-center justify-between gap-2 mb-4"><div><div className="font-extrabold">{doc.title}</div><div className="text-xs text-text-muted mt-1">Issue: {fmtDate(doc.issue_date)} • <Badge>{doc.status}</Badge></div></div><a className="btn btn-primary" href={`document.php?id=${doc.id}&download=1`}><i className="fa-solid fa-download"></i> Download Original</a></div>{busy?<div className="py-20 text-center text-text-muted"><i className="fa-solid fa-spinner fa-spin text-2xl"></i><div className="mt-3">Rendering document…</div></div>:err?<div className="p-4 border border-status-crit/40 bg-status-crit/10 rounded-xl text-status-crit">{err}</div>:html==='PDF'?<iframe src={`document.php?id=${doc.id}`} className="w-full h-[68vh] bg-white rounded-xl"></iframe>:<div className="doc-preview rounded-xl p-8 max-h-[68vh] overflow-auto" dangerouslySetInnerHTML={{__html:html}}></div>}<p className="text-[11px] text-text-muted mt-3">Embedded DOCX preview is for quick reference; the controlled original remains the downloadable source file.</p></Modal>}

function Documents({data,onRefresh,query}){const[preview,setPreview]=useState(null);const[edit,setEdit]=useState(null);const[del,setDel]=useState(null);const[upload,setUpload]=useState(false);const filtered=data.documents.filter(d=>!query||JSON.stringify(d).toLowerCase().includes(query.toLowerCase()));const fields=[{name:'document_no',label:'Document No.',required:true},{name:'title',label:'Title',required:true},{name:'rev',label:'Revision'},{name:'owner',label:'Owner'},{name:'issue_date',label:'Issue Date',type:'date'},{name:'status',label:'PGR Status',type:'select',options:['Draft','For Client Review','Under Review','Approved','Superseded']},{name:'client_status',label:'Client Status',type:'select',options:['Pending','For Client Review','Under Review','Revision Required','Accepted','Approved']},{name:'file_name',label:'File Name',help:'Do not change unless replacing the underlying file.'},{name:'file_path',label:'File Path',full:true,help:'Relative server path to the controlled file.'}];const save=async f=>{await api('save',{entity:'documents',data:f});await onRefresh()};return <div className="max-w-7xl mx-auto"><div className="flex flex-col md:flex-row justify-between md:items-end gap-3 mb-5"><div><h1 className="text-3xl font-extrabold">HSE Document Register</h1><p className="text-sm text-text-muted mt-1">Five attached Rev 0 controlled documents are embedded and downloadable from this register.</p></div><button className="btn btn-primary" onClick={()=>setUpload(true)}><i className="fa-solid fa-cloud-arrow-up"></i> Upload Document</button></div><div className="table-wrap"><table className="data-table"><thead><tr><th>Document</th><th>Rev</th><th>Owner</th><th>Issue Date</th><th>Client Status</th><th>Status</th><th>Commands</th></tr></thead><tbody>{filtered.map(d=><tr key={d.id}><td><div className="font-bold text-pgr">{d.document_no}</div><div className="text-xs text-text-muted mt-1 max-w-[360px]">{d.title}</div></td><td>{d.rev}</td><td>{d.owner}</td><td>{fmtDate(d.issue_date)}</td><td><Badge>{d.client_status}</Badge></td><td><Badge>{d.status}</Badge></td><td><div className="flex gap-2"><IconButton icon="fa-eye" title="Embedded preview" onClick={()=>setPreview(d)}/><a className="w-8 h-8 rounded-lg border border-marine-border text-text-muted hover:text-white flex items-center justify-center" href={`document.php?id=${d.id}&download=1`} title="Download original"><i className="fa-solid fa-download"></i></a><IconButton icon="fa-pen" title="Edit metadata" onClick={()=>setEdit(d)}/><IconButton icon="fa-trash" title="Delete" danger onClick={()=>setDel(d)}/></div></td></tr>)}</tbody></table></div>{preview&&<DocumentPreview doc={preview} onClose={()=>setPreview(null)}/>} {edit&&<FormModal title={`Edit ${edit.document_no}`} fields={fields} initial={edit} onClose={()=>setEdit(null)} onSave={save}/>} {del&&<Confirm text={`Delete ${del.document_no} from the register? The source file is only removed automatically for files uploaded through this portal.`} onClose={()=>setDel(null)} onYes={async()=>{await api('delete',{entity:'documents',id:del.id});await onRefresh()}}/>}{upload&&<UploadDocument onClose={()=>setUpload(false)} onDone={onRefresh}/>}</div>}

function UploadDocument({onClose,onDone}){const[busy,setBusy]=useState(false);const[err,setErr]=useState('');const submit=async e=>{e.preventDefault();setBusy(true);setErr('');try{const fd=new FormData(e.currentTarget);await api('upload_document',fd,{form:true});await onDone();onClose()}catch(e){setErr(e.message)}finally{setBusy(false)}};return <Modal title="Upload Controlled Document" onClose={onClose}><form onSubmit={submit} className="grid md:grid-cols-2 gap-4">{err&&<div className="md:col-span-2 p-3 rounded-xl bg-status-crit/10 border border-status-crit/40 text-status-crit">{err}</div>}<div><label className="label">Document No.</label><input className="input" name="document_no" required/></div><div><label className="label">Revision</label><input className="input" name="rev" defaultValue="0"/></div><div className="md:col-span-2"><label className="label">Title</label><input className="input" name="title" required/></div><div><label className="label">Owner</label><input className="input" name="owner" defaultValue="PGR Project / HSE Team"/></div><div><label className="label">Issue Date</label><input className="input" type="date" name="issue_date"/></div><div><label className="label">PGR Status</label><select className="input" name="status"><option>Draft</option><option>For Client Review</option><option>Under Review</option><option>Approved</option></select></div><div><label className="label">Client Status</label><select className="input" name="client_status"><option>Pending</option><option>For Client Review</option><option>Under Review</option><option>Accepted</option></select></div><div className="md:col-span-2"><label className="label">DOCX / PDF</label><input className="input" type="file" name="file" accept=".docx,.pdf" required/></div><div className="md:col-span-2 flex justify-end gap-2"><button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button><button className="btn btn-primary" disabled={busy}><i className={`fa-solid ${busy?'fa-spinner fa-spin':'fa-upload'}`}></i> Upload</button></div></form></Modal>}

function HEMP({data,project,onRefresh,query}){const fields=[{name:'risk_code',label:'Risk ID',required:true},{name:'hazard',label:'Activity / Hazard',required:true,full:true},{name:'top_event',label:'Top Event',full:true},{name:'consequence',label:'Credible Consequence',type:'textarea',full:true},{name:'initial_rating',label:'Initial Risk'},{name:'residual_rating',label:'Residual Risk'},{name:'controls',label:'Preventive / Recovery Controls',type:'textarea',full:true},{name:'owner',label:'Control Owner'},{name:'verification_status',label:'Verification',type:'select',options:['Pending Verification','In Progress','Verified','Not Applicable']}];const rows=data.risks.filter(r=>(!project||!r.project_id||Number(r.project_id)===project.id));return <EntityModule title="HEMP / Risk Register" sub="Project hazard register, controls, control ownership and verification." entity="risks" rows={rows} fields={fields} onRefresh={onRefresh} query={query} defaults={{project_id:project?.id||''}} addLabel="Add Hazard" columns={[{key:'risk_code',label:'ID',render:r=><span className="font-mono text-pgr">{r.risk_code}</span>},{key:'hazard',label:'Activity / Hazard'},{key:'top_event',label:'Top Event'},{key:'initial_rating',label:'Initial',render:r=><Badge type={(r.initial_rating||'').includes(' H')?'crit':'warn'}>{r.initial_rating}</Badge>},{key:'owner',label:'Owner'},{key:'residual_rating',label:'Residual',render:r=><Badge type={(r.residual_rating||'').includes(' L')?'safe':'warn'}>{r.residual_rating}</Badge>},{key:'verification_status',label:'Verification',render:r=><Badge>{r.verification_status}</Badge>} ]}/>} 

function ModuleRecords({title,sub,module,data,project,onRefresh,query,addLabel='Add Record'}){const rows=data.modules.filter(r=>r.module===module&&(!project||!r.project_id||Number(r.project_id)===project.id));const fields=[{name:'title',label:'Title / Check',required:true,full:true},{name:'details',label:'Details / Arrangement',type:'textarea',full:true},{name:'owner',label:'Owner'},{name:'status',label:'Status',type:'select',options:['TBC','Not Started','Pending','In Progress','Ready for Contact Completion','Verified','Completed','Closed']},{name:'date_value',label:'Date',type:'date'}];return <EntityModule title={title} sub={sub} entity="module_records" rows={rows} fields={fields} onRefresh={onRefresh} query={query} defaults={{project_id:project?.id||'',module,date_value:(module==='daily_hse'||module==='simops')?today():''}} addLabel={addLabel} columns={[{key:'title',label:'Item'},{key:'details',label:'Details',render:r=><div className="max-w-[520px] truncate-2 text-text-muted">{r.details||'—'}</div>},{key:'owner',label:'Owner',render:r=>r.owner||<span className="text-status-warn">TBC</span>},{key:'date_value',label:'Date',render:r=>fmtDate(r.date_value)},{key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>} ]}/>} 

function Vessel({data,project,onRefresh,query}){const fields=[{name:'vessel_name',label:'Vessel Name',help:'Leave blank until confirmed.'},{name:'registration_no',label:'Registration No.',help:'Leave blank until confirmed.'},{name:'owner_operator',label:'Owner / Operator'},{name:'vessel_type',label:'Vessel Type'},{name:'assurance_status',label:'Assurance Status',type:'select',options:['Pending','Document Review','Physical Inspection','Conditional','Accepted','Rejected']},{name:'status',label:'Operational Status',type:'select',options:['TBC','Proposed','Available','Mobilisation Ready','Active','Unavailable']},{name:'notes',label:'Due Diligence Notes',type:'textarea',full:true}];const rows=data.vessels.filter(v=>!project||!v.project_id||Number(v.project_id)===project.id);return <EntityModule title="Vessel Assurance" sub="Mode 3 boat acceptance, vessel particulars, registration and close-out status." entity="vessels" rows={rows} fields={fields} onRefresh={onRefresh} query={query} defaults={{project_id:project?.id||''}} addLabel="Add Proposed Vessel" columns={[{key:'vessel_name',label:'Vessel',render:r=>r.vessel_name||<span className="text-status-warn">TBC</span>},{key:'registration_no',label:'Registration',render:r=>r.registration_no||<span className="text-status-warn">TBC</span>},{key:'owner_operator',label:'Owner / Operator'},{key:'vessel_type',label:'Type'},{key:'assurance_status',label:'Assurance',render:r=><Badge>{r.assurance_status}</Badge>},{key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>} ]}/>} 

function Actions({data,project,onRefresh,query}){const fields=[{name:'title',label:'Action',required:true,full:true},{name:'owner',label:'Owner'},{name:'due_date',label:'Due Date',type:'date'},{name:'priority',label:'Priority',type:'select',options:['Low','Medium','High','Critical']},{name:'status',label:'Status',type:'select',options:['Open','In Progress','Pending','Closed']},{name:'source',label:'Source / Reference'},{name:'notes',label:'Notes / Evidence',type:'textarea',full:true}];const rows=data.actions.filter(r=>!project||!r.project_id||Number(r.project_id)===project.id);return <EntityModule title="Actions & Close-Out" sub="Track HSE findings, action owners, due dates, evidence and closure." entity="actions" rows={rows} fields={fields} onRefresh={onRefresh} query={query} defaults={{project_id:project?.id||''}} addLabel="New Action" columns={[{key:'title',label:'Action'},{key:'source',label:'Source'},{key:'owner',label:'Owner',render:r=>r.owner||<span className="text-status-warn">TBC</span>},{key:'due_date',label:'Due',render:r=>fmtDate(r.due_date)},{key:'priority',label:'Priority',render:r=><Badge type={r.priority==='Critical'?'crit':r.priority==='High'?'warn':'wait'}>{r.priority}</Badge>},{key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>} ]}/>} 

function Incidents({data,project,onRefresh,query}){const fields=[{name:'incident_no',label:'Incident No.',required:true},{name:'event_type',label:'Event / Scenario',required:true},{name:'severity',label:'Severity',type:'select',options:['Near Miss','Low','Medium','High','Critical']},{name:'event_date',label:'Event Date & Time',type:'datetime-local'},{name:'description',label:'Initial Description',type:'textarea',full:true},{name:'status',label:'Status',type:'select',options:['Open','Under Investigation','Corrective Action','Closed']}];const rows=data.incidents.filter(r=>!project||!r.project_id||Number(r.project_id)===project.id);return <EntityModule title="Incident Management" sub="Initial notification, event log, status and investigation tracking." entity="incidents" rows={rows} fields={fields} onRefresh={onRefresh} query={query} defaults={{project_id:project?.id||'',incident_no:`INC-${new Date().getFullYear()}-${String(data.incidents.length+1).padStart(3,'0')}`}} addLabel="Log Incident" columns={[{key:'incident_no',label:'No.',render:r=><span className="font-mono text-pgr">{r.incident_no}</span>},{key:'event_type',label:'Event'},{key:'event_date',label:'Date',render:r=>fmtDate(r.event_date)},{key:'severity',label:'Severity',render:r=><Badge type={r.severity==='Critical'?'crit':r.severity==='High'?'warn':'wait'}>{r.severity}</Badge>},{key:'description',label:'Description',render:r=><div className="max-w-[420px] truncate-2 text-text-muted">{r.description}</div>},{key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>} ]}/>} 

function Personnel({data,onRefresh,query}){
  const fields=[
    {name:'personnel_no',label:'No.',type:'number'},
    {name:'name',label:'Name',required:true},
    {name:'role',label:'Position'},
    {name:'status',label:'Status',type:'select',options:['Active','Standby','Unavailable']},
    {name:'competency',label:'Certificate Achieved',full:true,help:'Example: OGSP, RCOC, CIDB. Keep this as the certificate summary.'},
    {name:'phone',label:'Phone'},
    {name:'email',label:'Email',type:'email'},
    {name:'ogsp_expiry',label:'OGSP Expiry',type:'date'},
    {name:'rcoc_expiry',label:'RCoC Expiry',type:'date'},
    {name:'cidb_expiry',label:'CIDB Expiry',type:'date'},
    {name:'medical_expiry',label:'Medical / Fit-to-Work Expiry',type:'date'},
    {name:'first_aid_expiry',label:'First Aid / CPR Expiry',type:'date'},
    {name:'bosiet_expiry',label:'BOSIET / FOET Expiry',type:'date'},
    {name:'other_certificate',label:'Other Certificate / Competency'},
    {name:'other_expiry',label:'Other Certificate Expiry',type:'date'},
    {name:'medical_status',label:'Medical / Fit-to-Work Status',type:'select',options:['TBC','Pending','Fit','Restricted','Expired']}
  ];
  const certItems=r=>[
    ['OGSP',r.ogsp_expiry],['RCoC',r.rcoc_expiry],['CIDB',r.cidb_expiry],['Medical',r.medical_expiry],
    ['First Aid',r.first_aid_expiry],['BOSIET/FOET',r.bosiet_expiry],[r.other_certificate||'Other',r.other_expiry]
  ].filter(x=>x[1]);
  const tone=d=>{if(!d)return'wait';const x=new Date(d+'T00:00:00'),now=new Date();now.setHours(0,0,0,0);const days=Math.ceil((x-now)/86400000);return days<0?'crit':days<=60?'warn':'safe'};
  const expirySummary=r=>{const xs=certItems(r);return xs.length?<div className="space-y-1 min-w-[180px]">{xs.map(([n,d])=><div key={n} className="flex items-center justify-between gap-2 text-[11px]"><span className="text-text-muted">{n}</span><Badge type={tone(d)}>{fmtDate(d)}</Badge></div>)}</div>:<span className="text-text-muted">TBC</span>};
  return <EntityModule title="Personnel Register" sub="Editable personnel, competency and certificate-expiry register. Expiry dates within 60 days are highlighted amber; expired dates are red." entity="personnel" rows={data.personnel} fields={fields} onRefresh={onRefresh} query={query} addLabel="Add Personnel" columns={[
    {key:'personnel_no',label:'No.',render:r=>r.personnel_no||'—'},
    {key:'name',label:'Name'},
    {key:'role',label:'Position'},
    {key:'competency',label:'Certificate Achieved',render:r=>r.competency||'—'},
    {key:'certificate_expiries',label:'Certificate Expiry',render:expirySummary},
    {key:'medical_status',label:'Medical Status',render:r=><Badge>{r.medical_status}</Badge>},
    {key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>}
  ]}/>
}

function Equipment({data,onRefresh,query}){
  const fields=[
    {name:'asset_code',label:'Asset Code'},
    {name:'name',label:'Equipment / Resource Name',required:true},
    {name:'equipment_group',label:'Equipment Group',type:'select',options:['Aerial Mapping & LiDAR','Aerial Photogrammetry Drone','Supporting Equipment – Processing & GCP','Topographic & Engineering Survey Equipment','Supporting Equipment – Field & HSE','Other']},
    {name:'category',label:'Category',type:'select',options:['UAV','LiDAR Payload','Battery','GNSS Receiver','Workstation','Software','GCP / Survey Support','Communication','PPE','Field Device','Survey Consumables','Charger','Emergency Equipment','Other']},
    {name:'quantity',label:'Quantity',type:'number',min:0},
    {name:'associated_with',label:'Associated With',help:'Example: battery associated with a specific UAV model.'},
    {name:'serial_no',label:'Serial No. / Licence Ref.'},
    {name:'status',label:'Status',type:'select',options:['Available','Assigned','Inspection Due','Quarantined','Maintenance','Unavailable']},
    {name:'inspection_due',label:'Inspection / Renewal Due',type:'date'},
    {name:'notes',label:'Notes',type:'textarea',full:true}
  ];
  const rows=data.equipment||[];
  const totalQty=rows.reduce((n,r)=>n+(Number(r.quantity)||0),0);
  const uavQty=rows.filter(r=>r.category==='UAV').reduce((n,r)=>n+(Number(r.quantity)||0),0);
  const lidarQty=rows.filter(r=>r.category==='LiDAR Payload').reduce((n,r)=>n+(Number(r.quantity)||0),0);
  const gnssQty=rows.filter(r=>r.category==='GNSS Receiver').reduce((n,r)=>n+(Number(r.quantity)||0),0);
  return <div className="space-y-5">
    <div className="max-w-7xl mx-auto grid grid-cols-2 md:grid-cols-5 gap-3">
      <KPI label="Listed Items" value={rows.length} icon="fa-list-check" sub="Editable register lines"/>
      <KPI label="Total Quantity" value={totalQty} icon="fa-boxes-stacked" sub="Across all resources" tone="info"/>
      <KPI label="UAV Units" value={uavQty} icon="fa-plane-up" sub="Drone platforms"/>
      <KPI label="LiDAR Payloads" value={lidarQty} icon="fa-cube" sub="P1 / L2 / L3 listed" tone="info"/>
      <KPI label="GNSS Units" value={gnssQty} icon="fa-satellite-dish" sub="Survey receivers"/>
    </div>
    <EntityModule title="UAV & Equipment Register" sub="Editable project equipment list, quantity, asset status, inspection / renewal due date and mobilisation availability." entity="equipment" rows={rows} fields={fields} onRefresh={onRefresh} query={query} addLabel="Add Equipment" columns={[
      {key:'asset_code',label:'Asset Code',render:r=><span className="font-mono text-pgr">{r.asset_code||'—'}</span>},
      {key:'name',label:'Equipment / Resource'},
      {key:'equipment_group',label:'Group',render:r=><div className="max-w-[220px] text-xs text-text-muted">{r.equipment_group||'—'}</div>},
      {key:'category',label:'Category'},
      {key:'quantity',label:'Qty',render:r=><span className="font-extrabold text-white">{r.quantity??1}</span>},
      {key:'associated_with',label:'Associated With',render:r=>r.associated_with||'—'},
      {key:'serial_no',label:'Serial / Ref.',render:r=>r.serial_no||'—'},
      {key:'inspection_due',label:'Inspection / Renewal Due',render:r=>fmtDate(r.inspection_due)},
      {key:'status',label:'Status',render:r=><Badge>{r.status}</Badge>}
    ]}/>
  </div>
} 

function Audit({data,query}){const rows=data.audit.filter(r=>!query||JSON.stringify(r).toLowerCase().includes(query.toLowerCase()));return <div className="max-w-7xl mx-auto"><div className="mb-5"><h1 className="text-3xl font-extrabold">Audit Trail</h1><p className="text-sm text-text-muted mt-1">Recent sign-ins, creates, edits, uploads and deletions.</p></div><CrudTable rows={rows} columns={[{key:'created_at',label:'Time',render:r=>new Date(r.created_at.replace(' ','T')).toLocaleString('en-MY')},{key:'user_name',label:'User'},{key:'action',label:'Action',render:r=><span className="uppercase text-xs font-bold text-pgr">{r.action}</span>},{key:'entity',label:'Entity'},{key:'entity_id',label:'Record ID'},{key:'details',label:'Details'}]}/></div>}

function Emergency({data,project,onClose}){const nerf=data.modules.filter(r=>r.module==='nerf');const vessel=data.vessels.find(v=>!project||Number(v.project_id)===project.id)||{};const [event,setEvent]=useState(null);const contacts=data.personnel.filter(p=>p.phone).slice(0,6);return <div className="fixed inset-0 z-[120] bg-[#06101a] overflow-auto"><div className="sticky top-0 bg-status-crit h-16 px-5 flex items-center justify-between z-30 shadow-2xl"><div className="flex items-center gap-3"><i className="fa-solid fa-triangle-exclamation text-2xl animate-pulse"></i><div><div className="text-xl font-extrabold tracking-wider">EMERGENCY RESPONSE MODE</div><div className="text-[10px] uppercase tracking-[.16em]">NERF quick-response view</div></div></div><button className="btn bg-white/15 border border-white/40" onClick={onClose}>Exit Emergency View</button></div><div className="max-w-7xl mx-auto p-5 grid lg:grid-cols-3 gap-5"><div className="space-y-5"><Card title="Current Location / Project"><div className="space-y-3 text-sm"><div><div className="text-xs text-text-muted">Project</div><div className="font-bold">{project?.name||'TBC'}</div></div><div><div className="text-xs text-text-muted">Worksite / AOI</div><div className="font-bold">{project?.location||'TBC'}</div></div><div><div className="text-xs text-text-muted">Vessel</div><div className="font-bold">{vessel.vessel_name||'TBC'}</div></div><div><div className="text-xs text-text-muted">Registration</div><div className="font-bold">{vessel.registration_no||'TBC'}</div></div></div>{(!vessel.vessel_name||!vessel.registration_no)&&<div className="mt-4 p-3 rounded-xl bg-status-warn/10 border border-status-warn/40 text-status-warn text-xs"><i className="fa-solid fa-circle-exclamation mr-2"></i>Vessel emergency particulars are not yet confirmed.</div>}</Card><Card title="Project Contacts" sub="Only saved phone numbers are shown"><div className="space-y-2">{contacts.length?contacts.map(p=><div key={p.id} className="flex justify-between items-center gap-2 border border-marine-border rounded-xl p-3"><div><div className="font-bold text-sm">{p.name}</div><div className="text-xs text-text-muted">{p.role||'Project personnel'}</div></div><a href={`tel:${p.phone}`} className="w-10 h-10 rounded-full bg-status-safe flex items-center justify-center"><i className="fa-solid fa-phone"></i></a></div>):<div className="p-4 rounded-xl border border-status-warn/40 bg-status-warn/10 text-status-warn text-sm">Emergency contacts are TBC. Complete Personnel & Competency before mobilisation.</div>}</div></Card></div><div className="lg:col-span-2 space-y-5"><Card title="Immediate Response Priorities"><div className="grid sm:grid-cols-4 gap-3 text-center"><div className="p-4 bg-status-crit/15 border border-status-crit/40 rounded-xl"><div className="font-extrabold">1. PROTECT LIFE</div></div><div className="p-4 bg-status-warn/15 border border-status-warn/40 rounded-xl"><div className="font-extrabold">2. STOP / MAKE SAFE</div></div><div className="p-4 bg-pgr/15 border border-pgr/40 rounded-xl"><div className="font-extrabold">3. RAISE ALARM</div></div><div className="p-4 bg-status-info/15 border border-status-info/40 rounded-xl"><div className="font-extrabold">4. NOTIFY / ESCALATE</div></div></div><p className="text-xs text-text-muted mt-3">Life-saving response, rescue, first aid and immediate hazard control take priority over administrative reporting.</p></Card><Card title="Event-Specific NERF Response" sub="Select an event to open the stored response arrangement"><div className="grid sm:grid-cols-2 xl:grid-cols-3 gap-3">{nerf.map(n=><button key={n.id} onClick={()=>setEvent(n)} className="min-h-[96px] text-left p-4 rounded-xl border border-marine-border bg-marine-bg hover:border-status-crit hover:bg-status-crit/5 transition"><div className="text-sm font-bold">{n.title}</div><div className="text-xs text-text-muted truncate-2 mt-2">{n.details}</div></button>)}</div></Card><Card title="Initial NERF Flow"><div className="flex flex-col items-center py-4"><div className="w-full max-w-xl p-3 bg-status-crit text-white text-center rounded-xl font-extrabold">INCIDENT / EMERGENCY</div><div className="h-5 w-px bg-marine-border"></div><div className="w-full max-w-xl p-3 bg-status-warn text-[#1d2730] text-center rounded-xl font-extrabold">STOP WORK • MAKE SAFE • PROVIDE IMMEDIATE RESPONSE</div><div className="h-5 w-px bg-marine-border"></div><div className="w-full max-w-xl p-3 bg-pgr text-white text-center rounded-xl font-extrabold">RAISE ALARM • VESSEL MASTER / SITE AUTHORITY</div><div className="h-5 w-px bg-marine-border"></div><div className="w-full max-w-xl p-3 bg-status-info text-white text-center rounded-xl font-extrabold">NOTIFY PGR PROJECT MANAGER / HSE • CLIENT INTERFACE</div></div></Card></div></div>{event&&<Modal title={event.title} onClose={()=>setEvent(null)}><div className="p-4 rounded-xl border border-status-crit/30 bg-status-crit/5 text-sm whitespace-pre-line leading-6">{event.details}</div><div className="mt-4"><Badge>{event.status}</Badge></div></Modal>}</div>}


function ClientPortalAdmin({data,project,onRefresh}){
  const[clientEdit,setClientEdit]=useState(null);const[accessEdit,setAccessEdit]=useState(null);const[deleteAccess,setDeleteAccess]=useState(null);const[busy,setBusy]=useState('');const[tab,setTab]=useState('clients');
  if(!data.client_portal_ready)return <div className="max-w-5xl mx-auto"><div className="panel rounded-3xl p-7"><div className="flex items-start gap-5"><div className="w-14 h-14 rounded-2xl bg-status-warn/10 text-status-warn flex items-center justify-center text-xl"><i className="fa-solid fa-screwdriver-wrench"></i></div><div className="flex-1"><div className="text-xs text-pgr font-extrabold tracking-[.18em]">CLIENT PORTAL MODULE</div><h1 className="text-3xl font-extrabold mt-1">One-time database update required</h1><p className="text-text-muted mt-2">The interface files are installed, but the project-level client permission tables have not been created yet.</p><a href="update_client_portal.php" className="btn btn-primary mt-5"><i className="fa-solid fa-database"></i> Run Client Portal Update</a></div></div></div></div>;
  const clients=data.client_users||[];const accesses=data.client_access||[];const reviews=data.document_reviews||[];const currentProjectId=project?.id?Number(project.id):0;
  const clientFields=[{name:'name',label:'Client User Name',required:true},{name:'email',label:'Email',type:'email',required:true},{name:'company_name',label:'Company / Organisation'},{name:'role',label:'Role',type:'select',required:true,options:['Client Viewer','Client Approver']},{name:'password',label:'Password',type:'password',help:'Required for a new account. Leave blank while editing to keep the existing password.'},{name:'is_active',label:'Active Account',type:'checkbox',help:'Allow this client account to sign in.'}];
  const userOptions=clients.filter(c=>Number(c.is_active)).map(c=>[c.id,`${c.company_name?c.company_name+' • ':''}${c.name} (${c.role})`]);
  const projectOptions=data.projects.filter(p=>Number(p.is_active)).map(p=>[p.id,`${p.project_code} • ${p.name}`]);
  const accessFields=[{name:'user_id',label:'Client User',type:'select',required:true,options:userOptions},{name:'project_id',label:'Project',type:'select',required:true,options:projectOptions},{name:'can_view_documents',label:'Documents',type:'checkbox',help:'View documents released to the client.'},{name:'can_review_documents',label:'Review / Accept Documents',type:'checkbox',help:'Approver can Accept or Request Revision.'},{name:'can_view_actions',label:'HSE Actions',type:'checkbox',help:'View client-visible actions.'},{name:'can_view_vessel',label:'Vessel Assurance',type:'checkbox',help:'View vessel assurance summary.'},{name:'can_view_risk_summary',label:'HEMP Risk Summary',type:'checkbox',help:'Aggregate risk summary only.'},{name:'can_view_mersa',label:'MERSA',type:'checkbox',help:'View medical emergency readiness items.'},{name:'can_view_nerf',label:'NERF',type:'checkbox',help:'View notification/emergency response items.'},{name:'can_view_incidents',label:'Incidents',type:'checkbox',help:'View incidents explicitly marked client-visible.'}];
  const saveClient=async f=>{await api('client_save_user',{...f,id:f.id||0});await onRefresh()};
  const saveAccess=async f=>{await api('client_save_access',f);await onRefresh()};
  const toggleProject=async enabled=>{if(!currentProjectId)return;setBusy('project');try{await api('client_project_toggle',{project_id:currentProjectId,enabled});await onRefresh()}finally{setBusy('')}};
  const setVisibility=async(entity,row,visible)=>{setBusy(`${entity}-${row.id}`);try{await api('client_set_visibility',{entity,id:row.id,visible,project_id:currentProjectId});await onRefresh()}finally{setBusy('')}};
  const projectDocs=data.documents.filter(d=>Number(d.project_id||0)===currentProjectId||!d.project_id);const projectActions=data.actions.filter(a=>Number(a.project_id||0)===currentProjectId);const projectIncidents=data.incidents.filter(i=>Number(i.project_id||0)===currentProjectId);const projectAccess=accesses.filter(a=>Number(a.project_id)===currentProjectId);const projectReviews=reviews.filter(r=>Number(r.project_id)===currentProjectId);
  const Toggle=({on,disabled,onChange})=><button disabled={disabled} onClick={()=>onChange(!Number(on))} className={`relative w-11 h-6 rounded-full transition ${Number(on)?'bg-pgr':'bg-marine-accent/50'} ${disabled?'opacity-50':''}`}><span className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-all ${Number(on)?'left-6':'left-1'}`}></span></button>;
  return <div className="max-w-7xl mx-auto space-y-5"><div className="flex flex-col lg:flex-row lg:items-end justify-between gap-4"><div><div className="text-xs text-pgr font-extrabold tracking-[.18em]">ADMINISTRATION</div><h1 className="text-3xl font-extrabold mt-1">Client Portal Management</h1><p className="text-sm text-text-muted mt-1">Project-level access, client-visible content and document review controls.</p></div><a href="client/" target="_blank" className="btn btn-primary"><i className="fa-solid fa-arrow-up-right-from-square"></i> Open Client Portal</a></div>
  <div className="panel rounded-2xl p-4 flex flex-col md:flex-row md:items-center justify-between gap-4"><div><div className="text-xs text-text-muted uppercase tracking-wider">Selected Project</div><div className="font-extrabold text-lg mt-1">{project?.project_code||'—'} • {project?.name||'No project selected'}</div><div className="text-xs text-text-muted mt-1">Client portal can be disabled instantly without deleting client accounts or history.</div></div><div className="flex items-center gap-3"><span className={`font-bold ${Number(project?.client_portal_enabled)?'text-status-safe':'text-status-warn'}`}>{Number(project?.client_portal_enabled)?'PORTAL ENABLED':'PORTAL DISABLED'}</span><Toggle on={project?.client_portal_enabled} disabled={busy==='project'} onChange={toggleProject}/></div></div>
  <div className="flex flex-wrap gap-2">{[['clients','Client Accounts'],['access','Project Access'],['visibility','Client Visibility'],['reviews','Review History']].map(([id,label])=><button key={id} onClick={()=>setTab(id)} className={`btn ${tab===id?'btn-primary':'btn-ghost'}`}>{label}</button>)}</div>
  {tab==='clients'&&<Card title="Client Accounts" sub="Viewer is read-only. Approver can review documents only when review permission is also enabled." action={<button className="btn btn-primary text-xs" onClick={()=>setClientEdit({role:'Client Viewer',is_active:1,password:''})}><i className="fa-solid fa-user-plus"></i> Add Client</button>}><div className="table-wrap"><table className="data-table"><thead><tr><th>Name</th><th>Company</th><th>Email</th><th>Role</th><th>Status</th><th>Commands</th></tr></thead><tbody>{clients.map(c=><tr key={c.id}><td className="font-bold">{c.name}</td><td>{c.company_name||'—'}</td><td>{c.email}</td><td><Badge>{c.role}</Badge></td><td><Badge type={Number(c.is_active)?'safe':'warn'}>{Number(c.is_active)?'Active':'Disabled'}</Badge></td><td><IconButton icon="fa-pen" title="Edit client" onClick={()=>setClientEdit({...c,password:''})}/></td></tr>)}{!clients.length&&<tr><td colSpan="6" className="text-center text-text-muted py-8">No client accounts yet.</td></tr>}</tbody></table></div></Card>}
  {tab==='access'&&<Card title="Project Access & Permissions" sub="A client can only see projects explicitly assigned here." action={<button className="btn btn-primary text-xs" disabled={!clients.length} onClick={()=>setAccessEdit({user_id:'',project_id:currentProjectId||'',can_view_documents:1,can_review_documents:0,can_view_actions:1,can_view_vessel:1,can_view_risk_summary:1,can_view_mersa:1,can_view_nerf:1,can_view_incidents:0})}><i className="fa-solid fa-link"></i> Assign Project</button>}><div className="space-y-3">{projectAccess.map(a=><div key={a.id} className="border border-marine-border rounded-xl p-4 flex flex-col lg:flex-row lg:items-center justify-between gap-4"><div><div className="font-extrabold">{a.user_name}</div><div className="text-xs text-text-muted mt-1">{a.company_name||'Client'} • {a.email} • {a.role}</div><div className="flex flex-wrap gap-1.5 mt-3">{[['Docs',a.can_view_documents],['Review',a.can_review_documents],['Actions',a.can_view_actions],['Vessel',a.can_view_vessel],['Risk',a.can_view_risk_summary],['MERSA',a.can_view_mersa],['NERF',a.can_view_nerf],['Incidents',a.can_view_incidents]].filter(x=>Number(x[1])).map(x=><span key={x[0]} className="px-2 py-1 rounded-full border border-marine-border text-[10px] text-text-muted">{x[0]}</span>)}</div></div><div className="flex gap-2"><IconButton icon="fa-pen" title="Edit permissions" onClick={()=>setAccessEdit(a)}/><IconButton icon="fa-trash" title="Remove project access" danger onClick={()=>setDeleteAccess(a)}/></div></div>)}{!projectAccess.length&&<div className="p-8 text-center text-text-muted border border-dashed border-marine-border rounded-xl">No client account is assigned to this project.</div>}</div></Card>}
  {tab==='visibility'&&<div className="grid xl:grid-cols-2 gap-5"><Card title="Client-visible Documents" sub="Turning ON also assigns an unassigned document to the selected project."><div className="space-y-2">{projectDocs.map(d=><div key={d.id} className="border border-marine-border rounded-xl p-3 flex items-center justify-between gap-3"><div><div className="font-bold text-sm">{d.document_no} • Rev {d.rev}</div><div className="text-xs text-text-muted mt-1">{d.title}</div></div><Toggle on={d.client_visible&&Number(d.project_id)===currentProjectId} disabled={busy===`documents-${d.id}`} onChange={v=>setVisibility('documents',d,v)}/></div>)}{!projectDocs.length&&<div className="text-text-muted">No documents available.</div>}</div></Card><div className="space-y-5"><Card title="Client-visible Actions" sub="Internal action details remain hidden unless explicitly released."><div className="space-y-2">{projectActions.map(a=><div key={a.id} className="border border-marine-border rounded-xl p-3 flex items-center justify-between gap-3"><div><div className="font-bold text-sm">{a.title}</div><div className="text-xs text-text-muted mt-1">{a.priority} • {a.status}</div></div><Toggle on={a.client_visible} disabled={busy===`actions-${a.id}`} onChange={v=>setVisibility('actions',a,v)}/></div>)}{!projectActions.length&&<div className="text-text-muted">No project actions.</div>}</div></Card><Card title="Client-visible Incidents" sub="Recommended default is hidden until PGR approves client release."><div className="space-y-2">{projectIncidents.map(i=><div key={i.id} className="border border-marine-border rounded-xl p-3 flex items-center justify-between gap-3"><div><div className="font-bold text-sm">{i.incident_no} • {i.event_type}</div><div className="text-xs text-text-muted mt-1">{i.severity} • {i.status}</div></div><Toggle on={i.client_visible} disabled={busy===`incidents-${i.id}`} onChange={v=>setVisibility('incidents',i,v)}/></div>)}{!projectIncidents.length&&<div className="text-text-muted">No project incidents.</div>}</div></Card></div></div>}
  {tab==='reviews'&&<Card title="Client Document Review History" sub="Traceable decisions submitted from /client/."><div className="space-y-2">{projectReviews.map(r=><div key={r.id} className="border border-marine-border rounded-xl p-4 flex flex-col md:flex-row md:items-center justify-between gap-3"><div><div className="font-bold">{r.document_no} • Rev {r.rev}</div><div className="text-xs text-text-muted mt-1">{r.reviewer} {r.company_name?`• ${r.company_name}`:''} • {r.created_at}</div>{r.comments&&<div className="text-sm text-text-muted mt-2">{r.comments}</div>}</div><Badge type={r.decision==='Request Revision'?'crit':'safe'}>{r.decision}</Badge></div>)}{!projectReviews.length&&<div className="p-8 text-center text-text-muted">No client reviews recorded for this project.</div>}</div></Card>}
  {clientEdit&&<FormModal title={clientEdit.id?`Edit ${clientEdit.name}`:'Create Client Account'} fields={clientFields} initial={clientEdit} onClose={()=>setClientEdit(null)} onSave={saveClient}/>} {accessEdit&&<FormModal title="Client Project Permissions" fields={accessFields} initial={accessEdit} onClose={()=>setAccessEdit(null)} onSave={saveAccess}/>} {deleteAccess&&<Confirm text={`Remove ${deleteAccess.user_name}'s access to ${deleteAccess.project_code}?`} onClose={()=>setDeleteAccess(null)} onYes={async()=>{await api('client_delete_access',{id:deleteAccess.id});await onRefresh()}}/>}</div>
}

function PasswordModal({onClose}){const[err,setErr]=useState('');const[ok,setOk]=useState('');const submit=async e=>{e.preventDefault();const fd=new FormData(e.currentTarget);const a=fd.get('new_password'),b=fd.get('confirm');if(a!==b){setErr('New password and confirmation do not match.');return}try{await api('change_password',{current_password:fd.get('current_password'),new_password:a});setOk('Password changed successfully.');setErr('')}catch(e){setErr(e.message)}};return <Modal title="Change Password" onClose={onClose}><form onSubmit={submit} className="space-y-4">{err&&<div className="p-3 rounded-xl bg-status-crit/10 border border-status-crit/40 text-status-crit">{err}</div>}{ok&&<div className="p-3 rounded-xl bg-status-safe/10 border border-status-safe/40 text-status-safe">{ok}</div>}<div><label className="label">Current Password</label><input className="input" name="current_password" type="password" required/></div><div><label className="label">New Password</label><input className="input" name="new_password" type="password" minLength="8" required/></div><div><label className="label">Confirm New Password</label><input className="input" name="confirm" type="password" minLength="8" required/></div><div className="flex justify-end gap-2"><button type="button" className="btn btn-ghost" onClick={onClose}>Close</button><button className="btn btn-primary"><i className="fa-solid fa-key"></i> Change Password</button></div></form></Modal>}

function App(){const[user,setUser]=useState(null);const[data,setData]=useState(null);const[view,setView]=useState('dashboard');const[activeProjectId,setActiveProjectId]=useState(null);const[query,setQuery]=useState('');const[loading,setLoading]=useState(true);const[err,setErr]=useState('');const[emergency,setEmergency]=useState(false);const[pwModal,setPwModal]=useState(false);const[sidebarOpen,setSidebarOpen]=useState(false);
  const load=async()=>{setErr('');try{const r=await api('bootstrap');setData(r);setUser(r.user);setActiveProjectId(id=>id||r.projects?.[0]?.id||null)}catch(e){if(e.message==='Unauthorised'){setUser(null);setData(null)}else setErr(e.message)}};
  useEffect(()=>{(async()=>{try{const s=await api('session');if(s.authenticated){setUser(s.user);await load()}}catch(e){setErr(e.message)}finally{setLoading(false)}})()},[]);
  const project=useMemo(()=>data?.projects?.find(p=>Number(p.id)===Number(activeProjectId))||data?.projects?.[0]||null,[data,activeProjectId]);
  const logout=async()=>{try{await api('logout',{})}catch{}setUser(null);setData(null);setEmergency(false)};
  if(loading)return <div className="h-full grid place-items-center text-text-muted"><div className="text-center"><img src="assets/pgr-logo.png" className="w-24 h-24 object-contain mx-auto mb-4"/><i className="fa-solid fa-spinner fa-spin text-2xl"></i><div className="mt-3">Loading HSE Command Centre…</div></div></div>;
  if(!user)return <Login onLogin={async u=>{setUser(u);setLoading(true);await load();setLoading(false)}}/>;
  if(!data)return <div className="h-full grid place-items-center"><div className="panel rounded-2xl p-6 max-w-xl text-center"><div className="text-status-crit text-xl font-bold">Unable to load dashboard</div><div className="text-text-muted mt-2">{err}</div><button className="btn btn-primary mt-4" onClick={load}>Retry</button></div></div>;
  let content=null;const props={data,project,onRefresh:load,query};switch(view){case'dashboard':content=<Dashboard data={data} project={project} setView={setView}/>;break;case'projects':content=<Projects {...props}/>;break;case'docs':content=<Documents {...props}/>;break;case'hemp':content=<HEMP {...props}/>;break;case'mersa':content=<ModuleRecords {...props} module="mersa" title="MERSA" sub="Medical emergency response situational awareness, medevac interfaces and readiness verification." addLabel="Add Medical Readiness Item"/>;break;case'nerf':content=<ModuleRecords {...props} module="nerf" title="NERF" sub="Incident notification, emergency response scenarios, escalation and contact readiness." addLabel="Add NERF Scenario"/>;break;case'vessel':content=<Vessel {...props}/>;break;case'daily_hse':content=<ModuleRecords {...props} module="daily_hse" title="Daily HSE" sub="Daily toolbox talk, operational readiness, weather, deck and stop-work checks." addLabel="New Daily HSE Check"/>;break;case'simops':content=<ModuleRecords {...props} module="simops" title="SIMOPS" sub="Marine-UAV simultaneous-operations coordination, constraints and communication interfaces." addLabel="New SIMOPS Review"/>;break;case'incidents':content=<Incidents {...props}/>;break;case'actions':content=<Actions {...props}/>;break;case'personnel':content=<Personnel {...props}/>;break;case'equipment':content=<Equipment {...props}/>;break;case'audit':content=<Audit data={data} query={query}/>;break;case'client_admin':content=<ClientPortalAdmin {...props}/>;break;default:content=<Dashboard data={data} project={project} setView={setView}/>}
  return <div className="h-full flex bg-marine-bg contour-bg"><Sidebar view={view} setView={v=>{setView(v);setQuery('')}} open={sidebarOpen} setOpen={setSidebarOpen}/>{sidebarOpen&&<div className="fixed inset-0 bg-black/60 z-50 lg:hidden" onClick={()=>setSidebarOpen(false)}></div>}<div className="flex-1 min-w-0 flex flex-col h-full content-mobile"><Topbar data={data} activeProjectId={activeProjectId} setActiveProjectId={setActiveProjectId} setEmergency={setEmergency} query={query} setQuery={setQuery} onLogout={logout} onPassword={()=>setPwModal(true)} setSidebarOpen={setSidebarOpen}/>{err&&<div className="mx-5 mt-3 p-3 rounded-xl bg-status-crit/10 border border-status-crit/40 text-status-crit text-sm"><i className="fa-solid fa-circle-exclamation mr-2"></i>{err}</div>}<main className="flex-1 overflow-auto p-4 md:p-6">{content}</main></div>{emergency&&<Emergency data={data} project={project} onClose={()=>setEmergency(false)}/>} {pwModal&&<PasswordModal onClose={()=>setPwModal(false)}/>}</div>}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
