Files
sbd2-frontend/src/components/extractions/ExtractionsRow.tsx

135 lines
4.1 KiB
TypeScript

import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { TableCell, TableRow } from '@/components/ui/table'
import type { ExtractionInfo } from '@/lib/api/services/extractions'
import { formatDateDistanceToNow } from '@/utils/format-date'
import {
AlertCircle,
Calendar,
CheckCircle,
Clock,
ExternalLink,
Loader2,
User
} from 'lucide-react'
interface ExtractionsRowProps {
extraction: ExtractionInfo
}
export function ExtractionsRow({ extraction }: ExtractionsRowProps) {
const getStatusBadge = (status: ExtractionInfo['status']) => {
switch (status) {
case 'pending':
return (
<Badge variant="secondary" className="gap-1">
<Clock className="h-3 w-3" />
Pending
</Badge>
)
case 'processing':
return (
<Badge variant="outline" className="gap-1">
<Loader2 className="h-3 w-3 animate-spin" />
Processing
</Badge>
)
case 'completed':
return (
<Badge variant="default" className="gap-1">
<CheckCircle className="h-3 w-3" />
Completed
</Badge>
)
case 'failed':
return (
<Badge variant="destructive" className="gap-1">
<AlertCircle className="h-3 w-3" />
Failed
</Badge>
)
}
}
const getServiceBadge = (service: string | undefined) => {
if (!service) return <span className="text-muted-foreground">-</span>
const serviceColors: Record<string, string> = {
youtube: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
soundcloud: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300',
vimeo: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
tiktok: 'bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-300',
twitter: 'bg-sky-100 text-sky-800 dark:bg-sky-900 dark:text-sky-300',
instagram: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300',
}
const colorClass =
serviceColors[service.toLowerCase()] ||
'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300'
return (
<Badge variant="outline" className={colorClass}>
{service.toUpperCase()}
</Badge>
)
}
return (
<TableRow className="hover:bg-muted/50">
<TableCell>
<div className="min-w-0">
<div className="font-medium truncate">
{extraction.title || 'Extracting...'}
</div>
<div className="text-sm text-muted-foreground truncate max-w-64">
{extraction.url}
</div>
</div>
</TableCell>
<TableCell className="text-center">
{getServiceBadge(extraction.service)}
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<User className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">
{extraction.user_name || 'Unknown'}
</span>
</div>
</TableCell>
<TableCell>
<div className="space-y-1">
{getStatusBadge(extraction.status)}
{extraction.error && (
<div
className="text-xs text-destructive max-w-48 truncate"
title={extraction.error}
>
{extraction.error}
</div>
)}
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-1 text-sm text-muted-foreground">
<Calendar className="h-3 w-3" />
{formatDateDistanceToNow(extraction.created_at)}
</div>
</TableCell>
<TableCell className="text-center">
<div className="flex items-center justify-center gap-1">
<Button variant="ghost" size="sm" asChild>
<a
href={extraction.url}
target="_blank"
rel="noopener noreferrer"
title="Open original URL"
>
<ExternalLink className="h-4 w-4" />
</a>
</Button>
</div>
</TableCell>
</TableRow>
)
}