"use client"; import { useState, useEffect, useCallback } from "react"; import { TorrentDownloadDto } from "@/lib/api"; import { Card, CardContent, Button, Icon } from "@/app/components/ui"; import { useTranslation } from "@/lib/i18n/context"; import type { TranslationKey } from "@/lib/i18n/fr"; type TFunction = (key: TranslationKey, vars?: Record) => string; const STATUS_ACTIVE = new Set(["downloading", "completed", "importing"]); function statusLabel(status: string, t: TFunction): string { const map: Record = { downloading: "downloads.status.downloading", completed: "downloads.status.completed", importing: "downloads.status.importing", imported: "downloads.status.imported", error: "downloads.status.error", }; return t(map[status] ?? "downloads.status.error"); } function statusClass(status: string): string { switch (status) { case "downloading": return "bg-primary/10 text-primary"; case "completed": return "bg-warning/10 text-warning"; case "importing": return "bg-primary/10 text-primary"; case "imported": return "bg-success/10 text-success"; case "error": return "bg-destructive/10 text-destructive"; default: return "bg-muted/30 text-muted-foreground"; } } function formatVolumes(vols: number[]): string { return [...vols].sort((a, b) => a - b).map(v => `T${String(v).padStart(2, "0")}`).join(", "); } function formatDate(iso: string): string { return new Date(iso).toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); } interface DownloadsPageProps { initialDownloads: TorrentDownloadDto[]; } export function DownloadsPage({ initialDownloads }: DownloadsPageProps) { const { t } = useTranslation(); const [downloads, setDownloads] = useState(initialDownloads); const [filter, setFilter] = useState("all"); const [isRefreshing, setIsRefreshing] = useState(false); const refresh = useCallback(async (showSpinner = true) => { if (showSpinner) setIsRefreshing(true); try { const resp = await fetch("/api/torrent-downloads"); if (resp.ok) setDownloads(await resp.json()); } finally { if (showSpinner) setIsRefreshing(false); } }, []); // Auto-refresh every 5s while there are active downloads const hasActive = downloads.some(d => STATUS_ACTIVE.has(d.status)); useEffect(() => { if (!hasActive) return; const id = setInterval(() => refresh(false), 5000); return () => clearInterval(id); }, [hasActive, refresh]); const filters = [ { id: "all", label: t("common.all") }, { id: "active", label: t("downloads.filterActive") }, { id: "imported", label: t("downloads.status.imported") }, { id: "error", label: t("downloads.status.error") }, ]; const visible = downloads.filter(d => { if (filter === "all") return true; if (filter === "active") return STATUS_ACTIVE.has(d.status); return d.status === filter; }); return ( <>

{t("downloads.title")}

{/* Filter bar */}
{filters.map(f => ( ))}
{visible.length === 0 ? (

{t("downloads.empty")}

) : (
{visible.map(dl => ( ))}
)} ); } function DownloadCard({ dl }: { dl: TorrentDownloadDto }) { const { t } = useTranslation(); const importedCount = Array.isArray(dl.imported_files) ? dl.imported_files.length : 0; return (
{/* Status indicator */}
{dl.status === "importing" ? ( ) : dl.status === "imported" ? ( ) : dl.status === "error" ? ( ) : dl.status === "downloading" ? ( ) : ( )}
{/* Main info */}
{dl.series_name} {statusLabel(dl.status, t)}
{dl.expected_volumes.length > 0 && ( {t("downloads.volumes")} : {formatVolumes(dl.expected_volumes)} )} {dl.status === "imported" && importedCount > 0 && ( {importedCount} {t("downloads.filesImported")} )} {dl.qb_hash && ( {dl.qb_hash.slice(0, 8)}… )}
{dl.content_path && dl.status !== "imported" && (

{dl.content_path}

)} {dl.error_message && (

{dl.error_message}

)} {dl.status === "imported" && Array.isArray(dl.imported_files) && dl.imported_files.length > 0 && (
    {(dl.imported_files as Array<{ volume: number; destination: string }>).map((f, i) => (
  • T{String(f.volume).padStart(2, "0")} → {f.destination.split("/").pop()}
  • ))}
)}
{/* Timestamp */}

{formatDate(dl.created_at)}

{dl.updated_at !== dl.created_at && (

maj {formatDate(dl.updated_at)}

)}
); }