Spaces:
Build error
Build error
| import { useEffect, useState } from 'react'; | |
| import { Card, CardContent, CardHeader, CardTitle } from './ui/Card'; | |
| import { Button } from './ui/Button'; | |
| import { Filter, Download } from 'lucide-react'; | |
| export default function KnowledgeBase() { | |
| const [techniques, setTechniques] = useState([]); | |
| const [competition, setCompetition] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const fetchTechniques = async () => { | |
| setLoading(true); | |
| try { | |
| const response = await fetch(`/api/database/techniques${competition ? `?competition=${competition}` : ''}`); | |
| const data = await response.json(); | |
| setTechniques(data); | |
| } catch (error) { | |
| console.error('Failed to fetch techniques:', error); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const generateConfig = async () => { | |
| try { | |
| const response = await fetch('/api/config/generate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ competition, minConfidence: 0.8 }), | |
| }); | |
| const blob = await response.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'harvested_config.yaml'; | |
| a.click(); | |
| } catch (error) { | |
| console.error('Failed to generate config:', error); | |
| } | |
| }; | |
| useEffect(() => { | |
| fetchTechniques(); | |
| }, []); | |
| return ( | |
| <div className="space-y-6"> | |
| <h2 className="text-3xl font-bold text-gray-800">Knowledge Base</h2> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Extracted Techniques</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="flex gap-4 mb-6"> | |
| <input | |
| type="text" | |
| placeholder="Filter by competition" | |
| value={competition} | |
| onChange={(e) => setCompetition(e.target.value)} | |
| className="input-field flex-1" | |
| /> | |
| <Button onClick={fetchTechniques}> | |
| <Filter className="w-4 h-4 mr-2" /> | |
| Filter | |
| </Button> | |
| <Button onClick={generateConfig} variant="secondary"> | |
| <Download className="w-4 h-4 mr-2" /> | |
| Export Config | |
| </Button> | |
| </div> | |
| {loading ? ( | |
| <div className="text-center py-8">Loading...</div> | |
| ) : ( | |
| <div className="overflow-x-auto"> | |
| <table className="w-full"> | |
| <thead> | |
| <tr className="border-b"> | |
| <th className="text-left py-2">Parameter</th> | |
| <th className="text-left py-2">Value</th> | |
| <th className="text-left py-2">Confidence</th> | |
| <th className="text-left py-2">Source</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {techniques.map((technique) => ( | |
| <tr key={technique.id} className="border-b"> | |
| <td className="py-2 font-mono">{technique.name}</td> | |
| <td className="py-2">{technique.value}</td> | |
| <td className="py-2"> | |
| <div className="progress-container"> | |
| <div | |
| className="progress-bar" | |
| style={{ width: `${technique.confidence * 100}%` }} | |
| /> | |
| </div> | |
| <span className="text-sm">{(technique.confidence * 100).toFixed(1)}%</span> | |
| </td> | |
| <td className="py-2 text-sm text-gray-600"> | |
| {technique.rich_context ? 'AST' : 'Regex'} | |
| </td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| )} | |
| </CardContent> | |
| </Card> | |
| </div> | |
| ); | |
| } |