The backend now answers 503 with code MAINTENANCE_MODE while maintenance is on. Without this change a visitor got a red error toast over a broken page client-side, and a silently empty page server-side, because fetchReq discards the status and returns null on any failure. - lib/maintenance.js detects the state by BOTH status 503 and the error code; a bare 503 can come from a reverse proxy and is not maintenance - The axios interceptor checks it before the 401 branch, so a maintenance response never triggers the refresh-token path or logs the user out - fetchReq redirects to /maintenance, with a silentMaintenance opt-out used by getStateInfo: that one runs inside generateMetadata and while rendering the maintenance page itself, where a redirect is either ineffective or loops - redirect() works by throwing, so the try/catch blocks in the doctors, clinics and specialties pages now rethrow NEXT_REDIRECT instead of swallowing it - clinicApi.js handles 503 too; it previously rendered maintenance as a clinic with zero doctors - The page reuses the existing 404 design and is marked noindex Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
70 lines
2.7 KiB
JavaScript
70 lines
2.7 KiB
JavaScript
"use client";
|
||
|
||
import Image from "next/image";
|
||
import React, { useEffect, useState } from "react";
|
||
import { Button } from "@mui/material";
|
||
|
||
import { DEFAULT_MAINTENANCE_MESSAGE } from "@/lib/maintenance";
|
||
|
||
const RECHECK_INTERVAL_MS = 60_000;
|
||
|
||
function MaintenancePage() {
|
||
const [message, setMessage] = useState(DEFAULT_MAINTENANCE_MESSAGE);
|
||
|
||
// پیام واقعی را interceptor هنگام دریافت ۵۰۳ ذخیره کرده است.
|
||
useEffect(() => {
|
||
try {
|
||
const stored = sessionStorage.getItem("maintenance_message");
|
||
if (stored) setMessage(stored);
|
||
} catch { }
|
||
}, []);
|
||
|
||
// اگر تعمیرات تمام شد کاربر نباید تا رفرش دستی پشت این صفحه بماند.
|
||
useEffect(() => {
|
||
const timer = setInterval(async () => {
|
||
try {
|
||
const response = await fetch("/", { method: "HEAD", cache: "no-store" });
|
||
if (response.status !== 503) window.location.href = "/";
|
||
} catch { }
|
||
}, RECHECK_INTERVAL_MS);
|
||
|
||
return () => clearInterval(timer);
|
||
}, []);
|
||
|
||
return (
|
||
<div className="py-[105px] padding-responsive sm:py-[135px] md:py-[165px] lg:py-[201px] min-h-screen flex flex-col md:flex-row items-center justify-between gap-[50px]">
|
||
<div className="flex w-full md:w-fit flex-col justify-center items-center">
|
||
<Image
|
||
className=" flex md:hidden mt-[20px] w-[200px] sm:w-[270px] md:w-[340px] lg:w-[411px] h-[200px] sm:h-[270px] md:h-[340px] lg:h-[405px] "
|
||
src="/assets/images/notfound-cover.png"
|
||
alt="maintenance-cover"
|
||
height={405}
|
||
width={411}
|
||
/>
|
||
<p className="text-[#3B3B3B] text-[20px] sm:text-[24px] md:text-[28px] lg:text-[32px] font-bold mt-[24px] sm:mt-[30px] md:mt-[35px] lg:mt-[40px] mb-[12px] sm:mb-[16px] md:mb-[20px] lg:mb-[24px] text-center">
|
||
در حال بهروزرسانی سیستم
|
||
</p>
|
||
<p className="text-[#525252] text-[14px] md:text-[16px] font-normal text-center max-w-[520px]">
|
||
{message}
|
||
</p>
|
||
<Button
|
||
className="!h-[43px] md:!h-[45px] lg:!h-[46px] !mt-[20px] sm:!mt-[29px] md:!mt-[39px] lg:!mt-[48px] !px-[12px] !text-[#EFEFEF] !text-[14px] md:!text-[16px] !font-medium"
|
||
variant="contained"
|
||
onClick={() => { window.location.href = "/"; }}
|
||
>
|
||
تلاش مجدد
|
||
</Button>
|
||
</div>
|
||
<Image
|
||
className=" hidden md:flex w-[200px] sm:w-[270px] md:w-[340px] lg:w-[411px] h-[200px] sm:h-[270px] md:h-[340px] lg:h-[405px] "
|
||
src="/assets/images/notfound-cover.png"
|
||
alt="maintenance-cover"
|
||
height={405}
|
||
width={411}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default MaintenancePage;
|