/* ============================================================ REQUEST DETAIL — role-aware, governance-enforced ============================================================ */ (function(){ const {Icon,Btn,StatusBadge,PriorityBadge,RoleBadge,Avatar,Field,Textarea,Select,Banner,Modal,PermDenied}=window; const {useState}=React; const G=()=>window.GOV; const TARGET_LABEL={under_review:'Move to Under Review',needs_info:'Request Information',approved:'Approve',rejected:'Reject',closed:'Close case'}; const TARGET_VARIANT={approved:'primary',rejected:'danger',needs_info:'accent'}; function RequestDetail(){ const store=window.useStore(); const {state,me,route,go,sel,dispatch,toast,helpers}=store; const req=sel.request(route.id); const [reason,setReason]=useState(null); // {to} const [reasonText,setReasonText]=useState(''); const [assignOpen,setAssignOpen]=useState(false); const [noteScope,setNoteScope]=useState('partner'); const [noteText,setNoteText]=useState(''); const [respondText,setRespondText]=useState(''); const [respondFiles,setRespondFiles]=useState([]); if(!req) return ; if(!G().canViewRequest(me,req)) return ; const isPartner=me.role==='partner'; const isTeam=!isPartner; const settings=state.settings; const allowed=G().transitionsFor(req.status, me, settings); const canDecide=G().can(me,'change_status',settings); const canRecommend=G().can(me,'recommend')&&!canDecide; const canAssign=G().can(me,'assign_reviewer'); const canInternal=G().can(me,'add_internal_note'); const readOnly=['rejected','closed'].includes(req.status); const notes=G().visibleNotes(me,req); const files=G().visibleFiles(me,req); const reviewer=req.reviewerId?sel.user(req.reviewerId):null; const reviewers=state.users.filter(u=>u.role==='reviewer'&&u.status==='active'); function doStatus(to,reasonVal){ dispatch({type:'STATUS',reqId:req.id,to,reason:reasonVal||''}); toast({kind:'audit',icon:'shield',title:G().STATUS[to].label,desc:'Status changed · recorded to audit trail.'}); } function clickTransition(to){ if(G().REASON_REQUIRED.includes(to)){ setReason({to}); setReasonText(''); } else doStatus(to); } function confirmReason(){ if(!reasonText.trim()) return; doStatus(reason.to,reasonText.trim()); setReason(null); } function assign(reviewerId){ dispatch({type:'ASSIGN',reqId:req.id,reviewerId}); setAssignOpen(false); toast({kind:'audit',icon:'userCheck',title:'Reviewer assigned',desc:sel.userName(reviewerId)+' · recorded to audit.'}); } function recommend(rec){ dispatch({type:'RECOMMEND',reqId:req.id,rec}); toast({kind:'audit',icon:'shield',title:'Recommendation sent',desc:'An admin will make the final decision.'}); } function addNote(){ if(!noteText.trim()) return; dispatch({type:'NOTE',reqId:req.id,scope:noteScope,body:noteText.trim()}); toast({title:noteScope==='internal'?'Internal note added':'Note added',desc:noteScope==='internal'?'Hidden from the partner.':'Visible to the partner.'}); setNoteText(''); } function respond(){ if(!respondText.trim()) return; dispatch({type:'RESPOND',reqId:req.id,body:respondText.trim(),files:respondFiles}); toast({kind:'success',title:'Response sent',desc:'Request moved back to Under Review.'}); setRespondText(''); setRespondFiles([]); } function download(file){ // real, scope-checked download (server enforces visibility + logs the audit) window.open('/api/files/'+file.id,'_blank'); toast({title:'Download started',desc:file.name+(isTeam?' · logged to audit':'')}); } function addRespondFile(){ const n=respondFiles.length+1; setRespondFiles(x=>[...x,{name:'follow-up-'+n+'.pdf',type:'Additional report'}]); } const backTo=isPartner?'requests':(me.role==='reviewer'?'queue':'requests'); return
{/* header */}
go(backTo,{status:'all'})}>Back {isTeam?sel.hospitalName(req.hospitalId):'Request'}

{req.id}

Submitted {helpers.fmtDate(req.createdAt)} by {sel.userName(req.submittedById)} · updated {helpers.timeAgo(req.updatedAt)}

{/* partner needs-info action */} {isPartner && req.status==='needs_info' &&
A reviewer has asked for more details. Add a note (and any files) below to continue — this moves your request back to Under Review.