Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 29s
CI / test-go-edu-search (push) Successful in 28s
CI / test-python-klausur (push) Failing after 2m24s
CI / test-python-agent-core (push) Successful in 22s
CI / test-nodejs-website (push) Successful in 20s
Phase 1 of the clean architecture refactor: Replaces the 751-line ocr-overlay monolith with a modular pipeline. Each step gets its own component file. Frontend: /ai/ocr-kombi route with 11 steps (Upload, Orientation, PageSplit, Deskew, Dewarp, ContentCrop, OCR, Structure, GridBuild, GridReview, GroundTruth). Session list supports document grouping for multi-page uploads. Backend: New ocr_kombi/ module with multi-page PDF upload (splits PDF into N sessions with shared document_group_id). DB migration adds document_group_id and page_number columns. Old /ai/ocr-overlay remains fully functional for A/B testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { DOCUMENT_CATEGORIES, type DocumentCategory } from '@/app/(admin)/ai/ocr-pipeline/types'
|
|
|
|
interface SessionHeaderProps {
|
|
sessionName: string
|
|
activeCategory?: DocumentCategory
|
|
isGroundTruth: boolean
|
|
onUpdateCategory: (category: DocumentCategory) => void
|
|
}
|
|
|
|
export function SessionHeader({
|
|
sessionName,
|
|
activeCategory,
|
|
isGroundTruth,
|
|
onUpdateCategory,
|
|
}: SessionHeaderProps) {
|
|
const [showCategoryPicker, setShowCategoryPicker] = useState(false)
|
|
|
|
const catInfo = DOCUMENT_CATEGORIES.find(c => c.value === activeCategory)
|
|
|
|
return (
|
|
<div className="relative flex items-center gap-3 text-sm text-gray-500 dark:text-gray-400">
|
|
<span>
|
|
Aktive Session:{' '}
|
|
<span className="font-medium text-gray-700 dark:text-gray-300">{sessionName}</span>
|
|
</span>
|
|
<button
|
|
onClick={() => setShowCategoryPicker(!showCategoryPicker)}
|
|
className={`text-xs px-2.5 py-1 rounded-full border transition-colors ${
|
|
activeCategory
|
|
? 'bg-teal-50 dark:bg-teal-900/30 border-teal-200 dark:border-teal-700 text-teal-700 dark:text-teal-300 hover:bg-teal-100'
|
|
: 'bg-amber-50 dark:bg-amber-900/20 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-300 hover:bg-amber-100 animate-pulse'
|
|
}`}
|
|
>
|
|
{catInfo ? `${catInfo.icon} ${catInfo.label}` : 'Kategorie setzen'}
|
|
</button>
|
|
{isGroundTruth && (
|
|
<span className="text-xs px-2 py-0.5 rounded-full bg-amber-50 dark:bg-amber-900/20 border border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-300">
|
|
GT
|
|
</span>
|
|
)}
|
|
{showCategoryPicker && (
|
|
<div className="absolute left-0 top-full mt-1 z-20 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg p-2 grid grid-cols-2 gap-1 w-64">
|
|
{DOCUMENT_CATEGORIES.map(cat => (
|
|
<button
|
|
key={cat.value}
|
|
onClick={() => {
|
|
onUpdateCategory(cat.value)
|
|
setShowCategoryPicker(false)
|
|
}}
|
|
className={`text-xs px-2 py-1.5 rounded-md text-left transition-colors ${
|
|
activeCategory === cat.value
|
|
? 'bg-teal-100 dark:bg-teal-900/40 text-teal-700 dark:text-teal-300'
|
|
: 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
{cat.icon} {cat.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|