Streamline board chrome and case report

This commit is contained in:
2026-08-23 21:58:18 +02:00
parent 16df920374
commit 0484a07fff
3 changed files with 41 additions and 32 deletions
+26 -12
View File
@@ -612,7 +612,7 @@ export function App() {
</aside>
<div className="board-shell" onDragEnter={e => { if (e.dataTransfer.types.includes('Files')) { e.preventDefault(); setDraggingFiles(true) } }} onDragOver={e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }} onDragLeave={e => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setDraggingFiles(false) }} onDrop={e => { e.preventDefault(); if (e.dataTransfer.files.length) void uploadFiles(e.dataTransfer.files) }}>
<div className="case-heading"><div><small>{requestedEditMode && caseState.editingAllowed ? 'AUTHORING LEVEL' : 'ACTIVE INVESTIGATION'}</small><h1>{caseState.title}</h1><p>{caseState.subtitle || caseState.id.toUpperCase()}</p></div><div className="case-number">{requestedEditMode && caseState.editingAllowed ? 'DRAFT' : 'CASE'}<br/><b>{caseState.levelStatus?.toUpperCase() || 'ACTIVE'}</b></div></div>
<div className="case-heading"><h1>{caseState.title}</h1></div>
<Board state={caseState} selected={selected} locatorDocumentId={docsOpen && documents.some(document => document.id === selected) ? selected : null} linkFrom={linkFrom} recentlyCreatedExhibitId={recentlyCreatedExhibitId} arrivingExhibitIds={arrivingExhibitIds} recentlyCreatedConnectionId={recentlyCreatedConnectionId} tool={boardTool} boardRef={boardRef} update={update} onCardClick={handleCardClick} onConnectionTarget={completeThread} onEditConnection={connection => setThreadDraft(connection)} onDiscardExhibit={removeExhibit} onOpenSource={id => setOpenDoc(documents.find(d => d.id === id) || null)} onUpdateDocumentCue={(id, cue) => update(state => ({ ...state, exhibits: state.exhibits.map(exhibit => exhibit.id === id && exhibit.type === 'document' ? { ...exhibit, metadata: { ...exhibit.metadata, memory_cue: cue } } : exhibit) }))} onEditFolder={setEditingFolderId} onEditFile={setEditingFileId} onEditEvent={setEditingEventId} onEditParty={setEditingPartyId} />
{briefOpen && <BriefPanel
brief={caseState.brief}
@@ -1144,6 +1144,18 @@ function CaseReportPanel({ report, defaultInvestigator, hasNext, onClose, onSubm
onSubmit:(input:CaseReportSubmissionInput)=>Promise<CaseReport>;onContinue:()=>void
}) {
const investigatorName=report.investigatorName || defaultInvestigator
const unfinishedStatement=(value:string) => !value.trim() || /^proof that(?:\s*(?:…|\.\.\.))?$/i.test(value.trim())
const missingItems=report.claims.flatMap((claim,claimIndex) => claim.evidence.length
? claim.evidence.flatMap(item => [
!item.publishedAt ? `CLAIM ${claimIndex + 1} / EXHIBIT ${item.displayNumber} DATE` : '',
!item.sourceCitation?.trim() ? `CLAIM ${claimIndex + 1} / EXHIBIT ${item.displayNumber} SOURCE` : '',
unfinishedStatement(item.relationText) ? `CLAIM ${claimIndex + 1} / EXHIBIT ${item.displayNumber} STATEMENT` : '',
].filter(Boolean))
: [`CLAIM ${claimIndex + 1} SUPPORTING EVIDENCE`])
const reportStatus=report.status === 'accepted' ? 'ACCEPTED'
: report.status === 'evidence_insufficient' ? 'RETURNED — EVIDENCE NOT ESTABLISHED'
: report.status === 'evidence_accepted_report_incomplete' ? `RETURNED${missingItems.length ? `${missingItems.length} ITEM${missingItems.length === 1 ? '' : 'S'} MISSING` : ''}`
: missingItems.length ? `INCOMPLETE — ${missingItems.length} ITEM${missingItems.length === 1 ? '' : 'S'} MISSING` : 'READY TO FILE'
const [busy,setBusy]=useState(false)
const [error,setError]=useState('')
const submit=async (event:React.FormEvent) => {
@@ -1154,22 +1166,24 @@ function CaseReportPanel({ report, defaultInvestigator, hasNext, onClose, onSubm
}
return <div className="modal-shade case-report-shade"><form className="window case-report" onSubmit={event => void submit(event)}>
<header><ClipboardCheck size={16}/><b>{report.title}</b><span/><button type="button" aria-label="Close case report" onClick={onClose}><X size={14}/></button></header>
<div className="case-report-paper"><div className="case-report-letterhead"><small>GLITCH UNIVERSITY · PRINCIPAL INVESTIGATOR PROGRAMME</small><h2>CASE REPORT</h2><span>FORM GUPI7 / EVIDENTIARY FINDING</span></div>
<div className="case-report-paper"><div className="case-report-letterhead"><h2>CASE REPORT</h2><p>{report.title}</p></div>
<div className="report-summary"><p>INVESTIGATOR: <span>{investigatorName}</span></p><p>STATUS: <strong className={report.status === 'accepted' ? 'complete' : missingItems.length || report.status !== 'draft' ? 'incomplete' : ''}>{reportStatus}</strong></p></div>
<div className="report-rule" aria-hidden="true"/>
{report.claims.map((claim,claimIndex) => <section className="report-claim" key={claim.claimExhibitId}>
<h3><span>CLAIM {claimIndex + 1}</span>{claim.statement}</h3>
<h3>CLAIM {String(claimIndex + 1).padStart(2,'0')}</h3><blockquote>{claim.statement}</blockquote>
<h4>EVIDENCE</h4>
{claim.evidence.length === 0 ? <div className="report-empty-evidence">No source evidence is connected to this claim. Return to the board and use red thread to attach a document.</div>
{claim.evidence.length === 0 ? <div className="report-empty-evidence">[ SUPPORTING EVIDENCE MISSING ]</div>
: claim.evidence.map(item => { const rejected=report.status !== 'draft' && !item.evidenceAccepted; return <article className={`report-evidence ${item.evidenceAccepted ? 'verified' : rejected ? 'rejected' : ''}`} key={item.connectionId}>
<div className="report-evidence-heading"><b>Exhibit {item.displayNumber}</b><span>{item.fileType.replaceAll('_',' ')} · {item.documentTitle}</span>{item.evidenceAccepted ? <em>CONTENT VERIFIED</em> : rejected ? <em className="rejected">NOT VERIFIED</em> : null}</div>
{rejected && <p className="report-evidence-diagnostic">{item.verification.detail}</p>}
<div className="report-reference"><span>EVIDENTIARY STATEMENT</span><p>{item.relationText || 'No evidentiary statement attached.'}</p></div>
<div className="report-fields"><div className="report-reference"><span>DATED</span><p>{item.publishedAt?.slice(0,10) || 'NOT RECORDED'}</p></div>
<div className="report-reference"><span>SOURCE / PUBLICATION</span><p>{item.sourceCitation || 'NOT RECORDED'}</p></div></div>
<div className="report-reference"><span>SOURCE LINK · IF AVAILABLE</span>{item.sourceUri ? <a href={item.sourceUri} target="_blank" rel="noreferrer">{item.sourceUri}</a> : <p>NOT RECORDED</p>}</div>
<h5>EXHIBIT {item.displayNumber} {item.fileType.replaceAll('_',' ').toUpperCase()}</h5>
<p className="report-document-title">{item.documentTitle}</p>
<p className="report-evidence-sentence">Exhibit {item.displayNumber}, dated {item.publishedAt?.slice(0,10) || <strong className="report-missing">[ DATE MISSING ]</strong>}, from {item.sourceCitation || <strong className="report-missing">[ SOURCE MISSING ]</strong>}:</p>
<blockquote>{unfinishedStatement(item.relationText) ? <strong className="report-missing">[ CONNECTION STATEMENT MISSING ]</strong> : item.relationText}</blockquote>
<p className="report-source-line">SOURCE LINK: {item.sourceUri ? <a href={item.sourceUri} target="_blank" rel="noreferrer">{item.sourceUri}</a> : <span>[ NOT RECORDED · OPTIONAL ]</span>}</p>
{rejected && <p className="report-evidence-diagnostic">NOT VERIFIED: {item.verification.detail}</p>}
</article> })}
</section>)}
<div className="report-investigator report-reference"><span>INVESTIGATOR</span><p>{investigatorName}</p></div>
{report.status !== 'draft' && <section className={`report-verdict ${report.status}`} role="status"><small>{report.status === 'accepted' ? 'REPORT ACCEPTED' : report.status === 'evidence_accepted_report_incomplete' ? 'EVIDENCE PASSED · REPORT RETURNED' : 'EVIDENCE NOT ESTABLISHED'}</small><p>{report.feedback}</p></section>}
<div className="report-rule" aria-hidden="true"/>
{report.status !== 'draft' && <section className={`report-verdict ${report.status}`} role="status"><h3>{report.status === 'accepted' ? 'REPORT ACCEPTED' : 'REPORT RETURNED'}</h3><p>{report.feedback}</p></section>}
{error && <p className="flag-error">{error}</p>}
<div className="case-report-actions"><button type="button" onClick={onClose}>RETURN TO BOARD</button>{report.status === 'accepted'
? <button className="primary" type="button" onClick={onContinue}>{hasNext ? 'CONTINUE' : 'CLOSE CASE'} <span></span></button>