/ concept-collection / seqlab
Sign in
concept-collection / seqlab
seqlab / src / viewer / ReportPanel.tsx
54 lines · 1.8 KBBlameHistoryRaw
1import type { ParsedSeq } from '../seq/types.ts'
2import type { Reconstructed } from '../seq/reconstruct.ts'
3import { formatGrad, formatNumber, formatSlew, formatTime } from './format'
5export interface ReportPanelProps {
6 seq: ParsedSeq
7 rec: Reconstructed
8}
10export default function ReportPanel({ seq, rec }: ReportPanelProps) {
11 const name = seq.definitions.get('Name')
12 const fov = seq.definitions.get('FOV')
13 const maxGrad = Math.max(rec.stats.maxGrad.gx, rec.stats.maxGrad.gy, rec.stats.maxGrad.gz)
14 const maxSlew = Math.max(rec.stats.maxSlew.gx, rec.stats.maxSlew.gy, rec.stats.maxSlew.gz)
16 const items: [string, React.ReactNode][] = [
17 ['Name', typeof name === 'string' ? name : '—'],
18 ['Format', `v${seq.version.major}.${seq.version.minor}.${seq.version.revision}`],
19 ['Duration', formatTime(rec.duration)],
20 ['Blocks', String(seq.blocks.length)],
21 ['RF pulses', String(rec.stats.rfCount)],
22 ['ADC', `${rec.stats.adcCount} windows / ${rec.stats.adcSamples} samples`],
23 ]
24 if (Array.isArray(fov)) {
25 items.push(['FOV', fov.map((v) => `${formatNumber(v * 1e3, 4)}`).join(' × ') + ' mm'])
26 }
27 if (maxGrad > 0) items.push(['Peak gradient', formatGrad(maxGrad)])
28 if (maxSlew > 0) items.push(['Peak slew', formatSlew(maxSlew)])
30 return (
31 <div className="report">
32 {items.map(([label, value]) => (
33 <div className="item" key={label}>
34 <b>{label}</b>
35 <span>{value}</span>
36 </div>
37 ))}
38 <div className="item">
39 <b>Signature</b>
40 <span>
41 {seq.signature ? (
42 seq.signature.valid ? (
43 <span className="badge good">md5 verified</span>
44 ) : (
45 <span className="badge bad">md5 mismatch</span>
46 )
47 ) : (
48 <span className="badge neutral">none</span>
49 )}
50 </span>
51 </div>
52 </div>
53 )
moveopenescclose