amanpay / web /src /components /ResultPanel.tsx
MHamdan's picture
CI deploy e9bfd26
004f460 verified
Raw
History Blame Contribute Delete
2.14 kB
import { useI18n } from '../i18n'
import { Callout } from './ui'
import type { BiometricResult } from '../biometrics/results'
const TONE: Record<BiometricResult['summaryKey'], 'ok' | 'bad' | 'warn'> = {
'result.match': 'ok', 'result.pass': 'ok', 'result.enrolled': 'ok',
'result.noMatch': 'bad', 'result.fail': 'bad', 'result.enrollFailed': 'bad',
'result.inconclusive': 'warn',
}
/** Renders a backend-produced biometric decision. Shows scores as understandable
* labels + a collapsible technical panel. Never renders templates/embeddings. */
export function ResultPanel({ r }: { r: BiometricResult }) {
const { t } = useI18n()
return (
<div className="result-panel">
<Callout tone={TONE[r.summaryKey]}>{t(r.summaryKey)}</Callout>
<dl className="summary-grid">
{r.similarity !== undefined && (<><dt>{t('result.similarity')}</dt><dd>{r.similarity.toFixed(3)}</dd></>)}
{r.threshold !== undefined && (<><dt>{t('result.threshold')}</dt><dd>{r.threshold.toFixed(3)}</dd></>)}
{r.deepfakeScore != null && (
<><dt>{t('result.deepfake')}</dt>
<dd>{r.deepfakeScore.toFixed(3)} <span className="hint">({t('bio.experimental')})</span></dd></>
)}
</dl>
{r.perModality && Object.keys(r.perModality).length > 0 && (
<dl className="summary-grid">
<dt className="section">{t('bio.perModality')}</dt><dd />
{Object.entries(r.perModality).map(([m, s]) => (
<div key={m} style={{ display: 'contents' }}>
<dt>{t(`bio.modality.${m}` as 'bio.modality.face')}</dt><dd>{s.toFixed(3)}</dd>
</div>
))}
</dl>
)}
<details className="support">
<summary>{t('tech.title')}</summary>
<dl className="tech-grid mono" dir="ltr">
{r.userId && (<><dt>{t('bio.userId')}</dt><dd>{r.userId}</dd></>)}
{r.modalities && (<><dt>modalities</dt><dd>{r.modalities.join(', ')}</dd></>)}
<dt>{t('tech.requestId')}</dt><dd>{r.requestId ?? '—'}</dd>
<dt>{t('tech.created')}</dt><dd>{r.at}</dd>
</dl>
</details>
</div>
)
}