feat(ui): refine governed workbench and dual-screen flows

This commit is contained in:
Jens
2026-08-30 06:00:28 +02:00
parent 80a2d1654d
commit a0884d64c9
48 changed files with 1360 additions and 184 deletions
@@ -11,6 +11,7 @@ import { formatError } from '../../lib/formatError'
interface DetectionReviewPanelProps {
projectId: string
qualityCheckId: string
reviewLocked?: boolean
onOpenEvidenceMap?: (qualityCheckId: string) => void
}
@@ -58,6 +59,7 @@ function formatScore(value: number | null | undefined): string {
export function DetectionReviewPanel({
projectId,
qualityCheckId,
reviewLocked = false,
onOpenEvidenceMap,
}: DetectionReviewPanelProps): JSX.Element {
const [queue, setQueue] = useState<DetectionReviewList | null>(null)
@@ -118,8 +120,12 @@ export function DetectionReviewPanel({
<section className="detection-review-panel" aria-label="Handmatige controle van beeldanalyse">
<div className="panel-title-row">
<div>
<h3>Fouten controleren</h3>
<p className="muted">Beoordeel alleen twijfelgevallen. Bevestigde fouten kunnen later veilig als trainingsfeedback worden gebruikt.</p>
<h3>{reviewLocked ? 'Beoordelingen bekijken' : 'Fouten controleren'}</h3>
<p className="muted">
{reviewLocked
? 'De demo toont het bestaande kwaliteitsbewijs zonder beoordelingen te wijzigen.'
: 'Beoordeel alleen twijfelgevallen. Bevestigde fouten kunnen later veilig als trainingsfeedback worden gebruikt.'}
</p>
</div>
<button type="button" className="secondary-action" onClick={() => onOpenEvidenceMap?.(qualityCheckId)}>
Op kaart bekijken
@@ -214,6 +220,7 @@ export function DetectionReviewPanel({
Beoordeling
<select
value={decision}
disabled={reviewLocked}
onChange={(event) => setDraftDecisions((current) => ({
...current,
[key]: event.target.value as DetectionReviewDecision,
@@ -231,10 +238,11 @@ export function DetectionReviewPanel({
maxLength={2000}
placeholder="Waarom is dit correct, fout of onzeker?"
value={draftNotes[key] ?? ''}
readOnly={reviewLocked}
onChange={(event) => setDraftNotes((current) => ({ ...current, [key]: event.target.value }))}
/>
</label>
<button type="button" className="primary-action" disabled={savingKey === key} onClick={() => void save(item)}>
<button type="button" className="primary-action" disabled={reviewLocked || savingKey === key} onClick={() => void save(item)}>
{savingKey === key ? 'Bewaren...' : 'Beoordeling bewaren'}
</button>
</div>