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 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
196 lines
7.4 KiB
TypeScript
196 lines
7.4 KiB
TypeScript
'use client'
|
||
|
||
import React from 'react'
|
||
import type { UseRAGPageReturn } from '../_hooks/useRAGPage'
|
||
|
||
interface DataTabProps {
|
||
hook: UseRAGPageReturn
|
||
}
|
||
|
||
export function DataTab({ hook }: DataTabProps) {
|
||
const {
|
||
customDocuments,
|
||
uploadFile,
|
||
setUploadFile,
|
||
uploadTitle,
|
||
setUploadTitle,
|
||
uploadCode,
|
||
setUploadCode,
|
||
uploading,
|
||
handleUpload,
|
||
linkUrl,
|
||
setLinkUrl,
|
||
linkTitle,
|
||
setLinkTitle,
|
||
linkCode,
|
||
setLinkCode,
|
||
addingLink,
|
||
handleAddLink,
|
||
handleDeleteDocument,
|
||
fetchCustomDocuments,
|
||
} = hook
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* Upload Document */}
|
||
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
||
<h3 className="font-semibold text-slate-900 mb-4">Dokument hochladen (PDF)</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">PDF-Datei</label>
|
||
<input
|
||
type="file"
|
||
accept=".pdf"
|
||
onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
|
||
className="w-full px-3 py-2 border rounded-lg text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">Titel</label>
|
||
<input
|
||
type="text"
|
||
value={uploadTitle}
|
||
onChange={(e) => setUploadTitle(e.target.value)}
|
||
placeholder="z.B. Firmen-Datenschutzrichtlinie"
|
||
className="w-full px-3 py-2 border rounded-lg"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">Code (eindeutig)</label>
|
||
<input
|
||
type="text"
|
||
value={uploadCode}
|
||
onChange={(e) => setUploadCode(e.target.value.toUpperCase())}
|
||
placeholder="z.B. CUSTOM-DSR-01"
|
||
className="w-full px-3 py-2 border rounded-lg font-mono"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={handleUpload}
|
||
disabled={uploading || !uploadFile || !uploadTitle || !uploadCode}
|
||
className="mt-4 px-6 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 disabled:opacity-50"
|
||
>
|
||
{uploading ? 'Wird hochgeladen...' : 'Hochladen & Indexieren'}
|
||
</button>
|
||
</div>
|
||
|
||
{/* Add Link */}
|
||
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
||
<h3 className="font-semibold text-slate-900 mb-4">Link hinzufuegen (Webseite/PDF)</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">URL</label>
|
||
<input
|
||
type="url"
|
||
value={linkUrl}
|
||
onChange={(e) => setLinkUrl(e.target.value)}
|
||
placeholder="https://example.com/document.pdf"
|
||
className="w-full px-3 py-2 border rounded-lg"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">Titel</label>
|
||
<input
|
||
type="text"
|
||
value={linkTitle}
|
||
onChange={(e) => setLinkTitle(e.target.value)}
|
||
placeholder="z.B. BSI IT-Grundschutz"
|
||
className="w-full px-3 py-2 border rounded-lg"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 mb-2">Code (eindeutig)</label>
|
||
<input
|
||
type="text"
|
||
value={linkCode}
|
||
onChange={(e) => setLinkCode(e.target.value.toUpperCase())}
|
||
placeholder="z.B. BSI-GRUNDSCHUTZ"
|
||
className="w-full px-3 py-2 border rounded-lg font-mono"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={handleAddLink}
|
||
disabled={addingLink || !linkUrl || !linkTitle || !linkCode}
|
||
className="mt-4 px-6 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 disabled:opacity-50"
|
||
>
|
||
{addingLink ? 'Wird hinzugefuegt...' : 'Link hinzufuegen & Indexieren'}
|
||
</button>
|
||
</div>
|
||
|
||
{/* Custom Documents List */}
|
||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||
<div className="px-4 py-3 border-b bg-slate-50 flex items-center justify-between">
|
||
<h3 className="font-semibold text-slate-900">Eigene Dokumente ({customDocuments.length})</h3>
|
||
<button
|
||
onClick={fetchCustomDocuments}
|
||
className="text-sm text-teal-600 hover:text-teal-700"
|
||
>
|
||
Aktualisieren
|
||
</button>
|
||
</div>
|
||
{customDocuments.length === 0 ? (
|
||
<div className="p-8 text-center text-slate-500">
|
||
Noch keine eigenen Dokumente hinzugefuegt.
|
||
</div>
|
||
) : (
|
||
<div className="divide-y">
|
||
{customDocuments.map((doc) => (
|
||
<div key={doc.id} className="px-4 py-3 flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<span className="w-8 h-8 rounded-lg bg-slate-100 flex items-center justify-center text-lg">
|
||
{doc.url ? '🔗' : '📄'}
|
||
</span>
|
||
<div>
|
||
<p className="font-medium text-slate-900">{doc.title}</p>
|
||
<p className="text-sm text-slate-500">
|
||
<span className="font-mono text-teal-600">{doc.code}</span>
|
||
{' • '}
|
||
{doc.filename || doc.url}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-4">
|
||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
||
doc.status === 'indexed' ? 'bg-green-100 text-green-700' :
|
||
doc.status === 'error' ? 'bg-red-100 text-red-700' :
|
||
doc.status === 'processing' || doc.status === 'fetching' ? 'bg-blue-100 text-blue-700' :
|
||
'bg-slate-100 text-slate-700'
|
||
}`}>
|
||
{doc.status === 'indexed' ? `${doc.chunk_count} Chunks` :
|
||
doc.status === 'error' ? 'Fehler' :
|
||
doc.status === 'processing' ? 'Verarbeitung...' :
|
||
doc.status === 'fetching' ? 'Abruf...' :
|
||
doc.status}
|
||
</span>
|
||
<button
|
||
onClick={() => handleDeleteDocument(doc.id)}
|
||
className="text-red-500 hover:text-red-700 text-sm"
|
||
>
|
||
Loeschen
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Info Box */}
|
||
<div className="bg-teal-50 border border-teal-200 rounded-xl p-6">
|
||
<h4 className="font-semibold text-teal-800 flex items-center gap-2">
|
||
<span>ℹ️</span>
|
||
Hinweis zur Verwendung
|
||
</h4>
|
||
<p className="text-sm text-teal-700 mt-2">
|
||
Laden Sie eigene Dokumente (z.B. interne Datenschutzrichtlinien, Vertraege) oder
|
||
externe Links hoch. Diese werden automatisch in Chunks aufgeteilt und indexiert.
|
||
Nach dem Hinzufuegen koennen Sie im <strong>Pipeline</strong>-Tab die vollstaendige
|
||
Compliance-Analyse starten.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|