// Komoot import page — shows live progress for background bulk imports // and lets the user trigger a new import run. import { useEffect, useRef } from "react"; import { data, useFetcher, useRevalidator } from "react-router"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/sync.import.komoot"; import { loadKomootImport, komootImportAction } from "./sync.import.komoot.server"; export function meta() { return [{ title: "Import from Komoot — trails.cool" }]; } export async function loader({ request }: Route.LoaderArgs) { return data(await loadKomootImport(request)); } export async function action({ request }: Route.ActionArgs) { return await komootImportAction(request); } function formatDuration(seconds: number): string { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); if (h > 0) return `${h}h ${m}m`; return `${m}m`; } export default function KomootImportPage({ loaderData }: Route.ComponentProps) { const { batch } = loaderData; const { t } = useTranslation("journal"); const revalidator = useRevalidator(); const triggerFetcher = useFetcher<{ error?: string }>(); const pollingRef = useRef | null>(null); const isActive = batch?.status === "pending" || batch?.status === "running"; useEffect(() => { if (isActive) { pollingRef.current = setInterval(() => { revalidator.revalidate(); }, 2000); } return () => { if (pollingRef.current) clearInterval(pollingRef.current); }; }, [isActive]); const elapsedSeconds = batch ? Math.floor( (batch.completedAt ? new Date(batch.completedAt).getTime() : Date.now()) - new Date(batch.startedAt).getTime(), ) / 1000 : 0; return (

{t("sync.importFrom", { provider: "Komoot" })}

{!batch && (

{t("komoot.import.noImportYet")}

)} {batch && (
{(batch.status === "completed" || batch.status === "failed") && ( )}
{isActive && (
0 ? `${Math.round(((batch.importedCount + batch.duplicateCount) / batch.totalFound) * 100)}%` : "5%", }} />
)}
{batch.status === "completed" && (

{t("komoot.import.completedIn", { duration: formatDuration(elapsedSeconds) })}

)} {batch.status === "failed" && batch.errorMessage && (

{batch.errorMessage}

)}
)}
{batch?.status === "completed" && (

{t("komoot.import.viewActivities")}

)}
); } function StatusBadge({ status, t }: { status: string; t: (k: string) => string }) { const styles: Record = { pending: "bg-gray-100 text-gray-600", running: "bg-blue-100 text-blue-700", completed: "bg-green-100 text-green-700", failed: "bg-red-100 text-red-700", }; return ( {t(`komoot.import.status.${status}`)} ); } function StatBox({ label, value }: { label: string; value: number }) { return (

{value}

{label}

); }