All Requests
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)=>
)}
{h.notes&&
}
}
{tab==='users'&&
Name Email Status
{users.map(u=>{u.email} )}
}
{tab==='requests'&&
}
;
}
return
Partners {state.hospitals.length} partner hospitals.
{setForm({name:'',contact:'',email:'',phone:'',city:''});setAddOpen(true);}}>Add partner
Hospital Contact City Requests Status
{state.hospitals.map(h=>{setDetail(h.id);setTab('details');}}>
{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
Name Role Hospital Status Last login
{state.users.map(u=>setEdit(u.id)}>
{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&&
{const role=e.target.value;dispatch({type:'UPDATE_USER',userId:eu.id,patch:{role,hospitalId:role==='partner'?(eu.hospitalId||state.hospitals[0].id):null},detail:'Changed role → '+G().ROLES[role].label+' · '+eu.name});toast({kind:'audit',title:'Role updated',desc:'Recorded to audit.'});}}>
{Object.values(G().ROLES).map(r=>{r.label} )}
{eu.role==='partner'&&
{dispatch({type:'UPDATE_USER',userId:eu.id,patch:{hospitalId:e.target.value},detail:'Linked '+eu.name+' → '+sel.hospitalName(e.target.value)});toast({title:'Hospital linked'});}}>
{state.hospitals.map(h=>{h.name} )} }
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"/>
setIForm(s=>({...s,role:e.target.value}))}>{Object.values(G().ROLES).map(r=>{r.label} )}
{iform.role==='partner'&&
setIForm(s=>({...s,hospitalId:e.target.value}))}>{state.hospitals.map(h=>{h.name} )} }
;
}
/* ---------------- 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)=>
)}
;
}
/* ---------------- 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.
Action Detail Actor Target When
{rows.map(a=>
{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
onChange(!on)} style={{flex:'none',width:46,height:26,borderRadius:999,border:0,cursor:'pointer',background:on?'var(--bhf-blue)':'var(--border-strong)',position:'relative',transition:'background .2s'}}>
;
}
Object.assign(window,{AdminDashboard,ReviewerQueue,PartnerMgmt,UserMgmt,Reports,AuditLog,Settings});
})();