resolve merge conflicts

This commit is contained in:
Vishnu Sreekumaran Nair
2025-03-19 16:00:40 -04:00
22 changed files with 1014 additions and 301 deletions

6
package-lock.json generated
View File

@@ -9152,9 +9152,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"version": "18.3.19",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.19.tgz",
"integrity": "sha512-fcdJqaHOMDbiAwJnXv6XCzX0jDW77yI3tJqYh1Byn8EL5/S628WRx9b/y3DnNe55zTukUQKrfYxiZls2dHcUMw==",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",

View File

@@ -8,10 +8,10 @@ import ConfigButton from "./ConfigButton";
import SkeletonLayout from "./skeleton";
import PropTypes from "prop-types";
const MonitorStatusHeader = ({ path, shouldRender = true, isAdmin, monitor }) => {
const MonitorStatusHeader = ({ path, isLoading = false, isAdmin, monitor }) => {
const theme = useTheme();
const { statusColor, statusMsg, determineState } = useUtils();
if (!shouldRender) {
const { statusColor, determineState } = useUtils();
if (isLoading) {
return <SkeletonLayout />;
}
@@ -48,7 +48,7 @@ const MonitorStatusHeader = ({ path, shouldRender = true, isAdmin, monitor }) =>
MonitorStatusHeader.propTypes = {
path: PropTypes.string.isRequired,
shouldRender: PropTypes.bool,
isLoading: PropTypes.bool,
isAdmin: PropTypes.bool,
monitor: PropTypes.object,
};

View File

@@ -4,14 +4,14 @@ import SkeletonLayout from "./skeleton";
import PropTypes from "prop-types";
const MonitorTimeFrameHeader = ({
shouldRender = true,
isLoading = false,
hasDateRange = true,
dateRange,
setDateRange,
}) => {
const theme = useTheme();
if (!shouldRender) {
if (isLoading) {
return <SkeletonLayout />;
}
@@ -77,7 +77,7 @@ const MonitorTimeFrameHeader = ({
};
MonitorTimeFrameHeader.propTypes = {
shouldRender: PropTypes.bool,
isLoading: PropTypes.bool,
hasDateRange: PropTypes.bool,
dateRange: PropTypes.string,
setDateRange: PropTypes.func,

View File

@@ -1,5 +1,6 @@
import { useState, useMemo } from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
DialogContent,
@@ -8,11 +9,29 @@ import {
Typography,
Box,
Tabs,
Tab
Tab,
CircularProgress
} from "@mui/material";
import { useTheme } from "@emotion/react";
import TabPanel from "./TabPanel";
import TabComponent from "./TabComponent";
import useNotifications from "../Hooks/useNotification";
// Define constants for notification types to avoid magic values
const NOTIFICATION_TYPES = {
SLACK: 'slack',
DISCORD: 'discord',
TELEGRAM: 'telegram',
WEBHOOK: 'webhook'
};
// Define constants for field IDs
const FIELD_IDS = {
WEBHOOK: 'webhook',
TOKEN: 'token',
CHAT_ID: 'chatId',
URL: 'url'
};
const NotificationIntegrationModal = ({
open,
@@ -26,15 +45,30 @@ const NotificationIntegrationModal = ({
const theme = useTheme();
const [tabValue, setTabValue] = useState(0);
const [loading, _, sendTestNotification] = useNotifications();
// Helper to get the field state key with error handling
const getFieldKey = (typeId, fieldId) => {
if (typeof typeId !== 'string' || typeId === '') {
throw new Error('Invalid typeId provided to getFieldKey');
}
if (typeof fieldId !== 'string' || fieldId === '') {
throw new Error('Invalid fieldId provided to getFieldKey');
}
return `${typeId}${fieldId.charAt(0).toUpperCase() + fieldId.slice(1)}`;
};
// Define notification types
const DEFAULT_NOTIFICATION_TYPES = [
{
id: 'slack',
id: NOTIFICATION_TYPES.SLACK,
label: t('notifications.slack.label'),
description: t('notifications.slack.description'),
fields: [
{
id: 'webhook',
id: FIELD_IDS.WEBHOOK,
label: t('notifications.slack.webhookLabel'),
placeholder: t('notifications.slack.webhookPlaceholder'),
type: 'text'
@@ -42,12 +76,12 @@ const NotificationIntegrationModal = ({
]
},
{
id: 'discord',
id: NOTIFICATION_TYPES.DISCORD,
label: t('notifications.discord.label'),
description: t('notifications.discord.description'),
fields: [
{
id: 'webhook',
id: FIELD_IDS.WEBHOOK,
label: t('notifications.discord.webhookLabel'),
placeholder: t('notifications.discord.webhookPlaceholder'),
type: 'text'
@@ -55,18 +89,18 @@ const NotificationIntegrationModal = ({
]
},
{
id: 'telegram',
id: NOTIFICATION_TYPES.TELEGRAM,
label: t('notifications.telegram.label'),
description: t('notifications.telegram.description'),
fields: [
{
id: 'token',
id: FIELD_IDS.TOKEN,
label: t('notifications.telegram.tokenLabel'),
placeholder: t('notifications.telegram.tokenPlaceholder'),
type: 'text'
},
{
id: 'chatId',
id: FIELD_IDS.CHAT_ID,
label: t('notifications.telegram.chatIdLabel'),
placeholder: t('notifications.telegram.chatIdPlaceholder'),
type: 'text'
@@ -74,12 +108,12 @@ const NotificationIntegrationModal = ({
]
},
{
id: 'webhook',
id: NOTIFICATION_TYPES.WEBHOOK,
label: t('notifications.webhook.label'),
description: t('notifications.webhook.description'),
fields: [
{
id: 'url',
id: FIELD_IDS.URL,
label: t('notifications.webhook.urlLabel'),
placeholder: t('notifications.webhook.urlPlaceholder'),
type: 'text'
@@ -101,7 +135,7 @@ const NotificationIntegrationModal = ({
// Add state for each field in the notification type
type.fields.forEach(field => {
const fieldKey = `${type.id}${field.id.charAt(0).toUpperCase() + field.id.slice(1)}`;
const fieldKey = getFieldKey(type.id, field.id);
state[fieldKey] = monitor?.notifications?.find(n => n.type === type.id)?.[field.id] || "";
});
});
@@ -129,11 +163,26 @@ const NotificationIntegrationModal = ({
}));
};
const handleTestNotification = (type) => {
console.log(`Testing ${type} notification`);
//implement the test notification functionality
const handleTestNotification = async (type) => {
// Get the notification type details
const notificationType = activeNotificationTypes.find(t => t.id === type);
if (typeof notificationType === "undefined") {
return;
}
// Prepare config object based on notification type
const config = {};
// Add each field value to the config object
notificationType.fields.forEach(field => {
const fieldKey = getFieldKey(type, field.id);
config[field.id] = integrations[fieldKey];
});
await sendTestNotification(type, config);
};
const handleSave = () => {
//notifications array for selected integrations
const notifications = [...(monitor?.notifications || [])];
@@ -155,7 +204,7 @@ const NotificationIntegrationModal = ({
// Add each field value to the notification object
type.fields.forEach(field => {
const fieldKey = `${type.id}${field.id.charAt(0).toUpperCase() + field.id.slice(1)}`;
const fieldKey = getFieldKey(type.id, field.id);
notificationObject[field.id] = integrations[fieldKey];
});
@@ -240,6 +289,7 @@ const NotificationIntegrationModal = ({
handleIntegrationChange={handleIntegrationChange}
handleInputChange={handleInputChange}
handleTestNotification={handleTestNotification}
isLoading={loading}
/>
</TabPanel>
))}
@@ -257,13 +307,14 @@ const NotificationIntegrationModal = ({
variant="contained"
color="accent"
onClick={handleSave}
loading={loading}
sx={{
width: 'auto',
minWidth: theme.spacing(60),
px: theme.spacing(8)
}}
>
{t('common.save', 'Save')}
{t('commonSave')}
</Button>
</DialogActions>
</Dialog>

View File

@@ -2,19 +2,21 @@ import React from "react";
import {
Typography,
Box,
Button
Button,
CircularProgress
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { useTheme } from "@emotion/react";
import TextInput from "../../../src/Components/Inputs/TextInput";
import Checkbox from "../../../src/Components/Inputs/Checkbox";
import TextInput from "../../../Components/Inputs/TextInput";
import Checkbox from "../../../Components/Inputs/Checkbox";
const TabComponent = ({
type,
integrations,
handleIntegrationChange,
handleInputChange,
handleTestNotification
handleTestNotification,
isLoading
}) => {
const theme = useTheme();
const { t } = useTranslation();
@@ -55,6 +57,7 @@ const TabComponent = ({
label={t('notifications.enableNotifications', { platform: type.label })}
isChecked={integrations[type.id]}
onChange={(e) => handleIntegrationChange(type.id, e.target.checked)}
disabled={isLoading}
/>
</Box>
@@ -77,7 +80,7 @@ const TabComponent = ({
placeholder={field.placeholder}
value={integrations[fieldKey]}
onChange={(e) => handleInputChange(fieldKey, e.target.value)}
disabled={!integrations[type.id]}
disabled={!integrations[type.id] || isLoading}
/>
</Box>
);
@@ -88,8 +91,14 @@ const TabComponent = ({
variant="text"
color="info"
onClick={() => handleTestNotification(type.id)}
disabled={!integrations[type.id] || !areAllFieldsFilled()}
disabled={!integrations[type.id] || !areAllFieldsFilled() || isLoading}
>
{isLoading ? (
<CircularProgress
size={theme.spacing(8)}
sx={{ mr: theme.spacing(1), color: theme.palette.accent.main}}
/>
) : null}
{t('notifications.testNotification')}
</Button>
</Box>

View File

@@ -0,0 +1,119 @@
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useTranslation } from "react-i18next";
import { networkService } from '../../../Utils/NetworkService';
// Define constants for notification types to avoid magic values
const NOTIFICATION_TYPES = {
SLACK: 'slack',
DISCORD: 'discord',
TELEGRAM: 'telegram',
WEBHOOK: 'webhook'
};
// Define constants for field IDs
const FIELD_IDS = {
WEBHOOK: 'webhook',
TOKEN: 'token',
CHAT_ID: 'chatId',
URL: 'url'
};
/**
* Custom hook for notification-related operations
*/
const useNotifications = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(undefined);
const { t } = useTranslation();
/**
* Send a test notification
* @param {string} type - The notification type (slack, discord, telegram, webhook)
* @param {object} config - Configuration object with necessary params
*/
const sendTestNotification = async (type, config) => {
setLoading(true);
setError(undefined);
// Validation based on notification type
let payload = { platform: type };
let isValid = true;
let errorMessage = '';
switch(type) {
case NOTIFICATION_TYPES.SLACK:
payload.webhookUrl = config.webhook;
if (typeof payload.webhookUrl === 'undefined' || payload.webhookUrl === '') {
isValid = false;
errorMessage = t('notifications.slack.webhookRequired');
}
break;
case NOTIFICATION_TYPES.DISCORD:
payload.webhookUrl = config.webhook;
if (typeof payload.webhookUrl === 'undefined' || payload.webhookUrl === '') {
isValid = false;
errorMessage = t('notifications.discord.webhookRequired');
}
break;
case NOTIFICATION_TYPES.TELEGRAM:
payload.botToken = config.token;
payload.chatId = config.chatId;
if (typeof payload.botToken === 'undefined' || payload.botToken === '' ||
typeof payload.chatId === 'undefined' || payload.chatId === '') {
isValid = false;
errorMessage = t('notifications.telegram.fieldsRequired');
}
break;
case NOTIFICATION_TYPES.WEBHOOK:
payload.webhookUrl = config.url;
payload.platform = NOTIFICATION_TYPES.SLACK;
if (typeof payload.webhookUrl === 'undefined' || payload.webhookUrl === '') {
isValid = false;
errorMessage = t('notifications.webhook.urlRequired');
}
break;
default:
isValid = false;
errorMessage = t('notifications.unsupportedType');
}
// If validation fails, show error and return
if (isValid === false) {
toast.error(errorMessage);
setLoading(false);
return;
}
try {
const response = await networkService.testNotification({
platform: type,
payload: payload
});
if (response.data.success === true) {
toast.success(t('notifications.testSuccess'));
} else {
throw new Error(response.data.msg || t('notifications.testFailed'));
}
} catch (error) {
const errorMsg = error.response?.data?.msg || error.message || t('notifications.networkError');
toast.error(`${t('notifications.testFailed')}: ${errorMsg}`);
setError(errorMsg);
} finally {
setLoading(false);
}
};
return [
loading,
error,
sendTestNotification
];
};
export default useNotifications;

View File

@@ -0,0 +1,36 @@
import { useEffect, useState } from "react";
import { networkService } from "../main";
import { useNavigate } from "react-router-dom";
import { createToast } from "../Utils/toastUtils";
export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => {
const [networkError, setNetworkError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [monitor, setMonitor] = useState(undefined);
const [monitorStats, setMonitorStats] = useState(undefined);
const navigate = useNavigate();
useEffect(() => {
const fetchMonitors = async () => {
try {
const res = await networkService.getUptimeDetailsById({
monitorId: monitorId,
dateRange: dateRange,
normalize: true,
});
const { monitorData, monitorStats } = res?.data?.data ?? {};
setMonitor(monitorData);
setMonitorStats(monitorStats);
} catch (error) {
setNetworkError(true);
createToast({ body: error.message });
} finally {
setIsLoading(false);
}
};
fetchMonitors();
}, [dateRange, monitorId, navigate]);
return [monitor, monitorStats, isLoading, networkError];
};
export default useFetchUptimeMonitorDetails;

View File

@@ -27,7 +27,7 @@ const StatusPagesTable = ({ data }) => {
row.type === "distributed"
? `/status/distributed/public/${row.url}`
: `/status/uptime/public/${row.url}`;
window.open(url, "_blank", "noopener,noreferrer")
window.open(url, "_blank", "noopener,noreferrer");
}
},
render: (row) => {
@@ -40,13 +40,12 @@ const StatusPagesTable = ({ data }) => {
gap={theme.spacing(2)}
paddingLeft={theme.spacing(2)}
paddingRight={theme.spacing(2)}
borderRadius={theme.spacing(4)}
sx={{
...(row.isPublished && {
display: "inline-flex",
":hover": {
backgroundColor: `${theme.palette.primary.light}`,
cursor: "pointer",
borderRadius: 1,
borderBottom: 1,
},
}),
}}

View File

@@ -4,13 +4,13 @@ import { useNavigate, useParams } from "react-router-dom";
import { useEffect } from "react";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useTranslation } from "react-i18next";
// Utility and Network
import { checkEndpointResolution } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { monitorValidation } from "../../../Validation/validation";
import { getUptimeMonitorById } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { useTranslation } from "react-i18next";
// MUI
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
@@ -23,7 +23,7 @@ import Radio from "../../../Components/Inputs/Radio";
import Checkbox from "../../../Components/Inputs/Checkbox";
import Select from "../../../Components/Inputs/Select";
import ConfigBox from "../../../Components/ConfigBox";
import NotificationIntegrationModal from "../../../Components/NotificationIntegrationModal/NotificationIntegrationModal";
import NotificationIntegrationModal from "../../../Components/NotificationIntegrationModal/Components/NotificationIntegrationModal";
const CreateMonitor = () => {
const MS_PER_MINUTE = 60000;
const SELECT_VALUES = [
@@ -84,8 +84,8 @@ const CreateMonitor = () => {
const [isNotificationModalOpen, setIsNotificationModalOpen] = useState(false);
const handleOpenNotificationModal = () => {
setIsNotificationModalOpen(true);
};
setIsNotificationModalOpen(true);
};
const [errors, setErrors] = useState({});
const [https, setHttps] = useState(true);
const [monitor, setMonitor] = useState({
@@ -210,7 +210,7 @@ const CreateMonitor = () => {
const handleAddNotification = () => {
console.log("Add notification clicked");
};
};
useEffect(() => {
const fetchMonitor = async () => {
@@ -354,9 +354,7 @@ const CreateMonitor = () => {
<ConfigBox>
<Box>
<Typography component="h2">{t("settingsGeneralSettings")}</Typography>
<Typography component="p">
{t("distributedUptimeCreateSelectURL")}
</Typography>
<Typography component="p">{t("distributedUptimeCreateSelectURL")}</Typography>
</Box>
<Stack gap={theme.spacing(15)}>
<TextInput
@@ -399,7 +397,9 @@ const CreateMonitor = () => {
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">{t("distributedUptimeCreateIncidentNotification")}</Typography>
<Typography component="h2">
{t("distributedUptimeCreateIncidentNotification")}
</Typography>
<Typography component="p">
{t("distributedUptimeCreateIncidentDescription")}
</Typography>
@@ -415,21 +415,22 @@ const CreateMonitor = () => {
onChange={(event) => handleNotifications(event, "email")}
/>
{/* <Box mt={theme.spacing(2)}>
<Button
variant="contained"
color="accent"
onClick={handleOpenNotificationModal}
>
Notification Integration
</Button>
</Box> */}
<Box mt={theme.spacing(2)}>
<Button
variant="contained"
color="accent"
onClick={handleOpenNotificationModal}
>
{t("notifications.integrationButton")}
</Button>
</Box>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">{t("distributedUptimeCreateAdvancedSettings")}</Typography>
<Typography component="h2">
{t("distributedUptimeCreateAdvancedSettings")}
</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Select
@@ -520,11 +521,11 @@ const CreateMonitor = () => {
</Stack>
<NotificationIntegrationModal
open={isNotificationModalOpen}
onClose={() => setIsNotificationModalOpen(false)}
monitor={monitor}
setMonitor={setMonitor}
/>
open={isNotificationModalOpen}
onClose={() => setIsNotificationModalOpen(false)}
monitor={monitor}
setMonitor={setMonitor}
/>
</Box>
);
};

View File

@@ -12,29 +12,23 @@ import SkeletonLayout from "./skeleton";
import { formatDateWithTz } from "../../../../../Utils/timeUtils";
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { useState } from "react";
const ChartBoxes = ({
shouldRender = true,
monitor,
isLoading = false,
monitorData,
dateRange,
uiTimezone,
dateFormat,
hoveredUptimeData,
setHoveredUptimeData,
hoveredIncidentsData,
setHoveredIncidentsData,
}) => {
// Local state
const [hoveredUptimeData, setHoveredUptimeData] = useState(null);
const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null);
const theme = useTheme();
if (!shouldRender) {
if (isLoading) {
return <SkeletonLayout />;
}
const totalUpChecks = monitor?.upChecks?.totalChecks ?? 0;
const totalDownChecks = monitor?.downChecks?.totalChecks ?? 0;
const denominator =
totalUpChecks + totalDownChecks > 0 ? totalUpChecks + totalDownChecks : 1;
const groupedUptimePercentage = (totalUpChecks / denominator) * 100;
const noIncidentsMessage = "Great. No Incidents, yet!";
return (
@@ -46,7 +40,7 @@ const ChartBoxes = ({
<ChartBox
icon={<UptimeIcon />}
header="Uptime"
isEmpty={monitor?.uptimePercentage === 0 && !monitor?.upChecks?.length}
isEmpty={monitorData?.groupedUpChecks?.length === 0}
>
<Stack
width={"100%"}
@@ -58,7 +52,7 @@ const ChartBoxes = ({
<Typography component="span">
{hoveredUptimeData !== null
? hoveredUptimeData.totalChecks
: (monitor?.groupedUpChecks?.reduce((count, checkGroup) => {
: (monitorData?.groupedUpChecks?.reduce((count, checkGroup) => {
return count + checkGroup.totalChecks;
}, 0) ?? 0)}
</Typography>
@@ -81,7 +75,7 @@ const ChartBoxes = ({
<Typography component="span">
{hoveredUptimeData !== null
? Math.floor(hoveredUptimeData?.avgResponseTime ?? 0)
: Math.floor(groupedUptimePercentage)}
: Math.floor(monitorData?.groupedUptimePercentage * 100 ?? 0)}
<Typography component="span">
{hoveredUptimeData !== null ? " ms" : " %"}
</Typography>
@@ -89,7 +83,7 @@ const ChartBoxes = ({
</Box>
</Stack>
<UpBarChart
monitor={monitor}
groupedUpChecks={monitorData?.groupedUpChecks}
type={dateRange}
onBarHover={setHoveredUptimeData}
/>
@@ -98,14 +92,14 @@ const ChartBoxes = ({
icon={<IncidentsIcon />}
header="Incidents"
noDataMessage={noIncidentsMessage}
isEmpty={monitor?.groupedDownChecks?.length === 0}
isEmpty={monitorData?.groupedDownChecks?.length === 0}
>
<Stack width={"100%"}>
<Box position="relative">
<Typography component="span">
{hoveredIncidentsData !== null
? hoveredIncidentsData.totalChecks
: (monitor?.groupedDownChecks?.reduce((count, checkGroup) => {
: (monitorData?.groupedDownChecks?.reduce((count, checkGroup) => {
return count + checkGroup.totalChecks;
}, 0) ?? 0)}
</Typography>
@@ -123,7 +117,7 @@ const ChartBoxes = ({
</Box>
</Stack>
<DownBarChart
monitor={monitor}
groupedDownChecks={monitorData?.groupedDownChecks}
type={dateRange}
onBarHover={setHoveredIncidentsData}
/>
@@ -132,7 +126,7 @@ const ChartBoxes = ({
icon={<AverageResponseIcon />}
header="Average Response Time"
>
<ResponseGaugeChart avgResponseTime={monitor?.avgResponseTime ?? 0} />
<ResponseGaugeChart avgResponseTime={monitorData?.groupedAvgResponseTime ?? 0} />
</ChartBox>
</Stack>
);
@@ -141,8 +135,8 @@ const ChartBoxes = ({
export default ChartBoxes;
ChartBoxes.propTypes = {
shouldRender: PropTypes.bool,
monitor: PropTypes.object,
isLoading: PropTypes.bool,
monitorData: PropTypes.object,
dateRange: PropTypes.string.isRequired,
uiTimezone: PropTypes.string.isRequired,
dateFormat: PropTypes.string.isRequired,

View File

@@ -4,7 +4,7 @@ import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts";
import PropTypes from "prop-types";
import CustomLabels from "./CustomLabels";
const DownBarChart = memo(({ monitor, type, onBarHover }) => {
const DownBarChart = memo(({ groupedDownChecks = [], type, onBarHover }) => {
const theme = useTheme();
const [chartHovered, setChartHovered] = useState(false);
@@ -19,7 +19,7 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => {
<BarChart
width="100%"
height="100%"
data={monitor?.groupedDownChecks}
data={groupedDownChecks}
onMouseEnter={() => {
setChartHovered(true);
onBarHover({ time: null, totalChecks: 0 });
@@ -40,10 +40,8 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => {
y={0}
width="100%"
height="100%"
firstDataPoint={monitor?.groupedDownChecks?.[0] ?? {}}
lastDataPoint={
monitor?.groupedDownChecks?.[monitor?.groupedDownChecks?.length - 1] ?? {}
}
firstDataPoint={groupedDownChecks?.[0] ?? {}}
lastDataPoint={groupedDownChecks?.[groupedDownChecks?.length - 1] ?? {}}
type={type}
/>
}
@@ -53,7 +51,7 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => {
maxBarSize={7}
background={{ fill: "transparent" }}
>
{monitor?.groupedDownChecks?.map((entry, index) => {
{groupedDownChecks?.map((entry, index) => {
return (
<Cell
key={`cell-${entry.time}`}

View File

@@ -4,8 +4,8 @@ import ResponseTimeIcon from "../../../../../assets/icons/response-time-icon.svg
import SkeletonLayout from "./ResponseTimeChartSkeleton";
import PropTypes from "prop-types";
const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => {
if (!shouldRender) {
const ResponseTImeChart = ({ isLoading = false, groupedChecks = [], dateRange }) => {
if (isLoading) {
return <SkeletonLayout />;
}
@@ -15,7 +15,7 @@ const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => {
header="Response Times"
>
<MonitorDetailsAreaChart
checks={monitor?.groupedChecks ?? []}
checks={groupedChecks}
dateRange={dateRange}
/>
</ChartBox>
@@ -23,8 +23,8 @@ const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => {
};
ResponseTImeChart.propTypes = {
shouldRender: PropTypes.bool,
monitor: PropTypes.object,
isLoading: PropTypes.bool,
groupedChecks: PropTypes.array,
dateRange: PropTypes.string,
};

View File

@@ -14,7 +14,7 @@ const getThemeColor = (responseTime) => {
}
};
const UpBarChart = memo(({ monitor, type, onBarHover }) => {
const UpBarChart = memo(({ groupedUpChecks = [], type, onBarHover }) => {
const theme = useTheme();
const [chartHovered, setChartHovered] = useState(false);
const [hoveredBarIndex, setHoveredBarIndex] = useState(null);
@@ -28,7 +28,7 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => {
<BarChart
width="100%"
height="100%"
data={monitor?.groupedUpChecks}
data={groupedUpChecks}
onMouseEnter={() => {
setChartHovered(true);
onBarHover({ time: null, totalChecks: 0, avgResponseTime: 0 });
@@ -49,10 +49,8 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => {
y={0}
width="100%"
height="100%"
firstDataPoint={monitor?.groupedUpChecks?.[0]}
lastDataPoint={
monitor?.groupedUpChecks?.[monitor?.groupedUpChecks?.length - 1]
}
firstDataPoint={groupedUpChecks?.[0]}
lastDataPoint={groupedUpChecks?.[groupedUpChecks?.length - 1]}
type={type}
/>
}
@@ -62,7 +60,7 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => {
maxBarSize={7}
background={{ fill: "transparent" }}
>
{monitor?.groupedUpChecks?.map((entry, index) => {
{groupedUpChecks?.map((entry, index) => {
const themeColor = getThemeColor(entry.avgResponseTime);
return (
<Cell

View File

@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
import { formatDateWithTz } from "../../../../../Utils/timeUtils";
import SkeletonLayout from "./skeleton";
const ResponseTable = ({
shouldRender = true,
isLoading = false,
checks = [],
checksCount,
uiTimezone,
@@ -18,7 +18,7 @@ const ResponseTable = ({
setRowsPerPage,
}) => {
const { t } = useTranslation();
if (!shouldRender) {
if (isLoading) {
return <SkeletonLayout />;
}
@@ -78,7 +78,7 @@ const ResponseTable = ({
};
ResponseTable.propTypes = {
shouldRender: PropTypes.bool,
isLoading: PropTypes.bool,
checks: PropTypes.array,
checksCount: PropTypes.number,
uiTimezone: PropTypes.string.isRequired,

View File

@@ -1,23 +1,36 @@
import StatusBoxes from "../../../../../Components/StatusBoxes";
import StatBox from "../../../../../Components/StatBox";
import PropTypes from "prop-types";
import { getHumanReadableDuration } from "../../../../../Utils/timeUtils";
import { useTheme } from "@mui/material/styles";
import { Typography } from "@mui/material";
import useUtils from "../../../Monitors/Hooks/useUtils";
const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => {
const UptimeStatusBoxes = ({
isLoading = false,
monitor,
monitorStats,
certificateExpiry,
}) => {
const theme = useTheme();
const { determineState } = useUtils();
const { time: streakTime, units: streakUnits } = getHumanReadableDuration(
monitor?.uptimeStreak
);
// Determine time since last failure
const timeOfLastFailure = monitorStats?.timeOfLastFailure;
const timeSinceLastFailure = timeOfLastFailure > 0 ? Date.now() - timeOfLastFailure : 0;
const { time: lastCheckTime, units: lastCheckUnits } = getHumanReadableDuration(
monitor?.timeSinceLastCheck
);
// Determine time since last check
const timeOfLastCheck = monitorStats?.lastCheckTimestamp;
const timeSinceLastCheck = Date.now() - timeOfLastCheck;
const { time: streakTime, units: streakUnits } =
getHumanReadableDuration(timeSinceLastFailure);
const { time: lastCheckTime, units: lastCheckUnits } =
getHumanReadableDuration(timeSinceLastCheck);
return (
<StatusBoxes shouldRender={shouldRender}>
<StatusBoxes shouldRender={!isLoading}>
<StatBox
gradient={true}
status={determineState(monitor)}
@@ -43,7 +56,7 @@ const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => {
heading="last response time"
subHeading={
<>
{monitor?.latestResponseTime}
{monitorStats?.lastResponseTime}
<Typography component="span">{"ms"}</Typography>
</>
}
@@ -64,4 +77,11 @@ const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => {
);
};
UptimeStatusBoxes.propTypes = {
shouldRender: PropTypes.bool,
monitor: PropTypes.object,
monitorStats: PropTypes.object,
certificateExpiry: PropTypes.string,
};
export default UptimeStatusBoxes;

View File

@@ -16,7 +16,7 @@ import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import useMonitorFetch from "./Hooks/useMonitorFetch";
import useFetchUptimeMonitorDetails from "../../../Hooks/useFetchUptimeMonitorDetails";
import useCertificateFetch from "./Hooks/useCertificateFetch";
import useChecksFetch from "./Hooks/useChecksFetch";
import { useTranslation } from "react-i18next";
@@ -36,8 +36,7 @@ const UptimeDetails = () => {
// Local state
const [dateRange, setDateRange] = useState("recent");
const [hoveredUptimeData, setHoveredUptimeData] = useState(null);
const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
@@ -49,10 +48,13 @@ const UptimeDetails = () => {
const isAdmin = useIsAdmin();
const { t } = useTranslation();
const [monitor, monitorIsLoading, monitorNetworkError] = useMonitorFetch({
monitorId,
dateRange,
});
const [monitorData, monitorStats, monitorIsLoading, monitorNetworkError] =
useFetchUptimeMonitorDetails({
monitorId,
dateRange,
});
const monitor = monitorData?.monitor;
const [certificateExpiry, certificateIsLoading] = useCertificateFetch({
monitor,
@@ -62,7 +64,6 @@ const UptimeDetails = () => {
});
const monitorType = monitor?.type;
const [checks, checksCount, checksAreLoading, checksNetworkError] = useChecksFetch({
monitorId,
monitorType,
@@ -71,6 +72,8 @@ const UptimeDetails = () => {
rowsPerPage,
});
console.log("render");
// Handlers
const handlePageChange = (_, newPage) => {
setPage(newPage);
@@ -119,38 +122,35 @@ const UptimeDetails = () => {
<MonitorStatusHeader
path={"uptime"}
isAdmin={isAdmin}
shouldRender={!monitorIsLoading}
isLoading={monitorIsLoading}
monitor={monitor}
/>
<UptimeStatusBoxes
shouldRender={!monitorIsLoading}
isLoading={monitorIsLoading}
monitor={monitor}
monitorStats={monitorStats}
certificateExpiry={certificateExpiry}
/>
<MonitorTimeFrameHeader
shouldRender={!monitorIsLoading}
isLoading={monitorIsLoading}
hasDateRange={true}
dateRange={dateRange}
setDateRange={setDateRange}
/>
<ChartBoxes
shouldRender={!monitorIsLoading}
monitor={monitor}
isLoading={monitorIsLoading}
monitorData={monitorData}
uiTimezone={uiTimezone}
dateRange={dateRange}
dateFormat={dateFormat}
hoveredUptimeData={hoveredUptimeData}
setHoveredUptimeData={setHoveredUptimeData}
hoveredIncidentsData={hoveredIncidentsData}
setHoveredIncidentsData={setHoveredIncidentsData}
/>
<ResponseTimeChart
shouldRender={!monitorIsLoading}
monitor={monitor}
isLoading={monitorIsLoading}
groupedChecks={monitorData?.groupedChecks}
dateRange={dateRange}
/>
<ResponseTable
shouldRender={!checksAreLoading}
isLoading={checksAreLoading}
checks={checks}
uiTimezone={uiTimezone}
page={page}

View File

@@ -669,6 +669,28 @@ class NetworkService {
});
}
/**
* ************************************
* Test a notification integration
* ************************************
*
* @async
* @param {Object} config - The configuration object.
* @param {string} config.platform - The notification platform (slack, discord, telegram, webhook).
* @param {Object} config.payload - The payload with configuration for the notification.
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
*/
async testNotification(config) {
return this.axiosInstance.post('/notifications/test-webhook', {
platform: config.platform,
...config.payload
}, {
headers: {
"Content-Type": "application/json",
},
});
}
/**
* ************************************
* Creates a maintenance window

View File

@@ -83,8 +83,11 @@ export const getHumanReadableDuration = (ms) => {
const days = Math.floor(durationObj.asDays());
return { time: days, units: days === 1 ? "day" : "days" };
} else if (durationObj.asHours() >= 1) {
const hours = Math.floor(durationObj.asHours());
return { time: hours, units: hours === 1 ? "hour" : "hours" };
const hoursRounded = Math.round(durationObj.asHours() * 10) / 10;
const hours = Number.isInteger(hoursRounded)
? Math.floor(hoursRounded)
: hoursRounded;
return { time: hours, units: hours <= 1 ? "hour" : "hours" };
} else if (durationObj.asMinutes() >= 1) {
const minutes = Math.floor(durationObj.asMinutes());
return { time: minutes, units: minutes === 1 ? "minute" : "minutes" };

View File

@@ -107,28 +107,6 @@
"aboutus": "About Us",
"signUP": "Sign Up",
"now": "Now",
"createYour": "Create your",
"createMonitor": "Create monitor",
"pause": "Pause",
"resume": "Resume",
"editing": "Editing...",
"url": "URL",
"access": "Access",
"timezone": "Timezone",
"features": "Features",
"administrator": "Administrator?",
"loginHere": "Login here",
"displayName": "Display name",
"urlMonitor": "URL to monitor",
"portToMonitor": "Port to monitor",
"websiteMonitoring": "Website monitoring",
"websiteMonitoringDescription": "Use HTTP(s) to monitor your website or API endpoint.",
"pingMonitoring": "Ping monitoring",
"pingMonitoringDescription": "Check whether your server is available or not.",
"dockerContainerMonitoring": "Docker container monitoring",
"dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.",
"portMonitoring": "Port monitoring",
"portMonitoringDescription": "Check whether your port is open or not.",
"delete": "Delete",
"configure": "Configure",
"networkError": "Network error",
@@ -165,79 +143,6 @@
"distributedUptimeDetailsMonitorHeader": "Distributed Uptime Monitoring powered by DePIN",
"distributedUptimeDetailsStatusHeaderUptime": "Uptime:",
"distributedUptimeDetailsStatusHeaderLastUpdate": "Last updated",
"createMaintenanceWindow": "Create maintenance window",
"createMaintenance": "Create maintenance",
"editMaintenance": "Edit maintenance",
"maintenanceWindowName": "Maintenance Window Name",
"friendlyNameInput": "Friendly name",
"friendlyNamePlaceholder": "Maintenance at __ : __ for ___ minutes",
"maintenanceRepeat": "Maintenance Repeat",
"maintenance": "maintenance",
"duration": "Duration",
"addMonitors": "Add monitors",
"window": "window",
"cancel": "Cancel",
"message": "Message",
"low": "low",
"high": "high",
"statusCode": "Status code",
"date&Time": "Date & Time",
"type": "Type",
"statusPageName": "Status page name",
"publicURL": "Public URL",
"repeat": "Repeat",
"edit": "Edit",
"createA": "Create a",
"remove": "Remove",
"maintenanceWindowDescription": "Your pings won't be sent during this time frame",
"startTime": "Start time",
"timeZoneInfo": "All dates and times are in GMT+0 time zone.",
"monitorsToApply": "Monitors to apply maintenance window to",
"nextWindow": "Next window",
"notFoundButton": "Go to the main dashboard",
"pageSpeedConfigureSettingsDescription": "Here you can select the URL of the host, together with the type of monitor.",
"monitorDisplayName": "Monitor display name",
"whenNewIncident": "When there is a new incident,",
"notifySMS": "Notify via SMS (coming soon)",
"notifyEmails": "Also notify via email to multiple addresses (coming soon)",
"seperateEmails": "You can separate multiple emails with a comma",
"checkFrequency": "Check frequency",
"matchMethod": "Match Method",
"expectedValue": "Expected value",
"deleteDialogTitle": "Do you really want to delete this monitor?",
"deleteDialogDescription": "Once deleted, this monitor cannot be retrieved.",
"pageSpeedMonitor": "PageSpeed monitor",
"shown": "Shown",
"ago": "ago",
"companyName": "Company name",
"pageSpeedDetailsPerformanceReport": "Values are estimated and may vary.",
"pageSpeedDetailsPerformanceReportCalculator": "See calculator",
"checkingEvery": "Checking every",
"statusPageCreateSettings": "If your status page is ready, you can mark it as published.",
"basicInformation": "Basic Information",
"statusPageCreateBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
"statusPageCreateSelectTimeZoneDescription": "Select the timezone that your status page will be displayed in.",
"statusPageCreateAppearanceDescription": "Define the default look and feel of your public status page.",
"statusPageCreateSettingsCheckboxLabel": "Published and visible to the public",
"statusPageCreateBasicInfoStatusPageAddress": "Your status page address",
"statusPageCreateTabsContent": "Status page servers",
"statusPageCreateTabsContentDescription": "You can add any number of servers that you monitor to your status page. You can also reorder them for the best viewing experience.",
"statusPageCreateTabsContentFeaturesDescription": "Show more details on the status page",
"showCharts": "Show charts",
"showUptimePercentage": "Show uptime percentage",
"removeLogo": "Remove Logo",
"statusPageStatus": "A public status page is not set up.",
"statusPageStatusContactAdmin": "Please contact to your administrator",
"statusPageStatusNotPublic": "This status page is not public.",
"statusPageStatusNoPage": "There's no status page here.",
"statusPageStatusServiceStatus": "Service status",
"deleteStatusPage": "Do you want to delete this status page?",
"deleteStatusPageConfirm": "Yes, delete status page",
"deleteStatusPageDescription": "Once deleted, your status page cannot be retrieved.",
"uptimeCreate": "The expected value is used to match against response result, and the match determines the status.",
"uptimeCreateJsonPath": "This expression will be evaluated against the reponse JSON data and the result will be used to match against the expected value. See",
"uptimeCreateJsonPathQuery": "for query language documentation.",
"maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?",
"notifications": {
"enableNotifications": "Enable {{platform}} notifications",
"testNotification": "Test notification",
@@ -268,7 +173,8 @@
"urlLabel": "Webhook URL",
"urlPlaceholder": "https://your-server.com/webhook"
},
"testNotificationDevelop": "Test notification 2"
"testNotificationDevelop": "Test notification 2",
"integrationButton": "Notification Integration"
},
"testLocale": "",
"add": "Add",
@@ -362,5 +268,101 @@
"integrationsDiscord": "Discord",
"integrationsDiscordInfo": "Connect with Discord and view incidents directly in a channel",
"integrationsZapier": "Zapier",
"integrationsZapierInfo": "Send all incidents to Zapier, and then see them everywhere"
"integrationsZapierInfo": "Send all incidents to Zapier, and then see them everywhere",
"commonSave": "Save",
"createYour": "Create your",
"createMonitor": "Create monitor",
"pause": "Pause",
"resume": "Resume",
"editing": "Editing...",
"url": "URL",
"access": "Access",
"timezone": "Timezone",
"features": "Features",
"administrator": "Administrator?",
"loginHere": "Login here",
"displayName": "Display name",
"urlMonitor": "URL to monitor",
"portToMonitor": "Port to monitor",
"websiteMonitoring": "Website monitoring",
"websiteMonitoringDescription": "Use HTTP(s) to monitor your website or API endpoint.",
"pingMonitoring": "Ping monitoring",
"pingMonitoringDescription": "Check whether your server is available or not.",
"dockerContainerMonitoring": "Docker container monitoring",
"dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.",
"portMonitoring": "Port monitoring",
"portMonitoringDescription": "Check whether your port is open or not.",
"createMaintenanceWindow": "Create maintenance window",
"createMaintenance": "Create maintenance",
"editMaintenance": "Edit maintenance",
"maintenanceWindowName": "Maintenance Window Name",
"friendlyNameInput": "Friendly name",
"friendlyNamePlaceholder": "Maintenance at __ : __ for ___ minutes",
"maintenanceRepeat": "Maintenance Repeat",
"maintenance": "maintenance",
"duration": "Duration",
"addMonitors": "Add monitors",
"window": "window",
"cancel": "Cancel",
"message": "Message",
"low": "low",
"high": "high",
"statusCode": "Status code",
"date&Time": "Date & Time",
"type": "Type",
"statusPageName": "Status page name",
"publicURL": "Public URL",
"repeat": "Repeat",
"edit": "Edit",
"createA": "Create a",
"remove": "Remove",
"maintenanceWindowDescription": "Your pings won't be sent during this time frame",
"startTime": "Start time",
"timeZoneInfo": "All dates and times are in GMT+0 time zone.",
"monitorsToApply": "Monitors to apply maintenance window to",
"nextWindow": "Next window",
"notFoundButton": "Go to the main dashboard",
"pageSpeedConfigureSettingsDescription": "Here you can select the URL of the host, together with the type of monitor.",
"monitorDisplayName": "Monitor display name",
"whenNewIncident": "When there is a new incident,",
"notifySMS": "Notify via SMS (coming soon)",
"notifyEmails": "Also notify via email to multiple addresses (coming soon)",
"seperateEmails": "You can separate multiple emails with a comma",
"checkFrequency": "Check frequency",
"matchMethod": "Match Method",
"expectedValue": "Expected value",
"deleteDialogTitle": "Do you really want to delete this monitor?",
"deleteDialogDescription": "Once deleted, this monitor cannot be retrieved.",
"pageSpeedMonitor": "PageSpeed monitor",
"shown": "Shown",
"ago": "ago",
"companyName": "Company name",
"pageSpeedDetailsPerformanceReport": "Values are estimated and may vary.",
"pageSpeedDetailsPerformanceReportCalculator": "See calculator",
"checkingEvery": "Checking every",
"statusPageCreateSettings": "If your status page is ready, you can mark it as published.",
"basicInformation": "Basic Information",
"statusPageCreateBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
"statusPageCreateSelectTimeZoneDescription": "Select the timezone that your status page will be displayed in.",
"statusPageCreateAppearanceDescription": "Define the default look and feel of your public status page.",
"statusPageCreateSettingsCheckboxLabel": "Published and visible to the public",
"statusPageCreateBasicInfoStatusPageAddress": "Your status page address",
"statusPageCreateTabsContent": "Status page servers",
"statusPageCreateTabsContentDescription": "You can add any number of servers that you monitor to your status page. You can also reorder them for the best viewing experience.",
"statusPageCreateTabsContentFeaturesDescription": "Show more details on the status page",
"showCharts": "Show charts",
"showUptimePercentage": "Show uptime percentage",
"removeLogo": "Remove Logo",
"statusPageStatus": "A public status page is not set up.",
"statusPageStatusContactAdmin": "Please contact to your administrator",
"statusPageStatusNotPublic": "This status page is not public.",
"statusPageStatusNoPage": "There's no status page here.",
"statusPageStatusServiceStatus": "Service status",
"deleteStatusPage": "Do you want to delete this status page?",
"deleteStatusPageConfirm": "Yes, delete status page",
"deleteStatusPageDescription": "Once deleted, your status page cannot be retrieved.",
"uptimeCreate": "The expected value is used to match against response result, and the match determines the status.",
"uptimeCreateJsonPath": "This expression will be evaluated against the reponse JSON data and the result will be used to match against the expected value. See",
"uptimeCreateJsonPathQuery": "for query language documentation.",
"maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?"
}

364
src/locales/ru.json Normal file
View File

@@ -0,0 +1,364 @@
{
"dontHaveAccount": "Нет аккаунта",
"email": "Почта",
"forgotPassword": "Забыли пароль",
"password": "пароль",
"signUp": "Зарегистрироваться",
"submit": "Подтвердить",
"title": "Название",
"continue": "Продолжить",
"enterEmail": "Введите свой email",
"authLoginTitle": "Войти",
"authLoginEnterPassword": "Введите свой пароль",
"commonPassword": "Пароль",
"commonBack": "Назад",
"authForgotPasswordTitle": "Забыли пароль?",
"authForgotPasswordResetPassword": "Сбросить пароль",
"createPassword": "Создайте свой пароль",
"createAPassword": "Создайте пароль",
"authRegisterAlreadyHaveAccount": "Уже есть аккаунт?",
"commonAppName": "Checkmate",
"authLoginEnterEmail": "Введите свой email",
"authRegisterTitle": "Создать аккаунт",
"authRegisterStepOneTitle": "Создайте свой аккаут",
"authRegisterStepOneDescription": "Введите свои данные, чтобы начать",
"authRegisterStepTwoTitle": "Настройте свой профиль",
"authRegisterStepTwoDescription": "Расскажите нам о себе",
"authRegisterStepThreeTitle": "Почти готово!",
"authRegisterStepThreeDescription": "Проверьте свою информацию",
"authForgotPasswordDescription": "Не волнуйтесь, мы вышлем вам инструкции по сбросу настроек.",
"authForgotPasswordSendInstructions": "Отправить инструкции",
"authForgotPasswordBackTo": "Назад к",
"authCheckEmailTitle": "Проверьте свою почту",
"authCheckEmailDescription": "Мы отправили ссылку для сброса пароля",
"authCheckEmailResendEmail": "Отправить письмо повторно",
"authCheckEmailBackTo": "Назад к",
"goBackTo": "Назад к",
"authCheckEmailDidntReceiveEmail": "Не получили письмо?",
"authCheckEmailClickToResend": "Нажмите, чтобы отправить повторно",
"authSetNewPasswordTitle": "Установите новый пароль",
"authSetNewPasswordDescription": "Ваш новый пароль должен отличаться от ранее использованных паролей.",
"authSetNewPasswordNewPassword": "Новый пароль",
"authSetNewPasswordConfirmPassword": "Подтвердите пароль",
"confirmPassword": "Подтвердите ваш пароль",
"authSetNewPasswordResetPassword": "Сбросить пароль",
"authSetNewPasswordBackTo": "Назад к",
"authPasswordMustBeAtLeast": "Должно быть как минимум",
"authPasswordCharactersLong": "длиной 8 символов",
"authPasswordMustContainAtLeast": "Должен содержать как минимум",
"authPasswordSpecialCharacter": "один специальный символ",
"authPasswordOneNumber": "одно число",
"authPasswordUpperCharacter": "один верхний символ",
"authPasswordLowerCharacter": "один нижний символ",
"authPasswordConfirmAndPassword": "Подтвердите пароль и пароль",
"authPasswordMustMatch": "должен совпадать",
"authRegisterCreateAccount": "Создайте свою учетную запись, чтобы начать",
"authRegisterCreateSuperAdminAccount": "Создайте учетную запись суперадминистратора, чтобы начать работу",
"authRegisterSignUpWithEmail": "Зарегистрироваться по электронной почте",
"authRegisterBySigningUp": "Регистрируясь, вы соглашаетесь с нашими",
"distributedStatusHeaderText": "Охват реального времени и реального устройства",
"distributedStatusSubHeaderText": "Работает на миллионах устройств по всему миру, просматривайте производительность системы по глобальному региону, стране или городу",
"settingsGeneralSettings": "Общие настройки",
"settingsDisplayTimezone": "Отображать часовой пояс",
"settingsDisplayTimezoneDescription": "Часовой пояс панели мониторинга, которую вы публично отображаете.",
"settingsAppearance": "Внешний вид",
"settingsAppearanceDescription": "Переключение между светлым и темным режимом или изменение языка пользовательского интерфейса",
"settingsThemeMode": "Тема",
"settingsLanguage": "Язык",
"settingsDistributedUptime": "Distributed uptime",
"settingsDistributedUptimeDescription": "Включить/выключить distributed uptime monitoring.",
"settingsEnabled": "Включено",
"settingsDisabled": "Выключено",
"settingsHistoryAndMonitoring": "История и мониторинг",
"settingsHistoryAndMonitoringDescription": "Определите здесь, как долго вы хотите хранить данные. Вы также можете удалить все прошлые данные.",
"settingsTTLLabel": "Дни, за которыми вы хотите следить.",
"settingsTTLOptionalLabel": "0 для бесконечности",
"settingsClearAllStats": "Очистить всю статистику. Это необратимо.",
"settingsClearAllStatsButton": "Очистить всю статистику",
"settingsClearAllStatsDialogTitle": "Хотите очистить всю статистику?",
"settingsClearAllStatsDialogDescription": "После удаления ваши мониторы не могут быть восстановлены.",
"settingsClearAllStatsDialogConfirm": "Да, очистить всю статистику",
"settingsDemoMonitors": "Демо мониторы",
"settingsDemoMonitorsDescription": "Здесь вы можете добавлять и удалять демонстрационные мониторы.",
"settingsAddDemoMonitors": "Добавьте демонстрационные мониторы",
"settingsAddDemoMonitorsButton": "Добавьте демонстрационные мониторы",
"settingsRemoveAllMonitors": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsButton": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsDialogTitle": "Хотите удалить все мониторы?",
"settingsRemoveAllMonitorsDialogConfirm": "Да, очистить все мониторы",
"settingsWallet": "Кошелёк",
"settingsWalletDescription": "Подключите свой кошелек здесь. Это необходимо для того, чтобы монитор Distributed Uptime мог подключиться к нескольким узлам по всему миру.",
"settingsAbout": "О",
"settingsDevelopedBy": "Developed by Bluewave Labs.",
"settingsSave": "Сохранить",
"settingsSuccessSaved": "Настройки успешно сохранены",
"settingsFailedToSave": "Не удалось сохранить настройки",
"settingsStatsCleared": "Статистика успешно очищена",
"settingsFailedToClearStats": "Не удалось очистить статистику",
"settingsDemoMonitorsAdded": "Успешно добавлены демонстрационные мониторы",
"settingsFailedToAddDemoMonitors": "Не удалось добавить демонстрационные мониторы",
"settingsMonitorsDeleted": "Успешно удалены все мониторы",
"settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы",
"starPromptTitle": "Star Checkmate",
"starPromptDescription": "Ознакомьтесь с последними релизами и помогите развить сообщество на GitHub",
"https": "HTTPS",
"http": "HTTP",
"monitor": "монитор",
"aboutus": "О Нас",
"signUP": "Зарегистрироваться",
"now": "Сейчас",
"delete": "Удалить",
"configure": "Настроить",
"networkError": "Ошибка сети",
"responseTime": "Время ответа:",
"ms": "мс",
"bar": "Bar",
"area": "Area",
"country": "СТРАНА",
"city": "ГОРОД",
"response": "ОТВЕТ",
"checkConnection": "Пожалуйста, проверьте ваше соединение",
"passwordreset": "Сброс пароля",
"authRegisterStepOnePersonalDetails": "Введите свои личные данные",
"authCheckEmailOpenEmailButton": "Откройте приложение почты",
"authNewPasswordConfirmed": "Ваш пароль успешно сброшен. Нажмите ниже, чтобы войти в систему магическим образом.",
"monitorStatusUp": "Монитор {name} ({url}) теперь включен и отвечает",
"monitorStatusDown": "Монитор {name} ({url}) ОТКЛЮЧЕН и не отвечает",
"webhookSendSuccess": "Уведомление Webhook успешно отправлено",
"webhookSendError": "Ошибка отправки уведомления webhook на {platform}",
"webhookUnsupportedPlatform": "Неподдерживаемая платформа: {platform}",
"distributedRightCategoryTitle": "Монитор",
"distributedStatusServerMonitors": "Серверные мониторы",
"distributedStatusServerMonitorsDescription": "Мониторинг состояния связанных серверов",
"distributedUptimeCreateSelectURL": "Здесь вы можете выбрать URL-адрес хоста, а также тип монитора.",
"distributedUptimeCreateChecks": "Проверки, которые необходимо выполнить",
"distributedUptimeCreateChecksDescription": "Вы всегда можете добавить или удалить проверки после добавления своего сайта.",
"distributedUptimeCreateIncidentNotification": "Уведомления об инцидентах",
"distributedUptimeCreateIncidentDescription": "В случае возникновения инцидента сообщите об этом пользователям.",
"distributedUptimeCreateAdvancedSettings": "Расширенные настройки",
"distributedUptimeDetailsNoMonitorHistory": "Для этого монитора пока нет истории проверок.",
"distributedUptimeDetailsFooterHeading": "Made with ❤️ by UpRock & Bluewave Labs",
"distributedUptimeDetailsFooterBuilt": "Built on",
"distributedUptimeDetailsFooterSolana": "Solana",
"distributedUptimeDetailsMonitorHeader": "Distributed Uptime Monitoring powered by DePIN",
"distributedUptimeDetailsStatusHeaderUptime": "Аптайм:",
"distributedUptimeDetailsStatusHeaderLastUpdate": "Последнее обновление",
"notifications": {
"enableNotifications": "Включить уведомления {{platform}}",
"testNotification": "Тестовое уведомление",
"addOrEditNotifications": "Добавить или изменить уведомления",
"slack": {
"label": "Slack",
"description": "Чтобы включить уведомления Slack, создайте приложение Slack и включите входящие вебхуки. После этого просто укажите URL вебхука здесь.",
"webhookLabel": "URL вебхука",
"webhookPlaceholder": "https://hooks.slack.com/services/..."
},
"discord": {
"label": "Discord",
"description": "Чтобы отправить данные на канал Discord из Checkmate через уведомления Discord с использованием веб-хуков, вы можете использовать функцию входящих веб-хуков Discord.",
"webhookLabel": "Discord Webhook URL",
"webhookPlaceholder": "https://discord.com/api/webhooks/..."
},
"telegram": {
"label": "Telegram",
"description": "Чтобы включить уведомления Telegram, создайте бота Telegram с помощью BotFather, официального бота для создания и управления ботами Telegram. Затем получите токен API и идентификатор чата и запишите их здесь.",
"tokenLabel": "Ваш токен бота",
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
"chatIdLabel": "ID Вашего Чата",
"chatIdPlaceholder": "-1001234567890"
},
"webhook": {
"label": "Вебхуки",
"description": "Вы можете настроить пользовательский вебхук для получения уведомлений о возникновении инцидентов.",
"urlLabel": "URL вебхука",
"urlPlaceholder": "https://your-server.com/webhook"
},
"testNotificationDevelop": "Тестовое уведомление 2",
"integrationButton": ""
},
"testLocale": "testLocale",
"add": "Добавить",
"monitors": "мониторы",
"distributedUptimeStatusCreateStatusPage": "страница статуса",
"distributedUptimeStatusCreateStatusPageAccess": "Доступ",
"distributedUptimeStatusCreateStatusPageReady": "Если ваша страница статуса готова, вы можете отметить ее как опубликованную.",
"distributedUptimeStatusBasicInfoHeader": "Основная информация",
"distributedUptimeStatusBasicInfoDescription": "Определите название компании и поддомен, на который ссылается ваша страница статуса.",
"distributedUptimeStatusLogoHeader": "Логотип",
"distributedUptimeStatusLogoDescription": "Загрузите логотип для своей страницы статуса",
"distributedUptimeStatusLogoUploadButton": "Загрузить логотип",
"distributedUptimeStatusStandardMonitorsHeader": "Стандартные Мониторы",
"distributedUptimeStatusStandardMonitorsDescription": "Прикрепите стандартные мониторы к своей странице статуса.",
"distributedUptimeStatusCreateYour": "Создайте свой",
"distributedUptimeStatusEditYour": "Отредактируйте свой",
"distributedUptimeStatusPublishedLabel": "Опубликовано и доступно для общественности",
"distributedUptimeStatusCompanyNameLabel": "Название компании",
"distributedUptimeStatusPageAddressLabel": "Адрес вашей страницы статуса",
"distributedUptimeStatus30Days": "30 дней",
"distributedUptimeStatus60Days": "60 дней",
"distributedUptimeStatus90Days": "90 дней",
"distributedUptimeStatusPageNotSetUp": "Страница статуса не настроена.",
"distributedUptimeStatusContactAdmin": "Пожалуйста, свяжитесь с вашим администратором",
"distributedUptimeStatusPageNotPublic": "Эта страница статуса не является публичной.",
"distributedUptimeStatusPageDeleteDialog": "Вы хотите удалить эту страницу статуса?",
"distributedUptimeStatusPageDeleteConfirm": "Да, удалить страницу статуса",
"distributedUptimeStatusPageDeleteDescription": "После удаления ваша страница статуса не может быть восстановлена.",
"distributedUptimeStatusDevices": "Устройства",
"distributedUptimeStatusUpt": "UPT",
"distributedUptimeStatusUptBurned": "UPT Burned",
"distributedUptimeStatusUptLogo": "Upt Logo",
"incidentsTableNoIncidents": "Инцидентов не зафиксировано",
"incidentsTablePaginationLabel": "инциденты",
"incidentsTableMonitorName": "Имя Монитора",
"incidentsTableStatus": "Статус",
"incidentsTableDateTime": "Дата и Время",
"incidentsTableStatusCode": "Код Статуса",
"incidentsTableMessage": "Сообщение",
"incidentsOptionsHeader": "Инцидент для:",
"incidentsOptionsHeaderFilterBy": "Фильтровать по:",
"incidentsOptionsHeaderFilterAll": "Все",
"incidentsOptionsHeaderFilterDown": "Недоступно",
"incidentsOptionsHeaderFilterCannotResolve": "Невозможно решить",
"incidentsOptionsHeaderShow": "Показать:",
"incidentsOptionsHeaderLastHour": "Последний час",
"incidentsOptionsHeaderLastDay": "Последний день",
"incidentsOptionsHeaderLastWeek": "Последняя неделя",
"incidentsOptionsPlaceholderAllServers": "Все сервера",
"infrastructureCreateYour": "Создайте свой",
"infrastructureCreateGeneralSettingsDescription": "Здесь вы можете выбрать URL-адрес хоста, а также понятное имя и секретный ключ авторизации для подключения к агенту сервера.",
"infrastructureServerRequirement": "Сервер, который вы отслеживаете, должен быть запущен",
"infrastructureCustomizeAlerts": "Настройте оповещения",
"infrastructureAlertNotificationDescription": "Отправлять уведомления пользователям, когда пороговые значения превышают указанный процент.",
"infrastructureCreateMonitor": "Создать Монитор Инфраструктуры",
"infrastructureProtocol": "Протокол",
"infrastructureServerUrlLabel": "URL Сервера",
"infrastructureDisplayNameLabel": "Отображаемое имя",
"infrastructureAuthorizationSecretLabel": "Секрет авторизации",
"gb": "ГБ",
"mb": "МБ",
"mem": "Mem",
"memoryUsage": "Использование памяти",
"cpu": "CPU",
"cpuUsage": "Использование CPU",
"cpuTemperature": "Температура CPU",
"diskUsage": "Использование диска",
"used": "Использовал",
"total": "Всего",
"cores": "Ядра",
"frequency": "Частота",
"status": "Статус",
"cpuPhysical": "CPU (физический)",
"cpuLogical": "CPU (логический)",
"cpuFrequency": "Частота CPU",
"avgCpuTemperature": "Средняя температура CPU",
"memory": "Память",
"disk": "Диск",
"uptime": "Аптайм",
"os": "ОС",
"host": "Хост",
"actions": "Действия",
"integrations": "Интеграции",
"integrationsPrism": "Подключите Prism к вашему любимому сервису.",
"integrationsSlack": "Slack",
"integrationsSlackInfo": "Подключитесь к Slack и просматривайте инциденты в канале",
"integrationsDiscord": "Discord",
"integrationsDiscordInfo": "Подключитесь к Discord и просматривайте инциденты прямо в канале",
"integrationsZapier": "Zapier",
"integrationsZapierInfo": "Отправляйте все инциденты в Zapier, и тогда вы сможете видеть их везде",
"commonSave": "",
"createYour": "",
"createMonitor": "",
"pause": "",
"resume": "",
"editing": "",
"url": "",
"access": "",
"timezone": "",
"features": "",
"administrator": "",
"loginHere": "",
"displayName": "",
"urlMonitor": "",
"portToMonitor": "",
"websiteMonitoring": "",
"websiteMonitoringDescription": "",
"pingMonitoring": "",
"pingMonitoringDescription": "",
"dockerContainerMonitoring": "",
"dockerContainerMonitoringDescription": "",
"portMonitoring": "",
"portMonitoringDescription": "",
"createMaintenanceWindow": "",
"createMaintenance": "",
"editMaintenance": "",
"maintenanceWindowName": "",
"friendlyNameInput": "",
"friendlyNamePlaceholder": "",
"maintenanceRepeat": "",
"maintenance": "",
"duration": "",
"addMonitors": "",
"window": "",
"cancel": "",
"message": "",
"low": "",
"high": "",
"statusCode": "",
"date&Time": "",
"type": "",
"statusPageName": "",
"publicURL": "",
"repeat": "",
"edit": "",
"createA": "",
"remove": "",
"maintenanceWindowDescription": "",
"startTime": "",
"timeZoneInfo": "",
"monitorsToApply": "",
"nextWindow": "",
"notFoundButton": "",
"pageSpeedConfigureSettingsDescription": "",
"monitorDisplayName": "",
"whenNewIncident": "",
"notifySMS": "",
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
"deleteDialogDescription": "",
"pageSpeedMonitor": "",
"shown": "",
"ago": "",
"companyName": "",
"pageSpeedDetailsPerformanceReport": "",
"pageSpeedDetailsPerformanceReportCalculator": "",
"checkingEvery": "",
"statusPageCreateSettings": "",
"basicInformation": "",
"statusPageCreateBasicInfoDescription": "",
"statusPageCreateSelectTimeZoneDescription": "",
"statusPageCreateAppearanceDescription": "",
"statusPageCreateSettingsCheckboxLabel": "",
"statusPageCreateBasicInfoStatusPageAddress": "",
"statusPageCreateTabsContent": "",
"statusPageCreateTabsContentDescription": "",
"statusPageCreateTabsContentFeaturesDescription": "",
"showCharts": "",
"showUptimePercentage": "",
"removeLogo": "",
"statusPageStatus": "",
"statusPageStatusContactAdmin": "",
"statusPageStatusNotPublic": "",
"statusPageStatusNoPage": "",
"statusPageStatusServiceStatus": "",
"deleteStatusPage": "",
"deleteStatusPageConfirm": "",
"deleteStatusPageDescription": "",
"uptimeCreate": "",
"uptimeCreateJsonPath": "",
"uptimeCreateJsonPathQuery": "",
"maintenanceTableActionMenuDialogTitle": ""
}

View File

@@ -173,68 +173,69 @@
"urlLabel": "Web kanca URL:",
"urlPlaceholder": "https://sunucu-adresiniz.com/webhook"
},
"testNotificationDevelop": "Test bildirimi 2"
"testNotificationDevelop": "Test bildirimi 2",
"integrationButton": "Bildirim Entegrasyonu"
},
"testLocale": "TEST13 UPLOAD",
"add": "",
"monitors": "",
"distributedUptimeStatusCreateStatusPage": "",
"distributedUptimeStatusCreateStatusPageAccess": "",
"distributedUptimeStatusCreateStatusPageReady": "",
"distributedUptimeStatusBasicInfoHeader": "",
"add": "Ekle",
"monitors": "Monitörler",
"distributedUptimeStatusCreateStatusPage": "durum sayfası",
"distributedUptimeStatusCreateStatusPageAccess": "Erişim",
"distributedUptimeStatusCreateStatusPageReady": "Durum sayfanız hazırsa yayınlayabilirsiniz.",
"distributedUptimeStatusBasicInfoHeader": "Temel bilgiler",
"distributedUptimeStatusBasicInfoDescription": "",
"distributedUptimeStatusLogoHeader": "",
"distributedUptimeStatusLogoDescription": "",
"distributedUptimeStatusLogoUploadButton": "",
"distributedUptimeStatusStandardMonitorsHeader": "",
"distributedUptimeStatusStandardMonitorsDescription": "",
"distributedUptimeStatusLogoHeader": "Logo",
"distributedUptimeStatusLogoDescription": "Durum sayfanız için bir logo yükleyin",
"distributedUptimeStatusLogoUploadButton": "Logo yükle",
"distributedUptimeStatusStandardMonitorsHeader": "Standart monitörler",
"distributedUptimeStatusStandardMonitorsDescription": "Durum sayfanıza standart monitörleri ekleyin.",
"distributedUptimeStatusCreateYour": "",
"distributedUptimeStatusEditYour": "",
"distributedUptimeStatusPublishedLabel": "",
"distributedUptimeStatusCompanyNameLabel": "",
"distributedUptimeStatusPageAddressLabel": "",
"distributedUptimeStatus30Days": "",
"distributedUptimeStatus60Days": "",
"distributedUptimeStatus90Days": "",
"distributedUptimeStatusPageNotSetUp": "",
"distributedUptimeStatusContactAdmin": "",
"distributedUptimeStatusPageNotPublic": "",
"distributedUptimeStatusPageDeleteDialog": "",
"distributedUptimeStatusPageDeleteConfirm": "",
"distributedUptimeStatusPageDeleteDescription": "",
"distributedUptimeStatusDevices": "",
"distributedUptimeStatusUpt": "",
"distributedUptimeStatusUptBurned": "",
"distributedUptimeStatusUptLogo": "",
"incidentsTableNoIncidents": "",
"incidentsTablePaginationLabel": "",
"incidentsTableMonitorName": "",
"incidentsTableStatus": "",
"incidentsTableDateTime": "",
"incidentsTableStatusCode": "",
"incidentsTableMessage": "",
"incidentsOptionsHeader": "",
"incidentsOptionsHeaderFilterBy": "",
"incidentsOptionsHeaderFilterAll": "",
"incidentsOptionsHeaderFilterDown": "",
"incidentsOptionsHeaderFilterCannotResolve": "",
"incidentsOptionsHeaderShow": "",
"incidentsOptionsHeaderLastHour": "",
"incidentsOptionsHeaderLastDay": "",
"incidentsOptionsHeaderLastWeek": "",
"incidentsOptionsPlaceholderAllServers": "",
"distributedUptimeStatusPublishedLabel": "Yayında ve herkes tarafından görülebilir",
"distributedUptimeStatusCompanyNameLabel": "Şirket adı",
"distributedUptimeStatusPageAddressLabel": "Durum sayfası adresi",
"distributedUptimeStatus30Days": "30 gün",
"distributedUptimeStatus60Days": "60 gün",
"distributedUptimeStatus90Days": "90 gün",
"distributedUptimeStatusPageNotSetUp": "Henüz bir durum sayfası oluşturulmadı.",
"distributedUptimeStatusContactAdmin": "Lütfen yöneticiniz ile görüşün",
"distributedUptimeStatusPageNotPublic": "Bu durum sayfası herkese açık değil",
"distributedUptimeStatusPageDeleteDialog": "Bu durum sayfasını silmek istiyor musunuz? ",
"distributedUptimeStatusPageDeleteConfirm": "Evet, durum sayfasını sil",
"distributedUptimeStatusPageDeleteDescription": "Silindiği zaman durum sayfalarını geri getiremezsiniz.",
"distributedUptimeStatusDevices": "Cihazlar",
"distributedUptimeStatusUpt": "UPT",
"distributedUptimeStatusUptBurned": "Yakılan UPT",
"distributedUptimeStatusUptLogo": "Upt Logo",
"incidentsTableNoIncidents": "Hiç olay kaydedilmedi",
"incidentsTablePaginationLabel": "olaylar",
"incidentsTableMonitorName": "Monitör adı",
"incidentsTableStatus": "Durum",
"incidentsTableDateTime": "Tarih ve saat",
"incidentsTableStatusCode": "Durum kodu",
"incidentsTableMessage": "Mesaj",
"incidentsOptionsHeader": "Servisler:",
"incidentsOptionsHeaderFilterBy": "Filtrele:",
"incidentsOptionsHeaderFilterAll": "Tümü",
"incidentsOptionsHeaderFilterDown": "Erişilemiyor",
"incidentsOptionsHeaderFilterCannotResolve": "Çözümlenemiyor",
"incidentsOptionsHeaderShow": "Göster:",
"incidentsOptionsHeaderLastHour": "Son saat",
"incidentsOptionsHeaderLastDay": "Son gün",
"incidentsOptionsHeaderLastWeek": "Son hafta",
"incidentsOptionsPlaceholderAllServers": "Tüm sunucular",
"infrastructureCreateYour": "",
"infrastructureCreateGeneralSettingsDescription": "",
"infrastructureServerRequirement": "",
"infrastructureCustomizeAlerts": "",
"infrastructureCustomizeAlerts": "Alarmları özelleştir",
"infrastructureAlertNotificationDescription": "",
"infrastructureCreateMonitor": "",
"infrastructureProtocol": "",
"infrastructureServerUrlLabel": "",
"infrastructureDisplayNameLabel": "",
"infrastructureAuthorizationSecretLabel": "",
"gb": "",
"mb": "",
"infrastructureCreateMonitor": "Altyapı Monitörü oluştur",
"infrastructureProtocol": "Protokol",
"infrastructureServerUrlLabel": "Sunucu adresi",
"infrastructureDisplayNameLabel": "Görünen adı",
"infrastructureAuthorizationSecretLabel": "Kimlik denetimi parolası",
"gb": "GB",
"mb": "MB",
"mem": "",
"memoryUsage": "",
"cpu": "",
@@ -247,21 +248,117 @@
"frequency": "",
"status": "",
"cpuPhysical": "",
"cpuLogical": "",
"cpuFrequency": "",
"avgCpuTemperature": "",
"memory": "",
"disk": "",
"cpuLogical": "CPU (Mantıksal)",
"cpuFrequency": "CPU Frekansı",
"avgCpuTemperature": "Ortalama CPU Isısı",
"memory": "Bellek",
"disk": "Disk",
"uptime": "",
"os": "",
"host": "",
"actions": "",
"integrations": "",
"os": "İşletim sistemi",
"host": "Makine",
"actions": "İşlemler",
"integrations": "Entegrasyonlar",
"integrationsPrism": "",
"integrationsSlack": "",
"integrationsSlack": "Slack",
"integrationsSlackInfo": "",
"integrationsDiscord": "",
"integrationsDiscord": "Discord",
"integrationsDiscordInfo": "",
"integrationsZapier": "",
"integrationsZapierInfo": ""
"integrationsZapier": "Zapier",
"integrationsZapierInfo": "",
"commonSave": "Kaydet",
"createYour": "",
"createMonitor": "",
"pause": "",
"resume": "",
"editing": "",
"url": "",
"access": "",
"timezone": "",
"features": "",
"administrator": "",
"loginHere": "",
"displayName": "",
"urlMonitor": "",
"portToMonitor": "",
"websiteMonitoring": "",
"websiteMonitoringDescription": "",
"pingMonitoring": "",
"pingMonitoringDescription": "",
"dockerContainerMonitoring": "",
"dockerContainerMonitoringDescription": "",
"portMonitoring": "",
"portMonitoringDescription": "",
"createMaintenanceWindow": "",
"createMaintenance": "Bakım oluştur",
"editMaintenance": "Bakımı düzenle",
"maintenanceWindowName": "",
"friendlyNameInput": "Görünen ad",
"friendlyNamePlaceholder": "",
"maintenanceRepeat": "Bakım Tekrarı",
"maintenance": "bakım",
"duration": "Süre",
"addMonitors": "Monitör ekle",
"window": "pencere",
"cancel": "İptal",
"message": "Mesaj",
"low": "düşük",
"high": "",
"statusCode": "",
"date&Time": "",
"type": "",
"statusPageName": "",
"publicURL": "",
"repeat": "",
"edit": "",
"createA": "",
"remove": "",
"maintenanceWindowDescription": "",
"startTime": "Başlangıç saati",
"timeZoneInfo": "",
"monitorsToApply": "",
"nextWindow": "Sonraki pencere",
"notFoundButton": "",
"pageSpeedConfigureSettingsDescription": "",
"monitorDisplayName": "",
"whenNewIncident": "Yeni bir olay oluştuğunda,",
"notifySMS": "",
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "Frekansı denetle",
"matchMethod": "",
"expectedValue": "Beklenen değer",
"deleteDialogTitle": "Gerçekten bu monitörü silmek istiyor musunuz?",
"deleteDialogDescription": "Bir kez silindiği zaman tekrar getiremezsiniz.",
"pageSpeedMonitor": "Sayfa Hızı Monitörü",
"shown": "",
"ago": "",
"companyName": "Firma adı",
"pageSpeedDetailsPerformanceReport": "",
"pageSpeedDetailsPerformanceReportCalculator": "",
"checkingEvery": "",
"statusPageCreateSettings": "",
"basicInformation": "",
"statusPageCreateBasicInfoDescription": "",
"statusPageCreateSelectTimeZoneDescription": "",
"statusPageCreateAppearanceDescription": "",
"statusPageCreateSettingsCheckboxLabel": "",
"statusPageCreateBasicInfoStatusPageAddress": "",
"statusPageCreateTabsContent": "",
"statusPageCreateTabsContentDescription": "",
"statusPageCreateTabsContentFeaturesDescription": "",
"showCharts": "",
"showUptimePercentage": "",
"removeLogo": "",
"statusPageStatus": "",
"statusPageStatusContactAdmin": "",
"statusPageStatusNotPublic": "",
"statusPageStatusNoPage": "",
"statusPageStatusServiceStatus": "Servis durumu",
"deleteStatusPage": "Gerçekten bu durum sayfasını silmek istiyor musunuz?",
"deleteStatusPageConfirm": "Evet, durum sayfasını sil",
"deleteStatusPageDescription": "Silindikten sonra durum sayfasını geri getiremezsiniz.",
"uptimeCreate": "",
"uptimeCreateJsonPath": "",
"uptimeCreateJsonPathQuery": "",
"maintenanceTableActionMenuDialogTitle": "Gerçekten bu bakım aralığını silmek istiyor musunuz?"
}