/* ============================================================ TEAM SCREENS — admin dash, queue, partners, users, reports, audit, settings ============================================================ */ (function(){ const {Icon,Btn,StatusBadge,PriorityBadge,RoleBadge,Avatar,Field,Input,Textarea,Select,Banner,Modal,EmptyState,PermDenied,RequestTable}=window; const {useState}=React; const G=()=>window.GOV; /* Partner logo (from /assets/partners/) with a graceful icon fallback. */ function PartnerLogo({h,size=30}){ const [err,setErr]=useState(false); if(h&&h.logoKey&&!err) return setErr(true)} style={{width:size,height:size,borderRadius:6,objectFit:'contain',background:'#fff',border:'1px solid var(--border-default)',flex:'none'}}/>; return ; } window.PartnerLogo=PartnerLogo; /* ---------------- Admin / Founder Dashboard ---------------- */ function AdminDashboard(){ const {state,me,go,sel,helpers}=window.useStore(); const rq=state.requests; const count=(s)=>rq.filter(r=>r.status===s).length; const stats=[ {n:state.hospitals.filter(h=>h.status==='active').length,l:'Partners',go:()=>go('partners')}, {n:rq.length,l:'Requests',go:()=>go('requests',{status:'all'})}, {n:count('submitted'),l:'New',dot:'var(--st-submitted-dot)',go:()=>go('requests',{status:'submitted'})}, {n:count('under_review'),l:'Under review',dot:'var(--st-review-dot)',go:()=>go('requests',{status:'under_review'})}, {n:count('needs_info'),l:'Needs info',dot:'var(--st-needs-dot)',emph:true,go:()=>go('requests',{status:'needs_info'})}, {n:count('approved'),l:'Approved',dot:'var(--st-approved-dot)',go:()=>go('requests',{status:'approved'})}, {n:count('rejected'),l:'Rejected',dot:'var(--st-rejected-dot)',go:()=>go('requests',{status:'rejected'})}, ]; const recent=[...state.audit].slice(0,7); return

Dashboard

Organisation-wide overview · {me.role==='founder'?'full access':'admin access'}.

{stats.map((s,i)=>)}

All Requests

{e.preventDefault();go('requests',{status:'all'});}} style={{fontSize:13}}>View all →
b.updatedAt-a.updatedAt).slice(0,6)} columns={['id','hospital','type','prio','status','reviewer']}/>

Activity

{recent.map(a=>
{a.detail}
{sel.userName(a.actorId)} · {helpers.timeAgo(a.at)}{a.target!=='—'?' · '+a.target:''}
)}
; } const AUDIT_ICON={login:'logout',submission:'plus',status_change:'arrowR',assignment:'userCheck',file_download:'download',note_internal:'lock',user_change:'users',partner_change:'hospital',setting_change:'cog',recommendation:'shield'}; /* ---------------- Reviewer Queue ---------------- */ function ReviewerQueue(){ const {state,me,sel}=window.useStore(); const mine=state.requests.filter(r=>r.reviewerId===me.id); const open=mine.filter(r=>['submitted','under_review','needs_info'].includes(r.status)); const attention=mine.filter(r=>r.status==='under_review').length; return

My Queue

{mine.length} case{mine.length!==1?'s':''} assigned to you · {attention} awaiting your review.

{open.length>0 &&
Open a case to review the file, leave notes, and {state.settings.reviewersCanDecide?'set a decision':'send your recommendation to an admin'}.
}
; } /* ---------------- Partner Management ---------------- */ function PartnerMgmt(){ const store=window.useStore(); const {state,me,dispatch,toast,sel}=store; if(!G().can(me,'manage_partners')) return ; const [detail,setDetail]=useState(null); const [addOpen,setAddOpen]=useState(false); const [tab,setTab]=useState('details'); const [form,setForm]=useState({name:'',contact:'',email:'',phone:'',city:''}); if(detail){ const h=state.hospitals.find(x=>x.id===detail); const users=state.users.filter(u=>u.hospitalId===h.id); const reqs=state.requests.filter(r=>r.hospitalId===h.id); return
setDetail(null)}>Partners

{h.name}

{h.status==='active'?'Active':'Inactive'}

{h.city} · {reqs.length} request(s) · {users.length} user(s)

{const ns=h.status==='active'?'inactive':'active';dispatch({type:'UPDATE_PARTNER',id:h.id,patch:{status:ns},detail:(ns==='active'?'Activated':'Deactivated')+' partner · '+h.name});toast({kind:'audit',title:(ns==='active'?'Activated':'Deactivated')+' '+h.name,desc:'Recorded to audit.'});}}> {h.status==='active'?'Deactivate':'Activate'}
{['details','users','requests'].map(t=>
setTab(t)} style={{textTransform:'capitalize'}}>{t}{t==='users'?' ('+users.length+')':t==='requests'?' ('+reqs.length+')':''}
)}
{tab==='details'&&
{[['Hospital name',h.name],['Contact person',h.contact],['Email',h.email],['Phone',h.phone],['City',h.city]].map((r,i)=>
{r[0]}
{r[1]||'—'}
)} {h.notes&&
{h.notes}
}
} {tab==='users'&&
{users.map(u=>)}
NameEmailStatus
{u.name}
{u.email}
} {tab==='requests'&&}
; } return

