diff --git a/client/src/Pages/Auth/Login/Components/EmailStep.jsx b/client/src/Pages/Auth/Login/Components/EmailStep.jsx index 6615277ba..cea175da3 100644 --- a/client/src/Pages/Auth/Login/Components/EmailStep.jsx +++ b/client/src/Pages/Auth/Login/Components/EmailStep.jsx @@ -70,8 +70,17 @@ const EmailStep = ({ form, errors, onSubmit, onChange }) => { color="accent" type="submit" disabled={errors.email && true} + className="dashboard-style-button" sx={{ width: "30%", + px: theme.spacing(6), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "&.Mui-focusVisible": { outline: `2px solid ${theme.palette.primary.main}`, outlineOffset: `2px`, diff --git a/client/src/Pages/Auth/Login/Components/PasswordStep.jsx b/client/src/Pages/Auth/Login/Components/PasswordStep.jsx index 0b8142161..a57246e23 100644 --- a/client/src/Pages/Auth/Login/Components/PasswordStep.jsx +++ b/client/src/Pages/Auth/Login/Components/PasswordStep.jsx @@ -75,8 +75,16 @@ const PasswordStep = ({ form, errors, onSubmit, onChange, onBack }) => { variant="outlined" color="info" onClick={onBack} + className="dashboard-style-button" sx={{ px: theme.spacing(5), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "& svg.MuiSvgIcon-root": { mr: theme.spacing(3), }, @@ -95,13 +103,22 @@ const PasswordStep = ({ form, errors, onSubmit, onChange, onBack }) => { type="submit" loading={authState.isLoading} disabled={errors.password && true} + className="dashboard-style-button" sx={{ width: "30%", + px: theme.spacing(4), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "&.Mui-focusVisible": { outline: `2px solid ${theme.palette.primary.main}`, outlineOffset: `2px`, - boxShadow: `none`, }, + boxShadow: `none`, }} > {t("continue")} diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index aec72167b..8ac831a40 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -6,11 +6,9 @@ import { credentials } from "../../../Validation/validation"; import { login } from "../../../Features/Auth/authSlice"; import { useDispatch, useSelector } from "react-redux"; import { createToast } from "../../../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; 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"; @@ -45,21 +43,12 @@ const Login = () => { const [errors, setErrors] = useState({}); const [step, setStep] = useState(0); + + useEffect(() => { if (authToken) { navigate("/uptime"); - return; } - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); }, [authToken, navigate]); const handleChange = (event) => { @@ -238,6 +227,31 @@ const Login = () => { email={form.email} errorEmail={errors.email} /> + + {/* 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..aaa61cbc4 --- /dev/null +++ b/client/src/Pages/ServerUnreachable.jsx @@ -0,0 +1,161 @@ +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 + 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 f5a1adcae..e334af912 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"; @@ -285,6 +288,10 @@ const Routes = () => { element={} /> */} + } + /> } diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 76ad659ec..3be13bff9 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -45,9 +45,16 @@ class NetworkService { this.axiosInstance.interceptors.response.use( (response) => response, (error) => { + // Handle network errors (server unreachable) if (error.code === "ERR_NETWORK") { - // Do error handling here + // 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()); @@ -508,6 +515,8 @@ class NetworkService { return this.axiosInstance.get("/auth/users/superadmin"); } + + /** * ************************************ * Get all users diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json index d74df8f3d..a2db25374 100644 --- a/client/src/locales/gb.json +++ b/client/src/locales/gb.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Don't have account", + "doNotHaveAccount": "Do not have an account?", + "registerHere": "Register here", "email": "E-mail", "forgotPassword": "Forgot Password", "password": "Password", @@ -106,6 +108,14 @@ "settingsFailedToAddDemoMonitors": "Failed to add demo monitors", "settingsMonitorsDeleted": "Successfully deleted all monitors", "settingsFailedToDeleteMonitors": "Failed to delete all monitors", + "backendUnreachable": "Server Connection Error", + "backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or verify your deployment configuration if the problem persists.", + "backendUnreachableError": "Cannot connect to the server. Please try again later.", + "retryConnection": "Retry connection", + "retryingConnection": "Connecting...", + "backendReconnected": "Successfully reconnected to the server.", + "backendStillUnreachable": "Server is still unreachable. Please try again later.", + "backendConnectionError": "Error connecting to the server. Please check your network connection.", "starPromptTitle": "Star Checkmate", "starPromptDescription": "See the latest releases and help grow the community on GitHub", "https": "HTTPS", diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json index 700224b86..5536b5e45 100644 --- a/client/src/locales/ru.json +++ b/client/src/locales/ru.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Нет аккаунта", + "doNotHaveAccount": "У вас нет учетной записи?", + "registerHere": "Зарегистрироваться здесь", "email": "Почта", "forgotPassword": "Забыли пароль", "password": "Пароль", @@ -119,6 +121,14 @@ "settingsFailedToAddDemoMonitors": "Не удалось добавить демонстрационные мониторы", "settingsMonitorsDeleted": "Успешно удалены все мониторы", "settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы", + "backendUnreachable": "Ошибка подключения к серверу", + "backendUnreachableMessage": "Мы не можем подключиться к серверу. Пожалуйста, проверьте ваше интернет-соединение или проверьте конфигурацию развертывания, если проблема не устраняется.", + "backendUnreachableError": "Невозможно подключиться к серверу. Пожалуйста, повторите попытку позже.", + "retryConnection": "Повторить подключение", + "retryingConnection": "Подключение...", + "backendReconnected": "Успешно восстановлено подключение к серверу.", + "backendStillUnreachable": "Сервер по-прежнему недоступен. Пожалуйста, повторите попытку позже.", + "backendConnectionError": "Ошибка подключения к серверу. Пожалуйста, проверьте ваше сетевое подключение.", "starPromptTitle": "Star Checkmate", "starPromptDescription": "Ознакомьтесь с последними релизами и помогите развить сообщество на GitHub", "https": "HTTPS", diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json index 2323a54b8..5df644fdd 100644 --- a/client/src/locales/tr.json +++ b/client/src/locales/tr.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Hesabınız yok mu", + "doNotHaveAccount": "Hesabınız yok mu?", + "registerHere": "Buradan kayıt olun", "email": "E-posta", "forgotPassword": "Parolamı unuttum", "password": "Parola", @@ -104,6 +106,14 @@ "settingsFailedToAddDemoMonitors": "Demo monitörler eklenemedi", "settingsMonitorsDeleted": "Tüm monitörler başarıyla silindi", "settingsFailedToDeleteMonitors": "Monitörler silinemedi", + "backendUnreachable": "Sunucu Bağlantı Hatası", + "backendUnreachableMessage": "Sunucuya bağlanamıyoruz. Lütfen internet bağlantınızı kontrol edin veya sorun devam ederse dağıtım yapılandırmanızı doğrulayın.", + "backendUnreachableError": "Sunucuya bağlanılamıyor. Lütfen daha sonra tekrar deneyin.", + "retryConnection": "Bağlantıyı Yeniden Dene", + "retryingConnection": "Bağlanıyor...", + "backendReconnected": "Sunucuya başarıyla yeniden bağlandı.", + "backendStillUnreachable": "Sunucu hala erişilemez durumda. Lütfen daha sonra tekrar deneyin.", + "backendConnectionError": "Sunucuya bağlanırken hata oluştu. Lütfen ağ bağlantınızı kontrol edin.", "starPromptTitle": "Checkmate yıldızla değerlendirin", "starPromptDescription": "En son sürümleri görün ve GitHub'daki topluluğun büyümesine yardımcı olun", "https": "HTTPS",