diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx
index 0e5c5dcaf..e43346640 100644
--- a/client/src/Pages/Auth/Login/Login.jsx
+++ b/client/src/Pages/Auth/Login/Login.jsx
@@ -1,16 +1,13 @@
-import { useState, useEffect, useCallback } from "react";
+import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
-import { Box, Stack, Typography, Button } from "@mui/material";
-import Alert from "../../../Components/Alert";
+import { Box, Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import { credentials } from "../../../Validation/validation";
import { login } from "../../../Features/Auth/authSlice";
import { useDispatch, useSelector } from "react-redux";
import { createToast } from "../../../Utils/toastUtils";
-import { networkService } from "../../../main";
import Background from "../../../assets/Images/background-grid.svg?react";
import Logo from "../../../assets/icons/checkmate-icon.svg?react";
-import { logger } from "../../../Utils/Logger";
import "../index.css";
import EmailStep from "./Components/EmailStep";
import PasswordStep from "./Components/PasswordStep";
@@ -46,63 +43,13 @@ const Login = () => {
const [errors, setErrors] = useState({});
const [step, setStep] = useState(0);
- // State variables for backend connectivity status and loading state
- const [backendReachable, setBackendReachable] = useState(true);
- const [isCheckingConnection, setIsCheckingConnection] = useState(false);
- const [initialCheckComplete, setInitialCheckComplete] = useState(false);
- // Function to check if the backend server is reachable and handle connectivity status
- // Wrapped in useCallback to prevent recreation on each render
- const checkConnectivity = useCallback(async (isRetry = false) => {
- setIsCheckingConnection(true);
- try {
- const isReachable = await networkService.checkBackendReachability();
- setBackendReachable(isReachable);
-
- // Early return if backend is not reachable
- if (isReachable === false) {
- // Show toast only on retry attempts
- if (isRetry) {
- createToast({
- body: t("backendStillUnreachable"),
- });
- }
- return;
- }
-
- // Show reconnection toast if this was a retry attempt and backend is now reachable
- if (isRetry) {
- createToast({
- body: t("backendReconnected"),
- });
- }
- } catch (error) {
- logger.error("Error checking backend connectivity:", error);
- setBackendReachable(false);
-
- if (isRetry) {
- createToast({
- body: t("backendConnectionError"),
- });
- }
- } finally {
- setIsCheckingConnection(false);
- setInitialCheckComplete(true);
- }
- }, [t]); // Removed navigate since we no longer use it within this function
-
- // Function to handle retry button click
- const handleRetry = () => checkConnectivity(true);
useEffect(() => {
if (authToken) {
navigate("/uptime");
- return;
}
-
- // Initial connectivity check
- checkConnectivity();
- }, [authToken, navigate, checkConnectivity]);
+ }, [authToken, navigate]);
const handleChange = (event) => {
const { value, id } = event.target;
@@ -125,14 +72,6 @@ const Login = () => {
const handleSubmit = async (event) => {
event.preventDefault();
-
- // Check backend connectivity before proceeding
- if (!backendReachable) {
- createToast({
- body: t("backendUnreachableError"),
- });
- return;
- }
if (step === 0) {
const { error } = credentials.validate(
@@ -260,61 +199,7 @@ const Login = () => {
},
}}
>
- {!initialCheckComplete ? (
-
- {/* Show loading state while doing initial connectivity check */}
- {t("retryingConnection")}
-
- ) : !backendReachable ? (
-
-
-
-
-
-
- {t("backendUnreachableMessage")}
-
-
-
-
-
-
- ) : step === 0 ? (
+ {step === 0 ? (
{
/>
)
)}
- {backendReachable && (
- <>
-
-
- {/* Registration link */}
-
-
- {t("doNotHaveAccount")}
-
- navigate("/register")}
- >
- {t("registerHere")}
-
-
- >
- )}
+
+
+ {/* Registration link */}
+
+
+ {t("doNotHaveAccount")}
+
+ navigate("/register")}
+ >
+ {t("registerHere")}
+
+
);
diff --git a/client/src/Pages/ServerUnreachable.jsx b/client/src/Pages/ServerUnreachable.jsx
new file mode 100644
index 000000000..b635a8460
--- /dev/null
+++ b/client/src/Pages/ServerUnreachable.jsx
@@ -0,0 +1,171 @@
+import React, { useState } from "react";
+import { Box, Typography, Button, Stack } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { useNavigate } from "react-router";
+import { networkService } from "../Utils/NetworkService";
+import Alert from "../Components/Alert";
+import { createToast } from "../Utils/toastUtils";
+import { useTranslation } from "react-i18next";
+import Background from "../assets/Images/background-grid.svg?react";
+import Logo from "../assets/icons/checkmate-icon.svg?react";
+import ThemeSwitch from "../Components/ThemeSwitch";
+import LanguageSelector from "../Components/LanguageSelector";
+
+const ServerUnreachable = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const { t } = useTranslation();
+
+ // State for tracking connection check status
+ const [isCheckingConnection, setIsCheckingConnection] = useState(false);
+
+ const handleRetry = React.useCallback(async () => {
+ setIsCheckingConnection(true);
+ try {
+ // Try to connect to the backend with a simple API call
+ // We'll use any lightweight endpoint that doesn't require authentication
+ await networkService.axiosInstance.get('/health', { timeout: 5000 });
+
+ // If successful, show toast and navigate to login page
+ createToast({
+ body: t("backendReconnected", "Connection to server restored"),
+ });
+ navigate("/login");
+ } catch (error) {
+ // If still unreachable, stay on this page and show toast
+ console.error("Server still unreachable:", error);
+ createToast({
+ body: t("backendStillUnreachable", "Server is still unreachable"),
+ });
+ } finally {
+ setIsCheckingConnection(false);
+ }
+ }, [navigate, t]);
+
+ return (
+
+
+
+
+
+ {/* Header with logo */}
+
+
+
+ Checkmate
+
+
+
+
+
+
+ .MuiStack-root": {
+ border: 1,
+ borderRadius: theme.spacing(5),
+ borderColor: theme.palette.primary.lowContrast,
+ backgroundColor: theme.palette.primary.main,
+ padding: {
+ xs: theme.spacing(12),
+ sm: theme.spacing(20),
+ },
+ },
+ }}
+ >
+
+
+
+
+
+
+ {t("backendUnreachableMessage", "The Checkmate server is not responding. Please check your deployment configuration or try again later.")}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ServerUnreachable;
diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx
index 82d7af7ca..90be1282e 100644
--- a/client/src/Routes/index.jsx
+++ b/client/src/Routes/index.jsx
@@ -36,6 +36,9 @@ import DistributedUptimeDetails from "../Pages/DistributedUptime/Details";
import CreateDistributedUptimeStatus from "../Pages/DistributedUptimeStatus/Create";
import DistributedUptimeStatus from "../Pages/DistributedUptimeStatus/Status";
+// Server Status
+import ServerUnreachable from "../Pages/ServerUnreachable";
+
// Incidents
import Incidents from "../Pages/Incidents";
@@ -279,6 +282,10 @@ const Routes = () => {
element={}
/>
+ }
+ />
}
diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js
index 583b65089..8a06fc9da 100644
--- a/client/src/Utils/NetworkService.js
+++ b/client/src/Utils/NetworkService.js
@@ -45,6 +45,15 @@ class NetworkService {
this.axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
+ // Handle network errors (server unreachable)
+ if (error.code === "ERR_NETWORK") {
+ // Navigate to server unreachable page
+ navigate("/server-unreachable");
+ // Return an empty resolved promise to stop the error propagation
+ return Promise.reject(error);
+ }
+
+ // Handle authentication errors
if (error.response && error.response.status === 401) {
dispatch(clearAuthState());
dispatch(clearUptimeMonitorState());