fix: 8 quality + UX improvements
1. Cookie 'Zwecke' false positive: added 'um...zu', 'dienen', 'helfen', 'ermöglichen' patterns — catches purpose descriptions without 'Zweck' 2. Kurzhinweis: added empty all_checks for short documents (<200 words) 3. Bezeichnungsfeld: placeholder shows 'Version / Stand' for typed docs, 'Dokumentname' for 'Sonstiges' 4. DocCheckTab state persistence: entries + results survive navigation 5. DocCheck history: saves each check with date, doc count, findings 6. History display: 'Letzte Pruefungen' section at bottom of tab 7. ChecklistView: shows 'X von Y Pruefpunkten bestanden' per document 8. Results persist in localStorage across page navigation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,7 +64,11 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
|
|||||||
</span>
|
</span>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="text-sm font-medium text-gray-900 truncate">{r.label}</div>
|
<div className="text-sm font-medium text-gray-900 truncate">{r.label}</div>
|
||||||
<div className="text-xs text-gray-500 truncate">{r.url}</div>
|
<div className="text-xs text-gray-500 truncate">
|
||||||
|
{r.checks.length > 0
|
||||||
|
? `${r.checks.filter(c => c.passed).length} von ${r.checks.length} Pruefpunkten bestanden`
|
||||||
|
: r.url}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 shrink-0 ml-3">
|
<div className="flex items-center gap-3 shrink-0 ml-3">
|
||||||
|
|||||||
@@ -24,12 +24,25 @@ function newEntry(): DocEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function DocCheckTab() {
|
export function DocCheckTab() {
|
||||||
const [entries, setEntries] = useState<DocEntry[]>([newEntry()])
|
const [entries, setEntries] = useState<DocEntry[]>(() => {
|
||||||
|
if (typeof window === 'undefined') return [newEntry()]
|
||||||
|
try { const s = localStorage.getItem('doc-check-entries'); return s ? JSON.parse(s) : [newEntry()] } catch { return [newEntry()] }
|
||||||
|
})
|
||||||
const [checkCookieBanner, setCheckCookieBanner] = useState(false)
|
const [checkCookieBanner, setCheckCookieBanner] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [progress, setProgress] = useState('')
|
const [progress, setProgress] = useState('')
|
||||||
const [results, setResults] = useState<any>(null)
|
const [results, setResults] = useState<any>(() => {
|
||||||
|
if (typeof window === 'undefined') return null
|
||||||
|
try { const s = localStorage.getItem('doc-check-results'); return s ? JSON.parse(s) : null } catch { return null }
|
||||||
|
})
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [history, setHistory] = useState<{ date: string; urls: number; findings: number }[]>(() => {
|
||||||
|
if (typeof window === 'undefined') return []
|
||||||
|
try { return JSON.parse(localStorage.getItem('doc-check-history') || '[]') } catch { return [] }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Persist entries
|
||||||
|
React.useEffect(() => { localStorage.setItem('doc-check-entries', JSON.stringify(entries)) }, [entries])
|
||||||
|
|
||||||
const updateEntry = (id: string, field: keyof DocEntry, value: string) => {
|
const updateEntry = (id: string, field: keyof DocEntry, value: string) => {
|
||||||
setEntries(prev => prev.map(e => e.id === id ? { ...e, [field]: value } : e))
|
setEntries(prev => prev.map(e => e.id === id ? { ...e, [field]: value } : e))
|
||||||
@@ -94,6 +107,11 @@ export function DocCheckTab() {
|
|||||||
if (pollData.status === 'completed' && pollData.result) {
|
if (pollData.status === 'completed' && pollData.result) {
|
||||||
setResults(pollData.result)
|
setResults(pollData.result)
|
||||||
setProgress('')
|
setProgress('')
|
||||||
|
localStorage.setItem('doc-check-results', JSON.stringify(pollData.result))
|
||||||
|
const entry = { date: new Date().toISOString(), urls: validEntries.length, findings: pollData.result.total_findings || 0 }
|
||||||
|
const updated = [entry, ...history].slice(0, 30)
|
||||||
|
setHistory(updated)
|
||||||
|
localStorage.setItem('doc-check-history', JSON.stringify(updated))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (pollData.status === 'failed') {
|
if (pollData.status === 'failed') {
|
||||||
@@ -128,7 +146,7 @@ export function DocCheckTab() {
|
|||||||
type="text"
|
type="text"
|
||||||
value={entry.label}
|
value={entry.label}
|
||||||
onChange={e => updateEntry(entry.id, 'label', e.target.value)}
|
onChange={e => updateEntry(entry.id, 'label', e.target.value)}
|
||||||
placeholder="Bezeichnung (optional)"
|
placeholder={entry.type === 'other' ? 'Dokumentname' : 'Version / Stand (optional)'}
|
||||||
className="w-40 px-3 py-2.5 border border-gray-300 rounded-lg text-sm shrink-0"
|
className="w-40 px-3 py-2.5 border border-gray-300 rounded-lg text-sm shrink-0"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
@@ -243,6 +261,28 @@ export function DocCheckTab() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* History */}
|
||||||
|
{history.length > 0 && (
|
||||||
|
<div className="border border-gray-200 rounded-xl p-4">
|
||||||
|
<h4 className="text-sm font-medium text-gray-700 mb-2">Letzte Pruefungen</h4>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{history.map((h, i) => (
|
||||||
|
<div key={i} className="flex items-center justify-between text-sm py-1.5 border-b border-gray-50 last:border-0">
|
||||||
|
<span className="text-gray-600">
|
||||||
|
{new Date(h.date).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-xs text-gray-500">{h.urls} Dok.</span>
|
||||||
|
<span className={`text-xs font-medium ${h.findings > 0 ? 'text-amber-600' : 'text-green-600'}`}>
|
||||||
|
{h.findings} Findings
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,10 @@ COOKIE_CHECKLIST = [
|
|||||||
{"id": "cookie_types", "label": "Arten der Cookies",
|
{"id": "cookie_types", "label": "Arten der Cookies",
|
||||||
"patterns": [r"(?:notwendig|essentiell|funktional|statistik|marketing|tracking)", r"cookie.*(?:art|typ|kategori)"]},
|
"patterns": [r"(?:notwendig|essentiell|funktional|statistik|marketing|tracking)", r"cookie.*(?:art|typ|kategori)"]},
|
||||||
{"id": "purposes", "label": "Zwecke der Cookies",
|
{"id": "purposes", "label": "Zwecke der Cookies",
|
||||||
"patterns": [r"zweck.*cookie", r"cookie.*zweck", r"(?:wofuer|wozu|warum).*cookie"]},
|
"patterns": [r"zweck.*cookie", r"cookie.*zweck", r"(?:wofuer|wozu|warum).*cookie",
|
||||||
|
r"cookies?\s+(?:ein|ver)?\s*,?\s*um\s+", r"(?:setzen|verwenden|nutzen)\s+.*cookies?\s+.*(?:um|fuer|für)",
|
||||||
|
r"(?:analyse|marketing|tracking|funktional)\w*\s*cookies?\s*\.?\s*(?:um|damit|diese|sie)",
|
||||||
|
r"cookies?\s+(?:dienen|helfen|ermöglichen|ermoeglichen)"]},
|
||||||
{"id": "retention", "label": "Speicherdauer der Cookies",
|
{"id": "retention", "label": "Speicherdauer der Cookies",
|
||||||
"patterns": [r"(?:speicherdauer|laufzeit|gueltigk|ablauf).*cookie", r"cookie.*(?:\d+\s+(?:tag|monat|jahr)|session)"]},
|
"patterns": [r"(?:speicherdauer|laufzeit|gueltigk|ablauf).*cookie", r"cookie.*(?:\d+\s+(?:tag|monat|jahr)|session)"]},
|
||||||
{"id": "third_party", "label": "Drittanbieter-Cookies",
|
{"id": "third_party", "label": "Drittanbieter-Cookies",
|
||||||
@@ -232,6 +235,7 @@ def check_document_completeness(
|
|||||||
"doc_title": doc_title,
|
"doc_title": doc_title,
|
||||||
"doc_url": doc_url,
|
"doc_url": doc_url,
|
||||||
"doc_type": doc_type,
|
"doc_type": doc_type,
|
||||||
|
"all_checks": [], # No checks run for short documents
|
||||||
})
|
})
|
||||||
return findings
|
return findings
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user