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>
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import type { UseRAGPageReturn } from '../_hooks/useRAGPage'
|
|
|
|
interface SearchTabProps {
|
|
hook: UseRAGPageReturn
|
|
}
|
|
|
|
export function SearchTab({ hook }: SearchTabProps) {
|
|
const {
|
|
searchQuery,
|
|
setSearchQuery,
|
|
searchResults,
|
|
searching,
|
|
selectedRegulations,
|
|
setSelectedRegulations,
|
|
handleSearch,
|
|
} = hook
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Search Box */}
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
|
<h3 className="font-semibold text-slate-900 mb-4">Semantische Suche</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">Suchanfrage</label>
|
|
<textarea
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
placeholder="z.B. 'Welche Anforderungen gibt es fuer KI-Systeme mit hohem Risiko?'"
|
|
rows={3}
|
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">Filter (optional)</label>
|
|
<div className="flex flex-wrap gap-2">
|
|
{['GDPR', 'AIACT', 'CRA', 'NIS2', 'BSI-TR-03161-1'].map((code) => (
|
|
<button
|
|
key={code}
|
|
onClick={() => {
|
|
setSelectedRegulations((prev: string[]) =>
|
|
prev.includes(code) ? prev.filter((c: string) => c !== code) : [...prev, code]
|
|
)
|
|
}}
|
|
className={`px-3 py-1 text-sm rounded-full border transition-colors ${
|
|
selectedRegulations.includes(code)
|
|
? 'bg-teal-100 border-teal-300 text-teal-700'
|
|
: 'bg-white border-slate-200 text-slate-600 hover:border-slate-300'
|
|
}`}
|
|
>
|
|
{code}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleSearch}
|
|
disabled={searching || !searchQuery.trim()}
|
|
className="px-6 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 disabled:opacity-50"
|
|
>
|
|
{searching ? 'Suche...' : 'Suchen'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Search Results */}
|
|
{searchResults.length > 0 && (
|
|
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
<div className="px-4 py-3 border-b bg-slate-50">
|
|
<h3 className="font-semibold text-slate-900">{searchResults.length} Ergebnisse</h3>
|
|
</div>
|
|
<div className="divide-y">
|
|
{searchResults.map((result, i) => (
|
|
<div key={i} className="p-4">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="px-2 py-0.5 text-xs rounded bg-teal-100 text-teal-700">
|
|
{result.regulation_code}
|
|
</span>
|
|
{result.article && (
|
|
<span className="text-sm text-slate-500">Art. {result.article}</span>
|
|
)}
|
|
<span className="ml-auto text-sm text-slate-400">
|
|
Score: {(result.score * 100).toFixed(1)}%
|
|
</span>
|
|
</div>
|
|
<p className="text-slate-700 text-sm">{result.text}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|