|
- "use client";
-
- import { useEffect, useMemo, useState } from "react";
-
- const STORAGE_KEY = "fpsms_server_wait_until_ms";
- const WAIT_SECONDS = 30;
- const RELOAD_INTERVAL_SECONDS = 5;
-
- function formatSeconds(s: number) {
- const v = Math.max(0, Math.floor(s));
- return `${v} 秒`;
- }
-
- /**
- * Catches root-level errors (e.g. backend down during deploy).
- * Shows a reconnect countdown and retries, then forwards to login if still failing.
- * Must define <html> and <body> because this replaces the root layout.
- */
- export default function GlobalError({
- error,
- reset,
- }: {
- error: Error & { digest?: string };
- reset: () => void;
- }) {
- const [remaining, setRemaining] = useState(WAIT_SECONDS);
-
- const waitUntilMs = useMemo(() => {
- const existing = Number(window.localStorage.getItem(STORAGE_KEY) || "0");
- const now = Date.now();
- if (existing && existing > now) return existing;
- const next = now + WAIT_SECONDS * 1000;
- window.localStorage.setItem(STORAGE_KEY, String(next));
- return next;
- }, []);
-
- useEffect(() => {
- const start = Date.now();
- const tick = () => {
- const now = Date.now();
- const msLeft = Math.max(0, waitUntilMs - now);
- setRemaining(msLeft / 1000);
-
- if (msLeft <= 0) {
- window.localStorage.removeItem(STORAGE_KEY);
- window.location.href = "/login";
- }
- };
-
- tick();
- const interval = window.setInterval(tick, 250);
-
- const reloadTimer = window.setInterval(() => {
- const elapsedSec = (Date.now() - start) / 1000;
- if (elapsedSec >= WAIT_SECONDS) return;
- window.location.reload();
- }, RELOAD_INTERVAL_SECONDS * 1000);
-
- return () => {
- window.clearInterval(interval);
- window.clearInterval(reloadTimer);
- };
- }, [waitUntilMs]);
-
- return (
- <html lang="zh-TW">
- <body>
- <div
- style={{
- display: "flex",
- minHeight: "100vh",
- alignItems: "center",
- justifyContent: "center",
- fontFamily: "system-ui, sans-serif",
- padding: "1rem",
- }}
- >
- <p style={{ color: "#334155", fontSize: "0.95rem", fontWeight: 700 }}>
- 連線異常,伺服器暫停中
- </p>
- <p style={{ color: "#64748b", fontSize: "0.875rem", marginTop: 8 }}>
- 系統會在後台恢復後自動重試。倒數中:{formatSeconds(remaining)}
- </p>
- </div>
- </body>
- </html>
- );
- }
|