Partners

{state.hospitals.length} partner hospitals.

{setForm({name:'',contact:'',email:'',phone:'',city:''});setAddOpen(true);}}>Add partner
{state.hospitals.map(h=>{setDetail(h.id);setTab('details');}}> )}
HospitalContactCityRequestsStatus
{h.name}
{h.contact}{h.city}{state.requests.filter(r=>r.hospitalId===h.id).length} {h.status==='active'?'Active':'Inactive'}
setAddOpen(false)} title="Add partner hospital" icon="hospital" footer={<>setAddOpen(false)}>Cancel {dispatch({type:'ADD_PARTNER',data:form});toast({kind:'audit',title:'Partner added',desc:form.name+' · recorded to audit.'});setAddOpen(false);}}>Add partner}>
setForm(s=>({...s,name:e.target.value}))} placeholder="e.g. Lotus Hospital"/> setForm(s=>({...s,contact:e.target.value}))} placeholder="Dr. …"/> setForm(s=>({...s,email:e.target.value}))} placeholder="contact@hospital.in"/> setForm(s=>({...s,phone:e.target.value}))} placeholder="9XXXX XXXXX"/>
setForm(s=>({...s,city:e.target.value}))} placeholder="City"/>
; } function UserStatus({s}){const m={active:'st-approved',invited:'st-submitted',disabled:'st-rejected'};const l={active:'Active',invited:'Invited',disabled:'Disabled'};return {l[s]};} /* ---------------- User Management ---------------- */ function UserMgmt(){ const {state,me,dispatch,toast,sel}=window.useStore(); if(!G().can(me,'manage_users')) return ; const [edit,setEdit]=useState(null); const [invite,setInvite]=useState(false); const [iform,setIForm]=useState({name:'',email:'',role:'partner',hospitalId:''}); const eu=edit?state.users.find(u=>u.id===edit):null; return

Users

{state.users.length} users across BHF and partner hospitals.

{setIForm({name:'',email:'',role:'partner',hospitalId:state.hospitals[0].id});setInvite(true);}}>Invite user
{state.users.map(u=>setEdit(u.id)}> )}
NameRoleHospitalStatusLast login
{u.name}
{u.email}
{u.hospitalId?sel.hospitalName(u.hospitalId):'—'} {u.lastLogin?window.timeAgo(u.lastLogin):'never'}
{/* edit user */} setEdit(null)} title={eu&&eu.name} icon="user" footer={setEdit(null)}>Done}> {eu&&
{eu.email}

{eu.role==='partner'&& }
toast({title:'Reset link sent',desc:eu.email})}>Reset password {eu.status!=='disabled' ? {dispatch({type:'UPDATE_USER',userId:eu.id,patch:{status:'disabled'},detail:'Disabled user · '+eu.name});toast({kind:'audit',title:'User disabled',desc:eu.name+' · recorded to audit.'});}}>Disable account : {dispatch({type:'UPDATE_USER',userId:eu.id,patch:{status:'active'},detail:'Re-enabled user · '+eu.name});toast({kind:'audit',title:'User enabled'});}}>Enable account}
}
{/* invite */} setInvite(false)} title="Invite user" icon="mail" footer={<>setInvite(false)}>Cancel {dispatch({type:'INVITE_USER',data:{name:iform.name,email:iform.email,role:iform.role,hospitalId:iform.role==='partner'?iform.hospitalId:null}});toast({kind:'audit',title:'Invitation sent',desc:iform.email+' · recorded to audit.'});setInvite(false);}}>Send invite}>
setIForm(s=>({...s,name:e.target.value}))} placeholder="Dr. …"/> setIForm(s=>({...s,email:e.target.value}))} placeholder="name@org"/>
{iform.role==='partner'&& }
; } /* ---------------- Reports ---------------- */ function Reports(){ const {state,me,toast,sel}=window.useStore(); if(!G().can(me,'view_reports')) return ; const rq=state.requests; const byStatus=G().STATUS_ORDER.map(s=>({k:s,label:G().STATUS[s].label,n:rq.filter(r=>r.status===s).length,color:'var(--st-'+({submitted:'submitted',under_review:'review',needs_info:'needs',approved:'approved',rejected:'rejected',closed:'closed'}[s])+'-dot)'})); const maxS=Math.max(1,...byStatus.map(x=>x.n)); const byPartner=state.hospitals.map(h=>({label:h.name,n:rq.filter(r=>r.hospitalId===h.id).length})).sort((a,b)=>b.n-a.n); const maxP=Math.max(1,...byPartner.map(x=>x.n)); // avg review time (submit -> first decision) const decided=rq.filter(r=>['approved','rejected'].includes(r.status)); const avg=decided.length? (decided.reduce((acc,r)=>{const dec=r.history.find(h=>['approved','rejected'].includes(h.to));return acc+(dec?(dec.at-r.createdAt):0);},0)/decided.length/(86400000)):0; function exportCSV(){ const rows=[['Request','Hospital','Type','Priority','Status','Reviewer','Submitted','Updated']]; rq.forEach(r=>rows.push([r.id,sel.hospitalName(r.hospitalId),r.request.type,r.urgency,G().STATUS[r.status].label,r.reviewerId?sel.userName(r.reviewerId):'',new Date(r.createdAt).toISOString().slice(0,10),new Date(r.updatedAt).toISOString().slice(0,10)])); const csv=rows.map(r=>r.map(c=>'"'+String(c).replace(/"/g,'""')+'"').join(',')).join('\n'); try{const blob=new Blob([csv],{type:'text/csv'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='bhf-requests.csv';a.click();}catch(e){} toast({kind:'audit',title:'Export ready',desc:'requests.csv · export logged to audit.'}); } return

Reports

Programme metrics across all partner hospitals.

Export CSV
{[['Total requests',rq.length],['Approved',rq.filter(r=>r.status==='approved').length],['Active partners',state.hospitals.filter(h=>h.status==='active').length],['Avg review time',avg?avg.toFixed(1)+'d':'—']].map((s,i)=>
{s[1]}
{s[0]}
)}

Requests by status

{byStatus.map(s=>
{s.label}{s.n}
)}

Requests by partner

{byPartner.map((s,i)=>
{s.label}{s.n}
)}
; } /* ---------------- Audit Log ---------------- */ function AuditLog(){ const {state,me,sel,helpers}=window.useStore(); if(!G().can(me,'view_audit')) return ; const [filter,setFilter]=useState('all'); const [q,setQ]=useState(''); const ACTIONS={all:'All actions',login:'Logins',submission:'Submissions',status_change:'Status changes',assignment:'Assignments',file_download:'File downloads',note_internal:'Internal notes',user_change:'User changes',partner_change:'Partner changes',setting_change:'Setting changes',recommendation:'Recommendations'}; let rows=state.audit; if(filter!=='all') rows=rows.filter(a=>a.action===filter); if(q.trim()){const s=q.toLowerCase();rows=rows.filter(a=>(a.detail+' '+sel.userName(a.actorId)+' '+a.target).toLowerCase().includes(s));} return

Audit Log

Every sensitive action, immutably recorded. Founder & admin only.

setQ(e.target.value)}/>
{rows.length} event{rows.length!==1?'s':''}
{rows.map(a=>)}
ActionDetailActorTargetWhen
{ACTIONS[a.action]||a.action} {a.detail}
{sel.userName(a.actorId)}
{a.target} {helpers.timeAgo(a.at)}
{rows.length===0&&}
; } /* ---------------- Settings (founder) ---------------- */ function Settings(){ const {state,me,dispatch,toast}=window.useStore(); if(!G().can(me,'manage_settings')) return ; const s=state.settings; const [reset,setReset]=useState(false); return

Settings

Governance controls · founder only.

Review governance

{dispatch({type:'SETTINGS',patch:{reviewersCanDecide:v},detail:'Reviewers can set status: '+(v?'ON':'OFF')});toast({kind:'audit',title:'Setting updated',desc:'Reviewers can decide: '+(v?'ON':'OFF')+' · logged.'});}}/>

Demo data

Reset all requests, users, partners, and the audit log back to the seeded demo state. This cannot be undone.
setReset(true)}>Reset demo data
setReset(false)} title="Reset demo data?" icon="alert" iconColor="var(--st-rejected-fg)" footer={<>setReset(false)}>Cancel{dispatch({type:'RESET'});setReset(false);toast({title:'Demo data reset'});}}>Reset everything}>

All changes you've made in this session will be discarded and the prototype returns to its seeded state.

; } function Toggle({label,desc,on,onChange}){ return
{label}
{desc}
; } Object.assign(window,{AdminDashboard,ReviewerQueue,PartnerMgmt,UserMgmt,Reports,AuditLog,Settings}); })();