"use client"; import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAsyncEffect } from "@/hooks/use-async-effect"; import { useTranslationRef } from "@/hooks/use-translation-ref"; import { toast } from "sonner"; import { Download, RefreshCw } from "lucide-react"; import { downloadAdminReportJob, getAdminReportJobs } from "@/api/admin-report-jobs"; import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu"; import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state"; import { AdminStatusBadge } from "@/components/admin/admin-status-badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter"; import { LotteryApiBizError } from "@/types/api/errors"; import type { AdminReportJobRow } from "@/types/api/admin-report-jobs"; function downloadBlob(blob: Blob, filename: string): void { const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; document.body.appendChild(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); } type ReportJobsPanelProps = { canExport: boolean; refreshToken?: number; reportType?: string; }; export function ReportJobsPanel({ canExport, refreshToken = 0, reportType }: ReportJobsPanelProps) { const { t } = useTranslation(["reports", "common"]); const tRef = useTranslationRef(["reports", "common"]); const formatTs = useAdminDateTimeFormatter(); const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [downloadingId, setDownloadingId] = useState(null); const loadJobs = useCallback(async () => { setLoading(true); try { const data = await getAdminReportJobs({ page: 1, per_page: 10, report_type: reportType || undefined }); setJobs(data.items); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : tRef.current("tasks.loadFailed")); setJobs([]); } finally { setLoading(false); } }, [reportType, tRef]); useAsyncEffect(() => { void loadJobs(); }, [refreshToken]); async function handleDownload(job: AdminReportJobRow): Promise { if (!canExport || job.status !== "completed") { return; } setDownloadingId(job.id); try { const { blob, filename } = await downloadAdminReportJob(job.id); const fallback = `${job.job_no}.${job.export_format}`; downloadBlob(blob, filename ?? fallback); toast.success(t("tasks.downloadSuccess", { jobNo: job.job_no })); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("tasks.downloadFailed")); } finally { setDownloadingId(null); } } function reportTypeLabel(reportType: string): string { const key = `jobTypes.${reportType}`; const label = t(key); return label === key ? reportType : label; } return ( {t("recentTasks")}

{reportType ? t("tasks.currentReportHint") : t("exportHint")}

{t("tasks.columns.jobNo")} {t("tasks.columns.report")} {t("tasks.columns.format")} {t("tasks.columns.status")} {t("tasks.columns.createdAt")} {t("tasks.columns.actions")} {loading ? ( ) : jobs.length === 0 ? ( ) : ( jobs.map((job) => ( {job.job_no} {reportTypeLabel(job.report_type)} {job.export_format} {t(`tasks.status.${job.status}`, { defaultValue: job.status })} {formatTs(job.created_at ?? job.finished_at)} void handleDownload(job), }, ]} /> )) )}
); }