File size: 5,030 Bytes
b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 fd1e711 b2b6341 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import { FileText, Globe, Youtube, Loader2, CheckCircle, AlertCircle } from 'lucide-react';
import { KnowledgeSource } from '../types';
interface SourceCardProps {
source: KnowledgeSource;
compact?: boolean;
}
const languageColors = {
EN: 'bg-blue-500/10 text-blue-600 dark:text-blue-400',
HI: 'bg-orange-500/10 text-orange-600 dark:text-orange-400',
TE: 'bg-purple-500/10 text-purple-600 dark:text-purple-400',
ES: 'bg-green-500/10 text-green-600 dark:text-green-400',
FR: 'bg-pink-500/10 text-pink-600 dark:text-pink-400',
DE: 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400',
};
const statusIcons: Record<string, any> = {
pending: Loader2,
processing: Loader2,
completed: CheckCircle,
failed: AlertCircle,
IDLE: Loader2,
UPLOADING: Loader2,
EXTRACTING: Loader2,
CHUNKING: Loader2,
EMBEDDING: Loader2,
INDEXING: Loader2,
};
const statusColors: Record<string, string> = {
pending: 'text-gray-400',
processing: 'text-blue-500 animate-spin',
completed: 'text-green-500',
failed: 'text-red-500',
IDLE: 'text-gray-400',
UPLOADING: 'text-[#6366F1] animate-spin',
EXTRACTING: 'text-indigo-400 animate-spin',
CHUNKING: 'text-blue-400 animate-spin',
EMBEDDING: 'text-purple-400 animate-spin',
INDEXING: 'text-amber-500 animate-spin',
};
export function SourceCard({ source, compact = false }: SourceCardProps) {
const Icon = source.type === 'pdf' ? FileText : source.type === 'web' ? Globe : Youtube;
const StatusIcon = statusIcons[source.status] || Loader2;
if (compact) {
return (
<div className="p-3 rounded-lg bg-white/5 backdrop-blur-md border border-white/10 hover:border-[#6366F1] transition-colors group cursor-pointer">
<div className="flex items-start gap-3">
<div className="p-2 rounded-lg bg-white/5 border border-white/10 group-hover:border-[#6366F1] transition-colors">
<Icon className="w-3.5 h-3.5 text-white/60" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2 mb-1">
<h4 className="text-xs text-white truncate">
{source.title}
</h4>
<StatusIcon className={`w-3 h-3 flex-shrink-0 ${statusColors[source.status] || 'text-gray-400'}`} />
</div>
<div className="flex items-center gap-1.5">
<span className={`px-1.5 py-0.5 rounded text-[10px] ${languageColors[source.language]}`}>
{source.language}
</span>
{source.chunkCount !== undefined && source.chunkCount > 0 && (
<span className="text-[10px] text-gray-500 dark:text-gray-400">
{source.chunkCount} chunks
</span>
)}
</div>
</div>
</div>
</div>
);
}
return (
<div className="p-5 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 hover:border-[#6366F1] hover:shadow-lg transition-all group cursor-pointer">
<div className="flex items-start gap-4">
<div className="p-3 rounded-xl bg-white/5 border border-white/10 group-hover:border-[#6366F1] transition-colors">
<Icon className="w-5 h-5 text-white/60" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-3 mb-2">
<h3 className="text-white truncate">
{source.title}
</h3>
<StatusIcon className={`w-4 h-4 flex-shrink-0 ${statusColors[source.status] || 'text-gray-400'}`} />
</div>
<div className="flex items-center gap-2 mb-3">
<span className={`px-2 py-1 rounded-md text-xs ${languageColors[source.language]}`}>
{source.language}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400 capitalize font-medium">
{source.status.toLowerCase()} {source.progress_percentage !== undefined && source.progress_percentage > 0 && source.progress_percentage < 100 ? ` (${source.progress_percentage}%)` : ''}
</span>
</div>
<div className="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400">
{source.chunkCount !== undefined && source.chunkCount > 0 && (
<span>{source.chunkCount} chunks</span>
)}
{source.metadata?.pageCount && (
<span>{source.metadata.pageCount} pages</span>
)}
{source.metadata?.domain && (
<span className="truncate">{source.metadata.domain}</span>
)}
{source.metadata?.duration && (
<span>{source.metadata.duration}</span>
)}
</div>
{source.lastProcessed && (
<div className="text-xs text-gray-400 dark:text-gray-500 mt-2">
Processed {source.lastProcessed.toLocaleDateString()}
</div>
)}
</div>
</div>
</div>
);
}
|