FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

78 строки
2.2 KiB

  1. "use client";
  2. import { useEffect, useMemo, useState } from "react";
  3. const STORAGE_KEY = "fpsms_server_wait_until_ms";
  4. const WAIT_SECONDS = 30;
  5. const RELOAD_INTERVAL_SECONDS = 5;
  6. function formatSeconds(s: number) {
  7. const v = Math.max(0, Math.floor(s));
  8. return `${v} 秒`;
  9. }
  10. /**
  11. * When a server-side exception occurs (e.g. backend down during deploy),
  12. * show a reconnect countdown and retry instead of immediate redirect.
  13. */
  14. export default function Error({
  15. error,
  16. reset,
  17. }: {
  18. error: Error & { digest?: string };
  19. reset: () => void;
  20. }) {
  21. const [remaining, setRemaining] = useState(WAIT_SECONDS);
  22. const waitUntilMs = useMemo(() => {
  23. if (typeof window === "undefined") return Date.now() + WAIT_SECONDS * 1000;
  24. const existing = Number(window.localStorage.getItem(STORAGE_KEY) || "0");
  25. const now = Date.now();
  26. if (existing && existing > now) return existing;
  27. const next = now + WAIT_SECONDS * 1000;
  28. window.localStorage.setItem(STORAGE_KEY, String(next));
  29. return next;
  30. }, []);
  31. useEffect(() => {
  32. const start = Date.now();
  33. const tick = () => {
  34. const now = Date.now();
  35. const msLeft = Math.max(0, waitUntilMs - now);
  36. setRemaining(msLeft / 1000);
  37. // When waiting time ends, go to login.
  38. if (msLeft <= 0) {
  39. window.localStorage.removeItem(STORAGE_KEY);
  40. window.location.href = "/login";
  41. }
  42. };
  43. tick();
  44. const interval = window.setInterval(tick, 250);
  45. // Reload periodically to give backend time to come back.
  46. const reloadTimer = window.setInterval(() => {
  47. const elapsedSec = (Date.now() - start) / 1000;
  48. if (elapsedSec >= WAIT_SECONDS) return;
  49. window.location.reload();
  50. }, RELOAD_INTERVAL_SECONDS * 1000);
  51. return () => {
  52. window.clearInterval(interval);
  53. window.clearInterval(reloadTimer);
  54. };
  55. }, [waitUntilMs]);
  56. return (
  57. <div className="flex min-h-[200px] flex-col items-center justify-center gap-2 p-6 text-center">
  58. <p className="text-sm font-semibold text-slate-700 dark:text-slate-200">
  59. 連線異常,伺服器暫停中
  60. </p>
  61. <p className="text-sm text-slate-600 dark:text-slate-400">
  62. 系統會在後台恢復後自動重試。倒數中:{formatSeconds(remaining)}
  63. </p>
  64. </div>
  65. );
  66. }