mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-03 06:59:32 -05:00
Merge branch 'develop' into feat/fe/uptime-ignore-tsl-error
This commit is contained in:
@@ -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 />}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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": "",
|
||||
|
||||
Reference in New Issue
Block a user