import { useState } from "react"; import { Button, Empty, Progress, SideSheet, Tag, Toast, } from "@douyinfe/semi-ui-19"; import { api, formatTime, jobLabels, jobStateLabels, post } from "../api"; import type { Job } from "../types"; export function JobPanel({ visible, jobs, timezone, onClose, onChange, onAccount, }: { visible: boolean; jobs: Job[]; timezone?: string; onClose: () => void; onChange: () => void; onAccount: () => void; }) { const [errors, setErrors] = useState< Record >({}); const [busy, setBusy] = useState(""); async function action(job: Job, name: string) { setBusy(job.id); try { await post(`/sync-jobs/${job.id}/${name}`); onChange(); } catch (e) { Toast.error((e as Error).message); } finally { setBusy(""); } } return (

同步进度保存在服务端,关闭此面板不会中断任务。

{!jobs.length && } {jobs.map((job) => (
{jobLabels[job.kind] ?? job.kind} {jobStateLabels[job.status] ?? job.status}

{formatTime(job.created_at, timezone)}

已处理 {job.processed} 失败 {job.failed} 总数 {job.total ?? "计算中"}
{job.total !== null && job.total > 0 && ( )} {job.error &&

{job.error}

} {job.next_retry_at && (

下次重试:{formatTime(job.next_retry_at, timezone)}

)}
{[ "queued", "running", "waiting_auth", "waiting_connection", ].includes(job.status) && ( )} {["failed", "cancelled", "completed_with_errors"].includes( job.status, ) && ( )} {["waiting_auth", "waiting_connection"].includes(job.status) && ( )} {job.failed > 0 && ( )}
{errors[job.id]?.map((e) => (

{e.alpha_id}:{e.error}

))}
))}
); }