feat: TextReference component — original text, position, correction in findings
Shows for each finding: - Original text block from DSE (or "missing" indicator) - Position: section heading, number, parent section, paragraph index - Correction: insert/append/replace with copy button Falls back to plain correction view if no text reference available. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { TextReference } from './TextReference'
|
||||
|
||||
interface ServiceInfo {
|
||||
name: string
|
||||
@@ -14,11 +15,27 @@ interface ServiceInfo {
|
||||
status: string
|
||||
}
|
||||
|
||||
interface TextRef {
|
||||
found: boolean
|
||||
source_url: string
|
||||
document_type: string
|
||||
section_heading: string
|
||||
section_number: string
|
||||
parent_section: string
|
||||
paragraph_index: number
|
||||
original_text: string
|
||||
issue: string
|
||||
correction_type: string
|
||||
correction_text: string
|
||||
insert_after: string
|
||||
}
|
||||
|
||||
interface ScanFinding {
|
||||
code: string
|
||||
severity: string
|
||||
text: string
|
||||
correction: string
|
||||
text_reference: TextRef | null
|
||||
}
|
||||
|
||||
interface ScanData {
|
||||
@@ -157,7 +174,12 @@ export function ScanResult({ data }: { data: ScanData }) {
|
||||
</span>
|
||||
<p className="text-sm text-gray-800 flex-1">{f.text}</p>
|
||||
</div>
|
||||
{f.correction && (
|
||||
{/* Text Reference (original text + position + correction) */}
|
||||
{f.text_reference && (
|
||||
<TextReference ref={f.text_reference} correction={f.correction} />
|
||||
)}
|
||||
{/* Fallback: correction without text reference */}
|
||||
{!f.text_reference && f.correction && (
|
||||
<div className="mt-2">
|
||||
<button
|
||||
onClick={() => setExpandedCorrection(isExpanded ? null : f.code)}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
|
||||
interface TextRef {
|
||||
found: boolean
|
||||
source_url: string
|
||||
document_type: string
|
||||
section_heading: string
|
||||
section_number: string
|
||||
parent_section: string
|
||||
paragraph_index: number
|
||||
original_text: string
|
||||
issue: string
|
||||
correction_type: string
|
||||
correction_text: string
|
||||
insert_after: string
|
||||
}
|
||||
|
||||
const ISSUE_LABELS: Record<string, { label: string; color: string }> = {
|
||||
missing: { label: 'Fehlt in der DSE', color: 'text-red-700 bg-red-50' },
|
||||
incomplete: { label: 'Unvollstaendig', color: 'text-yellow-700 bg-yellow-50' },
|
||||
incorrect: { label: 'Fehlerhaft', color: 'text-orange-700 bg-orange-50' },
|
||||
}
|
||||
|
||||
const CORRECTION_LABELS: Record<string, string> = {
|
||||
insert: 'Neuen Abschnitt einfuegen',
|
||||
append: 'Am Ende des Absatzes ergaenzen',
|
||||
replace: 'Absatz ersetzen',
|
||||
}
|
||||
|
||||
export function TextReference({ ref, correction }: { ref: TextRef; correction?: string }) {
|
||||
const [showCorrection, setShowCorrection] = useState(false)
|
||||
const issue = ISSUE_LABELS[ref.issue] || null
|
||||
const correctionText = correction || ref.correction_text
|
||||
|
||||
return (
|
||||
<div className="mt-3 space-y-2 text-sm">
|
||||
{/* Original Text Block */}
|
||||
<div>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1 flex items-center gap-1">
|
||||
<span>📄</span> Originaltextblock:
|
||||
</p>
|
||||
<div className={`rounded-lg p-3 border ${ref.found ? 'bg-gray-50 border-gray-200' : 'bg-red-50 border-red-200'}`}>
|
||||
{ref.found ? (
|
||||
<p className="text-gray-700 text-xs whitespace-pre-wrap">{ref.original_text || '(Textinhalt konnte nicht extrahiert werden)'}</p>
|
||||
) : (
|
||||
<p className="text-red-600 text-xs italic">Nicht vorhanden — Eintrag fehlt in der {ref.document_type}.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Position */}
|
||||
<div>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1 flex items-center gap-1">
|
||||
<span>📍</span> Position:
|
||||
</p>
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2 text-xs text-blue-800">
|
||||
{ref.found ? (
|
||||
<>
|
||||
<span className="font-semibold">{ref.section_heading || 'Abschnitt unbekannt'}</span>
|
||||
{ref.section_number && <span className="text-blue-600 ml-1">(Nr. {ref.section_number})</span>}
|
||||
{ref.parent_section && <span className="text-blue-500 ml-1">in: {ref.parent_section}</span>}
|
||||
{ref.paragraph_index > 0 && <span className="text-blue-500 ml-1">| Absatz {ref.paragraph_index}</span>}
|
||||
</>
|
||||
) : ref.insert_after ? (
|
||||
<span><strong>{CORRECTION_LABELS[ref.correction_type] || 'Einfuegen'}</strong> nach Abschnitt "{ref.insert_after}"</span>
|
||||
) : (
|
||||
<span>Neuen Abschnitt in der {ref.document_type} anlegen</span>
|
||||
)}
|
||||
{ref.source_url && (
|
||||
<div className="text-blue-400 mt-1 truncate">in: {ref.source_url}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Correction */}
|
||||
{correctionText && (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowCorrection(!showCorrection)}
|
||||
className="text-xs text-purple-600 hover:text-purple-800 font-medium flex items-center gap-1"
|
||||
>
|
||||
<span>{showCorrection ? '▼' : '▶'}</span>
|
||||
<span>✏️</span> Korrekturvorschlag {showCorrection ? 'ausblenden' : 'anzeigen'}
|
||||
</button>
|
||||
{showCorrection && (
|
||||
<div className="mt-2 bg-white border border-purple-200 rounded-lg p-3 relative">
|
||||
{issue && (
|
||||
<span className={`text-[10px] px-2 py-0.5 rounded-full font-medium mb-2 inline-block ${issue.color}`}>
|
||||
{CORRECTION_LABELS[ref.correction_type] || issue.label}
|
||||
</span>
|
||||
)}
|
||||
<pre className="text-xs text-gray-700 whitespace-pre-wrap font-sans mt-1">{correctionText}</pre>
|
||||
<button
|
||||
onClick={() => navigator.clipboard.writeText(correctionText)}
|
||||
className="absolute top-2 right-2 text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded transition-colors"
|
||||
title="In Zwischenablage kopieren"
|
||||
>
|
||||
📋 Kopieren
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user