Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
204 lines
7.0 KiB
TypeScript
204 lines
7.0 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* DokumentCard - Card component for Abitur document grid view
|
|
* Features: Preview, Download, Add to Klausur actions
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import { FileText, Eye, Download, Plus, Calendar, Layers, BookOpen, ExternalLink } from 'lucide-react'
|
|
import type { AbiturDokument } from '@/lib/education/abitur-docs-types'
|
|
import { formatFileSize, FAECHER, NIVEAUS } from '@/lib/education/abitur-docs-types'
|
|
|
|
interface DokumentCardProps {
|
|
document: AbiturDokument
|
|
onPreview: (doc: AbiturDokument) => void
|
|
onDownload: (doc: AbiturDokument) => void
|
|
onAddToKlausur?: (doc: AbiturDokument) => void
|
|
}
|
|
|
|
export function DokumentCard({
|
|
document,
|
|
onPreview,
|
|
onDownload,
|
|
onAddToKlausur
|
|
}: DokumentCardProps) {
|
|
const [isHovered, setIsHovered] = useState(false)
|
|
|
|
const fachLabel = FAECHER.find(f => f.id === document.fach)?.label || document.fach
|
|
const niveauLabel = document.niveau === 'eA' ? 'Erhoehtes Niveau' : 'Grundlegendes Niveau'
|
|
|
|
const handleDownload = (e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
onDownload(document)
|
|
}
|
|
|
|
const handleAddToKlausur = (e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
onAddToKlausur?.(document)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg
|
|
transition-all duration-200 cursor-pointer group"
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
onClick={() => onPreview(document)}
|
|
>
|
|
{/* Header with Type Badge */}
|
|
<div className="relative h-32 bg-gradient-to-br from-slate-100 to-slate-50 flex items-center justify-center">
|
|
<FileText className="w-16 h-16 text-slate-300 group-hover:text-blue-400 transition-colors" />
|
|
|
|
{/* Type Badge */}
|
|
<div className="absolute top-3 left-3">
|
|
<span className={`px-2.5 py-1 rounded-full text-xs font-medium ${
|
|
document.typ === 'erwartungshorizont'
|
|
? 'bg-orange-100 text-orange-700'
|
|
: 'bg-purple-100 text-purple-700'
|
|
}`}>
|
|
{document.typ === 'erwartungshorizont' ? 'Erwartungshorizont' : 'Aufgabe'}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Year Badge */}
|
|
<div className="absolute top-3 right-3">
|
|
<span className="px-2 py-1 bg-white/80 backdrop-blur-sm rounded-lg text-xs font-semibold text-slate-700">
|
|
{document.jahr}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Status Badge */}
|
|
<div className="absolute bottom-3 right-3">
|
|
<span className={`px-2 py-0.5 rounded-full text-xs ${
|
|
document.status === 'indexed'
|
|
? 'bg-green-100 text-green-700'
|
|
: document.status === 'error'
|
|
? 'bg-red-100 text-red-700'
|
|
: 'bg-yellow-100 text-yellow-700'
|
|
}`}>
|
|
{document.status === 'indexed' ? 'Indexiert' : document.status === 'error' ? 'Fehler' : 'Ausstehend'}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Hover Overlay with Preview */}
|
|
{isHovered && (
|
|
<div className="absolute inset-0 bg-black/40 flex items-center justify-center">
|
|
<button
|
|
className="px-4 py-2 bg-white text-slate-800 rounded-lg font-medium
|
|
flex items-center gap-2 shadow-lg hover:bg-blue-50 transition-colors"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onPreview(document)
|
|
}}
|
|
>
|
|
<Eye className="w-4 h-4" />
|
|
Vorschau
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-4">
|
|
{/* Title */}
|
|
<h3 className="font-semibold text-slate-800 mb-2 line-clamp-2 min-h-[2.5rem]">
|
|
{fachLabel} {document.niveau} - Aufgabe {document.aufgaben_nummer}
|
|
</h3>
|
|
|
|
{/* Metadata */}
|
|
<div className="space-y-1.5 mb-4">
|
|
<div className="flex items-center gap-2 text-sm text-slate-500">
|
|
<BookOpen className="w-4 h-4" />
|
|
<span>{fachLabel}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-sm text-slate-500">
|
|
<Layers className="w-4 h-4" />
|
|
<span>{niveauLabel}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-sm text-slate-500">
|
|
<ExternalLink className="w-4 h-4" />
|
|
<span className="capitalize">{document.bundesland}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-xs text-slate-400">
|
|
<span>{formatFileSize(document.file_size)}</span>
|
|
<span>|</span>
|
|
<span>{document.dateiname}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => onPreview(document)}
|
|
className="flex-1 px-3 py-2 bg-blue-50 text-blue-700 rounded-lg hover:bg-blue-100
|
|
transition-colors text-sm font-medium flex items-center justify-center gap-1.5"
|
|
>
|
|
<Eye className="w-4 h-4" />
|
|
Vorschau
|
|
</button>
|
|
<button
|
|
onClick={handleDownload}
|
|
className="px-3 py-2 text-slate-600 hover:bg-slate-100 rounded-lg transition-colors"
|
|
title="Herunterladen"
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
</button>
|
|
{onAddToKlausur && (
|
|
<button
|
|
onClick={handleAddToKlausur}
|
|
className="px-3 py-2 text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
|
title="Zur Klausur hinzufuegen"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Compact card variant for list view or similar documents
|
|
*/
|
|
export function DokumentCardCompact({
|
|
document,
|
|
onPreview,
|
|
similarity_score
|
|
}: {
|
|
document: AbiturDokument
|
|
onPreview: (doc: AbiturDokument) => void
|
|
similarity_score?: number
|
|
}) {
|
|
const fachLabel = FAECHER.find(f => f.id === document.fach)?.label || document.fach
|
|
|
|
return (
|
|
<button
|
|
onClick={() => onPreview(document)}
|
|
className="w-full flex items-center gap-3 p-3 bg-white border border-slate-200 rounded-lg
|
|
hover:bg-slate-50 hover:border-slate-300 transition-colors text-left"
|
|
>
|
|
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center flex-shrink-0">
|
|
<FileText className="w-5 h-5 text-slate-400" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="font-medium text-slate-800 truncate">
|
|
{fachLabel} {document.jahr} - {document.niveau}
|
|
</div>
|
|
<div className="text-sm text-slate-500 truncate">
|
|
Aufgabe {document.aufgaben_nummer}
|
|
{document.typ === 'erwartungshorizont' && ' (EWH)'}
|
|
</div>
|
|
</div>
|
|
{similarity_score !== undefined && (
|
|
<div className="flex-shrink-0">
|
|
<span className="px-2 py-1 bg-green-100 text-green-700 text-xs rounded-full">
|
|
{Math.round(similarity_score * 100)}%
|
|
</span>
|
|
</div>
|
|
)}
|
|
</button>
|
|
)
|
|
}
|