| 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', |
| } |
|
|
| |
| |
| 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> |
| ) |
| } |
|
|