Merge remote-tracking branch 'upstream/develop' into feat/handleTestNotification

This commit is contained in:
Skorpios committed 2025-03-16 12:50:47 -07:00
commit b1194ebd48
36 files changed
+670 -406

No files matched your search

+5 -1
View File
@@ -13,7 +13,11 @@ Mention the issue number(s) this PR addresses (e.g., #123).
- [ ] (Do not skip this or your PR will be closed) I deployed the application locally.
- [ ] (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code.
- [ ] I have included the issue # in the PR.
- [ ] I have labelled the PR correctly.
- [ ] I have added i18n support to visible strings (instead of `<div>Add</div>`, use):
```Javascript
const { t } = useTranslation();
<div>{t('add')}</div>
```
- [ ] The issue I am working on is assigned to me.
- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I have no hardcoded dimensions.
+4 -3
View File
@@ -57,11 +57,12 @@ async function downloadTranslations() {
});
}
// Save file
const outputPath = path.join(process.cwd(), 'temp', `${language}.json`);
// Determine the output filename based on language
const filename = language === 'en' ? 'gb.json' : `${language}.json`;
const outputPath = path.join(process.cwd(), 'temp', filename);
await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 });
console.log(`Translations for ${language} language successfully downloaded and saved: ${outputPath}`);
console.log(`Translations for ${language} language successfully downloaded and saved as: ${filename}`);
}
console.log('All translations successfully downloaded!');
+33 -7
View File
@@ -17,15 +17,24 @@ on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: write
pull-requests: write
jobs:
sync-translations:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
@@ -83,10 +92,27 @@ jobs:
cp -r temp/* src/locales/
echo "Translation files copied to src/locales/"
- name: Commit changes
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add src/locales/*.json
git diff --staged --quiet || git commit -m "Translations updated from POEditor"
git push
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "feat: translations updated from POEditor"
title: "🌐 Updated Translations from POEditor"
body: |
This PR contains the latest translations from POEditor.
📅 Update Date: ${{ steps.date.outputs.date }}
🔄 Updated Languages: ${{ github.event.inputs.languages || 'tr,en' }}
⚠️ Please review the translations and approve the PR if everything looks correct.
branch: translation-update-${{ github.run_number }}
delete-branch: true
base: develop
add-paths: |
src/locales/*.json
committer: GitHub Action <github-actions[bot]@users.noreply.github.com>
author: GitHub Action <github-actions[bot]@users.noreply.github.com>
+19 -7
View File
@@ -51,14 +51,26 @@ jobs:
id: changed-files
if: github.event_name == 'pull_request'
run: |
# Get list of changed files in src/locales directory
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- src/locales/*.json)
echo "Changed files: $CHANGED_FILES"
# Get base and head commits
BASE_SHA=${{ github.event.pull_request.base.sha }}
HEAD_SHA=${{ github.event.pull_request.head.sha }}
# Create a JSON array of changed files with language codes
echo "CHANGED_FILES<<EOF" >> $GITHUB_ENV
echo "$CHANGED_FILES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "Base SHA: $BASE_SHA"
echo "Head SHA: $HEAD_SHA"
# Get list of changed files in src/locales directory
CHANGED_FILES=$(git diff --name-only $BASE_SHA..$HEAD_SHA -- 'src/locales/*.json' || git ls-files 'src/locales/*.json')
if [ -z "$CHANGED_FILES" ]; then
echo "No changes detected in locale files"
echo "CHANGED_FILES=" >> $GITHUB_ENV
else
echo "Changed files:"
echo "$CHANGED_FILES"
echo "CHANGED_FILES<<EOF" >> $GITHUB_ENV
echo "$CHANGED_FILES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
fi
- name: Upload changed translations to POEditor
if: env.CHANGED_FILES != ''
+3 -2
View File
@@ -71,7 +71,6 @@ If you have any questions, suggestions or comments, please use our [Discord chan
- Status pages (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1131
- Translations (i18n) (**in progress**)
- Better notification options (Webhooks, Discord, Telegram, Slack) (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1545
- Command line interface (CLI) (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1558
- JSON query monitoring https://github.com/bluewave-labs/Checkmate/issues/1573
- Tagging/grouping monitors https://github.com/bluewave-labs/Checkmate/issues/1546
- More configuration options
@@ -107,7 +106,9 @@ If you have any questions, suggestions or comments, please use our [Discord chan
## 🤝 Contributing
We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 4.4K+ stars and attracted 50+ contributors from around the globe.
We are [@ajhollid](http://github.com/ajhollid) (team lead), [@vishnusn77](http://github.com/vishnusn77), [@mohicody](http://github.com/mohicody), [@gorkem-bwl](http://github.com/gorkem-bwl/) and [@Owaiseimdad](http://github.com/Owaiseimdad), helping individuals and businesses monitor their infra and servers.
We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 4.6K+ stars and attracted 60 contributors from around the globe.
Our repo is starred by employees from **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes and NEC**, so don’t hold back — jump in, contribute and learn with us!
+11 -10
View File
@@ -1973,9 +1973,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
"integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
@@ -9189,9 +9189,9 @@
}
},
"node_modules/axios": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
"integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -12525,9 +12525,9 @@
}
},
"node_modules/i18next": {
"version": "24.2.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.2.tgz",
"integrity": "sha512-NE6i86lBCKRYZa5TaUDkU5S4HFgLIEJRLr3Whf2psgaxBleQ2LC1YW1Vc+SCgkAW7VEzndT6al6+CzegSUHcTQ==",
"version": "24.2.3",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz",
"integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==",
"funding": [
{
"type": "individual",
@@ -12542,8 +12542,9 @@
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.2"
"@babel/runtime": "^7.26.10"
},
"peerDependencies": {
"typescript": "^5"
+1 -1
View File
@@ -58,7 +58,7 @@ const BarChart = ({ checks = [] }) => {
<>
<Typography>
{formatDateWithTz(
check.createdAt,
check.updatedAt,
"ddd, MMMM D, YYYY, HH:mm A",
uiTimezone
)}
+13 -1
View File
@@ -15,6 +15,14 @@ Pagination.propTypes = {
const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25];
// Determine whether pagination buttons should be shown
const shouldShowPaginationButtons = (itemCount, rowsPerPage) => {
return Math.ceil(itemCount / rowsPerPage) > 1;
};
// Empty action component for pagination (no-op component)
const EmptyPaginationActions = () => <div />;
/**
* Pagination component for table navigation with customized styling and behavior.
*
@@ -41,6 +49,8 @@ function Pagination({
const end = Math.min(page * rowsPerPage + rowsPerPage, itemCount);
const range = `${start} - ${end}`;
const showPaginationButtons = shouldShowPaginationButtons(itemCount, rowsPerPage);
return (
<Stack
direction="row"
@@ -64,7 +74,9 @@ function Pagination({
rowsPerPage={rowsPerPage}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
ActionsComponent={
showPaginationButtons ? TablePaginationActions : EmptyPaginationActions
}
labelRowsPerPage="Rows per page"
labelDisplayedRows={({ page, count }) =>
`Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
@@ -1,11 +1,11 @@
import { useState, useEffect } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
import { networkService } from "../main";
import { createToast } from "../Utils/toastUtils";
import { useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils";
import { useMonitorUtils } from "./useMonitorUtils";
const useDUStatusPageFetchByUrl = ({ url, timeFrame, isCreate = false }) => {
const useFetchDepinStatusPage = ({ url, timeFrame, isCreate = false }) => {
const [isLoading, setIsLoading] = useState(true);
const [networkError, setNetworkError] = useState(false);
const [statusPage, setStatusPage] = useState(undefined);
@@ -50,9 +50,9 @@ const useDUStatusPageFetchByUrl = ({ url, timeFrame, isCreate = false }) => {
}
};
fetchStatusPageByUrl();
}, [authToken, url, getMonitorWithPercentage, theme, timeFrame]);
}, [authToken, url, getMonitorWithPercentage, theme, timeFrame, isCreate]);
return [isLoading, networkError, statusPage, monitorId, isPublished];
};
export { useDUStatusPageFetchByUrl };
export { useFetchDepinStatusPage };
+70
View File
@@ -0,0 +1,70 @@
import { useEffect, useState } from "react";
import { networkService } from "../main";
import { createToast } from "../Utils/toastUtils";
import { useTheme } from "@emotion/react";
import { useMonitorUtils } from "./useMonitorUtils";
export const useFetchMonitorsWithChecks = ({
teamId,
types,
limit,
page,
rowsPerPage,
filter,
field,
order,
}) => {
const [isLoading, setIsLoading] = useState(false);
const [count, setCount] = useState(undefined);
const [monitors, setMonitors] = useState(undefined);
const [networkError, setNetworkError] = useState(false);
const theme = useTheme();
const { getMonitorWithPercentage } = useMonitorUtils();
useEffect(() => {
const fetchMonitors = async () => {
try {
setIsLoading(true);
const res = await networkService.getMonitorsWithChecksByTeamId({
teamId,
limit,
types,
page,
rowsPerPage,
filter,
field,
order,
});
const { count, monitors } = res?.data?.data ?? {};
const mappedMonitors = monitors.map((monitor) =>
getMonitorWithPercentage(monitor, theme)
);
setMonitors(mappedMonitors);
setCount(count?.monitorsCount ?? 0);
} catch (error) {
console.error(error);
setNetworkError(true);
createToast({
body: error.message,
});
} finally {
setIsLoading(false);
}
};
fetchMonitors();
}, [
field,
filter,
getMonitorWithPercentage,
limit,
order,
page,
rowsPerPage,
teamId,
theme,
types,
]);
return [monitors, count, isLoading, networkError];
};
export default useFetchMonitorsWithChecks;
+37
View File
@@ -0,0 +1,37 @@
import { useEffect, useState } from "react";
import { networkService } from "../main";
import { createToast } from "../Utils/toastUtils";
export const useFetchMonitorsWithSummary = ({ teamId, types }) => {
const [isLoading, setIsLoading] = useState(false);
const [monitors, setMonitors] = useState(undefined);
const [monitorsSummary, setMonitorsSummary] = useState(undefined);
const [networkError, setNetworkError] = useState(false);
useEffect(() => {
const fetchMonitors = async () => {
try {
setIsLoading(true);
const res = await networkService.getMonitorsWithSummaryByTeamId({
teamId,
types,
});
const { monitors, summary } = res?.data?.data ?? {};
setMonitors(monitors);
setMonitorsSummary(summary);
} catch (error) {
console.error(error);
setNetworkError(true);
createToast({
body: error.message,
});
} finally {
setIsLoading(false);
}
};
fetchMonitors();
}, [teamId, types]);
return [monitors, monitorsSummary, isLoading, networkError];
};
export default useFetchMonitorsWithSummary;
@@ -1,8 +1,8 @@
import { useState, useEffect } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
import { networkService } from "../main";
import { createToast } from "../Utils/toastUtils";
const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange }) => {
const useSubscribeToDepinDetails = ({ monitorId, isPublic, isPublished, dateRange }) => {
const [isLoading, setIsLoading] = useState(true);
const [connectionStatus, setConnectionStatus] = useState(undefined);
const [retryCount, setRetryCount] = useState(0);
@@ -92,4 +92,4 @@ const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange })
return [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger];
};
export { useSubscribeToDetails };
export { useSubscribeToDepinDetails };
@@ -1,20 +1,19 @@
import { useEffect, useState } from "react";
import { networkService } from "../../../../main";
import { networkService } from "../main";
import { useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils";
import { createToast } from "../../../../Utils/toastUtils";
import { useMonitorUtils } from "./useMonitorUtils";
import { createToast } from "../Utils/toastUtils";
const useSubscribeToMonitors = (page, rowsPerPage) => {
const useSubscribeToDepinMonitors = (page, rowsPerPage) => {
// Redux
const { user } = useSelector((state) => state.auth);
// Local state
const [isLoading, setIsLoading] = useState(true);
const [networkError, setNetworkError] = useState(false);
const [count, setCount] = useState(undefined);
const [monitors, setMonitors] = useState(undefined);
const [monitorsSummary, setMonitorsSummary] = useState(undefined);
const [filteredMonitors, setFilteredMonitors] = useState(undefined);
const theme = useTheme();
const { getMonitorWithPercentage } = useMonitorUtils();
@@ -29,18 +28,17 @@ const useSubscribeToMonitors = (page, rowsPerPage) => {
page,
rowsPerPage,
});
const { count, monitors } = initialDataRes?.data?.data ?? {};
const responseData = initialDataRes?.data?.data;
if (typeof responseData === "undefined") throw new Error("No data");
const { monitors, filteredMonitors, summary } = responseData;
const mappedMonitors = filteredMonitors?.map((monitor) =>
const mappedMonitors = monitors?.map((monitor) =>
getMonitorWithPercentage(monitor, theme)
);
setMonitors(monitors);
setMonitorsSummary(summary);
setFilteredMonitors(mappedMonitors);
setMonitors(mappedMonitors);
setCount(count?.monitorsCount ?? 0);
} catch (error) {
setNetworkError(true);
createToast({
@@ -71,15 +69,12 @@ const useSubscribeToMonitors = (page, rowsPerPage) => {
if (isLoading === true) {
setIsLoading(false);
}
const res = data.monitors;
const { monitors, filteredMonitors, summary } = res;
const mappedMonitors = filteredMonitors.map((monitor) =>
const { count, monitors } = data;
const mappedMonitors = monitors.map((monitor) =>
getMonitorWithPercentage(monitor, theme)
);
setMonitors(monitors);
setMonitorsSummary(summary);
setFilteredMonitors(mappedMonitors);
setMonitors(mappedMonitors);
setCount(count?.monitorsCount ?? 0);
},
onError: () => {
setIsLoading(false);
@@ -94,6 +89,6 @@ const useSubscribeToMonitors = (page, rowsPerPage) => {
setNetworkError(true);
}
}, [user, getMonitorWithPercentage, theme, isLoading, page, rowsPerPage]);
return [isLoading, networkError, monitors, monitorsSummary, filteredMonitors];
return [monitors, count, isLoading, networkError];
};
export { useSubscribeToMonitors };
export { useSubscribeToDepinMonitors };
+1 -1
View File
@@ -142,7 +142,7 @@ const Login = () => {
overflow="hidden"
sx={{
"& h1": {
color: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
fontWeight: 600,
fontSize: 28,
},
+7 -7
View File
@@ -35,7 +35,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
textAlign="center"
>
<Box>
<Typography component="h1">{t("signUP")}</Typography>
<Typography component="h1">{t("signUp")}</Typography>
<Typography>
{isSuperAdmin
? t("authRegisterCreateSuperAdminAccount")
@@ -83,9 +83,9 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
},
}}
>
Terms of Service
{t("authRegisterTerms")}
</Typography>{" "}
and{" "}
{t("and")}{" "}
<Typography
component="span"
onClick={() => {
@@ -101,7 +101,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
},
}}
>
Privacy Policy.
{t("authRegisterPrivacy")}
</Typography>
</Typography>
</Box>
@@ -283,7 +283,7 @@ const Register = ({ isSuperAdmin }) => {
overflow="hidden"
sx={{
"& h1": {
color: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
fontWeight: 600,
fontSize: 28,
},
@@ -370,7 +370,7 @@ const Register = ({ isSuperAdmin }) => {
p={theme.spacing(12)}
>
<Typography display="inline-block">
{t("authRegisterAlreadyHaveAccount")} -
{t("authRegisterAlreadyHaveAccount")}
</Typography>
<Typography
component="span"
@@ -378,7 +378,7 @@ const Register = ({ isSuperAdmin }) => {
onClick={() => {
navigate("/login");
}}
sx={{ userSelect: "none", color: theme.palette.primary.main }}
sx={{ userSelect: "none", color: theme.palette.accent.main }}
>
{t("authLoginTitle")}
</Typography>
@@ -1,4 +1,3 @@
import { useSelector } from "react-redux";
import { useState } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
@@ -19,7 +19,7 @@ import { useTheme } from "@mui/material/styles";
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useSubscribeToDetails } from "./Hooks/useSubscribeToDetails";
import { useSubscribeToDepinDetails } from "../../../Hooks/useSubscribeToDepinDetails";
import { useDeleteMonitor } from "./Hooks/useDeleteMonitor";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
@@ -36,7 +36,7 @@ const DistributedUptimeDetails = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
useSubscribeToDetails({ monitorId, dateRange });
useSubscribeToDepinDetails({ monitorId, dateRange });
const [deleteMonitor, isDeleting] = useDeleteMonitor({ monitorId });
// Constants
@@ -11,7 +11,7 @@ import GenericFallback from "../../../Components/GenericFallback";
import { useState } from "react";
import { useTheme } from "@mui/material/styles";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useSubscribeToMonitors } from "./Hooks/useSubscribeToMonitors";
import { useSubscribeToDepinMonitors } from "../../../Hooks/useSubscribeToDepinMonitors";
import SkeletonLayout from "./Components/Skeleton";
import { useTranslation } from "react-i18next";
// Constants
@@ -26,8 +26,10 @@ const DistributedUptimeMonitors = () => {
const theme = useTheme();
const isAdmin = useIsAdmin();
const { t } = useTranslation();
const [isLoading, networkError, monitors, monitorsSummary, filteredMonitors] =
useSubscribeToMonitors(page, rowsPerPage);
const [monitors, count, isLoading, networkError] = useSubscribeToDepinMonitors(
page,
rowsPerPage
);
// Handlers
const handleChangePage = (event, newPage) => {
setPage(newPage);
@@ -57,7 +59,7 @@ const DistributedUptimeMonitors = () => {
);
}
if (typeof monitorsSummary === "undefined" || monitorsSummary.totalMonitors === 0) {
if (count === 0) {
return (
<Fallback
vowelStart={false}
@@ -83,10 +85,10 @@ const DistributedUptimeMonitors = () => {
/>
<MonitorTable
isLoading={isLoading}
monitors={filteredMonitors}
monitors={monitors}
/>
<Pagination
itemCount={monitorsSummary?.totalMonitors ?? 0}
itemCount={count}
paginationLabel="monitors"
page={page}
rowsPerPage={rowsPerPage}
@@ -19,18 +19,19 @@ import { buildErrors } from "../../../Validation/error";
import { createToast } from "../../../Utils/toastUtils";
import { useNavigate } from "react-router-dom";
import { useMonitorsFetch } from "../../StatusPage/Create/Hooks/useMonitorsFetch";
import { useDUStatusPageFetchByUrl } from "../Status/Hooks/useDUStatusPageFetchByUrl";
import { useTranslation } from "react-i18next";
import { useFetchDepinStatusPage } from "../../../Hooks/useFetchDepinStatusPage";
const CreateStatus = () => {
const theme = useTheme();
const { monitorId, url } = useParams();
const navigate = useNavigate();
const { t } = useTranslation();
const isCreate = typeof url === "undefined";
const [createStatusPage, isLoading, networkError] = useCreateStatusPage(isCreate);
const [statusPageIsLoading, statusPageNetworkError, statusPage, _, isPublished] =
useDUStatusPageFetchByUrl({
useFetchDepinStatusPage({
url,
timeFrame: 30,
isCreate,
@@ -176,7 +177,9 @@ const CreateStatus = () => {
component="span"
fontSize="inherit"
>
{isCreate ? "Create your" : "Edit your"}{" "}
{isCreate
? t("distributedUptimeStatusCreateYour")
: t("distributedUptimeStatusEditYour")}{" "}
</Typography>
<Typography
component="span"
@@ -184,21 +187,23 @@ const CreateStatus = () => {
fontSize="inherit"
fontWeight="inherit"
>
status page
{t("distributedUptimeStatusCreateStatusPage")}
</Typography>
</Typography>
<ConfigBox>
<Stack>
<Typography component="h2">Access</Typography>
<Typography component="h2">
{t("distributedUptimeStatusCreateStatusPageAccess")}
</Typography>
<Typography component="p">
If your status page is ready, you can mark it as published.
{t("distributedUptimeStatusCreateStatusPageReady")}
</Typography>
</Stack>
<Stack gap={theme.spacing(18)}>
<Checkbox
id="publish"
name="isPublished"
label={`Published and visible to the public`}
label={t("distributedUptimeStatusPublishedLabel")}
isChecked={form.isPublished}
onChange={handleFormChange}
/>
@@ -206,9 +211,11 @@ const CreateStatus = () => {
</ConfigBox>
<ConfigBox>
<Stack gap={theme.spacing(6)}>
<Typography component="h2">Basic Information</Typography>
<Typography component="h2">
{t("distributedUptimeStatusBasicInfoHeader")}
</Typography>
<Typography component="p">
Define company name and the subdomain that your status page points to.
{t("distributedUptimeStatusBasicInfoDescription")}
</Typography>
</Stack>
<Stack gap={theme.spacing(18)}>
@@ -216,7 +223,7 @@ const CreateStatus = () => {
id="companyName"
name="companyName"
type="text"
label="Company name"
label={t("distributedUptimeStatusCompanyNameLabel")}
placeholder="Company name"
value={form.companyName}
onChange={handleFormChange}
@@ -227,7 +234,7 @@ const CreateStatus = () => {
id="url"
name="url"
type="url"
label="Your status page address"
label={t("distributedUptimeStatusPageAddressLabel")}
disabled={!isCreate}
value={form.url}
onChange={handleFormChange}
@@ -238,8 +245,10 @@ const CreateStatus = () => {
</ConfigBox>
<ConfigBox>
<Stack gap={theme.spacing(6)}>
<Typography component="h2">Logo</Typography>
<Typography component="p">Upload a logo for your status page </Typography>
<Typography component="h2">{t("distributedUptimeStatusLogoHeader")}</Typography>
<Typography component="p">
{t("distributedUptimeStatusLogoDescription")}{" "}
</Typography>
</Stack>
<Stack
gap={theme.spacing(18)}
@@ -262,7 +271,7 @@ const CreateStatus = () => {
color="accent"
tabIndex={-1}
>
Upload logo
{t("distributedUptimeStatusLogoUploadButton")}
<VisuallyHiddenInput onChange={handleImageUpload} />
</Button>
</Box>
@@ -270,9 +279,11 @@ const CreateStatus = () => {
</ConfigBox>
<ConfigBox>
<Stack>
<Typography component="h2">Standard Monitors</Typography>
<Typography component="h2">
{t("distributedUptimeStatusStandardMonitorsHeader")}
</Typography>
<Typography component="p">
Attach standard monitors to your status page.
{t("distributedUptimeStatusStandardMonitorsDescription")}
</Typography>
</Stack>
<Stack gap={theme.spacing(18)}>
@@ -301,7 +312,7 @@ const CreateStatus = () => {
color="accent"
onClick={handleSubmit}
>
Save
{t("settingsSave")}
</Button>
</Stack>
</Stack>
@@ -1,8 +1,10 @@
import { Stack, Button, ButtonGroup } from "@mui/material";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
const TimeFrameHeader = ({ timeFrame, setTimeFrame, sx, ...props }) => {
const theme = useTheme();
const { t } = useTranslation();
return (
<Stack
direction="row"
@@ -16,21 +18,21 @@ const TimeFrameHeader = ({ timeFrame, setTimeFrame, sx, ...props }) => {
filled={(timeFrame === 30).toString()}
onClick={() => setTimeFrame(30)}
>
30 days
{t("distributedUptimeStatus30Days")}
</Button>
<Button
variant="group"
filled={(timeFrame === 60).toString()}
onClick={() => setTimeFrame(60)}
>
60 days
{t("distributedUptimeStatus60Days")}
</Button>
<Button
variant="group"
filled={(timeFrame === 90).toString()}
onClick={() => setTimeFrame(90)}
>
90 days
{t("distributedUptimeStatus90Days")}
</Button>
</ButtonGroup>
</Stack>
@@ -23,8 +23,8 @@ import { RowContainer } from "../../../Components/StandardContainer";
import { useTheme } from "@mui/material/styles";
import { useState, useEffect, useRef } from "react";
import { useParams } from "react-router-dom";
import { useSubscribeToDetails } from "../../DistributedUptime/Details/Hooks/useSubscribeToDetails";
import { useDUStatusPageFetchByUrl } from "./Hooks/useDUStatusPageFetchByUrl";
import { useFetchDepinStatusPage } from "../../../Hooks/useFetchDepinStatusPage";
import { useSubscribeToDepinDetails } from "../../../Hooks/useSubscribeToDepinDetails";
import { useStatusPageDelete } from "../../StatusPage/Status/Hooks/useStatusPageDelete";
import TimeFrameHeader from "./Components/TimeframeHeader";
import SubHeader from "../../../Components/Subheader";
@@ -64,13 +64,13 @@ const DistributedUptimeStatus = () => {
statusPage,
monitorId,
isPublished,
] = useDUStatusPageFetchByUrl({
] = useFetchDepinStatusPage({
url,
timeFrame,
});
const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
useSubscribeToDetails({ monitorId, dateRange, isPublic, isPublished });
useSubscribeToDepinDetails({ monitorId, dateRange, isPublic, isPublished });
const [deleteStatusPage, isDeleting] = useStatusPageDelete(() => {
navigate("/distributed-uptime");
@@ -124,9 +124,9 @@ const DistributedUptimeStatus = () => {
marginY={theme.spacing(4)}
color={theme.palette.primary.contrastTextTertiary}
>
A status page is not set up.
{t("distributedUptimeStatusPageNotSetUp")}
</Typography>
<Typography>Please contact your administrator</Typography>
<Typography>{t("distributedUptimeStatusContactAdmin")}</Typography>
</GenericFallback>
</Stack>
);
@@ -137,7 +137,7 @@ const DistributedUptimeStatus = () => {
return (
<Stack sx={sx}>
<GenericFallback>
<Typography>This status page is not public.</Typography>
<Typography>{t("distributedUptimeStatusPageNotPublic")}</Typography>
</GenericFallback>
</Stack>
);
@@ -155,9 +155,9 @@ const DistributedUptimeStatus = () => {
marginY={theme.spacing(4)}
color={theme.palette.primary.contrastTextTertiary}
>
Network error
{t("networkError")}
</Typography>
<Typography>Please check your connection</Typography>
<Typography>{t("checkConnection")}</Typography>
</GenericFallback>
);
}
@@ -171,7 +171,7 @@ const DistributedUptimeStatus = () => {
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<GenericFallback>
<Typography>There is no check history for this monitor yet.</Typography>
<Typography>{t("distributedUptimeDetailsNoMonitorHistory")}</Typography>
</GenericFallback>
</Stack>
);
@@ -246,17 +246,21 @@ const DistributedUptimeStatus = () => {
gap={theme.spacing(8)}
>
<InfoBox
heading="Devices"
heading={t("distributedUptimeStatusDevices")}
subHeading={monitor?.totalChecks ?? 0}
icon={PeopleAltOutlinedIcon}
alt="Upt Logo"
sx={{ width: "50%" }}
/>
<InfoBox
heading={isSmallScreen ? "UPT" : "UPT Burned"}
heading={
isSmallScreen
? t("distributedUptimeStatusUpt")
: t("distributedUptimeStatusUptBurned")
}
subHeading={safelyParseFloat(monitor?.uptBurnt).toFixed(4)}
img={UptLogo}
alt="Upt Logo"
alt={t("distributedUptimeStatusUptLogo")}
sx={{ width: "50%" }}
/>
</Stack>
@@ -295,7 +299,7 @@ const DistributedUptimeStatus = () => {
<Footer />
<Dialog
// open={isOpen.deleteStats}
title="Do you want to delete this status page?"
title={t("distributedUptimeStatusPageDeleteDialog")}
onConfirm={() => {
deleteStatusPage();
setIsDeleteOpen(false);
@@ -304,8 +308,8 @@ const DistributedUptimeStatus = () => {
setIsDeleteOpen(false);
}}
open={isDeleteOpen}
confirmationButtonLabel="Yes, delete status page"
description="Once deleted, your status page cannot be retrieved."
confirmationButtonLabel={t("distributedUptimeStatusPageDeleteConfirm")}
description={t("distributedUptimeStatusPageDeleteDescription")}
isLoading={isDeleting || isLoading}
/>
</Stack>
@@ -13,6 +13,7 @@ import { useSelector } from "react-redux";
import { useState } from "react";
import useChecksFetch from "../../Hooks/useChecksFetch";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
const IncidentTable = ({
shouldRender,
@@ -38,6 +39,8 @@ const IncidentTable = ({
rowsPerPage,
});
const { t } = useTranslation();
//Handlers
const handleChangePage = (_, newPage) => {
setPage(newPage);
@@ -50,12 +53,12 @@ const IncidentTable = ({
const headers = [
{
id: "monitorName",
content: "Monitor Name",
content: t("incidentsTableMonitorName"),
render: (row) => monitors[row.monitorId]?.name ?? "N/A",
},
{
id: "status",
content: "Status",
content: t("incidentsTableStatus"),
render: (row) => {
const status = row.status === true ? "up" : "down";
return (
@@ -69,7 +72,7 @@ const IncidentTable = ({
},
{
id: "dateTime",
content: "Date & Time",
content: t("incidentsTableDateTime"),
render: (row) => {
const formattedDate = formatDateWithTz(
row.createdAt,
@@ -81,10 +84,10 @@ const IncidentTable = ({
},
{
id: "statusCode",
content: "Status Code",
content: t("incidentsTableStatusCode"),
render: (row) => <HttpStatusLabel status={row.statusCode} />,
},
{ id: "message", content: "Message", render: (row) => row.message },
{ id: "message", content: t("incidentsTableMessage"), render: (row) => row.message },
];
if (!shouldRender || isLoading) return <TableSkeleton />;
@@ -98,7 +101,7 @@ const IncidentTable = ({
}
if (!isLoading && typeof checksCount === "undefined") {
return <GenericFallback>No incidents recorded</GenericFallback>;
return <GenericFallback>{t("incidentsTableNoIncidents")}</GenericFallback>;
}
return (
@@ -108,7 +111,7 @@ const IncidentTable = ({
data={checks}
/>
<Pagination
paginationLabel="incidents"
paginationLabel={t("incidentsTablePaginationLabel")}
itemCount={checksCount}
page={page}
rowsPerPage={rowsPerPage}
@@ -6,6 +6,7 @@ import PropTypes from "prop-types";
//Utils
import { useTheme } from "@emotion/react";
import SkeletonLayout from "./skeleton";
import { useTranslation } from "react-i18next";
const OptionsHeader = ({
shouldRender,
@@ -18,6 +19,7 @@ const OptionsHeader = ({
setDateRange,
}) => {
const theme = useTheme();
const { t } = useTranslation();
const monitorNames = typeof monitors !== "undefined" ? Object.values(monitors) : [];
// The stacks below which are three in number have the same style so
@@ -40,11 +42,11 @@ const OptionsHeader = ({
component="h1"
color={theme.palette.primary.contrastTextSecondary}
>
Incidents for:
{t("incidentsOptionsHeader")}
</Typography>
<Select
id="incidents-select-monitor"
placeholder="All servers"
placeholder={t("incidentsOptionsPlaceholderAllServers")}
value={selectedMonitor}
onChange={(e) => setSelectedMonitor(e.target.value)}
items={monitorNames}
@@ -61,7 +63,7 @@ const OptionsHeader = ({
component="h1"
color={theme.palette.primary.contrastTextSecondary}
>
Filter by:
{t("incidentsOptionsHeaderFilterBy")}
</Typography>
<ButtonGroup>
<Button
@@ -69,21 +71,21 @@ const OptionsHeader = ({
filled={(filter === "all").toString()}
onClick={() => setFilter("all")}
>
All
{t("incidentsOptionsHeaderFilterAll")}
</Button>
<Button
variant="group"
filled={(filter === "down").toString()}
onClick={() => setFilter("down")}
>
Down
{t("incidentsOptionsHeaderFilterDown")}
</Button>
<Button
variant="group"
filled={(filter === "resolve").toString()}
onClick={() => setFilter("resolve")}
>
Cannot resolve
{t("incidentsOptionsHeaderFilterCannotResolve")}
</Button>
</ButtonGroup>
</Stack>
@@ -93,7 +95,7 @@ const OptionsHeader = ({
component="h1"
color={theme.palette.primary.contrastTextSecondary}
>
Show:
{t("incidentsOptionsHeaderShow")}
</Typography>
<ButtonGroup>
<Button
@@ -101,28 +103,28 @@ const OptionsHeader = ({
filled={(dateRange === "hour").toString()}
onClick={() => setDateRange("hour")}
>
Last hour
{t("incidentsOptionsHeaderLastHour")}
</Button>
<Button
variant="group"
filled={(dateRange === "day").toString()}
onClick={() => setDateRange("day")}
>
Last day
{t("incidentsOptionsHeaderLastDay")}
</Button>
<Button
variant="group"
filled={(dateRange === "week").toString()}
onClick={() => setDateRange("week")}
>
Last week
{t("incidentsOptionsHeaderLastWeek")}
</Button>
<Button
variant="group"
filled={(dateRange === "all").toString()}
onClick={() => setDateRange("all")}
>
All
{t("incidentsOptionsHeaderFilterAll")}
</Button>
</ButtonGroup>
</Stack>
+19 -19
View File
@@ -8,6 +8,7 @@ import { useSelector, useDispatch } from "react-redux";
import { infrastructureMonitorValidation } from "../../../Validation/validation";
import { createInfrastructureMonitor } from "../../../Features/InfrastructureMonitors/infrastructureMonitorsSlice";
import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
import { useTranslation } from "react-i18next";
// MUI
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
@@ -52,6 +53,7 @@ const CreateInfrastructureMonitor = () => {
const monitorState = useSelector((state) => state.infrastructureMonitor);
const dispatch = useDispatch();
const navigate = useNavigate();
const { t } = useTranslation();
// State
const [errors, setErrors] = useState({});
@@ -233,7 +235,7 @@ const CreateInfrastructureMonitor = () => {
component="span"
fontSize="inherit"
>
Create your{" "}
{t("infrastructureCreateYour")}{" "}
</Typography>
<Typography
component="span"
@@ -241,18 +243,17 @@ const CreateInfrastructureMonitor = () => {
fontSize="inherit"
fontWeight="inherit"
>
monitor
{t("monitor")}
</Typography>
</Typography>
<ConfigBox>
<Stack gap={theme.spacing(6)}>
<Typography component="h2">General settings</Typography>
<Typography component="h2">{t("settingsGeneralSettings")}</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the friendly name and
authorization secret to connect to the server agent.
{t("infrastructureCreateGeneralSettingsDescription")}
</Typography>
<Typography component="p">
The server you are monitoring must be running the{" "}
{t("infrastructureServerRequirement")}{" "}
<Link
level="primary"
url="https://github.com/bluewave-labs/checkmate-agent"
@@ -267,7 +268,7 @@ const CreateInfrastructureMonitor = () => {
name="url"
startAdornment={<HttpAdornment https={https} />}
placeholder={"localhost:59232/api/v1/metrics"}
label="Server URL"
label={t("infrastructureServerUrlLabel")}
https={https}
value={infrastructureMonitor.url}
onChange={handleChange}
@@ -275,21 +276,21 @@ const CreateInfrastructureMonitor = () => {
helperText={errors["url"]}
/>
<Box>
<Typography component="p">Protocol</Typography>
<Typography component="p">{t("infrastructureProtocol")}</Typography>
<ButtonGroup>
<Button
variant="group"
filled={https.toString()}
onClick={() => setHttps(true)}
>
HTTPS
{t("https")}
</Button>
<Button
variant="group"
filled={(!https).toString()}
onClick={() => setHttps(false)}
>
HTTP
{t("http")}
</Button>
</ButtonGroup>
</Box>
@@ -297,7 +298,7 @@ const CreateInfrastructureMonitor = () => {
type="text"
id="name"
name="name"
label="Display name"
label={t("infrastructureDisplayNameLabel")}
placeholder="Google"
isOptional={true}
value={infrastructureMonitor.name}
@@ -308,7 +309,7 @@ const CreateInfrastructureMonitor = () => {
type="text"
id="secret"
name="secret"
label="Authorization secret"
label={t("infrastructureAuthorizationSecretLabel")}
value={infrastructureMonitor.secret}
onChange={handleChange}
error={errors["secret"] ? true : false}
@@ -318,9 +319,9 @@ const CreateInfrastructureMonitor = () => {
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Incident notifications</Typography>
<Typography component="h2">{t("distributedUptimeCreateIncidentNotification")}</Typography>
<Typography component="p">
When there is an incident, notify users.
{t("distributedUptimeCreateIncidentDescription")}
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>
@@ -337,10 +338,9 @@ const CreateInfrastructureMonitor = () => {
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Customize alerts</Typography>
<Typography component="h2">{t("infrastructureCustomizeAlerts")}</Typography>
<Typography component="p">
Send a notification to user(s) when thresholds exceed a specified
percentage.
{t("infrastructureAlertNotificationDescription")}
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>
@@ -385,7 +385,7 @@ const CreateInfrastructureMonitor = () => {
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Advanced settings</Typography>
<Typography component="h2">{t("distributedUptimeCreateAdvancedSettings")}</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Select
@@ -408,7 +408,7 @@ const CreateInfrastructureMonitor = () => {
onClick={handleCreateInfrastructureMonitor}
loading={monitorState?.isLoading}
>
Create infrastructure monitor
{t("infrastructureCreateMonitor")}
</Button>
</Stack>
</Stack>
@@ -12,8 +12,10 @@ import {
} from "../../../../../Components/Charts/Utils/chartUtils";
import { useTheme } from "@emotion/react";
import { useHardwareUtils } from "../../Hooks/useHardwareUtils";
import { useTranslation } from "react-i18next";
const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => {
const theme = useTheme();
const { t } = useTranslation();
const { buildTemps } = useHardwareUtils();
if (!shouldRender) {
@@ -31,10 +33,10 @@ const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => {
type: "memory",
data: checks,
dataKeys: ["avgMemoryUsage"],
heading: "Memory usage",
heading: t("memoryUsage"),
strokeColor: theme.palette.accent.main, // CAIO_REVIEW
gradientStartColor: theme.palette.accent.main, // CAIO_REVIEW
yLabel: "Memory usage",
yLabel: t("memoryUsage"),
yDomain: [0, 1],
yTick: <PercentTick />,
xTick: <TzTick dateRange={dateRange} />,
@@ -51,10 +53,10 @@ const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => {
type: "cpu",
data: checks,
dataKeys: ["avgCpuUsage"],
heading: "CPU usage",
heading: t("cpuUsage"),
strokeColor: theme.palette.success.main,
gradientStartColor: theme.palette.success.main,
yLabel: "CPU usage",
yLabel: t("cpuUsage"),
yDomain: [0, 1],
yTick: <PercentTick />,
xTick: <TzTick dateRange={dateRange} />,
@@ -73,7 +75,7 @@ const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => {
dataKeys: tempKeys,
strokeColor: theme.palette.error.main,
gradientStartColor: theme.palette.error.main,
heading: "CPU Temperature",
heading: t("cpuTemperature"),
yLabel: "Temperature",
xTick: <TzTick dateRange={dateRange} />,
yDomain: [
@@ -96,7 +98,7 @@ const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => {
heading: `Disk${idx} usage`,
strokeColor: theme.palette.warning.main,
gradientStartColor: theme.palette.warning.main,
yLabel: "Disk Usage",
yLabel: t("diskUsage"),
yDomain: [0, 1],
yTick: <PercentTick />,
xTick: <TzTick dateRange={dateRange} />,
@@ -6,10 +6,12 @@ import SkeletonLayout from "./skeleton";
// Utils
import { useHardwareUtils } from "../../Hooks/useHardwareUtils";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
const Gauges = ({ shouldRender, monitor }) => {
const { decimalToPercentage, formatBytes } = useHardwareUtils();
const theme = useTheme();
const { t } = useTranslation();
if (!shouldRender) {
return <SkeletonLayout />;
@@ -28,19 +30,19 @@ const Gauges = ({ shouldRender, monitor }) => {
{
type: "memory",
value: decimalToPercentage(memoryUsagePercent),
heading: "Memory usage",
metricOne: "Used",
heading: t("memoryUsage"),
metricOne: t("used"),
valueOne: formatBytes(memoryUsedBytes, true),
metricTwo: "Total",
metricTwo: t("total"),
valueTwo: formatBytes(memoryTotalBytes, true),
},
{
type: "cpu",
value: decimalToPercentage(cpuUsagePercent),
heading: "CPU usage",
metricOne: "Cores",
heading: t("cpuUsage"),
metricOne: t("cores"),
valueOne: cpuPhysicalCores ?? 0,
metricTwo: "Frequency",
metricTwo: t("frequency"),
valueTwo: `${(cpuFrequency / 1000).toFixed(2)} Ghz`,
},
...(latestCheck?.disk ?? []).map((disk, idx) => ({
@@ -48,9 +50,9 @@ const Gauges = ({ shouldRender, monitor }) => {
diskIndex: idx,
value: decimalToPercentage(disk.usage_percent),
heading: `Disk${idx} usage`,
metricOne: "Used",
metricOne: t("used"),
valueOne: formatBytes(disk.total_bytes - disk.free_bytes, true),
metricTwo: "Total",
metricTwo: t("total"),
valueTwo: formatBytes(disk.total_bytes, true),
})),
];
@@ -6,11 +6,13 @@ import StatBox from "../../../../../Components/StatBox";
//Utils
import useUtils from "../../../../../Pages/Uptime/Monitors/Hooks/useUtils";
import { useHardwareUtils } from "../../Hooks/useHardwareUtils";
import { useTranslation } from "react-i18next";
const InfraStatBoxes = ({ shouldRender, monitor }) => {
// Utils
const { formatBytes } = useHardwareUtils();
const { statusStyles, determineState } = useUtils();
const { t } = useTranslation();
const { stats, uptimePercentage } = monitor ?? {};
const latestCheck = stats?.aggregateData?.latestCheck;
@@ -40,11 +42,11 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
>
<StatBox
sx={statusStyles[determineState(monitor)]}
heading="Status"
heading={t("status")}
subHeading={determineState(monitor)}
/>
<StatBox
heading="CPU (Physical)"
heading={t("cpuPhysical")}
subHeading={
<>
{physicalCores}
@@ -54,7 +56,7 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
/>
<StatBox
key={2}
heading="CPU (Logical)"
heading={t("cpuLogical")}
subHeading={
<>
{logicalCores}
@@ -63,7 +65,7 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
}
/>
<StatBox
heading="CPU Frequency"
heading={t("cpuFrequency")}
subHeading={
<>
{(cpuFrequency / 1000).toFixed(2)}
@@ -72,7 +74,7 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
}
/>
<StatBox
heading="Average CPU Temperature"
heading={t("avgCpuTemperature")}
subHeading={
<>
{cpuTemperature.toFixed(2)}
@@ -81,15 +83,15 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
}
/>
<StatBox
heading="Memory"
heading={t("memory")}
subHeading={formatBytes(memoryTotalBytes)}
/>
<StatBox
heading="Disk"
heading={t("disk")}
subHeading={formatBytes(diskTotalBytes)}
/>
<StatBox
heading="Uptime"
heading={t("uptime")}
subHeading={
<>
{(uptimePercentage * 100).toFixed(2)}
@@ -99,7 +101,7 @@ const InfraStatBoxes = ({ shouldRender, monitor }) => {
/>
<StatBox
key={8}
heading="OS"
heading={t("os")}
subHeading={osPlatform}
/>
</StatusBoxes>
@@ -1,5 +1,6 @@
import { Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
// Constants
const BASE_BOX_PADDING_VERTICAL = 4;
@@ -9,6 +10,7 @@ const CHART_CONTAINER_HEIGHT = 300;
const useHardwareUtils = () => {
const theme = useTheme();
const { t } = useTranslation();
const getDimensions = () => {
const totalTypographyPadding = parseInt(theme.spacing(TYPOGRAPHY_PADDING), 10) * 2;
@@ -29,7 +31,7 @@ const useHardwareUtils = () => {
<>
{0}
{space ? " " : ""}
<Typography component="span">GB</Typography>
<Typography component="span">{t("gb")}</Typography>
</>
);
if (typeof bytes !== "number")
@@ -37,7 +39,7 @@ const useHardwareUtils = () => {
<>
{0}
{space ? " " : ""}
<Typography component="span">GB</Typography>
<Typography component="span">{t("gb")}</Typography>
</>
);
if (bytes === 0)
@@ -45,7 +47,7 @@ const useHardwareUtils = () => {
<>
{0}
{space ? " " : ""}
<Typography component="span">GB</Typography>
<Typography component="span">{t("gb")}</Typography>
</>
);
@@ -57,7 +59,7 @@ const useHardwareUtils = () => {
<>
{Number(GB.toFixed(0))}
{space ? " " : ""}
<Typography component="span">GB</Typography>
<Typography component="span">{t("gb")}</Typography>
</>
);
} else {
@@ -65,7 +67,7 @@ const useHardwareUtils = () => {
<>
{Number(MB.toFixed(0))}
{space ? " " : ""}
<Typography component="span">MB</Typography>
<Typography component="span">{t("mb")}</Typography>
</>
);
}
+5 -3
View File
@@ -13,6 +13,7 @@ import { useTheme } from "@emotion/react";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useHardwareMonitorsFetch } from "./Hooks/useHardwareMonitorsFetch";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
// Constants
@@ -29,6 +30,7 @@ const InfrastructureDetails = () => {
// Utils
const theme = useTheme();
const { monitorId } = useParams();
const { t } = useTranslation();
const { isLoading, networkError, monitor } = useHardwareMonitorsFetch({
monitorId,
@@ -43,9 +45,9 @@ const InfrastructureDetails = () => {
marginY={theme.spacing(4)}
color={theme.palette.primary.contrastTextTertiary}
>
Network error
{t("networkError")}
</Typography>
<Typography>Please check your connection</Typography>
<Typography>{t("checkConnection")}</Typography>
</GenericFallback>
);
}
@@ -61,7 +63,7 @@ const InfrastructureDetails = () => {
monitor={monitor}
/>
<GenericFallback>
<Typography>No check history for this monitor yet.</Typography>
<Typography>{t("distributedUptimeDetailsNoMonitorHistory")}</Typography>
</GenericFallback>
</Stack>
);
@@ -13,10 +13,12 @@ import { useTheme } from "@emotion/react";
import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils";
import { useNavigate } from "react-router-dom";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete }) => {
// Utils
const theme = useTheme();
const { t } = useTranslation();
const { determineState } = useUtils();
const navigate = useNavigate();
@@ -27,7 +29,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete
const headers = [
{
id: "host",
content: "Host",
content: t("host"),
render: (row) => (
<Host
title={row.name}
@@ -39,7 +41,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete
},
{
id: "status",
content: "Status",
content: t("incidentsTableStatus"),
render: (row) => (
<StatusLabel
status={row.status}
@@ -49,7 +51,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete
},
{
id: "frequency",
content: "Frequency",
content: t("frequency"),
render: (row) => (
<Stack
direction={"row"}
@@ -65,12 +67,12 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete
</Stack>
),
},
{ id: "cpu", content: "CPU", render: (row) => <CustomGauge progress={row.cpu} /> },
{ id: "mem", content: "Mem", render: (row) => <CustomGauge progress={row.mem} /> },
{ id: "disk", content: "Disk", render: (row) => <CustomGauge progress={row.disk} /> },
{ id: "cpu", content: t("cpu"), render: (row) => <CustomGauge progress={row.cpu} /> },
{ id: "mem", content: t("mem"), render: (row) => <CustomGauge progress={row.mem} /> },
{ id: "disk", content: t("disk"), render: (row) => <CustomGauge progress={row.disk} /> },
{
id: "actions",
content: "Actions",
content: t("actions"),
render: (row) => (
<InfrastructureMenu
monitor={row}
+5 -3
View File
@@ -12,6 +12,7 @@ import { useTheme } from "@emotion/react";
import { useMonitorFetch } from "./Hooks/useMonitorFetch";
import { useState } from "react";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useTranslation } from "react-i18next";
// Constants
const BREADCRUMBS = [{ name: `infrastructure`, path: "/infrastructure" }];
@@ -24,6 +25,7 @@ const InfrastructureMonitors = () => {
// Utils
const theme = useTheme();
const isAdmin = useIsAdmin();
const { t } = useTranslation();
// Handlers
const handleActionMenuDelete = () => {
@@ -52,9 +54,9 @@ const InfrastructureMonitors = () => {
marginY={theme.spacing(4)}
color={theme.palette.primary.contrastTextTertiary}
>
Network error
{t("networkError")}
</Typography>
<Typography>Please check your connection</Typography>
<Typography>{t("checkConnection")}</Typography>
</GenericFallback>
);
}
@@ -96,7 +98,7 @@ const InfrastructureMonitors = () => {
/>
<Pagination
itemCount={summary?.totalMonitors}
paginationLabel="monitors"
paginationLabel={t("monitors")}
page={page}
rowsPerPage={rowsPerPage}
handleChangePage={handleChangePage}
+12 -9
View File
@@ -4,6 +4,7 @@ import { useTheme } from "@emotion/react";
import Discord from "../../assets/icons/discord-icon.svg?react";
import Slack from "../../assets/icons/slack-icon.svg?react";
import Zapier from "../../assets/icons/zapier-icon.svg?react";
import { useTranslation } from "react-i18next";
import "./index.css";
@@ -18,6 +19,7 @@ import "./index.css";
*/
const IntegrationsComponent = ({ icon, header, info, onClick }) => {
const theme = useTheme();
const { t } = useTranslation();
return (
<Grid
@@ -59,7 +61,7 @@ const IntegrationsComponent = ({ icon, header, info, onClick }) => {
sx={{ alignSelf: "center" }}
disabled={true}
>
Add
{t("add")}
</Button>
</Stack>
</Grid>
@@ -81,6 +83,7 @@ IntegrationsComponent.propTypes = {
const Integrations = () => {
const theme = useTheme();
const { t } = useTranslation();
const integrations = [
{
@@ -90,8 +93,8 @@ const Integrations = () => {
style={{ width: "45px", height: "45px", alignSelf: "center" }}
/>
),
header: "Slack",
info: "Connect with Slack and see incidents in a channel",
header: t("integrationsSlack"),
info: t("integrationsSlackInfo"),
onClick: () => {},
},
{
@@ -101,8 +104,8 @@ const Integrations = () => {
style={{ width: "42px", height: "42px", alignSelf: "center" }}
/>
),
header: "Discord",
info: "Connect with Discord and view incidents directly in a channel",
header: t("integrationsDiscord"),
info: t("integrationsDiscordInfo"),
onClick: () => {},
},
{
@@ -112,8 +115,8 @@ const Integrations = () => {
style={{ width: "42px", height: "42px", alignSelf: "center" }}
/>
),
header: "Zapier",
info: "Send all incidents to Zapier, and then see them everywhere",
header: t("integrationsZapier"),
info: t("integrationsZapierInfo"),
onClick: () => {},
},
// Add more integrations as needed
@@ -130,9 +133,9 @@ const Integrations = () => {
},
}}
>
<Typography component="h1">Integrations</Typography>
<Typography component="h1">{t("integrations")}</Typography>
<Typography mb={theme.spacing(12)}>
Connect Prism to your favorite service.
{t("integrationsPrism")}
</Typography>
<Grid
container
+37 -25
View File
@@ -1,3 +1,8 @@
// Required Data
// 1. Monitor summary
// 2. List of monitors filtered by search term with 25 checks each
// 2a.List of monitors must have the total number of monitors that match.
// Components
import Breadcrumbs from "../../../Components/Breadcrumbs";
import Greeting from "../../../Utils/greeting";
@@ -23,9 +28,11 @@ import useMonitorsFetch from "./Hooks/useMonitorsFetch";
import { useSelector, useDispatch } from "react-redux";
import { setRowsPerPage } from "../../../Features/UI/uiSlice";
import PropTypes from "prop-types";
import useFetchMonitorsWithSummary from "../../../Hooks/useFetchMonitorsWithSummary";
import useFetchMonitorsWithChecks from "../../../Hooks/useFetchMonitorsWithChecks";
const BREADCRUMBS = [{ name: `Uptime`, path: "/uptime" }];
const TYPES = ["http", "ping", "docker", "port"];
const CreateMonitorButton = ({ shouldRender }) => {
// Utils
const navigate = useNavigate();
@@ -89,23 +96,32 @@ const UptimeMonitors = () => {
}, []);
const teamId = user.teamId;
const {
monitorsAreLoading,
monitors,
filteredMonitors,
monitorsSummary,
networkError,
} = useMonitorsFetch({
const [monitors, monitorsSummary, monitorsWithSummaryIsLoading, networkError] =
useFetchMonitorsWithSummary({
teamId,
types: TYPES,
});
const [
monitorsWithChecks,
monitorsWithChecksCount,
monitorsWithChecksIsLoading,
monitorsWithChecksNetworkError,
] = useFetchMonitorsWithChecks({
teamId,
types: TYPES,
limit: 25,
page: page,
rowsPerPage: rowsPerPage,
filter: search,
field: sort?.field,
order: sort?.order,
triggerUpdate: monitorUpdateTrigger,
triggerUpdate,
});
const totalMonitors = monitorsSummary?.totalMonitors;
const isLoading = monitorsWithSummaryIsLoading || monitorsWithChecksIsLoading;
if (networkError) {
return (
<GenericFallback>
@@ -121,8 +137,9 @@ const UptimeMonitors = () => {
);
}
if (
!monitorsAreLoading &&
(totalMonitors === 0 || typeof totalMonitors === "undefined")
!isLoading &&
(monitorsSummary?.totalMonitors === 0 ||
typeof monitorsSummary?.totalMonitors === "undefined")
) {
return (
<Fallback
@@ -147,19 +164,19 @@ const UptimeMonitors = () => {
<Breadcrumbs list={BREADCRUMBS} />
<CreateMonitorHeader
isAdmin={isAdmin}
shouldRender={!monitorsAreLoading}
shouldRender={!isLoading}
path="/uptime/create"
/>
<Greeting type="uptime" />
<StatusBoxes
monitorsSummary={monitorsSummary}
shouldRender={!monitorsAreLoading}
shouldRender={!monitorsWithSummaryIsLoading}
/>
<Stack direction={"row"}>
<MonitorCountHeader
shouldRender={monitors?.length > 0 && !monitorsAreLoading}
monitorCount={totalMonitors}
shouldRender={monitors?.length > 0 && !monitorsWithSummaryIsLoading}
monitorCount={monitorsSummary?.totalMonitors}
heading={"Uptime monitors"}
></MonitorCountHeader>
<SearchComponent
@@ -170,19 +187,14 @@ const UptimeMonitors = () => {
</Stack>
<UptimeDataTable
isAdmin={isAdmin}
isLoading={monitorsAreLoading}
filteredMonitors={filteredMonitors}
monitors={monitors}
monitorCount={totalMonitors}
isSearching={isSearching}
filteredMonitors={monitorsWithChecks}
sort={sort}
setSort={setSort}
setSearch={setSearch}
isSearching={isSearching}
monitorsAreLoading={monitorsAreLoading}
triggerUpdate={triggerUpdate}
monitorsAreLoading={monitorsWithChecksIsLoading}
/>
<Pagination
itemCount={totalMonitors}
itemCount={monitorsWithChecksCount}
paginationLabel="monitors"
page={page}
rowsPerPage={rowsPerPage}
+52
View File
@@ -1033,6 +1033,58 @@ class NetworkService {
return this.axiosInstance.delete(`/status-page/${encodedUrl}`, {});
}
// ************************************
// Fetch monitors with summary by TeamID
// ************************************
async getMonitorsWithSummaryByTeamId(config) {
const { teamId, types } = config;
const params = new URLSearchParams();
if (types) {
types.forEach((type) => {
params.append("type", type);
});
}
return this.axiosInstance.get(
`/monitors/summary/team/${teamId}?${params.toString()}`,
{
headers: {
"Content-Type": "application/json",
},
}
);
}
// ************************************
// Fetch monitors with checks by TeamID
// ************************************
async getMonitorsWithChecksByTeamId(config) {
const { teamId, limit, types, page, rowsPerPage, filter, field, order } = config;
const params = new URLSearchParams();
if (limit) params.append("limit", limit);
if (types) {
types.forEach((type) => {
params.append("type", type);
});
}
if (page) params.append("page", page);
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
if (filter) params.append("filter", filter);
if (field) params.append("field", field);
if (order) params.append("order", order);
return this.axiosInstance.get(
`/monitors/team/${teamId}/with-checks?${params.toString()}`,
{
headers: {
"Content-Type": "application/json",
},
}
);
}
}
export default NetworkService;
+177 -176
View File
@@ -1,178 +1,179 @@
{
"dontHaveAccount": "Don't have account",
"https": "HTTPS",
"http": "HTTP",
"monitor": "monitor",
"aboutus": "About Us",
"email": "E-mail",
"forgotPassword": "Forgot Password",
"password": "password",
"signUp": "Sign up",
"signUP": "Sign Up",
"submit": "Submit",
"title": "Title",
"continue": "Continue",
"now": "Now",
"delete": "Delete",
"configure": "Configure",
"networkError": "Network error",
"responseTime": "Response time:",
"ms": "ms",
"bar": "Bar",
"area": "Area",
"country": "COUNTRY",
"city": "CITY",
"response": "RESPONSE",
"checkConnection": "Please check your connection",
"enterEmail": "Enter your email",
"authLoginTitle": "Log In",
"authLoginEnterPassword": "Enter your password",
"commonPassword": "Password",
"createPassword": "Create your password",
"createAPassword": "Create a password",
"commonBack": "Back",
"authForgotPasswordTitle": "Forgot password?",
"authForgotPasswordResetPassword": "Reset password",
"passwordreset": "Password Reset",
"authRegisterAlreadyHaveAccount": "Already have an account?",
"commonAppName": "Checkmate",
"authLoginEnterEmail": "Enter your email",
"authRegisterTitle": "Create an account",
"authRegisterStepOneTitle": "Create your account",
"authRegisterStepOnePersonalDetails": "Enter your personal details",
"authRegisterStepOneDescription": "Enter your details to get started",
"authRegisterStepTwoTitle": "Set up your profile",
"authRegisterStepTwoDescription": "Tell us more about yourself",
"authRegisterStepThreeTitle": "Almost done!",
"authRegisterStepThreeDescription": "Review your information",
"authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
"authForgotPasswordSendInstructions": "Send instructions",
"authForgotPasswordBackTo": "Back to",
"authCheckEmailTitle": "Check your email",
"authCheckEmailDescription": "We sent a password reset link to",
"authCheckEmailResendEmail": "Resend email",
"authCheckEmailBackTo": "Back to",
"authCheckEmailOpenEmailButton": "Open email app",
"goBackTo": "Go back to",
"authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
"authCheckEmailClickToResend": "Click to resend",
"authSetNewPasswordTitle": "Set new password",
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
"authSetNewPasswordNewPassword": "New password",
"authSetNewPasswordConfirmPassword": "Confirm password",
"confirmPassword": "Confirm your password",
"authSetNewPasswordResetPassword": "Reset password",
"authNewPasswordConfirmed": "Your password has been successfully reset. Click below to log in magically.",
"authSetNewPasswordBackTo": "Back to",
"authPasswordMustBeAtLeast": "Must be at least",
"authPasswordCharactersLong": "8 characters long",
"authPasswordMustContainAtLeast": "Must contain at least",
"authPasswordSpecialCharacter": "one special character",
"authPasswordOneNumber": "one number",
"authPasswordUpperCharacter": "one upper character",
"authPasswordLowerCharacter": "one lower character",
"authPasswordConfirmAndPassword": "Confirm password and password",
"authPasswordMustMatch": "must match",
"authRegisterCreateAccount": "Create your account to get started",
"authRegisterCreateSuperAdminAccount": "Create your Super admin account to get started",
"authRegisterSignUpWithEmail": "Sign up with Email",
"authRegisterBySigningUp": "By signing up, you agree to our",
"monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
"monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding",
"webhookSendSuccess": "Webhook notification sent successfully",
"webhookSendError": "Error sending webhook notification to {platform}",
"webhookUnsupportedPlatform": "Unsupported platform: {platform}",
"distributedStatusHeaderText": "Real-time, real-device coverage",
"distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
"distributedRightCategoryTitle": "Monitor",
"distributedStatusServerMonitors": "Server Monitors",
"distributedStatusServerMonitorsDescription": "Monitor status of related servers",
"distributedUptimeCreateSelectURL": "Here you can select the URL of the host, together with the type of monitor.",
"distributedUptimeCreateChecks": "Checks to perform",
"distributedUptimeCreateChecksDescription": "You can always add or remove checks after adding your site.",
"distributedUptimeCreateIncidentNotification": "Incident notifications",
"distributedUptimeCreateIncidentDescription": "When there is an incident, notify users.",
"distributedUptimeCreateAdvancedSettings": "Advanced settings",
"distributedUptimeDetailsNoMonitorHistory": "There is no check history for this monitor yet.",
"distributedUptimeDetailsFooterHeading": "Made with ❤️ by UpRock & Bluewave Labs",
"distributedUptimeDetailsFooterBuilt": "Built on",
"distributedUptimeDetailsFooterSolana": "Solana",
"distributedUptimeDetailsMonitorHeader": "Distributed Uptime Monitoring powered by DePIN",
"distributedUptimeDetailsStatusHeaderUptime": "Uptime:",
"distributedUptimeDetailsStatusHeaderLastUpdate": "Last updated",
"settingsGeneralSettings": "General settings",
"settingsDisplayTimezone": "Display timezone",
"settingsDisplayTimezoneDescription": "The timezone of the dashboard you publicly display.",
"settingsAppearance": "Appearance",
"settingsAppearanceDescription": "Switch between light and dark mode, or change user interface language",
"settingsThemeMode": "Theme Mode",
"settingsLanguage": "Language",
"settingsDistributedUptime": "Distributed uptime",
"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.",
"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.",
"settingsClearAllStatsDialogConfirm": "Yes, clear all stats",
"settingsDemoMonitors": "Demo monitors",
"settingsDemoMonitorsDescription": "Here you can add and remove demo monitors.",
"settingsAddDemoMonitors": "Add demo monitors",
"settingsAddDemoMonitorsButton": "Add demo monitors",
"settingsRemoveAllMonitors": "Remove all monitors",
"settingsRemoveAllMonitorsButton": "Remove all monitors",
"settingsRemoveAllMonitorsDialogTitle": "Do you want to remove all monitors?",
"settingsRemoveAllMonitorsDialogConfirm": "Yes, clear 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",
"settingsDevelopedBy": "Developed by Bluewave Labs.",
"settingsSave": "Save",
"settingsSuccessSaved": "Settings saved successfully",
"settingsFailedToSave": "Failed to save settings",
"settingsStatsCleared": "Stats cleared successfully",
"settingsFailedToClearStats": "Failed to clear stats",
"settingsDemoMonitorsAdded": "Successfully added demo monitors",
"settingsFailedToAddDemoMonitors": "Failed to add demo monitors",
"settingsMonitorsDeleted": "Successfully deleted all monitors",
"settingsFailedToDeleteMonitors": "Failed to delete all monitors",
"starPromptTitle": "Star Checkmate",
"starPromptDescription": "See the latest releases and help grow the community on GitHub",
"notifications": {
"enableNotifications": "Enable {{platform}} notifications",
"testNotification": "Test notification",
"addOrEditNotifications": "Add or edit notifications",
"slack": {
"label": "Slack",
"description": "To enable Slack notifications, create a Slack app and enable incoming webhooks. After that, simply provide the webhook URL here.",
"webhookLabel": "Webhook URL",
"webhookPlaceholder": "https://hooks.slack.com/services/..."
},
"discord": {
"label": "Discord",
"description": "To send data to a Discord channel from Checkmate via Discord notifications using webhooks, you can use Discord's incoming Webhooks feature.",
"webhookLabel": "Discord Webhook URL",
"webhookPlaceholder": "https://discord.com/api/webhooks/..."
},
"telegram": {
"label": "Telegram",
"description": "To enable Telegram notifications, create a Telegram bot using BotFather, an official bot for creating and managing Telegram bots. Then, get the API token and chat ID and write them down here.",
"tokenLabel": "Your bot token",
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
"chatIdLabel": "Your Chat ID",
"chatIdPlaceholder": "-1001234567890"
},
"webhook": {
"label": "Webhooks",
"description": "You can set up a custom webhook to receive notifications when incidents occur.",
"urlLabel": "Webhook URL",
"urlPlaceholder": "https://your-server.com/webhook"
}
}
"dontHaveAccount": "Don't have account",
"email": "E-mail",
"forgotPassword": "Forgot Password",
"password": "password",
"signUp": "Sign up",
"submit": "Submit",
"title": "Title",
"continue": "Continue",
"enterEmail": "Enter your email",
"authLoginTitle": "Log In",
"authLoginEnterPassword": "Enter your password",
"commonPassword": "Password",
"commonBack": "Back",
"authForgotPasswordTitle": "Forgot password?",
"authForgotPasswordResetPassword": "Reset password",
"createPassword": "Create your password",
"createAPassword": "Create a password",
"authRegisterAlreadyHaveAccount": "Already have an account?",
"commonAppName": "Checkmate",
"authLoginEnterEmail": "Enter your email",
"authRegisterTitle": "Create an account",
"authRegisterStepOneTitle": "Create your account",
"authRegisterStepOneDescription": "Enter your details to get started",
"authRegisterStepTwoTitle": "Set up your profile",
"authRegisterStepTwoDescription": "Tell us more about yourself",
"authRegisterStepThreeTitle": "Almost done!",
"authRegisterStepThreeDescription": "Review your information",
"authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
"authForgotPasswordSendInstructions": "Send instructions",
"authForgotPasswordBackTo": "Back to",
"authCheckEmailTitle": "Check your email",
"authCheckEmailDescription": "We sent a password reset link to",
"authCheckEmailResendEmail": "Resend email",
"authCheckEmailBackTo": "Back to",
"goBackTo": "Go back to",
"authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
"authCheckEmailClickToResend": "Click to resend",
"authSetNewPasswordTitle": "Set new password",
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
"authSetNewPasswordNewPassword": "New password",
"authSetNewPasswordConfirmPassword": "Confirm password",
"confirmPassword": "Confirm your password",
"authSetNewPasswordResetPassword": "Reset password",
"authSetNewPasswordBackTo": "Back to",
"authPasswordMustBeAtLeast": "Must be at least",
"authPasswordCharactersLong": "8 characters long",
"authPasswordMustContainAtLeast": "Must contain at least",
"authPasswordSpecialCharacter": "one special character",
"authPasswordOneNumber": "one number",
"authPasswordUpperCharacter": "one upper character",
"authPasswordLowerCharacter": "one lower character",
"authPasswordConfirmAndPassword": "Confirm password and password",
"authPasswordMustMatch": "must match",
"authRegisterCreateAccount": "Create your account to get started",
"authRegisterCreateSuperAdminAccount": "Create your Super admin account to get started",
"authRegisterSignUpWithEmail": "Sign up with Email",
"authRegisterBySigningUp": "By signing up, you agree to our",
"distributedStatusHeaderText": "Real-time, real-device coverage",
"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.",
"settingsAppearance": "Appearance",
"settingsAppearanceDescription": "Switch between light and dark mode, or change user interface language",
"settingsThemeMode": "Theme Mode",
"settingsLanguage": "Language",
"settingsDistributedUptime": "Distributed uptime",
"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.",
"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.",
"settingsClearAllStatsDialogConfirm": "Yes, clear all stats",
"settingsDemoMonitors": "Demo monitors",
"settingsDemoMonitorsDescription": "Here you can add and remove demo monitors.",
"settingsAddDemoMonitors": "Add demo monitors",
"settingsAddDemoMonitorsButton": "Add demo monitors",
"settingsRemoveAllMonitors": "Remove all monitors",
"settingsRemoveAllMonitorsButton": "Remove all monitors",
"settingsRemoveAllMonitorsDialogTitle": "Do you want to remove all monitors?",
"settingsRemoveAllMonitorsDialogConfirm": "Yes, clear 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",
"settingsDevelopedBy": "Developed by Bluewave Labs.",
"settingsSave": "Save",
"settingsSuccessSaved": "Settings saved successfully",
"settingsFailedToSave": "Failed to save settings",
"settingsStatsCleared": "Stats cleared successfully",
"settingsFailedToClearStats": "Failed to clear stats",
"settingsDemoMonitorsAdded": "Successfully added demo monitors",
"settingsFailedToAddDemoMonitors": "Failed to add demo monitors",
"settingsMonitorsDeleted": "Successfully deleted all monitors",
"settingsFailedToDeleteMonitors": "Failed to delete all monitors",
"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:",
"ms": "ms",
"bar": "Bar",
"area": "Area",
"country": "COUNTRY",
"city": "CITY",
"response": "RESPONSE",
"checkConnection": "Please check your connection",
"passwordreset": "Password Reset",
"authRegisterStepOnePersonalDetails": "Enter your personal details",
"authCheckEmailOpenEmailButton": "Open email app",
"authNewPasswordConfirmed": "Your password has been successfully reset. Click below to log in magically.",
"monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
"monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding",
"webhookSendSuccess": "Webhook notification sent successfully",
"webhookSendError": "Error sending webhook notification to {platform}",
"webhookUnsupportedPlatform": "Unsupported platform: {platform}",
"distributedRightCategoryTitle": "Monitor",
"distributedStatusServerMonitors": "Server Monitors",
"distributedStatusServerMonitorsDescription": "Monitor status of related servers",
"distributedUptimeCreateSelectURL": "Here you can select the URL of the host, together with the type of monitor.",
"distributedUptimeCreateChecks": "Checks to perform",
"distributedUptimeCreateChecksDescription": "You can always add or remove checks after adding your site.",
"distributedUptimeCreateIncidentNotification": "Incident notifications",
"distributedUptimeCreateIncidentDescription": "When there is an incident, notify users.",
"distributedUptimeCreateAdvancedSettings": "Advanced settings",
"distributedUptimeDetailsNoMonitorHistory": "There is no check history for this monitor yet.",
"distributedUptimeDetailsFooterHeading": "Made with ❤️ by UpRock & Bluewave Labs",
"distributedUptimeDetailsFooterBuilt": "Built on",
"distributedUptimeDetailsFooterSolana": "Solana",
"distributedUptimeDetailsMonitorHeader": "Distributed Uptime Monitoring powered by DePIN",
"distributedUptimeDetailsStatusHeaderUptime": "Uptime:",
"distributedUptimeDetailsStatusHeaderLastUpdate": "Last updated",
"notifications": {
"enableNotifications": "Enable {{platform}} notifications",
"testNotification": "Test notification",
"addOrEditNotifications": "Add or edit notifications",
"slack": {
"label": "Slack",
"description": "To enable Slack notifications, create a Slack app and enable incoming webhooks. After that, simply provide the webhook URL here.",
"webhookLabel": "Webhook URL",
"webhookPlaceholder": "https://hooks.slack.com/services/..."
},
"discord": {
"label": "Discord",
"description": "To send data to a Discord channel from Checkmate via Discord notifications using webhooks, you can use Discord's incoming Webhooks feature.",
"webhookLabel": "Discord Webhook URL",
"webhookPlaceholder": "https://discord.com/api/webhooks/..."
},
"telegram": {
"label": "Telegram",
"description": "To enable Telegram notifications, create a Telegram bot using BotFather, an official bot for creating and managing Telegram bots. Then, get the API token and chat ID and write them down here.",
"tokenLabel": "Your bot token",
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
"chatIdLabel": "Your Chat ID",
"chatIdPlaceholder": "-1001234567890"
},
"webhook": {
"label": "Webhooks",
"description": "You can set up a custom webhook to receive notifications when incidents occur.",
"urlLabel": "Webhook URL",
"urlPlaceholder": "https://your-server.com/webhook"
},
"testNotificationDevelop": "Test notification 2"
},
"testLocale": ""
}
+1 -1
View File
@@ -175,5 +175,5 @@
},
"testNotificationDevelop": ""
},
"testLocale": "TEST4 UPLOAD"
"testLocale": "TEST11 UPLOAD"
}