Merge branch 'develop' into feat/fe/uptime-ignore-tsl-error

This commit is contained in:
Alexander Holliday
2025-05-08 12:00:50 -07:00
committed by GitHub
37 changed files with 638 additions and 194 deletions
+3 -3
View File
@@ -1,6 +1,7 @@
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { Box, Button, Stack, Typography, Link } from "@mui/material";
import { Link as RouterLink } from "react-router-dom";
import Skeleton from "../../assets/Images/create-placeholder.svg?react";
import SkeletonDark from "../../assets/Images/create-placeholder-dark.svg?react";
import Background from "../../assets/Images/background-grid.svg?react";
@@ -35,9 +36,8 @@ const Fallback = ({ title, checks, link = "/", isAdmin, vowelStart = false, show
<>
{t("pageSpeedWarning")} {" "}
<Link
href="https://docs.checkmate.so/users-guide/quickstart#env-vars-server"
target="_blank"
rel="noopener noreferrer"
component={RouterLink}
to="/settings"
sx={{
textDecoration: "underline",
color: "inherit",
@@ -6,18 +6,6 @@ import PropTypes from "prop-types";
const getSx = (theme, type, maxWidth) => {
const sx = {
maxWidth: maxWidth,
"& .MuiOutlinedInput-root ": {
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.primary.contrastText, // Adjust hover border color
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.accent.main, // Adjust focus border color
},
"&.Mui-disabled .MuiOutlinedInput-notchedOutline": {
borderColor: theme.palette.primary.contrastText, // CAIO_REVIEW
opacity: 0.5,
},
},
"& .MuiFormHelperText-root": {
position: "absolute",
@@ -116,6 +104,7 @@ const TextInput = forwardRef(
fontSize={"var(--env-var-font-size-medium)"}
color={theme.palette.primary.contrastTextSecondary}
fontWeight={500}
mb={theme.spacing(2)}
>
{label}
{isRequired && <Required />}
+3 -2
View File
@@ -20,8 +20,8 @@ const StatusBoxes = ({ shouldRender, flexWrap = "nowrap", children }) => {
direction="row"
flexWrap={flexWrap}
gap={theme.spacing(8)}
justifyContent="space-between"
display="flex"
justifyContent="flex-start"
display="flex"
>
{children}
</Stack>
@@ -30,6 +30,7 @@ const StatusBoxes = ({ shouldRender, flexWrap = "nowrap", children }) => {
StatusBoxes.propTypes = {
shouldRender: PropTypes.bool,
flexWrap: PropTypes.string,
children: PropTypes.node,
};
@@ -10,6 +10,7 @@ import { update } from "../../../Features/Auth/authSlice";
import { useDispatch, useSelector } from "react-redux";
import { createToast } from "../../../Utils/toastUtils";
import { getTouchedFieldErrors } from "../../../Validation/error";
import { useTranslation } from "react-i18next";
const defaultPasswordsState = {
password: "",
@@ -26,6 +27,7 @@ const defaultPasswordsState = {
const PasswordPanel = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { t } = useTranslation();
const SPACING_GAP = theme.spacing(12);
@@ -203,7 +205,7 @@ const PasswordPanel = () => {
<TextInput
type="password"
id="edit-confirm-password"
placeholder="Reenter your new password"
placeholder={t("confirmPassword")}
autoComplete="new-password"
value={localData.confirm}
onChange={handleChange}
@@ -344,7 +344,7 @@ const ProfilePanel = () => {
spellCheck="false"
>
<Box mb={theme.spacing(6)}>
<Typography component="h1">{t('DeleteAccount')}</Typography>
<Typography component="h1">{t('DeleteAccountTitle')}</Typography>
<Typography
component="p"
sx={{ opacity: 0.6 }}
@@ -357,7 +357,7 @@ const ProfilePanel = () => {
color="error"
onClick={() => setIsOpen("delete")}
>
{t('DeleteAccount')}
{t('DeleteAccountButton')}
</Button>
</Box>
)}
@@ -367,7 +367,7 @@ const ProfilePanel = () => {
title={t('DeleteWarningTitle')}
description={t('DeleteAccountWarning')}
onCancel={() => setIsOpen("")}
confirmationButtonLabel={t('DeleteAccount')}
confirmationButtonLabel={t('DeleteAccountButton')}
onConfirm={handleDeleteAccount}
isLoading={isLoading}
/>
@@ -101,7 +101,7 @@ export const updatePageSpeed = createAsyncThunk(
name: monitor.name,
description: monitor.description,
interval: monitor.interval,
// notifications: monitor.notifications,
notifications: monitor.notifications,
};
const res = await networkService.updateMonitor({
monitorId: monitor._id,
@@ -6,6 +6,7 @@ const initialState = {
apiBaseUrl: "",
logLevel: "debug",
language: "gb",
pagespeedApiKey: "",
};
export const getAppSettings = createAsyncThunk(
@@ -33,6 +34,7 @@ export const updateAppSettings = createAsyncThunk(
try {
const parsedSettings = {
language: settings.language,
pagespeedApiKey: settings.pagespeedApiKey,
};
const res = await networkService.updateAppSettings({ settings: parsedSettings });
return res.data;
@@ -56,6 +58,7 @@ const handleGetSettingsFulfilled = (state, action) => {
state.apiBaseUrl = action.payload.data.apiBaseUrl;
state.logLevel = action.payload.data.logLevel;
state.language = action.payload.data.language;
state.pagespeedApiKey = action.payload.data.pagespeedApiKey;
};
const handleGetSettingsRejected = (state, action) => {
state.isLoading = false;
+1 -1
View File
@@ -2,7 +2,7 @@ import { useState } from "react";
import { networkService } from "../main";
import { useTranslation } from "react-i18next";
const CLIENT_HOST = import.meta.env.VITE_CLIENT_HOST;
const CLIENT_HOST = import.meta.env.VITE_APP_CLIENT_HOST;
const useGetInviteToken = () => {
const { t } = useTranslation();
@@ -58,7 +58,7 @@ const EmailStep = ({ form, errors, onSubmit, onChange }) => {
onInput={(e) => (e.target.value = e.target.value.toLowerCase())}
onChange={onChange}
error={errors.email ? true : false}
helperText={errors.email}
helperText={errors.email ? t(errors.email) : ""}
ref={inputRef}
/>
<Stack
@@ -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`,
@@ -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")}
+35 -19
View File
@@ -9,7 +9,6 @@ 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";
@@ -25,11 +24,10 @@ const DEMO = import.meta.env.VITE_APP_DEMO;
*/
const Login = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const theme = useTheme();
const dispatch = useDispatch();
const { t } = useTranslation();
const navigate = useNavigate();
const authState = useSelector((state) => state.auth);
const { authToken } = authState;
@@ -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) => {
@@ -94,8 +83,10 @@ const Login = () => {
{ abortEarly: false }
);
if (error) {
setErrors((prev) => ({ ...prev, email: error.details[0].message }));
createToast({ body: error.details[0].message });
const errorMessage = error.details[0].message;
const translatedMessage = errorMessage.startsWith('auth') ? t(errorMessage) : errorMessage;
setErrors((prev) => ({ ...prev, email: translatedMessage }));
createToast({ body: translatedMessage });
} else {
setStep(1);
}
@@ -112,8 +103,8 @@ const Login = () => {
createToast({
body:
error.details && error.details.length > 0
? error.details[0].message
: "Error validating data.",
? (error.details[0].message.startsWith('auth') ? t(error.details[0].message) : error.details[0].message)
: t("Error validating data."),
});
} else {
const action = await dispatch(login(form));
@@ -236,6 +227,31 @@ const Login = () => {
email={form.email}
errorEmail={errors.email}
/>
{/* Registration link */}
<Box textAlign="center" >
<Typography
className="forgot-p"
display="inline-block"
color={theme.palette.primary.main}
>
{t("doNotHaveAccount")}
</Typography>
<Typography
component="span"
color={theme.palette.accent.main}
ml={theme.spacing(2)}
sx={{
cursor: 'pointer',
'&:hover': {
color: theme.palette.accent.darker
}
}}
onClick={() => navigate("/register")}
>
{t("registerHere")}
</Typography>
</Box>
</Stack>
</Stack>
);
+1 -1
View File
@@ -387,7 +387,7 @@ const Register = ({ isSuperAdmin }) => {
}}
sx={{ userSelect: "none", color: theme.palette.accent.main }}
>
{t("authLoginTitle")}
{t("authRegisterLoginLink")}
</Typography>
</Box>
</Stack>
@@ -68,7 +68,11 @@ function StepTwo({ form, errors, onSubmit, onChange, onBack }) {
onInput={(e) => (e.target.value = e.target.value.toLowerCase())}
onChange={onChange}
error={errors.email ? true : false}
helperText={errors.email && t("authRegisterEmailRequired")}
helperText={errors.email && (
errors.email.includes("required") ? t("authRegisterEmailRequired") :
errors.email.includes("valid email") ? t("authRegisterEmailInvalid") :
errors.email
)}
ref={inputRef}
/>
<Stack
@@ -51,7 +51,9 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
subHeading={
<>
{physicalCores}
<Typography component="span">cores</Typography>
<Typography component="span">
{physicalCores === 1 ? "core" : "cores"}
</Typography>
</>
}
/>
@@ -61,7 +63,9 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
subHeading={
<>
{logicalCores}
<Typography component="span">cores</Typography>
<Typography component="span">
{logicalCores === 1 ? "core" : "cores"}
</Typography>
</>
}
/>
@@ -79,7 +83,7 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
subHeading={
<>
{cpuTemperature.toFixed(2)}
<Typography component="span">C</Typography>
<Typography component="span">°C</Typography>
</>
}
/>
@@ -21,6 +21,7 @@ const PageSpeed = () => {
const { t } = useTranslation();
const isAdmin = useIsAdmin();
const { user } = useSelector((state) => state.auth);
const { pagespeedApiKey } = useSelector((state) => state.settings);
const { isLoading, monitors, summary, networkError } = useMonitorsFetch({
teamId: user.teamId,
@@ -52,7 +53,7 @@ const PageSpeed = () => {
]}
link="/pagespeed/create"
isAdmin={isAdmin}
showPageSpeedWarning={true}
showPageSpeedWarning={isAdmin && !pagespeedApiKey}
/>
);
}
@@ -62,7 +63,7 @@ const PageSpeed = () => {
<Breadcrumbs list={BREADCRUMBS} />
<CreateMonitorHeader
isAdmin={isAdmin}
shouldRender={!isLoading}
isLoading={isLoading}
path="/pagespeed/create"
/>
<MonitorCountHeader
+161
View File
@@ -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 (
<Stack
className="login-page auth"
overflow="hidden"
>
<Box
className="background-pattern-svg"
sx={{
"& svg g g:last-of-type path": {
stroke: theme.palette.primary.lowContrast,
},
}}
>
<Background style={{ width: "100%" }} />
</Box>
{/* Header with logo */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
px={theme.spacing(12)}
gap={theme.spacing(4)}
>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(4)}
>
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
<Typography sx={{ userSelect: "none" }}>Checkmate</Typography>
</Stack>
<Stack
direction="row"
spacing={2}
alignItems="center"
>
<LanguageSelector />
<ThemeSwitch />
</Stack>
</Stack>
<Stack
width="100%"
maxWidth={600}
flex={1}
justifyContent="center"
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
pb={theme.spacing(20)}
mx="auto"
rowGap={theme.spacing(8)}
sx={{
"& > .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),
},
},
}}
>
<Stack spacing={theme.spacing(6)} alignItems="center">
<Box sx={{
width: theme.spacing(220),
mx: 'auto',
'& .alert.row-stack': {
width: '100%',
alignItems: 'center',
gap: theme.spacing(3)
}
}}>
<Alert
variant="error"
body={t("backendUnreachable", "Server Unreachable")}
hasIcon={true}
/>
</Box>
<Box mt={theme.spacing(2)}>
<Typography
variant="body1"
align="center"
color={theme.palette.primary.contrastTextSecondary}
>
{t("backendUnreachableMessage", "The Checkmate server is not responding. Please check your deployment configuration or try again later.")}
</Typography>
</Box>
<Box sx={{ mt: theme.spacing(4) }}>
<Button
variant="contained"
color="accent"
onClick={handleRetry}
disabled={isCheckingConnection}
className="dashboard-style-button"
sx={{
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`
}
}}
>
{isCheckingConnection ?
t("retryingConnection", "Retrying Connection...") :
t("retryConnection", "Retry Connection")}
</Button>
</Box>
</Stack>
</Stack>
</Stack>
);
};
export default ServerUnreachable;
+114 -30
View File
@@ -6,6 +6,8 @@ import Select from "../../Components/Inputs/Select";
import { useIsAdmin } from "../../Hooks/useIsAdmin";
import Dialog from "../../Components/Dialog";
import ConfigBox from "../../Components/ConfigBox";
import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornments";
import { getAppSettings } from "../../Features/Settings/settingsSlice";
// import {
// WalletMultiButton,
// WalletDisconnectButton,
@@ -54,8 +56,11 @@ const Settings = () => {
const [form, setForm] = useState({
enableDistributedUptime: distributedUptimeEnabled,
ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0,
pagespeedApiKey: "",
});
const [version, setVersion] = useState("unknown");
const [apiKeyFieldType, setApiKeyFieldType] = useState("password");
const [isApiKeySet, setIsApiKeySet] = useState(false);
const [errors, setErrors] = useState({});
const deleteStatsMonitorsInitState = { deleteMonitors: false, deleteStats: false };
const [isOpen, setIsOpen] = useState(deleteStatsMonitorsInitState);
@@ -80,6 +85,28 @@ const Settings = () => {
fetchLatestVersion();
}, []);
useEffect(() => {
dispatch(getAppSettings());
}, []);
const { pagespeedApiKey } = useSelector((state) => state.settings);
useEffect(() => {
if (pagespeedApiKey) {
setIsApiKeySet(true);
setForm((prev) => ({
...prev,
pagespeedApiKey: t("maskedPageSpeedKeyPlaceholder"),
}));
} else {
setIsApiKeySet(false);
setForm((prev) => ({
...prev,
pagespeedApiKey: "",
}));
}
}, [pagespeedApiKey]);
const handleChange = (event) => {
const { type, checked, value, id } = event.target;
@@ -91,12 +118,17 @@ const Settings = () => {
return;
}
let inputValue = value;
if (id === "ttl") {
inputValue = value.replace(/[^0-9]/g, "");
}
const updatedForm = { ...form, [id]: inputValue };
const { error } = settingsValidation.validate(
{ [id]: value },
{
abortEarly: false,
}
updatedForm,
{ abortEarly: false }
);
if (!error || error.details.length === 0) {
setErrors({});
} else {
@@ -107,12 +139,8 @@ const Settings = () => {
setErrors(newErrors);
logger.error("Validation errors:", error.details);
}
let inputValue = value;
id === "ttl" && (inputValue = value.replace(/[^0-9]/g, ""));
setForm((prev) => ({
...prev,
[id]: inputValue,
}));
setForm(updatedForm);
};
// TODO Handle saving
@@ -125,7 +153,7 @@ const Settings = () => {
const updatedUser = { ...user, checkTTL: form.ttl };
const [userAction, settingsAction] = await Promise.all([
dispatch(update({ localData: updatedUser })),
dispatch(updateAppSettings({ settings: { language: language } })),
dispatch(updateAppSettings({ settings: { language: language, pagespeedApiKey: form.pagespeedApiKey } })),
]);
if (userAction.payload.success && settingsAction.payload.success) {
@@ -187,6 +215,14 @@ const Settings = () => {
}
};
const handleResetApiKey = () => {
setIsApiKeySet(false);
setForm((prev) => ({
...prev,
pagespeedApiKey: "",
}));
};
const languages = Object.keys(i18n.options.resources || {});
return (
@@ -277,6 +313,43 @@ const Settings = () => {
</Box>
</ConfigBox>
)} */}
<ConfigBox>
<Box>
<Typography component="h1">{t("pageSpeedApiKeyFieldTitle")}</Typography>
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(2) }}>
{t("pageSpeedApiKeyFieldDescription")}
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<TextInput
id="pagespeedApiKey"
label={t("pageSpeedApiKeyFieldLabel")}
value={form.pagespeedApiKey}
type={apiKeyFieldType}
onChange={handleChange}
disabled={isApiKeySet}
optionalLabel="(Optional)"
error={!!errors.pagespeedApiKey}
helperText={errors.pagespeedApiKey}
endAdornment={
!isApiKeySet && (
<PasswordEndAdornment
fieldType={apiKeyFieldType}
setFieldType={setApiKeyFieldType}
/>
)
}
/>
{isApiKeySet && (
<Box>
<Typography>{t("pageSpeedApiKeyFieldResetLabel")}</Typography>
<Button onClick={handleResetApiKey} variant="contained" color="error" sx={{ mt: theme.spacing(4) }}>
{t("reset")}
</Button>
</Box>
)}
</Stack>
</ConfigBox>
{/* {isAdmin && (
<ConfigBox>
<Box>
@@ -348,14 +421,15 @@ const Settings = () => {
</ConfigBox>
)}
{isAdmin && (
<ConfigBox>
<Box>
<Typography component="h1">{t("settingsDemoMonitors")}</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
{t("settingsDemoMonitorsDescription")}
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<>
{/* Demo Monitors Section */}
<ConfigBox>
<Box>
<Typography component="h1">{t("settingsDemoMonitors")}</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
{t("settingsDemoMonitorsDescription")}
</Typography>
</Box>
<Box>
<Typography>{t("settingsAddDemoMonitors")}</Typography>
<Button
@@ -368,6 +442,16 @@ const Settings = () => {
{t("settingsAddDemoMonitorsButton")}
</Button>
</Box>
</ConfigBox>
{/* System Reset Section */}
<ConfigBox>
<Box>
<Typography component="h1">{t("settingsSystemReset")}</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
{t("settingsSystemResetDescription")}
</Typography>
</Box>
<Box>
<Typography>{t("settingsRemoveAllMonitors")}</Typography>
<Button
@@ -382,17 +466,17 @@ const Settings = () => {
{t("settingsRemoveAllMonitorsButton")}
</Button>
</Box>
</Stack>
<Dialog
open={isOpen.deleteMonitors}
theme={theme}
title={t("settingsRemoveAllMonitorsDialogTitle")}
onCancel={() => setIsOpen(deleteStatsMonitorsInitState)}
confirmationButtonLabel={t("settingsRemoveAllMonitorsDialogConfirm")}
onConfirm={handleDeleteAllMonitors}
isLoading={isLoading || authIsLoading || checksIsLoading}
/>
</ConfigBox>
<Dialog
open={isOpen.deleteMonitors}
theme={theme}
title={t("settingsRemoveAllMonitorsDialogTitle")}
onCancel={() => setIsOpen(deleteStatsMonitorsInitState)}
confirmationButtonLabel={t("settingsRemoveAllMonitorsDialogConfirm")}
onConfirm={handleDeleteAllMonitors}
isLoading={isLoading || authIsLoading || checksIsLoading}
/>
</ConfigBox>
</>
)}
<ConfigBox>
@@ -61,6 +61,7 @@ const StatusPages = () => {
label="Create status page"
isAdmin={isAdmin}
path="/status/uptime/create"
isLoading={isLoading}
/>
<StatusPagesTable data={statusPages} />
</Stack>
@@ -61,18 +61,20 @@ const UptimeStatusBoxes = ({
</>
}
/>
<StatBox
heading="certificate expiry"
subHeading={
<Typography
component="span"
fontSize={13}
color={theme.palette.primary.contrastText}
>
{certificateExpiry}
</Typography>
}
/>
{monitor?.type === "http" && (
<StatBox
heading="certificate expiry"
subHeading={
<Typography
component="span"
fontSize={13}
color={theme.palette.primary.contrastText}
>
{certificateExpiry}
</Typography>
}
/>
)}
</StatusBoxes>
);
};
+7
View File
@@ -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={<DistributedUptimeStatus />}
/> */}
<Route
path="/server-unreachable"
element={<ServerUnreachable />}
/>
<Route
path="*"
element={<NotFound />}
+10 -1
View File
@@ -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
+9 -3
View File
@@ -63,7 +63,6 @@ const paletteColors = {
orange500: "#E88C30",
orange600: "#DC6803",
orange800: "#624711",
};
const semanticColors = {
@@ -155,7 +154,6 @@ const newSemanticColors = {
light: newColors.blueGray600,
dark: newColors.gray200,
},
// CAIO_REVIEW, need a brighter color for dark bg
contrastTextSecondaryDarkBg: {
light: newColors.gray200,
dark: newColors.gray200,
@@ -164,6 +162,14 @@ const newSemanticColors = {
light: newColors.blueGray500,
dark: newColors.gray500,
},
contrastBorder: {
light: newColors.gray500,
dark: newColors.blueGray600,
},
contrastBorderDisabled: {
light: newColors.gray100,
dark: newColors.blueGray800,
},
lowContrast: {
light: newColors.gray200,
dark: newColors.blueGray600,
@@ -264,7 +270,7 @@ const newSemanticColors = {
dark: newColors.red600,
},
},
/* These are temporary, just for everything not to break */
gradient: {
color1: {
+16 -6
View File
@@ -1,5 +1,4 @@
import { typographyLevels } from "./constants";
const fontFamilyPrimary = '"Inter" , sans-serif';
// const fontFamilySecondary = '"Avenir", sans-serif';
@@ -300,13 +299,15 @@ const baseTheme = (palette) => ({
}),
},
},
MuiTextField: {
styleOverrides: {
root: ({ theme }) => ({
"& fieldset": {
borderColor: theme.palette.primary.lowContrast,
borderColor: theme.palette.primary.contrastBorder,
borderRadius: theme.shape.borderRadius,
},
"& .MuiInputBase-input": {
padding: ".75em",
minHeight: "var(--env-var-height-2)",
@@ -356,10 +357,19 @@ const baseTheme = (palette) => ({
MuiOutlinedInput: {
styleOverrides: {
root: {
color: palette.primary.contrastTextTertiary,
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: palette.primary.contrastTextTertiary,
"&.Mui-disabled .MuiOutlinedInput-notchedOutline": {
borderColor: palette.primary.contrastBorderDisabled,
},
"&.Mui-disabled:hover .MuiOutlinedInput-notchedOutline": {
borderColor: palette.primary.contrastBorderDisabled,
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: palette.primary.contrastText, // Adjust hover border color
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: palette.accent.main, // Adjust focus border color
},
color: palette.primary.contrastTextTertiary,
},
},
},
@@ -510,7 +520,7 @@ const baseTheme = (palette) => ({
root: ({ theme }) => ({
ml: "auto",
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
borderColor: theme.palette.primary.lowContrast,
borderColor: theme.palette.primary.contrastBorder,
width: "auto",
whiteSpace: "nowrap",
},
+8 -4
View File
@@ -66,8 +66,8 @@ const credentials = joi.object({
return lowercasedValue;
})
.messages({
"string.empty": "Email is required",
"string.email": "Must be a valid email address",
"string.empty": "authRegisterEmailRequired",
"string.email": "authRegisterEmailInvalid",
}),
password: passwordSchema,
newPassword: passwordSchema,
@@ -254,9 +254,13 @@ const statusPageValidation = joi.object({
});
const settingsValidation = joi.object({
ttl: joi.number().required().messages({
"string.empty": "TTL is required",
"string.empty": "Please enter a value",
"number.base": "Please enter a valid number",
"any.required": "Please enter a value"
}),
});
pagespeedApiKey: joi.string().allow("").optional(),
})
.unknown(true);
const dayjsValidator = (value, helpers) => {
if (!dayjs(value).isValid()) {
+49 -25
View File
@@ -1,9 +1,11 @@
{
"dontHaveAccount": "Don't have account",
"doNotHaveAccount": "Do not have an account?",
"registerHere": "Register here",
"email": "E-mail",
"forgotPassword": "Forgot Password",
"password": "password",
"signUp": "Sign up",
"password": "Password",
"signUp": "Sign Up",
"submit": "Submit",
"title": "Title",
"continue": "Continue",
@@ -15,8 +17,9 @@
"authForgotPasswordTitle": "Forgot password?",
"authForgotPasswordResetPassword": "Reset password",
"createPassword": "Create your password",
"createAPassword": "Create a password",
"createAPassword": "Password",
"authRegisterAlreadyHaveAccount": "Already have an account?",
"authRegisterLoginLink": "Log In",
"commonAppName": "Checkmate",
"welcomeBack": "Welcome back! You're successfully logged in.",
"authLoginEnterEmail": "Enter your email",
@@ -41,7 +44,7 @@
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
"authSetNewPasswordNewPassword": "New password",
"authSetNewPasswordConfirmPassword": "Confirm password",
"confirmPassword": "Confirm your password",
"confirmPassword": "Re-enter password to confirm",
"authSetNewPasswordResetPassword": "Reset password",
"authSetNewPasswordBackTo": "Back to",
"authPasswordMustBeAtLeast": "Must be at least",
@@ -52,7 +55,10 @@
"authPasswordUpperCharacter": "one upper character",
"authPasswordLowerCharacter": "one lower character",
"authPasswordConfirmAndPassword": "Confirm password and password",
"authPasswordMustMatch": "must match",
"authPasswordMustMatch": "Passwords must match",
"validationNameRequired": "Please enter your name",
"validationNameTooLong": "Name should be less than 50 characters",
"validationNameInvalidCharacters": "Please use only letters, spaces, apostrophes, or hyphens",
"authRegisterCreateAccount": "Create your account to get started",
"authRegisterCreateSuperAdminAccount": "Create your super admin account to get started",
"authRegisterSignUpWithEmail": "Create super admin account",
@@ -61,7 +67,7 @@
"distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
"settingsGeneralSettings": "General settings",
"settingsDisplayTimezone": "Display timezone",
"settingsDisplayTimezoneDescription": "The timezone of the dashboard you publicly display.",
"settingsDisplayTimezoneDescription": "Select the timezone used to display dates and times throughout the application.",
"settingsAppearance": "Appearance",
"settingsAppearanceDescription": "Switch between light and dark mode, or change user interface language",
"settingsThemeMode": "Theme Mode",
@@ -70,23 +76,25 @@
"settingsDistributedUptimeDescription": "Enable/disable distributed uptime monitoring.",
"settingsEnabled": "Enabled",
"settingsDisabled": "Disabled",
"settingsHistoryAndMonitoring": "History and monitoring",
"settingsHistoryAndMonitoringDescription": "Define here for how long you want to keep the data. You can also remove all past data.",
"settingsHistoryAndMonitoring": "History of monitoring",
"settingsHistoryAndMonitoringDescription": "Define how long you want to retain historical data. You can also clear all existing data.",
"settingsTTLLabel": "The days you want to keep monitoring history.",
"settingsTTLOptionalLabel": "0 for infinite",
"settingsClearAllStats": "Clear all stats. This is irreversible.",
"settingsClearAllStatsButton": "Clear all stats",
"settingsClearAllStatsDialogTitle": "Do you want to clear all stats?",
"settingsClearAllStatsDialogDescription": "Once deleted, your monitors cannot be retrieved.",
"settingsClearAllStatsDialogDescription": "Once removed, the monitoring history and stats cannot be retrieved.",
"settingsClearAllStatsDialogConfirm": "Yes, clear all stats",
"settingsDemoMonitors": "Demo monitors",
"settingsDemoMonitorsDescription": "Here you can add and remove demo monitors.",
"settingsAddDemoMonitors": "Add demo monitors",
"settingsDemoMonitorsDescription": "Add sample monitors for demonstration purposes.",
"settingsAddDemoMonitors": "Adding demo monitors",
"settingsAddDemoMonitorsButton": "Add demo monitors",
"settingsRemoveAllMonitors": "Remove all monitors",
"settingsSystemReset": "System reset",
"settingsSystemResetDescription": "Remove all monitors from your system.",
"settingsRemoveAllMonitors": "Removing all monitors",
"settingsRemoveAllMonitorsButton": "Remove all monitors",
"settingsRemoveAllMonitorsDialogTitle": "Do you want to remove all monitors?",
"settingsRemoveAllMonitorsDialogConfirm": "Yes, clear all monitors",
"settingsRemoveAllMonitorsDialogConfirm": "Yes, remove all monitors",
"settingsWallet": "Wallet",
"settingsWalletDescription": "Connect your wallet here. This is required for the Distributed Uptime monitor to connect to multiple nodes globally.",
"settingsAbout": "About",
@@ -100,18 +108,26 @@
"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",
"http": "HTTP",
"monitor": "monitor",
"aboutus": "About Us",
"signUP": "Sign Up",
"now": "Now",
"delete": "Delete",
"configure": "Configure",
"networkError": "Network error",
"responseTime": "Response time:",
"responseTime": "Response time",
"ms": "ms",
"bar": "Bar",
"area": "Area",
@@ -377,9 +393,9 @@
"errorInvalidTypeId": "Invalid notification type provided",
"errorInvalidFieldId": "Invalid field ID provided",
"inviteNoTokenFound": "No invite token found",
"pageSpeedWarning": "Warning: You haven't added a Google PageSpeed API key. Without it, the PageSpeed monitor won't function.",
"pageSpeedLearnMoreLink": "Click here to learn",
"pageSpeedAddApiKey": "how to add your API key.",
"pageSpeedWarning": "Warning: You haven't added a Google PageSpeed API key yet. Without it, the PageSpeed monitor won't function.",
"pageSpeedLearnMoreLink": "Click here",
"pageSpeedAddApiKey": "to add your API key.",
"update": "Update",
"invalidFileFormat": "Unsupported file format!",
"invalidFileSize": "File size is too large!",
@@ -393,17 +409,19 @@
"ignoreTLSError": "Ignore TLS/SSL error",
"tlsErrorIgnored": "TLS/SSL errors ignored",
"ignoreTLSErrorDescription": "Ignore TLS/SSL errors and continue checking the website's availability",
"YourPhoto": "Your photo",
"YourPhoto": "Profile photo",
"PhotoDescriptionText": "This photo will be displayed in your profile page.",
"save": "Save",
"DeleteAccount": "Delete account",
"DeleteDescriptionText": "Note that deleting your account will remove all data from the server. This is permanent and non-recoverable.",
"DeleteAccountWarning": "If you delete your account, you will no longer be able to sign in, and all of your data will be deleted. Deleting your account is permanent and non-recoverable action.",
"DeleteWarningTitle": "Really delete this account?",
"DeleteAccountTitle": "Remove account",
"DeleteAccountButton": "Remove account",
"DeleteDescriptionText": "This will remove the account and all associated data from the server. This isn't reversible.",
"DeleteAccountWarning": "Removing your account means you won't be able to sign in again and all your data will be removed. This isn't reversible.",
"DeleteWarningTitle": "Really remove this account?",
"authRegisterFirstName": "Name",
"authRegisterLastName": "Surname",
"authRegisterEmail": "Email",
"authRegisterEmailRequired": "Email is required",
"authRegisterEmailRequired": "To continue, please enter your email address",
"authRegisterEmailInvalid": "Please enter a valid email address",
"bulkImport": {
"title": "Bulk Import",
"selectFileTips": "Select CSV file to upload",
@@ -415,6 +433,12 @@
"noFileSelected": "No file selected",
"fallbackPage": "Import a file to upload a list of servers in bulk"
},
"publicLink": "Public link"
"publicLink": "Public link",
"maskedPageSpeedKeyPlaceholder": "*************************************",
"pageSpeedApiKeyFieldTitle": "Google PageSpeed API key",
"pageSpeedApiKeyFieldLabel": "PageSpeed API key",
"pageSpeedApiKeyFieldDescription": "Enter your Google PageSpeed API key to enable pagespeed monitoring. Click Reset to update the key.",
"pageSpeedApiKeyFieldResetLabel": "API key is set. Click Reset to change it.",
"reset": "Reset"
}
+41 -16
View File
@@ -1,8 +1,10 @@
{
"dontHaveAccount": "Нет аккаунта",
"doNotHaveAccount": "У вас нет учетной записи?",
"registerHere": "Зарегистрироваться здесь",
"email": "Почта",
"forgotPassword": "Забыли пароль",
"password": "пароль",
"password": "Пароль",
"signUp": "Зарегистрироваться",
"submit": "Подтвердить",
"title": "Название",
@@ -15,8 +17,9 @@
"authForgotPasswordTitle": "Забыли пароль?",
"authForgotPasswordResetPassword": "Сбросить пароль",
"createPassword": "Создайте свой пароль",
"createAPassword": "Создайте пароль",
"createAPassword": "Пароль",
"authRegisterAlreadyHaveAccount": "Уже есть аккаунт?",
"authRegisterLoginLink": "Войти",
"commonAppName": "Checkmate",
"welcomeBack": "Добро пожаловать обратно! Вы успешно вошли в систему.",
"authLoginEnterEmail": "Введите свой email",
@@ -41,7 +44,8 @@
"authSetNewPasswordDescription": "Ваш новый пароль должен отличаться от ранее использованных паролей.",
"authSetNewPasswordNewPassword": "Новый пароль",
"authSetNewPasswordConfirmPassword": "Подтвердите пароль",
"confirmPassword": "Подтвердите ваш пароль",
"confirmPassword": "Введите пароль еще раз для подтверждения",
"confirmNewPasswordPlaceholder": "Подтвердите ваш новый пароль",
"authSetNewPasswordResetPassword": "Сбросить пароль",
"authSetNewPasswordBackTo": "Назад к",
"authPasswordMustBeAtLeast": "Должно быть как минимум",
@@ -52,7 +56,7 @@
"authPasswordUpperCharacter": "один верхний символ",
"authPasswordLowerCharacter": "один нижний символ",
"authPasswordConfirmAndPassword": "Подтвердите пароль и пароль",
"authPasswordMustMatch": "должен совпадать",
"authPasswordMustMatch": "Пароли должны совпадать",
"authRegisterCreateAccount": "Создайте свою учетную запись, чтобы начать",
"authRegisterCreateSuperAdminAccount": "Создайте учетную запись суперадминистратора, чтобы начать работу",
"authRegisterSignUpWithEmail": "Создать учетную запись суперадминистратора",
@@ -60,12 +64,13 @@
"authRegisterFirstName": "Имя",
"authRegisterLastName": "Фамилия",
"authRegisterEmail": "Эл. почта",
"authRegisterEmailRequired": "Эл. почта обязательна",
"authRegisterEmailRequired": "Чтобы продолжить, пожалуйста, введите ваш адрес электронной почты",
"authRegisterEmailInvalid": "Пожалуйста, введите корректный адрес электронной почты",
"distributedStatusHeaderText": "Охват реального времени и реального устройства",
"distributedStatusSubHeaderText": "Работает на миллионах устройств по всему миру, просматривайте производительность системы по глобальному региону, стране или городу",
"settingsGeneralSettings": "Общие настройки",
"settingsDisplayTimezone": "Отображать часовой пояс",
"settingsDisplayTimezoneDescription": "Часовой пояс панели мониторинга, которую вы публично отображаете.",
"settingsDisplayTimezoneDescription": "Выберите часовой пояс, используемый для отображения дат и времени в приложении.",
"settingsAppearance": "Внешний вид",
"settingsAppearanceDescription": "Переключение между светлым и темным режимом или изменение языка пользовательского интерфейса",
"settingsThemeMode": "Тема",
@@ -74,23 +79,25 @@
"settingsDistributedUptimeDescription": "Включить/выключить distributed uptime monitoring.",
"settingsEnabled": "Включено",
"settingsDisabled": "Выключено",
"settingsHistoryAndMonitoring": "История и мониторинг",
"settingsHistoryAndMonitoringDescription": "Определите здесь, как долго вы хотите хранить данные. Вы также можете удалить все прошлые данные.",
"settingsHistoryAndMonitoring": "История мониторинга",
"settingsHistoryAndMonitoringDescription": "Определите, как долго вы хотите хранить исторические данные. Вы также можете очистить все существующие данные.",
"settingsTTLLabel": "Дни, за которыми вы хотите следить.",
"settingsTTLOptionalLabel": "0 для бесконечности",
"settingsClearAllStats": "Очистить всю статистику. Это необратимо.",
"settingsClearAllStatsButton": "Очистить всю статистику",
"settingsClearAllStatsDialogTitle": "Хотите очистить всю статистику?",
"settingsClearAllStatsDialogDescription": "После удаления ваши мониторы не могут быть восстановлены.",
"settingsClearAllStatsDialogDescription": "После удаления история мониторинга и статистика не могут быть восстановлены.",
"settingsClearAllStatsDialogConfirm": "Да, очистить всю статистику",
"settingsDemoMonitors": "Демо мониторы",
"settingsDemoMonitorsDescription": "Здесь вы можете добавлять и удалять демонстрационные мониторы.",
"settingsAddDemoMonitors": "Добавьте демонстрационные мониторы",
"settingsDemoMonitorsDescription": "Добавьте примеры мониторов для демонстрации.",
"settingsAddDemoMonitors": "Добавление демонстрационных мониторов",
"settingsAddDemoMonitorsButton": "Добавьте демонстрационные мониторы",
"settingsRemoveAllMonitors": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsButton": "Удалить все демонстрационные мониторы",
"settingsSystemReset": "Сброс системы",
"settingsSystemResetDescription": "Удалить все мониторы из вашей системы.",
"settingsRemoveAllMonitors": "Удаление всех мониторов",
"settingsRemoveAllMonitorsButton": "Удалить все мониторы",
"settingsRemoveAllMonitorsDialogTitle": "Хотите удалить все мониторы?",
"settingsRemoveAllMonitorsDialogConfirm": "Да, очистить все мониторы",
"settingsRemoveAllMonitorsDialogConfirm": "Да, удалить все мониторы",
"settingsWallet": "Кошелёк",
"settingsWalletDescription": "Подключите свой кошелек здесь. Это необходимо для того, чтобы монитор Distributed Uptime мог подключиться к нескольким узлам по всему миру.",
"settingsAbout": "О",
@@ -100,22 +107,40 @@
"settingsFailedToSave": "Не удалось сохранить настройки",
"settingsStatsCleared": "Статистика успешно очищена",
"settingsFailedToClearStats": "Не удалось очистить статистику",
"FirstName": "Имя",
"LastName": "Фамилия",
"YourPhoto": "Фото профиля",
"PhotoDescriptionText": "Это фото будет отображаться на странице вашего профиля.",
"EmailDescriptionText": "Ваш текущий email—его нельзя изменить.",
"DeleteAccountTitle": "Удалить аккаунт",
"DeleteAccountButton": "Удалить аккаунт",
"DeleteDescriptionText": "Это удалит аккаунт и все связанные данные с сервера. Это необратимо.",
"DeleteAccountWarning": "Удаление аккаунта означает, что вы не сможете снова войти в систему, и все ваши данные будут удалены. Это необратимо.",
"DeleteWarningTitle": "Действительно удалить этот аккаунт?",
"settingsDemoMonitorsAdded": "Успешно добавлены демонстрационные мониторы",
"settingsFailedToAddDemoMonitors": "Не удалось добавить демонстрационные мониторы",
"settingsMonitorsDeleted": "Успешно удалены все мониторы",
"settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы",
"backendUnreachable": "Ошибка подключения к серверу",
"backendUnreachableMessage": "Мы не можем подключиться к серверу. Пожалуйста, проверьте ваше интернет-соединение или проверьте конфигурацию развертывания, если проблема не устраняется.",
"backendUnreachableError": "Невозможно подключиться к серверу. Пожалуйста, повторите попытку позже.",
"retryConnection": "Повторить подключение",
"retryingConnection": "Подключение...",
"backendReconnected": "Успешно восстановлено подключение к серверу.",
"backendStillUnreachable": "Сервер по-прежнему недоступен. Пожалуйста, повторите попытку позже.",
"backendConnectionError": "Ошибка подключения к серверу. Пожалуйста, проверьте ваше сетевое подключение.",
"starPromptTitle": "Star Checkmate",
"starPromptDescription": "Ознакомьтесь с последними релизами и помогите развить сообщество на GitHub",
"https": "HTTPS",
"http": "HTTP",
"monitor": "монитор",
"aboutus": "О Нас",
"signUP": "Зарегистрироваться",
"now": "Сейчас",
"delete": "Удалить",
"configure": "Настроить",
"networkError": "Ошибка сети",
"responseTime": "Время ответа:",
"responseTime": "Время ответа",
"ms": "мс",
"bar": "Bar",
"area": "Area",
+42 -26
View File
@@ -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",
@@ -15,8 +17,9 @@
"authForgotPasswordTitle": "Parolanızı mı unuttunuz?",
"authForgotPasswordResetPassword": "Parola sıfırla",
"createPassword": "Parolanızı oluşturun",
"createAPassword": "Bir parola oluşturun",
"createAPassword": "Parola",
"authRegisterAlreadyHaveAccount": "Zaten hesabınız var mı?",
"authRegisterLoginLink": "Giriş Yap",
"commonAppName": "Checkmate",
"welcomeBack": "Tekrar hoş geldiniz! Başarıyla giriş yaptınız.",
"authLoginEnterEmail": "E-posta adresinizi girin",
@@ -41,7 +44,8 @@
"authSetNewPasswordDescription": "Yeni şifreniz daha önce kullanılan şifrelerden farklı olmalıdır.",
"authSetNewPasswordNewPassword": "Yeni şifre",
"authSetNewPasswordConfirmPassword": "Parolayı onayla",
"confirmPassword": "Parolanızı onaylayın",
"confirmPassword": "Onaylamak için parolayı tekrar girin",
"confirmNewPasswordPlaceholder": "Yeni parolanızı onaylayın",
"authSetNewPasswordResetPassword": "Parola sıfırla",
"authSetNewPasswordBackTo": "Geri dön",
"authPasswordMustBeAtLeast": "En az",
@@ -52,7 +56,7 @@
"authPasswordUpperCharacter": "bir büyük harf",
"authPasswordLowerCharacter": "bir küçük harf",
"authPasswordConfirmAndPassword": "Onay şifresi ve şifre",
"authPasswordMustMatch": "eşleşmelidir",
"authPasswordMustMatch": "Parolalar eşleşmelidir",
"authRegisterCreateAccount": "Hesap oluşturmak için devam et",
"authRegisterCreateSuperAdminAccount": "Super admin hesabınızı oluşturmak için devam edin",
"authRegisterSignUpWithEmail": "E-posta ile kayıt ol",
@@ -61,7 +65,7 @@
"distributedStatusSubHeaderText": "Dünya çapında milyonlarca cihaz tarafından desteklenen sistem performansını küresel bölgeye, ülkeye veya şehre göre görüntüleyin",
"settingsGeneralSettings": "Genel ayarlar",
"settingsDisplayTimezone": "Görüntüleme saat dilimi",
"settingsDisplayTimezoneDescription": "Herkese açık olarak görüntülediğiniz kontrol panelinin saat dilimi.",
"settingsDisplayTimezoneDescription": "Uygulama genelinde tarih ve saatlerin görüntülenmesi için kullanılacak saat dilimini seçin.",
"settingsAppearance": "Görünüm",
"settingsAppearanceDescription": "Açık ve koyu mod arasında geçiş yapın veya kullanıcı arayüzü dilini değiştirin",
"settingsThemeMode": "Tema",
@@ -70,23 +74,25 @@
"settingsDistributedUptimeDescription": "Dağıtılmış çalışma süresi izlemeyi etkinleştirin/devre dışı bırakın.",
"settingsEnabled": "Etkin",
"settingsDisabled": "Devre dışı",
"settingsHistoryAndMonitoring": "Geçmiş ve izleme",
"settingsHistoryAndMonitoringDescription": "Verileri ne kadar süreyle saklamak istediğinizi burada tanımlayın. Ayrıca tüm geçmiş verileri kaldırabilirsiniz.",
"settingsHistoryAndMonitoring": "İzleme geçmişi",
"settingsHistoryAndMonitoringDescription": "Geçmiş verileri ne kadar süre saklamak istediğinizi tanımlayın. Ayrıca mevcut tüm verileri temizleyebilirsiniz.",
"settingsTTLLabel": "İzleme geçmişini saklamak istediğiniz gün sayısı.",
"settingsTTLOptionalLabel": "Sınırsız için 0",
"settingsClearAllStats": "Tüm istatistikleri temizle. Bu geri alınamaz.",
"settingsClearAllStatsButton": "Tüm istatistikleri temizle",
"settingsClearAllStatsDialogTitle": "Tüm istatistikleri temizlemek istiyor musunuz?",
"settingsClearAllStatsDialogDescription": "Silindikten sonra, monitörleriniz geri alınamaz.",
"settingsClearAllStatsDialogDescription": "Kaldırıldıktan sonra, izleme geçmişi ve istatistikleri geri alınamaz.",
"settingsClearAllStatsDialogConfirm": "Evet, tüm istatistikleri temizle",
"settingsDemoMonitors": "Demo monitörler",
"settingsDemoMonitorsDescription": "Burada demo monitörler ekleyebilir ve kaldırabilirsiniz.",
"settingsAddDemoMonitors": "Demo monitörler ekle",
"settingsDemoMonitorsDescription": "Gösterim amaçlı örnek monitörler ekleyin.",
"settingsAddDemoMonitors": "Demo monitörler ekleniyor",
"settingsAddDemoMonitorsButton": "Demo monitörler ekle",
"settingsRemoveAllMonitors": "Tüm monitörleri kaldır",
"settingsSystemReset": "Sistem sıfırlama",
"settingsSystemResetDescription": "Sisteminizden tüm monitörleri kaldırın.",
"settingsRemoveAllMonitors": "Tüm monitörler kaldırılıyor",
"settingsRemoveAllMonitorsButton": "Tüm monitörleri kaldır",
"settingsRemoveAllMonitorsDialogTitle": "Tüm monitörleri kaldırmak istiyor musunuz?",
"settingsRemoveAllMonitorsDialogConfirm": "Evet, tüm monitörleri temizle",
"settingsRemoveAllMonitorsDialogConfirm": "Evet, tüm monitörleri kaldır",
"settingsWallet": "Cüzdan",
"settingsWalletDescription": "Cüzdanınızı buradan bağlayın. Bu, Dağıtılmış Çalışma Süresi monitörünün küresel olarak birden çok düğüme bağlanması için gereklidir.",
"settingsAbout": "Hakkında",
@@ -100,18 +106,26 @@
"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",
"http": "HTTP",
"monitor": "monitör",
"aboutus": "Hakkımızda",
"signUP": "Hesap Oluştur",
"now": "Şimdi",
"delete": "Sil",
"configure": "Yapılandır",
"networkError": "Ağ hatası",
"responseTime": "Yanıt süresi:",
"responseTime": "Yanıt süresi",
"ms": "ms",
"bar": "Çubuk",
"area": "Alan",
@@ -387,20 +401,22 @@
"DragandDrop": "",
"MaxSize": "",
"SupportedFormats": "",
"FirstName": "",
"LastName": "",
"EmailDescriptionText": "",
"YourPhoto": "",
"PhotoDescriptionText": "",
"FirstName": "Ad",
"LastName": "Soyad",
"EmailDescriptionText": "Mevcut e-postanız—değiştirilemez.",
"YourPhoto": "Profil fotoğrafı",
"PhotoDescriptionText": "Bu fotoğraf profil sayfanızda görüntülenecektir.",
"save": "",
"DeleteAccount": "",
"DeleteDescriptionText": "",
"DeleteAccountWarning": "",
"DeleteWarningTitle": "",
"authRegisterFirstName": "",
"authRegisterLastName": "",
"authRegisterEmail": "",
"authRegisterEmailRequired": "",
"DeleteAccountTitle": "Hesabı kaldır",
"DeleteAccountButton": "Hesabı kaldır",
"DeleteDescriptionText": "Bu, hesabı ve tüm ilişkili verileri sunucudan kaldıracaktır. Bu geri alınamaz.",
"DeleteAccountWarning": "Hesabınızı kaldırmak, tekrar oturum açamayacağınız ve tüm verilerinizin kaldırılacağı anlamına gelir. Bu geri alınamaz.",
"DeleteWarningTitle": "Bu hesabı gerçekten kaldırmak istiyor musunuz?",
"authRegisterFirstName": "Ad",
"authRegisterLastName": "Soyad",
"authRegisterEmail": "E-posta",
"authRegisterEmailRequired": "Devam etmek için lütfen e-posta adresinizi girin",
"authRegisterEmailInvalid": "Lütfen geçerli bir e-posta adresi girin",
"bulkImport": {
"title": "",
"selectFileTips": "",