// src/components/KnowledgePanel.tsx import { useState, useEffect } from 'react'; import apiClient from '../services/api'; import TileCard from './TileCard'; // Assuming TileCard is in the same directory // Define the TypeScript interfaces matching the backend schema interface VerificationMark { verification_type: string; is_expert_verified: boolean; expert_orcid_id?: string; expert_name?: string; verification_date?: string; verification_count: number; } interface KnowledgeTile { tile_id: string; domain_id: string; topic: string; content_preview: string; created_at: string; updated_at: string; verification_mark: VerificationMark; contributor_id?: string; confidence_score: number; tags: string[]; } interface DomainConfig { // Also defined in DBManager, but local here for clarity domain_id: string; name: string; } const KnowledgePanel = () => { const [tiles, setTiles] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(false); // Filter states const [searchTerm, setSearchTerm] = useState(''); const [filterDomain, setFilterDomain] = useState(''); const [availableDomains, setAvailableDomains] = useState([]); const [isExporting, setIsExporting] = useState(false); useEffect(() => { const fetchDomains = async () => { try { const response = await apiClient.get('/config/domains'); setAvailableDomains(response.data); } catch (err) { console.error("Failed to fetch domains:", err); setError("Failed to load domain filters."); } }; fetchDomains(); }, []); useEffect(() => { const fetchTiles = async () => { setIsLoading(true); setError(null); try { const response = await apiClient.get('/knowledge/', { params: { page: page, page_size: 20, domain_id: filterDomain || undefined, // Only send if not empty search: searchTerm || undefined, // Only send if not empty }, }); setTiles(response.data.tiles); setHasMore(response.data.has_more); } catch (err) { setError('Failed to fetch knowledge tiles.'); console.error(err); } finally { setIsLoading(false); } }; fetchTiles(); }, [page, filterDomain, searchTerm]); // Re-fetch tiles when page, filterDomain, or searchTerm changes const handleExportIath = async () => { setIsExporting(true); try { const response = await apiClient.get('/knowledge/export/iath', { params: { domain_id: filterDomain || undefined, precision: 'standard' // List View uses standard precision }, responseType: 'blob' // Important for binary data }); // Create download link const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; // Extract filename from Content-Disposition header or use default const contentDisposition = response.headers['content-disposition']; let filename = 'knowledge_base.iath'; if (contentDisposition) { const filenameMatch = contentDisposition.match(/filename="?(.+)"?/); if (filenameMatch) { filename = filenameMatch[1]; } } link.setAttribute('download', filename); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); } catch (err) { console.error('Export failed:', err); setError('Failed to export .iath file.'); } finally { setIsExporting(false); } }; const handleDomainUpdate = async (tileId: string, newDomainId: string) => { try { await apiClient.patch(`/knowledge/${tileId}/domain`, null, { params: { domain_id: newDomainId } }); // Update the tile in the local state setTiles(prev => prev.map(tile => tile.tile_id === tileId ? { ...tile, domain_id: newDomainId } : tile )); } catch (err) { console.error('Failed to update domain:', err); setError('Failed to update domain.'); } }; return (

Knowledge Browser

{ setSearchTerm(e.target.value); setPage(1); // Reset to first page on search }} />
{isLoading &&

Loading knowledge base... ...

} {error &&

{error}

} {!isLoading && !error && ( <>
{tiles.map(tile => ( ))}
Page {page}
)}
); }; export default KnowledgePanel;