50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
"use client";
|
|
import { useI18n } from "@/components/I18nProvider";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import {
|
|
processingStage,
|
|
type ProcessingJob,
|
|
} from "@/shared/processing-progress";
|
|
|
|
export function ProcessingProgress({ job }: { job: ProcessingJob | null }) {
|
|
const { t } = useI18n();
|
|
const [now, setNow] = useState<number | null>(null);
|
|
useEffect(() => {
|
|
setNow(Date.now());
|
|
const timer = setInterval(() => setNow(Date.now()), 5000);
|
|
return () => clearInterval(timer);
|
|
}, []);
|
|
if (!job || !["queued", "running"].includes(job.state)) return null;
|
|
const progress = Math.max(0, Math.min(100, job.progress));
|
|
const stopped =
|
|
now !== null &&
|
|
job.state === "running" &&
|
|
(!job.lease_until || Date.parse(job.lease_until) < now);
|
|
return (
|
|
<div className="processing-progress" role="status" aria-live="polite">
|
|
<p>
|
|
{job.state === "queued"
|
|
? t("Waiting for a processing worker")
|
|
: `${progress}% · ${job.kind === "map" ? t("Refreshing guessing map") : t(processingStage(progress))}`}
|
|
</p>
|
|
<progress
|
|
max={100}
|
|
value={progress}
|
|
aria-label={t("Map processing progress")}
|
|
/>
|
|
<p className="muted">
|
|
{stopped
|
|
? t(
|
|
"The worker has stopped reporting. Check its terminal and restart it if necessary. Your uploaded export is saved.",
|
|
)
|
|
: job.state === "running"
|
|
? t(
|
|
"Worker connected. Large exports can take a while; progress measures completed work.",
|
|
)
|
|
: t("Processing will start when a worker is available.")}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|