Files
CPLv4.0/components/common/GlobalActivityOverlay.tsx

133 lines
4.7 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import { useAppSelector } from "@/redux/store";
export default function GlobalActivityOverlay() {
const anyLoop = useAppSelector((s) => s.deviceEvents.anyLoopActive);
const anyTdr = useAppSelector((s) => s.deviceEvents.anyTdrActive);
const anyAlign = useAppSelector((s) => s.deviceEvents.anyAlignmentActive);
const ksx = useAppSelector((s) => s.deviceEvents.ksx);
const ksy = useAppSelector((s) => s.deviceEvents.ksy);
const ksz = useAppSelector((s) => s.deviceEvents.ksz);
const loopStartedAt = useAppSelector((s) => s.deviceEvents.loopStartedAt);
const tdrStartedAt = useAppSelector((s) => s.deviceEvents.tdrStartedAt);
const alignmentStartedAt = useAppSelector(
(s) => s.deviceEvents.alignmentStartedAt
);
const fmt = (arr: number[]) =>
arr
.map((v, i) => (v ? i + 1 : 0))
.filter((n) => n !== 0)
.join(", ");
// Simple 1s ticker so progress bars advance while overlay is shown
const [now, setNow] = useState<number>(Date.now());
useEffect(() => {
const active = anyLoop || anyTdr || anyAlign;
if (!active) return;
const id = setInterval(() => setNow(Date.now()), 1000);
return () => clearInterval(id);
}, [anyLoop, anyTdr, anyAlign]);
const active = anyLoop || anyTdr || anyAlign;
if (!active) return null;
const clamp = (v: number, min = 0, max = 1) =>
Math.max(min, Math.min(max, v));
const compute = (startedAt: number | null, durationMs: number) => {
if (!startedAt) return { pct: 0, remaining: durationMs };
const elapsed = now - startedAt;
const pct = clamp(elapsed / durationMs) * 100;
const remaining = Math.max(0, durationMs - Math.max(0, elapsed));
return { pct, remaining };
};
// Durations
const LOOP_MS = 2 * 60 * 1000; // ~2 min
const TDR_MS = 30 * 1000; // ~30 s
const ALIGN_MS = 10 * 60 * 1000; // ~10 min
return (
<div className="fixed inset-0 z-[2000] flex items-center justify-center bg-white/70 backdrop-blur-sm">
<div className="p-4 rounded-md shadow bg-white border border-gray-200 w-[min(90vw,680px)]">
<div className="font-semibold mb-3">Bitte warten</div>
<div className="space-y-3">
{anyLoop && (
<div>
<div className="text-sm text-gray-800 mb-1">
Schleifenmessung läuft (: {fmt(ksx)})
</div>
{(() => {
const { pct } = compute(loopStartedAt, LOOP_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
<div
className="h-full bg-littwin-blue transition-all"
style={{ width: `${pct}%` }}
/>
</div>
<div className="text-xs text-gray-600 mt-1">
{Math.round(pct)}%
</div>
</div>
);
})()}
</div>
)}
{anyTdr && (
<div>
<div className="text-sm text-gray-800 mb-1">
TDR-Messung läuft (: {fmt(ksy)})
</div>
{(() => {
const { pct } = compute(tdrStartedAt, TDR_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
<div
className="h-full bg-littwin-blue transition-all"
style={{ width: `${pct}%` }}
/>
</div>
<div className="text-xs text-gray-600 mt-1">
{Math.round(pct)}%
</div>
</div>
);
})()}
</div>
)}
{anyAlign && (
<div>
<div className="text-sm text-gray-800 mb-1">
Abgleich läuft (: {fmt(ksz)}) kann bis zu 10 Minuten dauern
</div>
{(() => {
const { pct } = compute(alignmentStartedAt, ALIGN_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
<div
className="h-full bg-littwin-blue transition-all"
style={{ width: `${pct}%` }}
/>
</div>
<div className="text-xs text-gray-600 mt-1">
{Math.round(pct)}%
</div>
</div>
);
})()}
</div>
)}
</div>
</div>
</div>
);
}