Spaces:
Sleeping
Sleeping
| // src/components/TileCard.tsx | |
| import React from 'react'; | |
| // Define the TypeScript interfaces corresponding to Pydantic schemas | |
| 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 { | |
| domain_id: string; | |
| name: string; | |
| } | |
| interface TileCardProps { | |
| tile: KnowledgeTile; | |
| availableDomains?: DomainConfig[]; | |
| onDomainChange?: (tileId: string, newDomainId: string) => void; | |
| } | |
| const TileCard: React.FC<TileCardProps> = ({ tile, availableDomains, onDomainChange }) => { | |
| const getVerificationColor = (type: string) => { | |
| switch (type) { | |
| case 'expert': | |
| case 'multi_expert': | |
| return 'border-green-500'; | |
| case 'community': | |
| return 'border-blue-500'; | |
| default: | |
| return 'border-gray-500'; | |
| } | |
| }; | |
| return ( | |
| <div className={`panel-section ${getVerificationColor(tile.verification_mark.verification_type)} border-l-4`}> | |
| <div className="flex justify-between items-start"> | |
| <h4 className="font-bold text-lg mb-1">{tile.topic}</h4> | |
| {availableDomains && availableDomains.length > 0 && onDomainChange ? ( | |
| <select | |
| value={tile.domain_id} | |
| onChange={(e) => onDomainChange(tile.tile_id, e.target.value)} | |
| style={{ | |
| fontSize: '12px', | |
| padding: '4px 8px', | |
| backgroundColor: '#2a2a2a', | |
| border: '1px solid #444', | |
| borderRadius: '4px', | |
| color: '#aaa', | |
| cursor: 'pointer' | |
| }} | |
| > | |
| {availableDomains.map(domain => ( | |
| <option key={domain.domain_id} value={domain.domain_id}> | |
| {domain.name} | |
| </option> | |
| ))} | |
| </select> | |
| ) : ( | |
| <span className="text-xs text-gray-400 font-mono">{tile.domain_id}</span> | |
| )} | |
| </div> | |
| <p className="text-sm text-gray-300 mb-3">{tile.content_preview}</p> | |
| <div className="text-xs text-gray-500 space-y-1"> | |
| <div> | |
| <strong>Confidence:</strong> | |
| <span style={{ color: `hsl(${(tile.confidence_score * 120)}, 100%, 50%)` }}> | |
| {` ${(tile.confidence_score * 100).toFixed(1)}%`} | |
| </span> | |
| </div> | |
| <div> | |
| <strong>Verification:</strong> {tile.verification_mark.verification_type} ({tile.verification_mark.verification_count}) | |
| </div> | |
| {tile.verification_mark.is_expert_verified && ( | |
| <div> | |
| <strong>Expert:</strong> {tile.verification_mark.expert_name || tile.verification_mark.expert_orcid_id} | |
| </div> | |
| )} | |
| <div> | |
| <strong>Updated:</strong> {new Date(tile.updated_at).toLocaleDateString()} | |
| </div> | |
| </div> | |
| {tile.tags.length > 0 && ( | |
| <div className="mt-2 flex flex-wrap gap-1"> | |
| {tile.tags.map(tag => ( | |
| <span key={tag} className="text-xs bg-gray-700 text-gray-300 px-2 py-1 rounded-full"> | |
| {tag} | |
| </span> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export default TileCard; | |