diff --git a/client/src/App.tsx b/client/src/App.tsx index a8a25669e..127426c85 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,16 +3,14 @@ import { useSelector } from "react-redux"; import "react-toastify/dist/ReactToastify.css"; import { ToastContainer } from "react-toastify"; import { ThemeProvider } from "@emotion/react"; -import lightTheme from "./Utils/Theme/lightTheme"; -import darkTheme from "./Utils/Theme/darkTheme"; import { CssBaseline, GlobalStyles } from "@mui/material"; import { Routes } from "./Routes"; -import AppLayout from "@/Components/v2/layout/AppLayout"; +import AppLayout from "@/Components/layout/AppLayout"; import type { RootState } from "@/Types/state"; +import { lightTheme, darkTheme } from "@/Utils/Theme/Theme"; function App() { const mode = useSelector((state: RootState) => state.ui.mode); - const theme = mode === "light" ? lightTheme : darkTheme; return ( @@ -21,8 +19,7 @@ function App() { diff --git a/client/src/Components/v2/actions-menu/index.tsx b/client/src/Components/actions-menu/index.tsx similarity index 100% rename from client/src/Components/v2/actions-menu/index.tsx rename to client/src/Components/actions-menu/index.tsx diff --git a/client/src/Components/v2/common/charts/HeatmapResponseTime.tsx b/client/src/Components/common/charts/HeatmapResponseTime.tsx similarity index 97% rename from client/src/Components/v2/common/charts/HeatmapResponseTime.tsx rename to client/src/Components/common/charts/HeatmapResponseTime.tsx index 972291f57..8178ffb92 100644 --- a/client/src/Components/v2/common/charts/HeatmapResponseTime.tsx +++ b/client/src/Components/common/charts/HeatmapResponseTime.tsx @@ -2,7 +2,7 @@ import Box from "@mui/material/Box"; import { useTheme } from "@mui/material/styles"; import type { CheckSnapshot } from "@/Types/Check"; import { getResponseColor } from "@/Utils/DataUtils"; -import { HeatmapResponseTimeTooltip } from "@/Components/v2/common/charts/HeatmapResponseTimeTooltip"; +import { HeatmapResponseTimeTooltip } from "@/Components/common/charts/HeatmapResponseTimeTooltip"; import type { SxProps } from "@mui/material/styles"; import type { ResponsiveStyleValue } from "@mui/system"; diff --git a/client/src/Components/v2/common/charts/HeatmapResponseTimeTooltip.tsx b/client/src/Components/common/charts/HeatmapResponseTimeTooltip.tsx similarity index 100% rename from client/src/Components/v2/common/charts/HeatmapResponseTimeTooltip.tsx rename to client/src/Components/common/charts/HeatmapResponseTimeTooltip.tsx diff --git a/client/src/Components/v2/common/charts/HistogramResponseTime.tsx b/client/src/Components/common/charts/HistogramResponseTime.tsx similarity index 100% rename from client/src/Components/v2/common/charts/HistogramResponseTime.tsx rename to client/src/Components/common/charts/HistogramResponseTime.tsx diff --git a/client/src/Components/v2/common/controls/HeaderCreate.tsx b/client/src/Components/common/controls/HeaderCreate.tsx similarity index 100% rename from client/src/Components/v2/common/controls/HeaderCreate.tsx rename to client/src/Components/common/controls/HeaderCreate.tsx diff --git a/client/src/Components/v2/common/controls/HeaderTimeRange.tsx b/client/src/Components/common/controls/HeaderTimeRange.tsx similarity index 96% rename from client/src/Components/v2/common/controls/HeaderTimeRange.tsx rename to client/src/Components/common/controls/HeaderTimeRange.tsx index c66ee9ff6..d7c7102bc 100644 --- a/client/src/Components/v2/common/controls/HeaderTimeRange.tsx +++ b/client/src/Components/common/controls/HeaderTimeRange.tsx @@ -1,7 +1,7 @@ import Stack from "@mui/material/Stack"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import { ToggleButtonGroup, ToggleButton } from "@/Components/v2/inputs"; +import { ToggleButtonGroup, ToggleButton } from "@/Components/inputs"; import { useTheme } from "@mui/material/styles"; import CircularProgress from "@mui/material/CircularProgress"; import { useTranslation } from "react-i18next"; diff --git a/client/src/Components/common/index.ts b/client/src/Components/common/index.ts new file mode 100644 index 000000000..4db9cac34 --- /dev/null +++ b/client/src/Components/common/index.ts @@ -0,0 +1,5 @@ +export * from "./charts/HistogramResponseTime"; +export * from "./charts/HeatmapResponseTime"; +export * from "./charts/HeatmapResponseTimeTooltip"; +export * from "./controls/HeaderCreate"; +export * from "./controls/HeaderTimeRange"; diff --git a/client/src/Components/v2/design-elements/Avatar.tsx b/client/src/Components/design-elements/Avatar.tsx similarity index 100% rename from client/src/Components/v2/design-elements/Avatar.tsx rename to client/src/Components/design-elements/Avatar.tsx diff --git a/client/src/Components/v2/design-elements/BaseBox.tsx b/client/src/Components/design-elements/BaseBox.tsx similarity index 100% rename from client/src/Components/v2/design-elements/BaseBox.tsx rename to client/src/Components/design-elements/BaseBox.tsx diff --git a/client/src/Components/v2/design-elements/BaseChart.tsx b/client/src/Components/design-elements/BaseChart.tsx similarity index 96% rename from client/src/Components/v2/design-elements/BaseChart.tsx rename to client/src/Components/design-elements/BaseChart.tsx index fd9251915..569652b20 100644 --- a/client/src/Components/v2/design-elements/BaseChart.tsx +++ b/client/src/Components/design-elements/BaseChart.tsx @@ -1,4 +1,4 @@ -import { BaseBox } from "../design-elements"; +import { BaseBox } from "."; import Stack from "@mui/material/Stack"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; diff --git a/client/src/Components/v2/design-elements/BasePage.tsx b/client/src/Components/design-elements/BasePage.tsx similarity index 97% rename from client/src/Components/v2/design-elements/BasePage.tsx rename to client/src/Components/design-elements/BasePage.tsx index 48c836e54..cf28da40f 100644 --- a/client/src/Components/v2/design-elements/BasePage.tsx +++ b/client/src/Components/design-elements/BasePage.tsx @@ -8,8 +8,8 @@ import { EmptyFallback, EmptyMonitorFallback, BaseFallback, -} from "@/Components/v2/design-elements/Fallback"; -import { Breadcrumb } from "@/Components/v2/design-elements/Breadcrumb"; +} from "@/Components/design-elements/Fallback"; +import { Breadcrumb } from "@/Components/design-elements/Breadcrumb"; import CircularProgress from "@mui/material/CircularProgress"; import { HeaderAuthControls } from "@/Pages/Auth/components/HeaderAuthControls"; diff --git a/client/src/Components/v2/design-elements/Breadcrumb.tsx b/client/src/Components/design-elements/Breadcrumb.tsx similarity index 100% rename from client/src/Components/v2/design-elements/Breadcrumb.tsx rename to client/src/Components/design-elements/Breadcrumb.tsx diff --git a/client/src/Components/v2/design-elements/BulletPointCheck.tsx b/client/src/Components/design-elements/BulletPointCheck.tsx similarity index 100% rename from client/src/Components/v2/design-elements/BulletPointCheck.tsx rename to client/src/Components/design-elements/BulletPointCheck.tsx diff --git a/client/src/Components/v2/design-elements/Dot.tsx b/client/src/Components/design-elements/Dot.tsx similarity index 100% rename from client/src/Components/v2/design-elements/Dot.tsx rename to client/src/Components/design-elements/Dot.tsx diff --git a/client/src/Components/v2/design-elements/Fallback.tsx b/client/src/Components/design-elements/Fallback.tsx similarity index 93% rename from client/src/Components/v2/design-elements/Fallback.tsx rename to client/src/Components/design-elements/Fallback.tsx index 16eece25a..93cdac410 100644 --- a/client/src/Components/v2/design-elements/Fallback.tsx +++ b/client/src/Components/design-elements/Fallback.tsx @@ -1,7 +1,7 @@ import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; -import { BulletPointCheck, SkeletonCard } from "@/Components/v2/design-elements"; -import { Button } from "@/Components/v2/inputs"; +import { BulletPointCheck, SkeletonCard } from "@/Components/design-elements"; +import { Button } from "@/Components/inputs"; import { useNavigate } from "react-router"; import useMediaQuery from "@mui/material/useMediaQuery"; @@ -85,12 +85,7 @@ export const EmptyFallback = ({ zIndex={1} alignItems="center" > - - {title} - + {title} { const theme = useTheme(); @@ -19,7 +19,6 @@ export const MonitorStatus = ({ monitor }: { monitor: Monitor }) => { {linkText} diff --git a/client/src/Components/v2/design-elements/Tooltip.tsx b/client/src/Components/design-elements/Tooltip.tsx similarity index 100% rename from client/src/Components/v2/design-elements/Tooltip.tsx rename to client/src/Components/design-elements/Tooltip.tsx diff --git a/client/src/Components/v2/design-elements/index.tsx b/client/src/Components/design-elements/index.tsx similarity index 100% rename from client/src/Components/v2/design-elements/index.tsx rename to client/src/Components/design-elements/index.tsx diff --git a/client/src/Components/v2/i18nLoader/index.tsx b/client/src/Components/i18nLoader/index.tsx similarity index 100% rename from client/src/Components/v2/i18nLoader/index.tsx rename to client/src/Components/i18nLoader/index.tsx diff --git a/client/src/Components/v2/inputs/AutoComplete.tsx b/client/src/Components/inputs/AutoComplete.tsx similarity index 97% rename from client/src/Components/v2/inputs/AutoComplete.tsx rename to client/src/Components/inputs/AutoComplete.tsx index af0e8cbfd..c98e8ff32 100644 --- a/client/src/Components/v2/inputs/AutoComplete.tsx +++ b/client/src/Components/inputs/AutoComplete.tsx @@ -1,6 +1,6 @@ import Autocomplete from "@mui/material/Autocomplete"; import type { AutocompleteProps } from "@mui/material/Autocomplete"; -import { TextField, Checkbox } from "@/Components/v2/inputs"; +import { TextField, Checkbox } from "@/Components/inputs"; import ListItem from "@mui/material/ListItem"; import Stack from "@mui/material/Stack"; import { useTheme } from "@mui/material/styles"; diff --git a/client/src/Components/v2/inputs/Button.tsx b/client/src/Components/inputs/Button.tsx similarity index 100% rename from client/src/Components/v2/inputs/Button.tsx rename to client/src/Components/inputs/Button.tsx diff --git a/client/src/Components/v2/inputs/Checkbox.tsx b/client/src/Components/inputs/Checkbox.tsx similarity index 100% rename from client/src/Components/v2/inputs/Checkbox.tsx rename to client/src/Components/inputs/Checkbox.tsx diff --git a/client/src/Components/v2/inputs/ColorPicker.tsx b/client/src/Components/inputs/ColorPicker.tsx similarity index 89% rename from client/src/Components/v2/inputs/ColorPicker.tsx rename to client/src/Components/inputs/ColorPicker.tsx index f52e66317..de736c85d 100644 --- a/client/src/Components/v2/inputs/ColorPicker.tsx +++ b/client/src/Components/inputs/ColorPicker.tsx @@ -1,9 +1,9 @@ import { MuiColorInput } from "mui-color-input"; import type { MuiColorInputProps } from "mui-color-input"; -import { typographyLevels } from "@/Utils/Theme/v2Palette"; +import { typographyLevels } from "@/Utils/Theme/Palette"; import { useTheme } from "@mui/material"; import Stack from "@mui/material/Stack"; -import { FieldLabel } from "@/Components/v2/inputs/FieldLabel"; +import { FieldLabel } from "@/Components/inputs/FieldLabel"; interface ColorPickerProps extends MuiColorInputProps { fieldLabel?: string; diff --git a/client/src/Components/v2/inputs/DatePicker.tsx b/client/src/Components/inputs/DatePicker.tsx similarity index 100% rename from client/src/Components/v2/inputs/DatePicker.tsx rename to client/src/Components/inputs/DatePicker.tsx diff --git a/client/src/Components/v2/inputs/Dialog.tsx b/client/src/Components/inputs/Dialog.tsx similarity index 94% rename from client/src/Components/v2/inputs/Dialog.tsx rename to client/src/Components/inputs/Dialog.tsx index b581e7390..b0c924f55 100644 --- a/client/src/Components/v2/inputs/Dialog.tsx +++ b/client/src/Components/inputs/Dialog.tsx @@ -4,8 +4,8 @@ import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; -import { Button } from "@/Components/v2/inputs"; -import { typographyLevels } from "@/Utils/Theme/v2Palette"; +import { Button } from "@/Components/inputs"; +import { typographyLevels } from "@/Utils/Theme/Palette"; import { useTranslation } from "react-i18next"; import type { ReactNode } from "react"; diff --git a/client/src/Components/v2/inputs/FieldLabel.tsx b/client/src/Components/inputs/FieldLabel.tsx similarity index 100% rename from client/src/Components/v2/inputs/FieldLabel.tsx rename to client/src/Components/inputs/FieldLabel.tsx diff --git a/client/src/Components/v2/inputs/ImageUpload.tsx b/client/src/Components/inputs/ImageUpload.tsx similarity index 100% rename from client/src/Components/v2/inputs/ImageUpload.tsx rename to client/src/Components/inputs/ImageUpload.tsx diff --git a/client/src/Components/v2/inputs/LanguageSelector.tsx b/client/src/Components/inputs/LanguageSelector.tsx similarity index 97% rename from client/src/Components/v2/inputs/LanguageSelector.tsx rename to client/src/Components/inputs/LanguageSelector.tsx index 2a1274ef2..a6d52a555 100644 --- a/client/src/Components/v2/inputs/LanguageSelector.tsx +++ b/client/src/Components/inputs/LanguageSelector.tsx @@ -1,7 +1,7 @@ import Stack from "@mui/material/Stack"; import Box from "@mui/material/Box"; import MenuItem from "@mui/material/MenuItem"; -import { Select } from "@/Components/v2/inputs"; +import { Select } from "@/Components/inputs"; import "flag-icons/css/flag-icons.min.css"; import { useTranslation } from "react-i18next"; diff --git a/client/src/Components/v2/inputs/Radio.tsx b/client/src/Components/inputs/Radio.tsx similarity index 100% rename from client/src/Components/v2/inputs/Radio.tsx rename to client/src/Components/inputs/Radio.tsx diff --git a/client/src/Components/v2/inputs/Select.tsx b/client/src/Components/inputs/Select.tsx similarity index 100% rename from client/src/Components/v2/inputs/Select.tsx rename to client/src/Components/inputs/Select.tsx diff --git a/client/src/Components/v2/inputs/Slider.tsx b/client/src/Components/inputs/Slider.tsx similarity index 100% rename from client/src/Components/v2/inputs/Slider.tsx rename to client/src/Components/inputs/Slider.tsx diff --git a/client/src/Components/v2/inputs/Switch.tsx b/client/src/Components/inputs/Switch.tsx similarity index 100% rename from client/src/Components/v2/inputs/Switch.tsx rename to client/src/Components/inputs/Switch.tsx diff --git a/client/src/Components/v2/inputs/SwitchTheme.tsx b/client/src/Components/inputs/SwitchTheme.tsx similarity index 100% rename from client/src/Components/v2/inputs/SwitchTheme.tsx rename to client/src/Components/inputs/SwitchTheme.tsx diff --git a/client/src/Components/v2/inputs/TextInput.tsx b/client/src/Components/inputs/TextInput.tsx similarity index 95% rename from client/src/Components/v2/inputs/TextInput.tsx rename to client/src/Components/inputs/TextInput.tsx index 9a16a43c7..718b60327 100644 --- a/client/src/Components/v2/inputs/TextInput.tsx +++ b/client/src/Components/inputs/TextInput.tsx @@ -1,7 +1,7 @@ import { forwardRef } from "react"; import TextField from "@mui/material/TextField"; import type { TextFieldProps } from "@mui/material"; -import { typographyLevels } from "@/Utils/Theme/v2Palette"; +import { typographyLevels } from "@/Utils/Theme/Palette"; import { useTheme } from "@mui/material/styles"; import Stack from "@mui/material/Stack"; import { FieldLabel } from "./FieldLabel"; diff --git a/client/src/Components/v2/inputs/TimePicker.tsx b/client/src/Components/inputs/TimePicker.tsx similarity index 100% rename from client/src/Components/v2/inputs/TimePicker.tsx rename to client/src/Components/inputs/TimePicker.tsx diff --git a/client/src/Components/v2/inputs/ToggleButton.tsx b/client/src/Components/inputs/ToggleButton.tsx similarity index 100% rename from client/src/Components/v2/inputs/ToggleButton.tsx rename to client/src/Components/inputs/ToggleButton.tsx diff --git a/client/src/Components/v2/inputs/index.tsx b/client/src/Components/inputs/index.tsx similarity index 100% rename from client/src/Components/v2/inputs/index.tsx rename to client/src/Components/inputs/index.tsx diff --git a/client/src/Components/v2/layout/AppLayout.tsx b/client/src/Components/layout/AppLayout.tsx similarity index 74% rename from client/src/Components/v2/layout/AppLayout.tsx rename to client/src/Components/layout/AppLayout.tsx index 7c9c3c903..baadd85ca 100644 --- a/client/src/Components/v2/layout/AppLayout.tsx +++ b/client/src/Components/layout/AppLayout.tsx @@ -1,11 +1,10 @@ import Box from "@mui/material/Box"; import { useState, useEffect, useRef } from "react"; -import { ThemeProvider, useTheme } from "@mui/material/styles"; +import { useTheme } from "@mui/material/styles"; import { useSelector } from "react-redux"; import BackgroundSVG from "@/assets/Images/background.svg"; import type { RootState } from "@/Types/state"; -import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme"; -import { OfflineBanner } from "@/Components/v2/design-elements"; +import { OfflineBanner } from "@/Components/design-elements"; import { setServerUnreachableCallback, get } from "@/Utils/ApiClient"; interface AppLayoutProps { @@ -15,8 +14,6 @@ interface AppLayoutProps { const AppLayout = ({ children }: AppLayoutProps) => { const theme = useTheme(); const mode = useSelector((state: RootState) => state.ui.mode); - const v2theme = mode === "dark" ? darkTheme : lightTheme; - const [serverUnreachable, setServerUnreachable] = useState(false); const retryIntervalRef = useRef(null); @@ -49,18 +46,14 @@ const AppLayout = ({ children }: AppLayoutProps) => { - - - + {children} ); diff --git a/client/src/Components/v2/layout/RootLayout.tsx b/client/src/Components/layout/RootLayout.tsx similarity index 66% rename from client/src/Components/v2/layout/RootLayout.tsx rename to client/src/Components/layout/RootLayout.tsx index 0b6defb91..c22bd7455 100644 --- a/client/src/Components/v2/layout/RootLayout.tsx +++ b/client/src/Components/layout/RootLayout.tsx @@ -1,26 +1,19 @@ -import { Sidebar } from "@/Components/v2/sidebar"; +import { Sidebar } from "@/Components/sidebar"; import { Outlet } from "react-router"; import Stack from "@mui/material/Stack"; import { useMediaQuery } from "@mui/material"; import { useSidebar } from "@/Hooks/useSidebar"; -import { useSelector } from "react-redux"; -import type { RootState } from "@/Types/state"; -import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme"; -import { ThemeProvider, useTheme } from "@mui/material"; +import { useTheme } from "@mui/material"; const RootLayout = () => { - const mode = useSelector((state: RootState) => state.ui.mode); - const v2theme = mode === "dark" ? darkTheme : lightTheme; const theme = useTheme(); const isSmall = useMediaQuery(theme.breakpoints.down("md")); const { collapsedWidth } = useSidebar(); return ( - - - + { - if (type === "double") { - return ( - - ); - } else { - return ( - - ); - } -}; - -ArrowRight.propTypes = { - type: PropTypes.oneOf(["double", "default"]), - color: PropTypes.string, -}; - -export default ArrowRight; diff --git a/client/src/Components/v1/Breadcrumbs/index.css b/client/src/Components/v1/Breadcrumbs/index.css deleted file mode 100644 index 022db9a83..000000000 --- a/client/src/Components/v1/Breadcrumbs/index.css +++ /dev/null @@ -1,21 +0,0 @@ -.MuiBreadcrumbs-root { - min-height: 34px; -} -.MuiBreadcrumbs-root svg { - width: 16px; - min-height: 16px; -} -.MuiBreadcrumbs-root .MuiBreadcrumbs-li a { - font-weight: 400; -} -.MuiBreadcrumbs-root .MuiBreadcrumbs-li:not(:last-child) { - cursor: pointer; -} -.MuiBreadcrumbs-root .MuiBreadcrumbs-li:last-child a { - font-weight: 500; - opacity: 1; - cursor: default; -} -.MuiBreadcrumbs-root .MuiBreadcrumbs-separator { - margin: 0; -} diff --git a/client/src/Components/v1/Breadcrumbs/index.jsx b/client/src/Components/v1/Breadcrumbs/index.jsx deleted file mode 100644 index 34e5d5e95..000000000 --- a/client/src/Components/v1/Breadcrumbs/index.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import PropTypes from "prop-types"; -import { Box, Breadcrumbs as MUIBreadcrumbs } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import { useNavigate } from "react-router-dom"; -import ArrowRight from "../ArrowRight/index.jsx"; -import "./index.css"; - -/** - * Breadcrumbs component that displays a list of breadcrumb items. - * - * @param {Object} props - * @param {Array} props.list - Array of breadcrumb items. Each item should have `name` and `path` properties. - * @param {string} props.list.name - The name to display for the breadcrumb. - * @param {string} props.list.path - The path to navigate to when the breadcrumb is clicked. - * - * @returns {JSX.Element} The rendered Breadcrumbs component. - */ - -const Breadcrumbs = ({ list }) => { - const theme = useTheme(); - const navigate = useNavigate(); - - return ( - } - aria-label="breadcrumb" - px={theme.spacing(2)} - py={theme.spacing(3.5)} - width="fit-content" - backgroundColor={theme.palette.secondary.main} - borderRadius={theme.shape.borderRadius} - lineHeight="18px" - sx={{ - "& .MuiBreadcrumbs-li a": { - transition: "background-color 0.2s ease-in-out, color 0.2s ease-in-out", - }, - "& .MuiBreadcrumbs-li:not(:last-of-type):hover a": { - backgroundColor: theme.palette.secondary.contrastText, - color: theme.palette.secondary.main, - }, - }} - > - {list.map((item, index) => { - return ( - navigate(item.path)} - sx={{ - opacity: 0.8, - textTransform: "capitalize", - "&, &:hover": { - color: theme.palette.secondary.contrastText, - }, - }} - > - {item.name} - - ); - })} - - ); -}; - -Breadcrumbs.propTypes = { - list: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - }).isRequired - ).isRequired, -}; - -export default Breadcrumbs; diff --git a/client/src/Components/v1/ConfigBox/index.jsx b/client/src/Components/v1/ConfigBox/index.jsx deleted file mode 100644 index 01b35729e..000000000 --- a/client/src/Components/v1/ConfigBox/index.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Stack, styled } from "@mui/material"; - -const ConfigBox = styled(Stack)(({ theme }) => ({ - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - backgroundColor: theme.palette.primary.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.spacing(2), - "& > *": { - paddingTop: theme.spacing(15), - paddingBottom: theme.spacing(15), - }, - "& > div:first-of-type": { - flex: 0.7, - borderRight: 1, - borderRightStyle: "solid", - borderRightColor: theme.palette.primary.lowContrast, - paddingRight: theme.spacing(15), - paddingLeft: theme.spacing(15), - backgroundColor: theme.palette.tertiary.background, - "& :is(h1, h2):first-of-type": { - fontWeight: 600, - marginBottom: theme.spacing(4), - }, - }, - "& > div:last-of-type": { - flex: 1, - paddingRight: theme.spacing(20), - paddingLeft: theme.spacing(20), - }, - "& h1, & h2": { - color: theme.palette.primary.contrastTextSecondary, - }, -})); - -export default ConfigBox; diff --git a/client/src/Components/v1/Dialog/genericDialog.jsx b/client/src/Components/v1/Dialog/genericDialog.jsx deleted file mode 100644 index a13eba849..000000000 --- a/client/src/Components/v1/Dialog/genericDialog.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useId } from "react"; -import PropTypes from "prop-types"; -import { Modal, Stack, Typography } from "@mui/material"; - -const GenericDialog = ({ title, description, open, onClose, theme, children, width }) => { - const titleId = useId(); - const descriptionId = useId(); - const ariaDescribedBy = description?.length > 0 ? descriptionId : ""; - return ( - e.stopPropagation()} - > - - - {title} - - {description && ( - - {description} - - )} - {children} - - - ); -}; - -GenericDialog.propTypes = { - title: PropTypes.string.isRequired, - description: PropTypes.string, - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - theme: PropTypes.object.isRequired, - children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]) - .isRequired, - width: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]), -}; - -export { GenericDialog }; diff --git a/client/src/Components/v1/Dialog/index.jsx b/client/src/Components/v1/Dialog/index.jsx deleted file mode 100644 index b8cf07439..000000000 --- a/client/src/Components/v1/Dialog/index.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import PropTypes from "prop-types"; -import { Button, Stack } from "@mui/material"; -import { GenericDialog } from "./genericDialog.jsx"; -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; - -const Dialog = ({ - title, - description, - open, - onCancel, - confirmationButtonLabel, - onConfirm, - isLoading, -}) => { - const theme = useTheme(); - const { t } = useTranslation(); - - return ( - - - - - - - ); -}; - -Dialog.propTypes = { - title: PropTypes.string.isRequired, - description: PropTypes.string, - open: PropTypes.bool.isRequired, - onCancel: PropTypes.func.isRequired, - confirmationButtonLabel: PropTypes.string.isRequired, - onConfirm: PropTypes.func.isRequired, - isLoading: PropTypes.bool, -}; - -export default Dialog; diff --git a/client/src/Components/v1/Icon/iconMap.js b/client/src/Components/v1/Icon/iconMap.js deleted file mode 100644 index 58da7304b..000000000 --- a/client/src/Components/v1/Icon/iconMap.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Maps legacy icon names to Lucide icon names - * Use: import { iconMap } from './iconMap'; LucideIcons[iconMap['monitors']] - */ -export const iconMap = { - // Navigation/Sidebar icons - monitors: "Globe", - incidents: "AlertTriangle", - integrations: "Link", - "page-speed": "Gauge", - settings: "Settings", - notifications: "Bell", - maintenance: "Wrench", - "status-pages": "Wifi", - docs: "FileText", - discussions: "MessageCircle", - support: "HelpCircle", - changeLog: "Code", - logs: "Database", - - // User icons - user: "User", - "user-two": "Users", - "user-edit": "UserPen", - groups: "Users", - lock: "Lock", - key: "Key", - logout: "LogOut", - - // Action icons - edit: "Pencil", - "trash-bin": "Trash2", - search: "Search", - "close-icon": "X", - check: "Check", - "check-icon": "CheckCircle", - "check-outlined": "CheckCircle", - - // Arrow icons - "left-arrow": "ChevronLeft", - "right-arrow": "ChevronRight", - "up-arrow": "ChevronUp", - "down-arrow": "ChevronDown", - "left-arrow-double": "ChevronsLeft", - "right-arrow-double": "ChevronsRight", - "left-arrow-long": "ArrowLeft", - "top-right-arrow": "ArrowUpRight", - "open-in-new-page": "ExternalLink", - "selector-vertical": "ChevronsUpDown", - - // Status/Alert icons - "alert-icon": "Bell", - "warning-icon": "AlertCircle", - "history-icon": "History", - - // Data/Chart icons - dashboard: "LayoutGrid", - "monitor-graph-line": "TrendingUp", - "response-time-icon": "TrendingUp", - "uptime-icon": "Activity", - "speedometer-icon": "Gauge", - "spedometer-icon": "Gauge", - "ruler-icon": "Ruler", - "performance-report": "Layers", - "average-response-icon": "BarChart3", - "distributed-uptime": "Network", - - // Misc icons - calendar: "Calendar", - "calendar-check": "CalendarCheck", - "clock-snooze": "Clock", - mail: "Mail", - email: "Mail", - folder: "Folder", - "dots-vertical": "MoreVertical", - "pause-icon": "PauseCircle", - "resume-icon": "PlayCircle", - "checkbox-filled": "CheckSquare", - "checkbox-outline": "Square", - "radio-checked": "Circle", - "cpu-chip": "Cpu", - certificate: "Award", - "interval-check": "RefreshCw", -}; - -/** - * MUI icon to Lucide mapping - */ -export const muiToLucideMap = { - AddCircleOutline: "PlusCircle", - ArrowDownwardRounded: "ArrowDown", - ArrowUpwardRounded: "ArrowUp", - ArrowDropDown: "ChevronDown", - ArrowOutward: "ExternalLink", - CheckCircle: "CheckCircle2", - Clear: "X", - Close: "X", - CloseRounded: "X", - CloudUpload: "Upload", - Delete: "Trash2", - DeleteOutlineRounded: "Trash2", - Email: "Mail", - ErrorOutline: "AlertTriangle", - ErrorOutlineOutlined: "AlertTriangle", - Image: "ImageIcon", - InfoOutlined: "Info", - KeyboardArrowDown: "ChevronDown", - Menu: "Menu", - PauseCircleOutline: "PauseCircle", - PauseOutlined: "Pause", - PlayArrowOutlined: "Play", - PlayCircleOutlineRounded: "PlayCircle", - RadioButtonChecked: "Circle", - ReorderRounded: "GripVertical", - SettingsOutlined: "Settings", - Share: "Share2", - Visibility: "Eye", - VisibilityOff: "EyeOff", - WarningAmberOutlined: "AlertTriangle", -}; diff --git a/client/src/Components/v1/Icon/index.jsx b/client/src/Components/v1/Icon/index.jsx deleted file mode 100644 index 9f7c61585..000000000 --- a/client/src/Components/v1/Icon/index.jsx +++ /dev/null @@ -1,198 +0,0 @@ -import { useTheme } from "@mui/material"; -import PropTypes from "prop-types"; -import { - Activity, - AlertCircle, - AlertTriangle, - ArrowDown, - ArrowLeft, - ArrowUp, - ArrowUpRight, - Bell, - Calendar, - Check, - CheckCircle, - CheckCircle2, - CheckSquare, - ChevronDown, - ChevronLeft, - ChevronRight, - ChevronsLeft, - ChevronsRight, - ChevronsUpDown, - Circle, - CircleDot, - Clock, - Code, - Cpu, - Database, - ExternalLink, - Eye, - EyeOff, - FileText, - Gauge, - Globe, - GripVertical, - HelpCircle, - History, - Image as ImageIcon, - Info, - Key, - Layers, - Link, - Lock, - LogOut, - Mail, - Menu, - MessageCircle, - MoreVertical, - Pause, - PauseCircle, - Play, - PlayCircle, - PlusCircle, - RefreshCw, - Ruler, - Search, - Settings, - Square, - Trash2, - TrendingUp, - Upload, - User, - Users, - Wifi, - Wrench, - X, -} from "lucide-react"; - -/** - * Map of icon names to Lucide icon components. - * Only icons explicitly imported here will be included in the bundle (tree-shaking). - * To add a new icon: import it above and add it to this object. - */ -const iconComponents = { - Activity, - AlertCircle, - AlertTriangle, - ArrowDown, - ArrowLeft, - ArrowUp, - ArrowUpRight, - Bell, - Calendar, - Check, - CheckCircle, - CheckCircle2, - CheckSquare, - ChevronDown, - ChevronLeft, - ChevronRight, - ChevronsLeft, - ChevronsRight, - ChevronsUpDown, - Circle, - CircleDot, - Clock, - Code, - Cpu, - Database, - ExternalLink, - Eye, - EyeOff, - FileText, - Gauge, - Globe, - GripVertical, - HelpCircle, - History, - Image: ImageIcon, - Info, - Key, - Layers, - Link, - Lock, - LogOut, - Mail, - Menu, - MessageCircle, - MoreVertical, - Pause, - PauseCircle, - Play, - PlayCircle, - PlusCircle, - RefreshCw, - Ruler, - Search, - Settings, - Square, - Trash2, - TrendingUp, - Upload, - User, - Users, - Wifi, - Wrench, - X, -}; - -/** - * Theme-aware icon component wrapping Lucide icons - * - * @param {string} name - Lucide icon name (e.g., "Bell", "Check", "ArrowLeft") - * @param {number} size - Icon size in pixels (default: 20) - * @param {string} color - Direct color value OR theme path like "primary.contrastText" - * @param {number} strokeWidth - Stroke width (default: 1.5) - * @param {string} fill - Fill color (default: "none") - */ -const Icon = ({ name, size = 20, color, strokeWidth = 1.5, fill = "none", ...props }) => { - const theme = useTheme(); - - // Resolve color from theme path or use direct value - const resolveColor = (colorValue) => { - if (!colorValue) { - return theme.palette.primary.contrastTextTertiary; // Default icon color - } - - // If it's a theme path like "primary.contrastText" - if (typeof colorValue === "string" && colorValue.includes(".")) { - const parts = colorValue.split("."); - let resolved = theme.palette; - for (const part of parts) { - resolved = resolved?.[part]; - } - return resolved || colorValue; - } - - // Direct color value - return colorValue; - }; - - const LucideIcon = iconComponents[name]; - - if (!LucideIcon) { - console.warn(`Icon "${name}" not found in Icon component`); - return null; - } - - return ( - - ); -}; - -Icon.propTypes = { - name: PropTypes.string.isRequired, - size: PropTypes.number, - color: PropTypes.string, - strokeWidth: PropTypes.number, - fill: PropTypes.string, -}; - -export default Icon; diff --git a/client/src/Components/v1/Inputs/Checkbox/index.jsx b/client/src/Components/v1/Inputs/Checkbox/index.jsx deleted file mode 100644 index caa6830e7..000000000 --- a/client/src/Components/v1/Inputs/Checkbox/index.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import PropTypes from "prop-types"; -import { FormControlLabel, Checkbox as MuiCheckbox } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import Icon from "../../Icon"; - -/** - * Checkbox Component - * - * A customized checkbox component using Material-UI that supports custom sizing, - * disabled states, and custom icons. - * - * @component - * @param {Object} props - Component properties - * @param {string} props.id - Unique identifier for the checkbox input - * @param {string} [props.name] - Optional name attribute for the checkbox - * @param {(string|React.ReactNode)} props.label - Label text or node for the checkbox - * @param {('small'|'medium'|'large')} [props.size='medium'] - Size of the checkbox icon - * @param {boolean} props.isChecked - Current checked state of the checkbox - * @param {string} [props.value] - Optional value associated with the checkbox - * @param {Function} [props.onChange] - Callback function triggered when checkbox state changes - * @param {boolean} [props.isDisabled] - Determines if the checkbox is disabled - * - * @returns {React.ReactElement} Rendered Checkbox component - * - * @example - * // Basic usage - * - * - * @example - * // With custom size and disabled state - * - */ -const Checkbox = ({ - id, - name, - label, - size = "medium", - isChecked, - value, - onChange, - isDisabled, -}) => { - /* TODO move sizes to theme */ - const sizes = { small: "14px", medium: "16px", large: "18px" }; - const theme = useTheme(); - return ( - } - checkedIcon={} - inputProps={{ - "aria-label": "controlled checkbox", - id: id, - }} - sx={{ - "&:hover": { backgroundColor: "transparent" }, - "& svg": { width: sizes[size], height: sizes[size] }, - }} - /> - } - label={label} - disabled={isDisabled} - sx={{ - borderRadius: theme.shape.borderRadius, - p: theme.spacing(2.5), - "& .MuiButtonBase-root": { - width: theme.spacing(10), - p: 0, - mr: theme.spacing(6), - }, - "&:not(:has(.Mui-disabled)):hover": { - backgroundColor: theme.palette.tertiary.main, - }, - "& span.MuiTypography-root": { - fontSize: 13, - color: theme.palette.primary.contrastTextTertiary, - }, - ".MuiFormControlLabel-label.Mui-disabled": { - color: theme.palette.primary.contrastTextTertiary, - opacity: 0.25, - }, - }} - /> - ); -}; - -Checkbox.propTypes = { - id: PropTypes.string, - name: PropTypes.string, - label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, - size: PropTypes.oneOf(["small", "medium", "large"]), - isChecked: PropTypes.bool.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), - onChange: PropTypes.func, - isDisabled: PropTypes.bool, -}; - -export default Checkbox; diff --git a/client/src/Components/v1/Inputs/ColorPicker/index.jsx b/client/src/Components/v1/Inputs/ColorPicker/index.jsx deleted file mode 100644 index e64c936fc..000000000 --- a/client/src/Components/v1/Inputs/ColorPicker/index.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import PropTypes from "prop-types"; -import { Stack, Typography } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import { MuiColorInput } from "mui-color-input"; - -/** - * - * @param {*} id The ID of the component - * @param {*} value The color value of the component - * @param {*} error The error of the component - * @param {*} onChange The Change handler function - * @param {*} onBlur The Blur handler function - * @returns The ColorPicker component - * Example usage: - * - * - */ -const ColorPicker = ({ id, name, value, error, onChange, onBlur }) => { - const theme = useTheme(); - return ( - - onChange({ target: { name, value: color } })} - onBlur={onBlur} - /> - {error && ( - - {error} - - )} - - ); -}; - -ColorPicker.propTypes = { - id: PropTypes.string.isRequired, - value: PropTypes.string, - error: PropTypes.string, - onChange: PropTypes.func.isRequired, - onBlur: PropTypes.func, - name: PropTypes.string, -}; - -export default ColorPicker; diff --git a/client/src/Components/v1/Inputs/FieldWrapper/index.jsx b/client/src/Components/v1/Inputs/FieldWrapper/index.jsx deleted file mode 100644 index cc5061af8..000000000 --- a/client/src/Components/v1/Inputs/FieldWrapper/index.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Stack, Typography } from "@mui/material"; -import PropTypes from "prop-types"; -import { useTheme } from "@emotion/react"; - -const DEFAULT_GAP = 6; -const FieldWrapper = ({ - label, - children, - gap, - labelMb, - labelFontWeight = 500, - labelVariant = "h3", - labelSx = {}, - sx = {}, -}) => { - const theme = useTheme(); - return ( - - {label && ( - - {label} - - )} - {children} - - ); -}; - -FieldWrapper.propTypes = { - label: PropTypes.node, - children: PropTypes.node.isRequired, - gap: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]), - labelMb: PropTypes.number, - labelFontWeight: PropTypes.number, - labelVariant: PropTypes.string, - labelSx: PropTypes.object, - sx: PropTypes.object, -}; - -export default FieldWrapper; diff --git a/client/src/Components/v1/Inputs/ImageUpload/index.jsx b/client/src/Components/v1/Inputs/ImageUpload/index.jsx deleted file mode 100644 index d224418e0..000000000 --- a/client/src/Components/v1/Inputs/ImageUpload/index.jsx +++ /dev/null @@ -1,249 +0,0 @@ -// Components -import { Box, Stack, Typography } from "@mui/material"; -import Icon from "../../Icon"; -import Image from "../../Image/index.jsx"; -import TextField from "@mui/material/TextField"; -import IconButton from "@mui/material/IconButton"; -import ProgressUpload from "../../ProgressBars/index.jsx"; - -// Utils -import PropTypes from "prop-types"; -import { useCallback, useState, useRef, useEffect } from "react"; -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; - -/** - * ImageUpload component allows users to upload images with drag-and-drop functionality. - * It supports file size and format validation. - * - * @component - * @param {Object} props - Component props - * @param {boolean} [props.previewIsRound=false] - Determines if the image preview should be round - * @param {string} [props.src] - Source URL of the image to display - * @param {function} props.onChange - Callback function to handle file change, takes a file as an argument - * @param {number} [props.maxSize=3145728] - Maximum file size allowed in bytes (default is 3MB) - * @param {Array} [props.accept=['jpg', 'jpeg', 'png']] - Array of accepted file formats - * @param {Object} [props.errors] - Object containing error messages - * @returns {JSX.Element} The rendered component - */ -const ImageUpload = ({ - previewIsRound = false, - src, - onChange, - maxSize = 3 * 1024 * 1024, - accept = ["jpg", "jpeg", "png"], - error, -}) => { - const theme = useTheme(); - const { t } = useTranslation(); - const [uploadComplete, setUploadComplete] = useState(false); - const [completedFile, setCompletedFile] = useState(null); - const [file, setFile] = useState(null); - const [progress, setProgress] = useState({ value: 0, isLoading: false }); - const intervalRef = useRef(null); - const [localError, setLocalError] = useState(null); - const [isDragging, setIsDragging] = useState(false); - - const roundStyle = previewIsRound ? { borderRadius: "50%" } : {}; - - const handleImageChange = useCallback( - (file) => { - if (!file) return; - - const isValidType = accept.some((type) => file.type.includes(type)); - const isValidSize = file.size <= maxSize; - - if (!isValidType) { - setLocalError(t("invalidFileFormat")); - return; - } - if (!isValidSize) { - setLocalError(t("invalidFileSize")); - return; - } - - setLocalError(null); - - const previewFile = { - src: URL.createObjectURL(file), - name: file.name, - file, - }; - - setFile(previewFile); - setProgress({ value: 0, isLoading: true }); - - intervalRef.current = setInterval(() => { - setProgress((prev) => { - const buffer = 12; - if (prev.value + buffer >= 100) { - clearInterval(intervalRef.current); - setUploadComplete(true); - setCompletedFile(previewFile); - return { value: 100, isLoading: false }; - } - return { value: prev.value + buffer, isLoading: true }; - }); - }, 120); - }, - [maxSize, accept] - ); - - useEffect(() => { - if (uploadComplete && completedFile) { - onChange?.(completedFile); - setUploadComplete(false); - setCompletedFile(null); - } - }, [uploadComplete, completedFile, onChange]); - - return ( - <> - {src ? ( - - Uploaded preview - - ) : ( - <> - setIsDragging(true)} - onDragLeave={() => setIsDragging(false)} - onDrop={() => setIsDragging(false)} - sx={{ - position: "relative", - height: "fit-content", - border: "dashed", - borderRadius: theme.shape.borderRadius, - borderColor: isDragging - ? theme.palette.primary.main - : theme.palette.primary.lowContrast, - backgroundColor: isDragging ? "hsl(215, 87%, 51%, 0.05)" : "transparent", - borderWidth: "2px", - transition: "0.2s", - "&:hover": { - borderColor: theme.palette.primary.main, - backgroundColor: "hsl(215, 87%, 51%, 0.05)", - }, - }} - > - handleImageChange(e?.target?.files?.[0])} - sx={{ - width: "100%", - "& .MuiInputBase-input[type='file']": { - opacity: 0, - cursor: "pointer", - maxWidth: "500px", - minHeight: "175px", - zIndex: 1, - }, - "& fieldset": { - padding: 0, - border: "none", - }, - }} - /> - - - - - - - {t("ClickUpload")} - {" "} - or {t("DragandDrop")} - - - ({t("MaxSize")}: {Math.round(maxSize / 1024 / 1024)}MB) - - - - {(localError || progress.isLoading || progress.value !== 0) && ( - - } - label={file?.name || "Upload failed"} - size={file?.size} - progress={progress.value} - onClick={() => { - clearInterval(intervalRef.current); - setFile(null); - setProgress({ value: 0, isLoading: false }); - setLocalError(null); - onChange(undefined); - }} - error={localError || error} - /> - )} - - {t("SupportedFormats")}: {accept.join(", ").toUpperCase()} - - - )} - - ); -}; - -ImageUpload.propTypes = { - previewIsRound: PropTypes.bool, - src: PropTypes.string, - onChange: PropTypes.func, - maxSize: PropTypes.number, - accept: PropTypes.array, - error: PropTypes.string, -}; - -export default ImageUpload; diff --git a/client/src/Components/v1/Inputs/Radio/index.css b/client/src/Components/v1/Inputs/Radio/index.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/src/Components/v1/Inputs/Radio/index.jsx b/client/src/Components/v1/Inputs/Radio/index.jsx deleted file mode 100644 index b6bd4e69c..000000000 --- a/client/src/Components/v1/Inputs/Radio/index.jsx +++ /dev/null @@ -1,117 +0,0 @@ -import PropTypes from "prop-types"; -import { FormControlLabel, Radio as MUIRadio, Typography } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import Icon from "../../Icon"; -import "./index.css"; - -/** - * Radio component. - * - * @component - * @example - * // Usage: - * - * - * @param {Object} props - The component - * @param {string} id - The id of the radio button. - * @param {string} title - The title of the radio button. - * @param {string} [desc] - The description of the radio button. - * @param {string} [size="small"] - The size of the radio button. - * @returns {JSX.Element} - The rendered Radio component. - */ - -const Radio = ({ - name, - checked, - value, - id, - size, - title, - desc, - onChange, - labelSpacing, -}) => { - const theme = useTheme(); - - return ( - - } - sx={{ - color: "transparent", - width: 16, - height: 16, - boxShadow: `inset 0 0 0 1px ${theme.palette.secondary.main}`, - "&:not(.Mui-checked)": { - boxShadow: `inset 0 0 0 1px ${theme.palette.primary.contrastText}70`, // Use theme text color for the outline - }, - mt: theme.spacing(0.5), - }} - /> - } - onChange={onChange} - label={ - <> - - {title} - - - {desc} - - - } - labelPlacement="end" - sx={{ - alignItems: "flex-start", - p: theme.spacing(2.5), - m: theme.spacing(-2.5), - borderRadius: theme.shape.borderRadius, - "&:hover": { - backgroundColor: theme.palette.tertiary.main, - }, - "& .MuiButtonBase-root": { - p: 0, - mr: theme.spacing(6), - }, - }} - /> - ); -}; - -Radio.propTypes = { - title: PropTypes.string, - desc: PropTypes.string, - size: PropTypes.string, - name: PropTypes.string, - checked: PropTypes.bool, - value: PropTypes.string, - id: PropTypes.string, - onChange: PropTypes.func, -}; - -export default Radio; diff --git a/client/src/Components/v1/Inputs/Search/index.jsx b/client/src/Components/v1/Inputs/Search/index.jsx deleted file mode 100644 index 4371ef971..000000000 --- a/client/src/Components/v1/Inputs/Search/index.jsx +++ /dev/null @@ -1,315 +0,0 @@ -import PropTypes from "prop-types"; -import { - Box, - ListItem, - Autocomplete, - TextField, - Typography, - Checkbox, -} from "@mui/material"; -import { useTheme } from "@emotion/react"; -import Icon from "../../Icon"; -import React, { useEffect } from "react"; -import { useTranslation } from "react-i18next"; -import FieldWrapper from "../FieldWrapper/index.jsx"; - -/** - * Search component using Material UI's Autocomplete. - * - * @param {Object} props - * @param {string} props.id - Unique identifier for the Autocomplete component - * @param {Array} props.options - Options to display in the Autocomplete dropdown - * @param {string} props.filteredBy - Key to access the option label from the options - * @param {string} props.value - Current input value for the Autocomplete - * @param {Function} props.handleChange - Function to call when the input changes - * @param {Function} Prop.onBlur - Function to call when the input is blured - * @param {Object} props.sx - Additional styles to apply to the component - * @param {string} props.unit - Label to identify type of options - * @returns {JSX.Element} The rendered Search component - */ - -const SearchAdornment = () => { - const theme = useTheme(); - return ( - - - - ); -}; - -//TODO keep search state inside of component. -const Search = ({ - label, - id, - options, - filteredBy, - secondaryLabel, - value, - inputValue, - handleInputChange, - handleChange, - sx, - multiple = false, - isAdorned = true, - error, - disabled, - startAdornment, - endAdornment, - onBlur, - //FieldWrapper's props - gap, - labelMb, - labelFontWeight, - labelVariant, - labelSx = {}, - unit = "option", - maxWidth = "100%", -}) => { - const theme = useTheme(); - const { t } = useTranslation(); - const [selectAll, setSelectAll] = React.useState(false); - - const [open, setOpen] = React.useState(false); - const enhancedOptions = React.useMemo(() => { - return multiple && isAdorned - ? [ - { - [filteredBy]: t("selectAll"), - isSelectAll: true, - _id: "select_all", - id: "select_all", - }, - ...options, - ] - : options; - }, [multiple, isAdorned, options, filteredBy]); - const isOptionSelected = (option) => { - if (!multiple && !isAdorned) return false; - if (Array.isArray(value)) { - return value.some((item) => (item._id ?? item.id) === (option._id ?? option.id)); - } - return false; - }; - const handleSelectAll = (isSelectAll) => { - const newValue = isSelectAll ? [...options] : []; - handleChange(newValue); - setSelectAll(isSelectAll); - }; - useEffect(() => { - const allSelected = - Array.isArray(value) && Array.isArray(options) && value.length === options.length; - if (selectAll !== allSelected) setSelectAll(allSelected); - }, [value, options]); - return ( - setOpen(true)} - onClose={(event, reason) => { - if (reason === "blur" || reason === "escape") { - setOpen(false); - } - }} - inputValue={inputValue} - onInputChange={(_, newValue) => { - handleInputChange(newValue); - }} - onChange={(_, newValue) => { - if (multiple && isAdorned) { - const hasSelectAllSelected = - Array.isArray(newValue) && newValue.some((item) => item.isSelectAll); - if (hasSelectAllSelected) { - handleSelectAll(!selectAll); - } else { - handleChange(newValue); - setSelectAll(Array.isArray(newValue) && newValue.length === options.length); - } - } else { - handleChange(newValue); - setOpen(false); - } - }} - fullWidth - freeSolo - disabled={disabled} - disableClearable - options={enhancedOptions} - getOptionLabel={(option) => option[filteredBy]} - isOptionEqualToValue={(option, value) => - (option._id ?? option.id) === (value?._id ?? value?.id) - } // Compare by unique identifier - renderInput={(params) => ( - - }), - ...(startAdornment && { startAdornment: startAdornment }), - ...(endAdornment && { endAdornment: endAdornment }), - }, - }} - sx={{}} - /> - {error && ( - - {error} - - )} - - )} - filterOptions={(options, { inputValue }) => { - if (inputValue.trim() === "" && multiple && isAdorned) { - return enhancedOptions; - } - const filtered = options.filter((option) => - option[filteredBy].toLowerCase().includes(inputValue.toLowerCase()) - ); - - if (filtered.length === 0) { - return [ - { - [filteredBy]: t("general.noOptionsFound", { unit: unit }), - noOptions: true, - }, - ]; - } - return filtered; - }} - getOptionKey={(option) => { - return option._id ?? option.id; - }} - renderOption={(props, option) => { - const { key, ...optionProps } = props; - const hasSecondaryLabel = secondaryLabel && option[secondaryLabel] !== undefined; - const port = option["port"]; - const selected = isOptionSelected(option); - return ( - - {multiple && isAdorned && !option.noOptions && ( - - )} - {option[filteredBy] + - (hasSecondaryLabel - ? ` (${option[secondaryLabel]}${port ? `: ${port}` : ""})` - : "")} - - ); - }} - slotProps={{ - popper: { - keepMounted: true, - sx: { - "& ul": { p: 2, backgroundColor: theme.palette.primary.main }, - "& li.MuiAutocomplete-option": { - color: theme.palette.primary.contrastTextSecondary, - px: 4, - borderRadius: theme.shape.borderRadius, - }, - - "& .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'], & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'].Mui-focused, & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true']:hover": - { - backgroundColor: theme.palette.primary.lowContrast, - color: "red", - }, - "& li.MuiAutocomplete-option:hover:not([aria-selected='true'])": { - color: theme.palette.secondary.contrastText, - backgroundColor: theme.palette.secondary.main, - }, - "& .MuiAutocomplete-noOptions": { - px: theme.spacing(6), - py: theme.spacing(5), - }, - }, - }, - }} - sx={{ - /* height: 34,*/ - "&.MuiAutocomplete-root .MuiAutocomplete-input": { - padding: `0 ${theme.spacing(5)}`, - }, - ...sx, - }} - /> - ); -}; - -Search.propTypes = { - label: PropTypes.string, - id: PropTypes.string, - multiple: PropTypes.bool, - options: PropTypes.array.isRequired, - filteredBy: PropTypes.string.isRequired, - secondaryLabel: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - inputValue: PropTypes.string.isRequired, - handleInputChange: PropTypes.func.isRequired, - handleChange: PropTypes.func, - isAdorned: PropTypes.bool, - sx: PropTypes.object, - error: PropTypes.string, - disabled: PropTypes.bool, - startAdornment: PropTypes.object, - endAdornment: PropTypes.object, - onBlur: PropTypes.func, - unit: PropTypes.string, -}; - -export default Search; diff --git a/client/src/Components/v1/Inputs/Select/index.css b/client/src/Components/v1/Inputs/Select/index.css deleted file mode 100644 index b40cf8564..000000000 --- a/client/src/Components/v1/Inputs/Select/index.css +++ /dev/null @@ -1,7 +0,0 @@ -.select-wrapper .select-component > .MuiSelect-select { - padding: 0 10px; - min-height: 34px; - display: flex; - align-items: center; - line-height: 1; -} diff --git a/client/src/Components/v1/Inputs/Select/index.jsx b/client/src/Components/v1/Inputs/Select/index.jsx deleted file mode 100644 index ec2522d17..000000000 --- a/client/src/Components/v1/Inputs/Select/index.jsx +++ /dev/null @@ -1,213 +0,0 @@ -import PropTypes from "prop-types"; -import { useTheme } from "@emotion/react"; -import { MenuItem, Select as MuiSelect, Stack, Typography } from "@mui/material"; -import Icon from "../../Icon"; -import FieldWrapper from "../FieldWrapper/index.jsx"; - -import "./index.css"; - -/** - * @component - * @param {object} props - * @param {string} props.id - The ID attribute for the select element. - * @param {string} props.placeholder - The label of the select element. - * @param {string} props.placeholder - The placeholder text when no option is selected. - * @param {boolean} props.isHidden - Whether the placeholder should be hidden. - * @param {(string | number | boolean)} props.value - The currently selected value. - * @param {object[]} props.items - The array of items to populate in the select dropdown. - * @param {(string | number | boolean)} props.items._id - The unique identifier of each item. - * @param {string} props.items.name - The display name of each item. - * @param {function} props.onChange - The function to handle onChange event. - * @param {object} props.sx - The custom styles object for MUI Select component. - * @param {number} props.maxWidth - Maximum width in pixels for the select component. Enables responsive text truncation. - * @returns {JSX.Element} - * - * @example - * const frequencies = [ - * { _id: 1, name: "1 minute" }, - * { _id: 2, name: "2 minutes" }, - * { _id: 3, name: "3 minutes" }, - * ]; - * - * dispatch(setMode(e.target.value))} - items={[ - { _id: "light", name: "Light" }, - { _id: "dark", name: "Dark" }, - ]} - > - - - - - - - - ); -}; - -SettingsUI.propTypes = { - HEADING_SX: PropTypes.object, -}; - -export default SettingsUI; diff --git a/client/src/Pages/Settings/SettingsURL.jsx b/client/src/Pages/Settings/SettingsURL.jsx deleted file mode 100644 index 11e7ea821..000000000 --- a/client/src/Pages/Settings/SettingsURL.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import Box from "@mui/material/Box"; -import Stack from "@mui/material/Stack"; -import Typography from "@mui/material/Typography"; -import ConfigBox from "@/Components/v1/ConfigBox/index.jsx"; -import Select from "@/Components/v1/Inputs/Select/index.jsx"; -import { Controller } from "react-hook-form"; - -// Utils -import { useTheme } from "@emotion/react"; -import { PropTypes } from "prop-types"; -import { useTranslation } from "react-i18next"; - -const SettingsURL = ({ HEADING_SX, control, defaults }) => { - const { t } = useTranslation(); - const theme = useTheme(); - return ( - - - - {t("pages.settings.urlSettings.title")} - - - {t("pages.settings.urlSettings.description")} - - - - ( - + + {t("pages.settings.form.ui.option.theme.light")} + + + {t("pages.settings.form.ui.option.theme.dark")} + + + + + + + } + /> + {isAdmin && ( + + {(isApiKeySet === false || apiKeyHasBeenReset === true) && ( + ( + + )} + /> + )} + + {isApiKeySet === true && apiKeyHasBeenReset === false && ( + + + {t("pages.settings.form.pagespeed.option.apiKey.labelSet")} + + + + )} + + } + /> + )} + + {/* URL Settings */} + ( + + )} + /> + } + /> + + {/* Clear All Stats */} + {isAdmin && ( + setIsStatsDialogOpen(true)} + > + {t("common.buttons.clear")} + + } + /> + )} + + {/* Global Thresholds */} + {isAdmin && ( + + ( + { + const val = e.target.value; + field.onChange(val === "" ? 0 : Number(val)); + }} + fieldLabel={t("pages.settings.form.thresholds.option.cpu.label")} + type="number" + inputProps={{ min: 0 }} + placeholder={t( + "pages.settings.form.thresholds.option.cpu.placeholder" + )} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + ( + { + const val = e.target.value; + field.onChange(val === "" ? 0 : Number(val)); + }} + fieldLabel={t("pages.settings.form.thresholds.option.memory.label")} + type="number" + inputProps={{ min: 0 }} + placeholder={t( + "pages.settings.form.thresholds.option.memory.placeholder" + )} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + ( + { + const val = e.target.value; + field.onChange(val === "" ? 0 : Number(val)); + }} + fieldLabel={t("pages.settings.form.thresholds.option.disk.label")} + type="number" + inputProps={{ min: 0 }} + placeholder={t( + "pages.settings.form.thresholds.option.disk.placeholder" + )} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + ( + { + const val = e.target.value; + field.onChange(val === "" ? 0 : Number(val)); + }} + fieldLabel={t( + "pages.settings.form.thresholds.option.temperature.label" + )} + type="number" + inputProps={{ min: 0 }} + placeholder={t( + "pages.settings.form.thresholds.option.temperature.placeholder" + )} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + + } + /> + )} + + + {/* Email Settings - Admin Only */} + {isAdmin && ( + + + + + {JSON.stringify( + { + host: form.watch("systemEmailHost") || "", + port: form.watch("systemEmailPort") || "", + secure: form.watch("systemEmailSecure") ?? false, + auth: { + user: + form.watch("systemEmailUser") || + form.watch("systemEmailAddress") || + "", + pass: "", + }, + name: form.watch("systemEmailConnectionHost") || "localhost", + pool: form.watch("systemEmailPool") ?? false, + tls: { + rejectUnauthorized: + form.watch("systemEmailRejectUnauthorized") ?? true, + ignoreTLS: form.watch("systemEmailIgnoreTLS") ?? false, + requireTLS: form.watch("systemEmailRequireTLS") ?? false, + servername: form.watch("systemEmailTLSServername") || "", + }, + }, + null, + 2 + )} + + + + } + rightContent={ + + {/* Email Host */} + ( + + )} + /> + + {/* Email Port */} + ( + { + const val = e.target.value; + field.onChange(val === "" ? 0 : Number(val)); + }} + fieldLabel={t("pages.settings.form.email.option.port.label")} + type="number" + inputProps={{ min: 0 }} + placeholder={t("pages.settings.form.email.option.port.placeholder")} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + + {/* Email Address */} + ( + + )} + /> + + {/* Email User (Optional) */} + ( + + )} + /> + + {/* Email Password with Reset Pattern */} + {isEmailPasswordSet && !emailPasswordHasBeenReset ? ( + + + {t("pages.settings.form.email.option.password.labelSet")} + + + + + + ) : ( + ( + + )} + /> + )} + + {/* TLS Servername (Optional) */} + ( + + )} + /> + + {/* Connection Host (Optional) */} + ( + + )} + /> + + {/* Boolean Switches */} + + {[ + { + name: "systemEmailSecure", + label: t("pages.settings.form.email.option.secure.label"), + }, + { + name: "systemEmailPool", + label: t("pages.settings.form.email.option.pool.label"), + }, + { + name: "systemEmailIgnoreTLS", + label: t("pages.settings.form.email.option.ignoreTLS.label"), + }, + { + name: "systemEmailRequireTLS", + label: t("pages.settings.form.email.option.requireTLS.label"), + }, + { + name: "systemEmailRejectUnauthorized", + label: t("pages.settings.form.email.option.rejectUnauthorized.label"), + }, + ].map(({ name, label }) => ( + ( + + {label} + ) => + field.onChange(e.target.checked) + } + /> + + )} + /> + ))} + + + {/* Test Email Button */} + + + + + } + /> + )} + + {/* Demo Monitors - Admin Only */} + {isAdmin && ( + + + + } + /> + )} + + {/* Remove All Monitors - Admin Only */} + {isAdmin && ( + + + + } + /> + )} + + {/* Export Monitors - Admin Only */} + {isAdmin && ( + + + + } + /> + )} + + {/* About */} + + + {t("common.appName")} {__APP_VERSION__} + + + {t("pages.settings.form.about.developedBy")} + + + https://github.com/bluewave-labs/checkmate + + + } + /> + + {/* Clear Stats Confirmation Dialog */} + setIsStatsDialogOpen(false)} + onConfirm={handleClearStats} + loading={isDeletingStats} + /> + + {/* Delete All Monitors Confirmation Dialog */} + setIsDemoMonitorsDialogOpen(false)} + onConfirm={async () => { + await deleteAllMonitors("/monitors/"); + setIsDemoMonitorsDialogOpen(false); + }} + loading={isDeletingAllMonitors} + /> + + {/* Sticky Save Button */} + + + + + ); +}; + +export default SettingsPage; diff --git a/client/src/Pages/StatusPage/Create/Components/HeaderConfigStatusControls.tsx b/client/src/Pages/StatusPage/Create/Components/HeaderConfigStatusControls.tsx index 99ae46cdd..407306f1b 100644 --- a/client/src/Pages/StatusPage/Create/Components/HeaderConfigStatusControls.tsx +++ b/client/src/Pages/StatusPage/Create/Components/HeaderConfigStatusControls.tsx @@ -1,6 +1,6 @@ import Stack from "@mui/material/Stack"; -import { Icon } from "@/Components/v2/design-elements"; -import { Button } from "@/Components/v2/inputs"; +import { Icon } from "@/Components/design-elements"; +import { Button } from "@/Components/inputs"; import { Trash } from "lucide-react"; import { useTranslation } from "react-i18next"; diff --git a/client/src/Pages/StatusPage/Create/index.tsx b/client/src/Pages/StatusPage/Create/index.tsx index 8b4394f14..b1b43d8bb 100644 --- a/client/src/Pages/StatusPage/Create/index.tsx +++ b/client/src/Pages/StatusPage/Create/index.tsx @@ -1,4 +1,4 @@ -import { BasePage, ConfigBox } from "@/Components/v2/design-elements"; +import { BasePage, ConfigBox } from "@/Components/design-elements"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; import IconButton from "@mui/material/IconButton"; @@ -15,7 +15,7 @@ import { Checkbox, Dialog, ColorInput, -} from "@/Components/v2/inputs"; +} from "@/Components/inputs"; import { useTheme } from "@mui/material/styles"; import { useTranslation } from "react-i18next"; diff --git a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx index 0e8f444c9..2cc2395bb 100644 --- a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx +++ b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx @@ -1,8 +1,8 @@ import Stack from "@mui/material/Stack"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import { Icon } from "@/Components/v2/design-elements"; -import { Button } from "@/Components/v2/inputs"; +import { Icon } from "@/Components/design-elements"; +import { Button } from "@/Components/inputs"; import { Settings, ExternalLink } from "lucide-react"; import { useTheme } from "@mui/material"; diff --git a/client/src/Pages/StatusPage/Status/Components/MonitorsList.tsx b/client/src/Pages/StatusPage/Status/Components/MonitorsList.tsx index 35aad7a26..b94b20a80 100644 --- a/client/src/Pages/StatusPage/Status/Components/MonitorsList.tsx +++ b/client/src/Pages/StatusPage/Status/Components/MonitorsList.tsx @@ -2,9 +2,9 @@ import Stack from "@mui/material/Stack"; import { useTranslation } from "react-i18next"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import { HistogramResponseTime, HeatmapResponseTime } from "@/Components/v2/common"; -import { StatusLabel, BaseBox } from "@/Components/v2/design-elements"; -import { SwitchComponent } from "@/Components/v2/inputs"; +import { HistogramResponseTime, HeatmapResponseTime } from "@/Components/common"; +import { StatusLabel, BaseBox } from "@/Components/design-elements"; +import { SwitchComponent } from "@/Components/inputs"; import { useTheme } from "@mui/material/styles"; import { useSelector } from "react-redux"; diff --git a/client/src/Pages/StatusPage/Status/index.tsx b/client/src/Pages/StatusPage/Status/index.tsx index b1826b867..0ca629581 100644 --- a/client/src/Pages/StatusPage/Status/index.tsx +++ b/client/src/Pages/StatusPage/Status/index.tsx @@ -1,4 +1,4 @@ -import { BasePage, BaseFallback } from "@/Components/v2/design-elements"; +import { BasePage, BaseFallback } from "@/Components/design-elements"; import { StatusBar } from "@/Pages/StatusPage/Status/Components/StatusBar"; import { MonitorsList } from "@/Pages/StatusPage/Status/Components/MonitorsList"; import Typography from "@mui/material/Typography"; diff --git a/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx b/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx index 192f9c101..978466bb4 100644 --- a/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx +++ b/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx @@ -1,8 +1,8 @@ import Box from "@mui/material/Box"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; -import { Table, type Header, ValueLabel } from "@/Components/v2/design-elements"; -import { ActionsMenu, type ActionMenuItem } from "@/Components/v2/actions-menu"; +import { Table, type Header, ValueLabel } from "@/Components/design-elements"; +import { ActionsMenu, type ActionMenuItem } from "@/Components/actions-menu"; import { ExternalLink } from "lucide-react"; import { useTranslation } from "react-i18next"; diff --git a/client/src/Pages/StatusPage/StatusPages/index.tsx b/client/src/Pages/StatusPage/StatusPages/index.tsx index 85b84e638..9468b7e12 100644 --- a/client/src/Pages/StatusPage/StatusPages/index.tsx +++ b/client/src/Pages/StatusPage/StatusPages/index.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; -import { BasePageWithStates } from "@/Components/v2/design-elements"; -import { Dialog } from "@/Components/v2/inputs"; +import { BasePageWithStates } from "@/Components/design-elements"; +import { Dialog } from "@/Components/inputs"; import { StatusPagesTable } from "./Components/StatusPagesTable"; import { useGet, useDelete } from "@/Hooks/UseApi"; import type { StatusPage } from "@/Types/StatusPage"; import { useTranslation } from "react-i18next"; -import { HeaderCreate } from "@/Components/v2/common"; +import { HeaderCreate } from "@/Components/common"; import { useIsAdmin } from "@/Hooks/useIsAdmin"; const StatusPages = () => { diff --git a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx index ceb8e1a28..26905e7cc 100644 --- a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx +++ b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx @@ -1,6 +1,6 @@ -import { Table, Pagination, StatusLabel } from "@/Components/v2/design-elements"; +import { Table, Pagination, StatusLabel } from "@/Components/design-elements"; import Box from "@mui/material/Box"; -import type { Header } from "@/Components/v2/design-elements/"; +import type { Header } from "@/Components/design-elements"; import type { Check } from "@/Types/Check"; import { useNavigate } from "react-router"; diff --git a/client/src/Pages/Uptime/Details/index.tsx b/client/src/Pages/Uptime/Details/index.tsx index b7910a17c..23f9a2196 100644 --- a/client/src/Pages/Uptime/Details/index.tsx +++ b/client/src/Pages/Uptime/Details/index.tsx @@ -1,15 +1,15 @@ -import { BasePage } from "@/Components/v2/design-elements"; -import { HeaderTimeRange } from "@/Components/v2/common"; +import { BasePage } from "@/Components/design-elements"; +import { HeaderTimeRange } from "@/Components/common"; import Stack from "@mui/material/Stack"; import { HistogramStatus, RadialAvgResponse, HistogramDetails, HeaderMonitorControls, -} from "@/Components/v2/monitors"; +} from "@/Components/monitors"; import { TrendingUp, AlertTriangle } from "lucide-react"; import { ChecksTable } from "@/Pages/Uptime/Details/Components/ChecksTable"; -import { MonitorStatBoxes } from "@/Components/v2/monitors"; +import { MonitorStatBoxes } from "@/Components/monitors"; import { useTheme } from "@mui/material/styles"; import { useIsAdmin } from "@/Hooks/useIsAdmin"; diff --git a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx index 75738e6ad..87db4246d 100644 --- a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx +++ b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx @@ -6,9 +6,9 @@ import { type Header, Pagination, StatusLabel, -} from "@/Components/v2/design-elements"; -import { HeatmapResponseTime, HistogramResponseTime } from "@/Components/v2/common"; -import { ActionsMenu } from "@/Components/v2/actions-menu"; +} from "@/Components/design-elements"; +import { HeatmapResponseTime, HistogramResponseTime } from "@/Components/common"; +import { ActionsMenu } from "@/Components/actions-menu"; import { ArrowDown, ArrowUp } from "lucide-react"; import { useTranslation } from "react-i18next"; @@ -18,7 +18,7 @@ import { usePost } from "@/Hooks/UseApi"; import { useSelector } from "react-redux"; import type { Monitor } from "@/Types/Monitor"; -import type { ActionMenuItem } from "@/Components/v2/actions-menu"; +import type { ActionMenuItem } from "@/Components/actions-menu"; import type { RootState } from "@/Types/state"; export const MonitorTable = ({ diff --git a/client/src/Pages/Uptime/Monitors/index.tsx b/client/src/Pages/Uptime/Monitors/index.tsx index 9e36ccc86..5c419738b 100644 --- a/client/src/Pages/Uptime/Monitors/index.tsx +++ b/client/src/Pages/Uptime/Monitors/index.tsx @@ -1,9 +1,9 @@ -import { ControlsFilter, HeaderMonitorsSummary } from "@/Components/v2/monitors"; -import { MonitorBasePageWithStates } from "@/Components/v2/design-elements"; -import { TextField, Dialog } from "@/Components/v2/inputs"; +import { ControlsFilter, HeaderMonitorsSummary } from "@/Components/monitors"; +import { MonitorBasePageWithStates } from "@/Components/design-elements"; +import { TextField, Dialog } from "@/Components/inputs"; import Stack from "@mui/material/Stack"; import { MonitorTable } from "@/Pages/Uptime/Monitors/Components/UptimeMonitorsTable"; -import { HeaderCreate } from "@/Components/v2/common"; +import { HeaderCreate } from "@/Components/common"; import { useTranslation } from "react-i18next"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/client/src/Routes/index.tsx b/client/src/Routes/index.tsx index e3c0b0667..20869c47c 100644 --- a/client/src/Routes/index.tsx +++ b/client/src/Routes/index.tsx @@ -1,11 +1,5 @@ -// Temp v2 theme -import { ThemeProvider } from "@mui/material"; -import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme"; - -import { useSelector } from "react-redux"; -import type { RootState } from "@/store"; import { Navigate, Route, Routes as LibRoutes } from "react-router"; -import RootLayout from "@/Components/v2/layout/RootLayout"; +import RootLayout from "@/Components/layout/RootLayout"; import NotFound from "@/Pages/NotFound"; // Auth @@ -52,16 +46,11 @@ import CreateNewMaintenanceWindow from "@/Pages/Maintenance/create"; import Logs from "@/Pages/Logs"; // Routing -import { - ProtectedRoute, - RoleProtectedRoute, -} from "@/Components/v2/routing/RouteProtected"; +import { ProtectedRoute, RoleProtectedRoute } from "@/Components/routing/RouteProtected"; import CreateMonitor from "@/Pages/CreateMonitor"; const Routes = () => { - const mode = useSelector((state: RootState) => state.ui.mode); - const v2theme = mode === "light" ? lightTheme : darkTheme; return ( { /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> } /> + - - - } + element={} /> - - - } + element={} /> - - - } + element={} /> - - - + } /> @@ -360,9 +198,7 @@ const Routes = () => { path="logs" element={ - - - + } /> @@ -372,75 +208,37 @@ const Routes = () => { path="/login" element={ <> - - - + } /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> - - - - - } + element={} /> ); diff --git a/client/src/Types/env.d.ts b/client/src/Types/env.d.ts index 74a2a29e7..cd714db75 100644 --- a/client/src/Types/env.d.ts +++ b/client/src/Types/env.d.ts @@ -1,5 +1,7 @@ /// +declare const __APP_VERSION__: string; + interface ImportMetaEnv { readonly VITE_APP_API_V2_BASE_URL?: string; } diff --git a/client/src/Types/state.ts b/client/src/Types/state.ts index b5bfcfdb8..d8d291fc7 100644 --- a/client/src/Types/state.ts +++ b/client/src/Types/state.ts @@ -1,3 +1,4 @@ +import type { ChartType } from "@/Features/UI/uiSlice"; import type { User } from "./User"; export interface AuthState { @@ -37,7 +38,7 @@ export interface UIState { distributedUptimeEnabled: boolean; language: string; starPromptOpen: boolean; - chartType: string; + chartType: ChartType; } export interface RootState { diff --git a/client/src/Utils/MonitorUtils.ts b/client/src/Utils/MonitorUtils.ts index b73fc5bc4..a19316c62 100644 --- a/client/src/Utils/MonitorUtils.ts +++ b/client/src/Utils/MonitorUtils.ts @@ -1,6 +1,6 @@ import type { MonitorStatus, MonitorType } from "@/Types/Monitor"; -import type { PaletteKey } from "@/Utils/Theme/v2Theme"; -import type { ValueType } from "@/Components/v2/design-elements/StatusLabel"; +import type { PaletteKey } from "@/Utils/Theme/Theme"; +import type { ValueType } from "@/Components/design-elements/StatusLabel"; export const getMonitorPath = (type: MonitorType): string => { const pathMap: Record = { diff --git a/client/src/Utils/Theme/v2Palette.ts b/client/src/Utils/Theme/Palette.ts similarity index 100% rename from client/src/Utils/Theme/v2Palette.ts rename to client/src/Utils/Theme/Palette.ts diff --git a/client/src/Utils/Theme/v2Theme.ts b/client/src/Utils/Theme/Theme.ts similarity index 99% rename from client/src/Utils/Theme/v2Theme.ts rename to client/src/Utils/Theme/Theme.ts index 39ede6c7e..49c1636ec 100644 --- a/client/src/Utils/Theme/v2Theme.ts +++ b/client/src/Utils/Theme/Theme.ts @@ -1,5 +1,5 @@ import { createTheme } from "@mui/material"; -import { lightPalette, darkPalette, typographyLevels } from "@/Utils/Theme/v2Palette"; +import { lightPalette, darkPalette, typographyLevels } from "@/Utils/Theme/Palette"; import type { Theme } from "@mui/material/styles"; diff --git a/client/src/Utils/Theme/constants.js b/client/src/Utils/Theme/constants.js deleted file mode 100644 index 2664c6126..000000000 --- a/client/src/Utils/Theme/constants.js +++ /dev/null @@ -1,291 +0,0 @@ -import { lighten } from "@mui/material/styles"; // CAIO_REVIEW - -const typographyBase = 13; - -/* TODO -Check for px in codebase. All font sizes should be in REM and should live here. -Rest should be checked on each case -*/ -const typographyLevels = { - base: typographyBase, - xs: `${(typographyBase - 4) / 16}rem`, - s: `${(typographyBase - 2) / 16}rem`, - m: `${typographyBase / 16}rem`, - l: `${(typographyBase + 2) / 16}rem`, - xl: `${(typographyBase + 10) / 16}rem`, -}; - -/* TODO Review color palette and semantic colors */ -const paletteColors = { - white: "#FFFFFF", - gray50: "#FEFEFE", - gray60: "#FEFDFE", - gray70: "#FDFDFD", - gray80: "#FDFCFD", - gray90: "#FCFCFD", - gray100: "#F4F4F4", - gray150: "#EFEFEF", - gray200: "#E3E3E3", - gray300: "#A2A3A3", - gray500: "#838C99", - gray600: "#454546", - gray750: "#36363E", - gray800: "#2D2D33", - gray850: "#131315", - gray860: "#111113", - gray870: "#0F0F11", - gray880: "#0C0C0E", - gray890: "#09090B", - blueGray20: "#E8F0FE", - blueGray150: "#667085", - blueGray200: "#475467", - blueGray400: "#344054", - blueBlueWave: "#1570EF", - blue700: "#4E5BA6", - purple300: "#664EFF", - purple400: "#3A1BFF", - green50: "#D4F4E1", - green150: "#45BB7A", - green400: "#079455", - green500: "#07B467", - green800: "#1C4428", - green900: "#12261E", - red50: "#F9ECED", - red100: "#FBD1D1", - red200: "#F04438", - red300: "#D32F2F", - red700: "#542426", - red800: "#912018", - orange50: "#FEF8EA", - orange100: "#FFECBC", - orange300: "#FDB022", - orange400: "#FF9F00", - orange500: "#E88C30", - orange600: "#DC6803", - orange800: "#624711", -}; - -const semanticColors = { - unresolved: { - main: { - light: paletteColors.blue700, - dark: paletteColors.purple300, - }, - light: { - light: paletteColors.blueGray20, - dark: paletteColors.purple400, - }, - bg: { - light: paletteColors.gray100, - dark: paletteColors.gray100, - }, - }, -}; - -const newColors = { - offWhite: "#FEFEFE", - offBlack: "#131315", - gray0: "#FDFDFD", - gray10: "#F4F4FF", - gray50: "#F9F9F9", - gray100: "#F3F3F3", - gray200: "#EFEFEF", - gray250: "#DADADA", - gray500: "#A2A3A3", - gray900: "#1c1c1c", - blueGray50: "#E8F0FE", - blueGray500: "#475467", - blueGray600: "#344054", - blueGray800: "#1C2130", - blueGray900: "#515151", - blueBlueWave: "#1570EF", - lightBlueWave: "#CDE2FF", - green100: "#67cd78", - green200: "#4B9B77", - green400: "#079455", - green700: "#026513", - orange100: "#FD8F22", - orange200: "#D69A5D", - orange600: "#9B734B", - orange700: "#884605", - red100: "#F27C7C", - red400: "#D92020", - red600: "#9B4B4B", - red700: "#980303", -}; - -/* -Structure: -main: background color -contrastText: color for main contrast text -contrastTextSecondary: if needed -contrastTextTertiary: if needed -lowContrast: if we need some low contrast for that color (for borders, and decorative elements). This should never be usend in text - -*/ -const newSemanticColors = { - accent: { - main: { - light: newColors.blueBlueWave, - dark: newColors.blueBlueWave, - }, - light: { - light: lighten(newColors.blueBlueWave, 0.2), //CAIO_REVIEW - dark: lighten(newColors.blueBlueWave, 0.2), //CAIO_REVIEW - }, - contrastText: { - light: newColors.offWhite, - dark: newColors.offWhite, - }, - }, - primary: { - main: { - light: newColors.offWhite, - dark: newColors.offBlack, - }, - contrastText: { - light: newColors.blueGray800, - dark: newColors.blueGray50, - }, - contrastTextSecondary: { - light: newColors.blueGray600, - dark: newColors.gray200, - }, - contrastTextSecondaryDarkBg: { - light: newColors.gray200, - dark: newColors.gray200, - }, - contrastTextTertiary: { - light: newColors.blueGray500, - dark: newColors.gray500, - }, - lowContrast: { - light: newColors.gray250, - dark: newColors.blueGray600, - }, - }, - primaryBackground: { - main: { - light: newColors.gray0, - dark: "#000000", - }, - }, - secondary: { - main: { - light: newColors.gray200, - dark: "#313131" /* newColors.blueGray600 */, - }, - light: { - light: newColors.lightBlueWave, - dark: newColors.lightBlueWave, - }, - contrastText: { - light: newColors.blueGray600, - dark: newColors.gray200, - }, - }, - tertiary: { - main: { - light: newColors.gray100, - dark: newColors.blueGray800, - }, - contrastText: { - light: newColors.blueGray800, - dark: newColors.gray100, - }, - }, - success: { - main: { - light: newColors.green700, - dark: newColors.green100, - }, - contrastText: { - light: newColors.offWhite, - dark: newColors.offBlack, - }, - lowContrast: { - light: newColors.green400, - dark: newColors.green200, - }, - }, - warning: { - main: { - light: newColors.orange700, - dark: newColors.orange200, - }, - contrastText: { - light: newColors.offWhite, - dark: newColors.offBlack, - }, - lowContrast: { - light: newColors.orange100, - dark: newColors.orange600, - }, - }, - /* Custom palette for PageSpeed warning box */ - warningSecondary: { - main: { - light: paletteColors.orange50, - dark: paletteColors.orange800, - }, - contrastText: { - light: paletteColors.orange600, - dark: paletteColors.orange100, - }, - lowContrast: { - light: paletteColors.orange300, - dark: paletteColors.orange400, - }, - }, - error: { - main: { - light: newColors.red700, - dark: newColors.red100, - }, - contrastText: { - light: newColors.offWhite, - dark: newColors.offBlack, - }, - lowContrast: { - light: newColors.red400, - dark: newColors.red600, - }, - }, - - /* These are temporary, just for everything not to break */ - gradient: { - color1: { - light: paletteColors.gray90, - dark: paletteColors.gray890, - }, - color2: { - light: paletteColors.gray80, - dark: paletteColors.gray880, - }, - color3: { - light: paletteColors.gray70, - dark: paletteColors.gray870, - }, - color4: { - light: paletteColors.gray60, - dark: paletteColors.gray860, - }, - color5: { - light: paletteColors.gray50, - dark: paletteColors.gray850, - }, - }, - - alert: { - main: { - light: newColors.gray200, - dark: newColors.gray900, - }, - contrastText: { - light: newColors.blueGray600, - dark: newColors.blueGray900, - }, - }, -}; - -export { typographyLevels, semanticColors as colors, newSemanticColors }; diff --git a/client/src/Utils/Theme/darkTheme.js b/client/src/Utils/Theme/darkTheme.js deleted file mode 100644 index 1ec5a2c3a..000000000 --- a/client/src/Utils/Theme/darkTheme.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createTheme } from "@mui/material"; -import { baseTheme } from "./globalTheme"; -import { /* colors, */ newSemanticColors } from "./constants"; -import { extractThemeColors } from "./extractColorObject"; - -const palette = extractThemeColors("dark", newSemanticColors); - -const darkTheme = createTheme({ - palette, - ...baseTheme(palette), -}); - -export default darkTheme; diff --git a/client/src/Utils/Theme/extractColorObject.js b/client/src/Utils/Theme/extractColorObject.js deleted file mode 100644 index bfd1a5aff..000000000 --- a/client/src/Utils/Theme/extractColorObject.js +++ /dev/null @@ -1,25 +0,0 @@ -function extractThemeColors(themeType, colorObject) { - if (!["light", "dark"].includes(themeType)) { - throw new Error('Invalid theme type. Use "light" or "dark".'); - } - - const extract = (obj) => { - if (typeof obj !== "object" || obj === null) { - return obj; - } - - if ("light" in obj && "dark" in obj) { - // CAIO_REVIEW: This will break if the root object has light and dark properties - return obj[themeType]; // Return the value for the specified theme - } - - // Reduce the object, keeping only the extracted themeType values - return Object.keys(obj).reduce((acc, key) => { - acc[key] = extract(obj[key]); - return acc; - }, {}); - }; - return extract(colorObject); -} - -export { extractThemeColors }; diff --git a/client/src/Utils/Theme/globalTheme.js b/client/src/Utils/Theme/globalTheme.js deleted file mode 100644 index 98f3479be..000000000 --- a/client/src/Utils/Theme/globalTheme.js +++ /dev/null @@ -1,793 +0,0 @@ -import { typographyLevels } from "./constants"; -const fontFamilyPrimary = '"Inter" , sans-serif'; -import { darken } from "@mui/material/styles"; -// const fontFamilySecondary = '"Avenir", sans-serif'; - -/* TODO take the color out from here */ -const shadow = - "0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)"; - -const baseTheme = (palette) => ({ - typography: { - fontFamily: fontFamilyPrimary, - fontSize: typographyLevels.base, - h1: { - fontSize: typographyLevels.xl, - color: palette.primary.contrastText, - fontWeight: 500, - }, - h2: { - fontSize: typographyLevels.l, - color: palette.primary.contrastTextSecondary, - fontWeight: 400, - }, - // CAIO_REVIEW, need a brighter color for dark bg - h2DarkBg: { - fontSize: typographyLevels.l, - color: palette.primary.contrastTextSecondaryDarkBg, - fontWeight: 400, - }, - body1: { - fontSize: typographyLevels.m, - color: palette.primary.contrastTextTertiary, - fontWeight: 400, - }, - body2: { - fontSize: typographyLevels.s, - color: palette.primary.contrastTextTertiary, - fontWeight: 400, - }, - label: { - color: palette.primary.contrastTextSecondary, - fontWeight: 500, - }, - }, - /* TODO change to 4 */ - spacing: 2, - /* TODO we can skip using the callback functions on the next lines since we are already accessing it on line 10. That was the last thing I managed to do, so we are sort of doing it twice*/ - /* TODO All these should live inside of a component*/ - components: { - MuiButton: { - defaultProps: { - disableRipple: true, - }, - - styleOverrides: { - root: ({ theme }) => ({ - variants: [ - { - props: (props) => props.variant === "contained" && props.color === "accent", - backgroundColor: theme.palette.accent.main, - style: { - color: theme.palette.primary.contrastTextSecondaryDarkBg, - letterSpacing: "0.5px", - textShadow: "0 0 1px rgba(0, 0, 0, 0.15)", - "&:hover": { - backgroundColor: darken(theme.palette.accent.main, 0.25), - boxShadow: `0 2px 6px rgba(0, 0, 0, 0.1)`, - transition: "all 0.2s ease-in-out", - }, - }, - }, - { - props: (props) => props.color === "accent", - style: { - "&:hover": { - backgroundColor: darken(theme.palette.accent.main, 0.2), - }, - }, - }, - { - props: (props) => props.color === "error", - style: { - "&.Mui-disabled": { - backgroundColor: theme.palette.error.lowContrast, - }, - "& .MuiButton-loadingIndicator": { - // styles for error variant loading indicator - color: theme.palette.error.contrastText, - }, - }, - }, - { - props: (props) => props.variant === "group", - style: { - /* color: theme.palette.secondary.contrastText, */ - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - }, - }, - { - props: (props) => props.variant === "group" && props.filled === "true", - style: { - backgroundColor: theme.palette.secondary.main, - }, - }, - /* { - props: (props) => props.variant === "contained", - style: { - backgroundColor: `${theme.palette.accent.main} !important`, - }, - }, */ - - { - props: (props) => - props.variant === "contained" && props.color === "secondary", - style: { - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - }, - }, - { - props: (props) => { - return ( - props.variant === "contained" && - props.disabled && - props?.classes?.loadingIndicator === undefined // Do not apply to loading button - ); - }, - style: { - backgroundColor: `${theme.palette.secondary.main} !important`, - color: `${theme.palette.secondary.contrastText} !important`, - }, - }, - - { - props: { variant: "text", color: "info" }, - style: { - textDecoration: "underline", - color: theme.palette.primary.contrastText, - padding: 0, - margin: 0, - fontSize: typographyLevels.m, - fontWeight: theme.typography.body2.fontWeight, - backgroundColor: "transparent", - "&:hover": { - backgroundColor: "transparent", - textDecoration: "underline", - }, - "&.Mui-disabled": { - backgroundColor: "transparent", - color: theme.palette.text.primary, - opacity: 0.5, - "&.MuiButton-text": { - backgroundColor: "transparent", - }, - }, - minWidth: 0, - boxShadow: "none", - border: "none", - }, - }, - ], - height: 34, - fontWeight: 400, - borderRadius: 4, - boxShadow: "none", - textTransform: "none", - "&:focus": { - outline: "none", - }, - "&:hover": { - boxShadow: "none", - }, - "&.Mui-disabled": { - backgroundColor: theme.palette.secondary.main, - color: theme.palette.primary.contrastText, - }, - "&.MuiButton-root": { - "&:disabled": { - backgroundColor: theme.palette.secondary.main, - color: theme.palette.primary.contrastText, - }, - "&.MuiButton-colorAccent:hover": { - boxShadow: `0 0 0 1px ${theme.palette.accent.main}`, // CAIO_REVIEW, this should really have a solid BG color - }, - }, - "&.MuiButton-loading": { - "&:disabled": { - color: "transparent", - }, - - "& .MuiButton-loadingIndicator": { - color: theme.palette.primary.contrastText, - }, - }, - }), - }, - }, - - MuiIconButton: { - styleOverrides: { - root: ({ theme }) => ({ - padding: 4, - transition: "none", - "&:hover": { - backgroundColor: theme.palette.primary.lowContrast, - }, - }), - }, - }, - MuiPaper: { - styleOverrides: { - root: ({ theme }) => { - return { - marginTop: 4, - padding: 0, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - borderRadius: 4, - boxShadow: shadow, - backgroundColor: theme.palette.primary.main, - backgroundImage: "none", - }; - }, - }, - }, - MuiList: { - styleOverrides: { - root: ({ theme }) => ({ - padding: 0, - }), - }, - }, - MuiListItemButton: { - styleOverrides: { - root: ({ theme }) => ({ - transition: "background-color .3s", - }), - }, - }, - MuiListItemText: { - styleOverrides: { - root: ({ theme }) => ({ - "& .MuiTypography-root": { - color: theme.palette.primary.contrastText, - }, - }), - }, - }, - MuiMenuItem: { - styleOverrides: { - root: ({ theme }) => ({ - borderRadius: 4, - backgroundColor: "inherit", - padding: "4px 6px", - color: theme.palette.primary.contrastTextSecondary, - fontSize: 13, - margin: 2, - minWidth: 100, - "&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible": - { - backgroundColor: theme.palette.primary.lowContrast, - }, - }), - }, - }, - MuiTableCell: { - styleOverrides: { - root: ({ theme }) => ({ - fontSize: typographyLevels.base, - borderBottomColor: theme.palette.primary.lowContrast, - }), - }, - }, - - MuiTableHead: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.tertiary.main, - }), - }, - }, - MuiPagination: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - "& button": { - color: theme.palette.primary.contrastTextTertiary, - borderRadius: 4, - }, - "& li:first-of-type button, & li:last-of-type button": { - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - }, - }), - }, - }, - MuiPaginationItem: { - styleOverrides: { - root: ({ theme }) => ({ - "&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": { - backgroundColor: theme.palette.primary.lowContrast, - }, - }), - }, - }, - MuiSkeleton: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.lowContrast, - }), - }, - }, - - MuiTextField: { - styleOverrides: { - root: ({ theme }) => ({ - "& fieldset": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - - "& .MuiInputBase-input": { - padding: ".75em", - fontWeight: 400, - color: palette.primary.contrastTextSecondary, - "&.Mui-disabled": { - opacity: 0.3, - WebkitTextFillColor: "unset", - }, - "& .Mui-focused": { - /* color: "#ff0000", */ - /* borderColor: theme.palette.primary.contrastText, */ - }, - }, - - "& .MuiInputBase-input:-webkit-autofill": { - transition: "background-color 5000s ease-in-out 0s", - WebkitBoxShadow: `0 0 0px 1000px ${theme.palette.primary.main} inset`, - WebkitTextFillColor: theme.palette.primary.contrastText, - }, - - "& .MuiInputBase-input.MuiOutlinedInput-input": { - padding: `0 ${theme.spacing(5)}`, - }, - - "& .MuiOutlinedInput-root": { - color: theme.palette.primary.contrastTextSecondary, - borderRadius: 4, - }, - - "& .MuiOutlinedInput-notchedOutline": { - borderRadius: 4, - }, - - "& .MuiFormHelperText-root": { - color: palette.error.main, - opacity: 0.8, - fontSize: "var()", - marginLeft: 0, - }, - - "& .MuiFormHelperText-root.Mui-error": { - opacity: 0.8, - color: palette.error.main, - whiteSpace: "nowrap", - }, - }), - }, - }, - - MuiOutlinedInput: { - styleOverrides: { - root: { - "&.Mui-disabled .MuiOutlinedInput-notchedOutline": { - borderColor: palette.primary.contrastBorderDisabled, - }, - "&.Mui-disabled:hover .MuiOutlinedInput-notchedOutline": { - borderColor: palette.primary.contrastBorderDisabled, - }, - "&:hover .MuiOutlinedInput-notchedOutline": { - borderColor: palette.primary.lowContrast, // Adjust hover border color - }, - "&.Mui-focused .MuiOutlinedInput-notchedOutline": { - borderColor: palette.accent.main, // Adjust focus border color - }, - color: palette.primary.contrastText, - }, - }, - }, - - MuiAutocomplete: { - styleOverrides: { - root: ({ theme }) => ({ - "& .MuiOutlinedInput-root": { - paddingTop: 0, - paddingBottom: 0, - paddingRight: theme.spacing(5), - }, - "& fieldset": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": - { - borderColor: theme.palette.primary.lowContrast, - }, - - "& .MuiAutocomplete-tag": { - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.lowContrast, - }, - "& .MuiChip-deleteIcon": { - color: theme.palette.primary.contrastText, // CAIO_REVIEW - }, - }), - }, - }, - - MuiTab: { - styleOverrides: { - root: ({ theme }) => ({ - color: theme.palette.tertiary.contrastText, - height: "34px", - minHeight: "34px", - borderRadius: 0, - textTransform: "none", - minWidth: "fit-content", - padding: `${theme.spacing(6)}px ${theme.spacing(4)}px`, - fontWeight: 400, - "&:focus-visible": { - color: theme.palette.primary.contrastText, - borderColor: theme.palette.tertiary.contrastText, - borderRightColor: theme.palette.primary.lowContrast, - }, - "&.Mui-selected": { - color: theme.palette.secondary.contrastText, - }, - "&:hover": { - borderColor: theme.palette.primary.lowContrast, - }, - }), - }, - variants: [ - { - props: { orientation: "vertical" }, - style: ({ theme }) => ({ - alignItems: "flex-start", - padding: `${theme.spacing(1)}px ${theme.spacing(2)}px ${theme.spacing(1)}px ${theme.spacing(6)}px`, - minHeight: theme.spacing(12), - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.main, - border: "none", - borderBottom: "none", - borderRight: "none", - borderRadius: theme.shape.borderRadius * 3, - margin: `${theme.spacing(1)}px ${theme.spacing(2)}px`, - "&.Mui-selected": { - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.tertiary.main, - opacity: 1, - border: "none", - borderBottom: "none", - borderRight: "none", - borderRadius: theme.shape.borderRadius * 3, - minHeight: theme.spacing(14), - }, - "&:hover": { - backgroundColor: theme.palette.tertiary.main, - border: "none", - borderRadius: theme.shape.borderRadius * 3, - minHeight: theme.spacing(14), - }, - }), - }, - ], - }, - MuiSvgIcon: { - styleOverrides: { - root: ({ theme }) => ({ - color: theme.palette.primary.contrastTextTertiary, - }), - }, - }, - MuiTabs: { - styleOverrides: { - root: ({ theme }) => ({ - height: "34px", - minHeight: "34px", - display: "inline-flex", - borderRadius: 0, - "& .MuiTabs-indicator": { - backgroundColor: theme.palette.tertiary.contrastText, - }, - }), - }, - variants: [ - { - props: { orientation: "vertical" }, - style: { - "& .MuiTabs-indicator": { - display: "none", - }, - }, - }, - ], - }, - MuiSwitch: { - styleOverrides: { - root: ({ theme }) => ({ - width: 42, - height: 26, - padding: 0, - "& .MuiSwitch-switchBase": { - padding: 0, - margin: 2, - transitionDuration: "300ms", - "&.Mui-checked": { - transform: "translateX(16px)", - color: "#fff", - "& + .MuiSwitch-track": { - backgroundColor: theme.palette.accent.main, - opacity: 1, - border: 0, - }, - }, - }, - "& .MuiSwitch-thumb": { - boxSizing: "border-box", - width: 22, - height: 22, - }, - "& .MuiSwitch-track": { - borderRadius: 13, - backgroundColor: theme.palette.secondary.light, - opacity: 1, - }, - }), - }, - }, - MuiSelect: { - styleOverrides: { - root: ({ theme }) => ({ - "& .MuiOutlinedInput-input": { - color: theme.palette.primary.contrastText, - }, - "& .MuiOutlinedInput-notchedOutline": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - "& .MuiSelect-icon": { - color: theme.palette.primary.contrastTextSecondary, // Dropdown + color - }, - "& .MuiSelect-select": { - display: "flex", - alignItems: "center", - }, - "&:hover": { - backgroundColor: theme.palette.primary.main, // Background on hover - }, - "&:hover .MuiOutlinedInput-notchedOutline": { - borderColor: theme.palette.primary.lowContrast, - }, - padding: `0 ${theme.spacing(5)}`, - minHeight: "34px", - display: "flex", - alignItems: "center", - lineHeight: 1, - }), - }, - }, - MuiButtonGroup: { - styleOverrides: { - root: ({ theme }) => ({ - ml: "auto", - "& .MuiButtonBase-root, & .MuiButtonBase-root:hover": { - borderColor: theme.palette.primary.lowContrast, - width: "auto", - whiteSpace: "nowrap", - }, - }), - }, - }, - - // This code is added for clock in maintenance page - // code starts from here. - MuiClock: { - // Directly target the clock component - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, // Alternative target - "& .MuiClock-clock": { - // Inner clock face - backgroundColor: theme.palette.secondary.main, - }, - }), - }, - }, - MuiClockPicker: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.secondary.main, // Outer container background - "& .MuiClock-root": { - color: theme.palette.primary.lowContrast, - }, - "& .MuiClock-clock": { - backgroundColor: theme.palette.background.default, // Clock face background - borderColor: theme.palette.secondary.lowContrast, - }, - }), - }, - }, - // The clock pointer ( pointer to number like hour/minute hand) - MuiClockPointer: { - styleOverrides: { - root: ({ theme }) => ({ - // Main pointer line color - backgroundColor: theme.palette.accent.main, - "& .MuiClockPointer-thumb": { - backgroundColor: theme.palette.grey[500], // Use your desired grey - }, - }), - }, - }, - // This is for numbers in the clock (circular one's) - MuiClockNumber: { - styleOverrides: { - root: ({ theme }) => ({ - color: theme.palette.primary.contrastText, - "&.Mui-selected": { - color: theme.palette.accent.contrastText, - backgroundColor: theme.palette.accent.main, - }, - }), - }, - }, - // This is for 00:00 am and pm (top bar) - MuiTimePickerToolbar: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.secondary.lowContrast, - // General text color - "& .MuiTypography-root": { - color: theme.palette.primary.contrastTextTertiary, - }, - // Selected time (hour/minute) color - "& .Mui-selected": { - color: `${theme.palette.accent.main} !important`, - }, - // AM/PM buttons color - "& .MuiButtonBase-root": { - "&.Mui-selected": { - backgroundColor: theme.palette.accent.main, - }, - }, - }), - }, - }, - // left and right direction style - MuiPickersArrowSwitcher: { - styleOverrides: { - root: ({ theme }) => ({ - "& .MuiIconButton-root": { - color: theme.palette.primary.contrastText, - }, - }), - }, - }, - // cancel and okay actions style - MuiDialogActions: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, - }), - }, - }, - // DatePicker calendar popup styling - MuiPickersPopper: { - styleOverrides: { - paper: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, - }), - }, - }, - MuiDateCalendar: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, - }), - }, - }, - MuiPickersCalendarHeader: { - styleOverrides: { - root: ({ theme }) => ({ - "& .MuiPickersCalendarHeader-label": { - color: theme.palette.primary.contrastText, - }, - }), - }, - }, - MuiDayCalendar: { - styleOverrides: { - weekDayLabel: ({ theme }) => ({ - color: theme.palette.primary.contrastTextTertiary, - }), - }, - }, - MuiPickersDay: { - styleOverrides: { - root: ({ theme }) => ({ - color: theme.palette.primary.contrastText, - "&:hover": { - backgroundColor: theme.palette.tertiary.main, - }, - "&.Mui-selected": { - backgroundColor: theme.palette.accent.main, - color: theme.palette.accent.contrastText, - "&:hover": { - backgroundColor: theme.palette.accent.main, - }, - }, - "&.Mui-disabled": { - color: theme.palette.primary.contrastTextTertiary, - opacity: 0.5, - }, - }), - }, - }, - // code ends here. - - // For labels of input fields - MuiInputLabel: { - styleOverrides: { - root: ({ theme }) => ({ - "&.Mui-focused": { - color: theme.palette.accent.main, - }, - }), - }, - }, - MuiTypography: { - variants: [ - { - props: { variant: "monitorName" }, - style: { - fontSize: typographyLevels.xl, - color: palette.primary.contrastText, - fontWeight: 500, - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - }, - }, - { - props: { variant: "monitorUrl" }, - style: { - fontSize: typographyLevels.l, - color: palette.primary.contrastTextSecondary, - fontWeight: "bolder", - fontFamily: "monospace", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - }, - }, - ], - }, - - MuiTooltip: { - styleOverrides: { - tooltip: () => ({ - fontSize: typographyLevels.m, - }), - }, - }, - }, - shape: { - borderRadius: 2, - borderThick: 2, - boxShadow: shadow, - }, -}); - -export { baseTheme }; diff --git a/client/src/Utils/Theme/lightTheme.js b/client/src/Utils/Theme/lightTheme.js deleted file mode 100644 index 94e8d3d4a..000000000 --- a/client/src/Utils/Theme/lightTheme.js +++ /dev/null @@ -1,12 +0,0 @@ -import { createTheme } from "@mui/material"; -import { baseTheme } from "./globalTheme"; -import { /* colors, */ newSemanticColors } from "./constants"; -import { extractThemeColors } from "./extractColorObject"; - -const palette = extractThemeColors("light", newSemanticColors); -const lightTheme = createTheme({ - palette, - ...baseTheme(palette), -}); - -export default lightTheme; diff --git a/client/src/Validation/settings.ts b/client/src/Validation/settings.ts index 7fb9ddcd8..4c89a4c2c 100644 --- a/client/src/Validation/settings.ts +++ b/client/src/Validation/settings.ts @@ -1,69 +1,71 @@ import { z } from "zod"; -// Helper to normalize empty strings to undefined -const optionalString = () => - z.preprocess( - (val) => - val === "" || val === null || val === undefined ? undefined : String(val).trim(), - z.string().optional() - ); - -// Helper for threshold fields -const thresholdField = (min: number, max: number, unit: string) => - z.preprocess( - (val) => (val === "" || val === null || val === undefined ? undefined : val), - z.coerce - .number() - .int() - .min(min, `Min ${min}${unit}`) - .max(max, `Max ${max}${unit}`) - .optional() - ); - export const settingsSchema = z.object({ systemEmailIgnoreTLS: z.boolean(), systemEmailRequireTLS: z.boolean(), systemEmailRejectUnauthorized: z.boolean(), - systemEmailConnectionHost: optionalString(), + systemEmailConnectionHost: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), systemEmailSecure: z.boolean().optional(), systemEmailPool: z.boolean().optional(), showURL: z.boolean().optional(), - checkTTL: z.coerce - .number() - .int() - .min(1, "Please enter a value") - .max(365, "Maximum 365 days"), - pagespeedApiKey: optionalString(), - systemEmailHost: z.preprocess( - (val) => - val === "" || val === null || val === undefined ? undefined : String(val).trim(), - z - .string() - .regex(/^[a-zA-Z0-9.-]+$/, "Invalid hostname or IP address") - .optional() - ), - systemEmailPort: z.preprocess( - (val) => (val === "" || val === null || val === undefined ? undefined : val), - z.coerce - .number() - .int() - .min(1, "Port must be at least 1") - .max(65535, "Port must be at most 65535") - .optional() - ), - systemEmailAddress: z.preprocess((val) => { - if (val === "" || val === null || val === undefined) return undefined; - return String(val).toLowerCase().trim(); - }, z.string().email("Please enter a valid email address").optional()), - systemEmailUser: optionalString(), - systemEmailPassword: optionalString(), - systemEmailTLSServername: optionalString(), + checkTTL: z.number().int().min(1, "Please enter a value").max(365, "Maximum 365 days"), + pagespeedApiKey: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailHost: z + .string() + .regex(/^[a-zA-Z0-9.-]*$/, "Invalid hostname or IP address") + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailPort: z.number().int().min(1).max(65535).optional(), + systemEmailAddress: z + .string() + .email("Please enter a valid email address") + .or(z.literal("")) + .transform((val) => (val === "" ? undefined : val.toLowerCase().trim())) + .optional(), + systemEmailUser: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailPassword: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailTLSServername: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), globalThresholds: z .object({ - cpu: thresholdField(0, 100, "%"), - memory: thresholdField(0, 100, "%"), - disk: thresholdField(0, 100, "%"), - temperature: thresholdField(0, 150, "°C"), + cpu: z + .number() + .int() + .min(1, "Please enter a valid value") + .max(100, "Maximum value is 100") + .optional(), + memory: z + .number() + .int() + .min(1, "Please enter a valid value") + .max(100, "Maximum value is 100") + .optional(), + disk: z + .number() + .int() + .min(1, "Please enter a valid value") + .max(100, "Maximum value is 100") + .optional(), + temperature: z + .number() + .int() + .min(1, "Please enter a valid value") + .max(150, "Maximum value is 150") + .optional(), }) .optional(), }); diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 52ae92fe5..e47ab64cc 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -38,7 +38,13 @@ "toggleTheme": "Toggles light & dark", "flushQueue": "Flush queue", "notFound": "Go to the main dashboard", - "resetPassword": "Reset password" + "resetPassword": "Reset password", + "reset": "Reset", + "clear": "Clear", + "addDemo": "Add demo monitors", + "removeMonitors": "Remove monitors", + "sendTestEmail": "Send test email", + "exportToJSON": "Export to JSON" }, "charts": { "labels": { @@ -543,6 +549,27 @@ "label": "Temperature alert threshold (°C)" } } + }, + "url": { + "title": "Monitor IP/URL on Status Page", + "description": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.", + "option": { + "showURL": { + "label": "Display IP/URL on status page", + "enabled": "Enabled", + "disabled": "Disabled" + } + } + }, + "stats": { + "title": "Monitor history", + "description": "Clear all monitoring history and statistics for your team. This action is irreversible.", + "buttonText": "Clear all stats", + "dialog": { + "title": "Clear all monitoring history?", + "description": "This will permanently delete all monitoring history, statistics, and check data for your team. This action cannot be undone.", + "confirm": "Yes, clear all stats" + } } } }, @@ -908,92 +935,166 @@ } }, "settings": { - "aboutSettings": { - "labelDevelopedBy": "Developed by Bluewave Labs", - "labelVersion": "Version", - "title": "About" - }, - "demoMonitorsSettings": { - "buttonAddMonitors": "Add demo monitors", - "description": "Add sample monitors for demonstration purposes.", - "title": "Demo monitors" - }, - "emailSettings": { - "buttonSendTestEmail": "Send test e-mail", - "description": "Configure the email settings for your system. This is used to send notifications and alerts.", - "descriptionTransport": "This builds an SMTP transport for NodeMailer", - "labelAddress": "Email address - Used for authentication", - "labelConnectionHost": "Email connection host - Hostname to use in the HELO/EHLO greeting", - "labelHost": "Email host - Hostname or IP address to connect to", - "labelIgnoreTLS": "Disable STARTTLS: Don't use TLS even if the server supports it", - "labelPassword": "Email password - Password for authentication", - "labelPasswordSet": "Password is set. Click Reset to change it.", - "labelPool": "Enable connection pooling: Reuse existing connections to improve performance", - "labelPort": "Email port - Port to connect to", - "labelRejectUnauthorized": "Reject invalid certificates: Reject connections with self-signed or untrusted certificates", - "labelRequireTLS": "Force STARTTLS: Require TLS upgrade, fail if not supported", - "labelSecure": "Use SSL (recommended): Encrypt the connection using SSL/TLS", - "labelTLSServername": "TLS Servername - Optional Hostname for TLS Validation when host is an IP", - "labelUser": "Email user - Username for authentication, overrides email address if specified", - "linkTransport": "See specifications here", - "placeholderUser": "Leave empty if not required", - "title": "Email", - "toastEmailRequiredFieldsError": "Email address, host, port and password are required" - }, - "globalThresholds": { - "title": "Global Thresholds", - "description": "Configure global CPU, Memory, Disk, and Temperature thresholds. If a value is provided, it will automatically be enabled for monitoring." - }, - "pageSpeedSettings": { - "description": "Enter your Google PageSpeed API key to enable Google PageSpeed monitoring. Click Reset to update the key.", - "labelApiKeySet": "API key is set. Click Reset to change it.", - "labelApiKey": "PageSpeed API key", - "title": "Google PageSpeed API key" - }, - "saveButtonLabel": "Save", - "statsSettings": { - "clearAllStatsButton": "Clear all stats", - "clearAllStatsDescription": "Clear all stats. This is irreversible.", - "clearAllStatsDialogConfirm": "Yes, clear all stats", - "clearAllStatsDialogDescription": "Once removed, the monitoring history and stats cannot be retrieved.", - "clearAllStatsDialogTitle": "Do you want to clear all stats?", - "description": "Define how long you want to retain historical data. You can also clear all existing data.", - "labelTTL": "The days you want to keep monitoring history.", - "labelTTLOptional": "0 for infinite", - "title": "Monitor history" - }, - "systemResetSettings": { - "buttonRemoveAllMonitors": "Remove all monitors", - "description": "Remove all monitors from your system.", - "dialogConfirm": "Yes, remove all monitors", - "dialogDescription": "Once removed, the monitors cannot be retrieved.", - "dialogTitle": "Do you want to remove all monitors?", - "title": "System reset" - }, - "timezoneSettings": { - "description": "Select the timezone used to display dates and times throughout the application.", - "label": "Display timezone", - "title": "Display timezone" - }, - "title": "Settings", - "uiSettings": { - "description": "Switch between light and dark mode, or change user interface language.", - "labelLanguage": "Language", - "labelTheme": "Theme mode", - "labelChartType": "Chart type", - "chartTypeHistogram": "Histogram", - "chartTypeHeatmap": "Heatmap", - "title": "Appearance" - }, - "urlSettings": { - "description": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.", - "label": "Display IP/URL on status page", - "selectDisabled": "Disabled", - "selectEnabled": "Enabled", - "title": "Monitor IP/URL on Status Page" + "form": { + "timezone": { + "title": "Display timezone", + "description": "Select the timezone used to display dates and times throughout the application.", + "option": { + "timezone": { + "label": "Display timezone" + } + } + }, + "ui": { + "title": "Appearance", + "description": "Switch between light and dark mode, change language, or customize chart display type.", + "option": { + "theme": { + "label": "Theme mode", + "light": "Light", + "dark": "Dark" + }, + "language": { + "label": "Language" + }, + "chartType": { + "label": "Chart type", + "histogram": "Histogram", + "heatmap": "Heatmap" + } + } + }, + "pagespeed": { + "title": "Google PageSpeed API key", + "description": "Enter your Google PageSpeed API key to enable Google PageSpeed monitoring. Click Reset to update the key.", + "option": { + "apiKey": { + "label": "PageSpeed API key", + "labelSet": "API key is set. Click Reset to change it.", + "placeholder": "Enter your Google PageSpeed API key" + } + } + }, + "url": { + "title": "Monitor IP/URL on Status Page", + "description": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.", + "option": { + "showURL": { + "label": "Display IP/URL on status page", + "enabled": "Enabled", + "disabled": "Disabled" + } + } + }, + "stats": { + "title": "Monitor history", + "description": "Define how long you want to retain historical data. You can also clear all existing data.", + "option": { + "clear": { + "label": "Clear all stats. This is irreversible." + } + }, + "dialog": { + "title": "Clear all monitoring history?", + "description": "This cannot be undone" + } + }, + "thresholds": { + "title": "Global Thresholds", + "description": "Set global CPU, Memory, Disk, and Temperature thresholds for infrastructure monitoring. These apply to all hardware monitors unless overridden.", + "option": { + "cpu": { + "label": "CPU Threshold (%)", + "placeholder": "1 - 100" + }, + "memory": { + "label": "Memory Threshold (%)", + "placeholder": "1 - 100" + }, + "disk": { + "label": "Disk Threshold (%)", + "placeholder": "1 - 100" + }, + "temperature": { + "label": "Temperature Threshold (°C)", + "placeholder": "1 - 150" + } + } + }, + "email": { + "title": "Email settings", + "description": "Configure the email settings for your system. This is used to send notifications and alerts.", + "descriptionTransport": "This builds an SMTP transport for NodeMailer", + "descriptionTransportLink": "See specifications here", + "option": { + "host": { + "label": "Email host - Hostname or IP address to connect to", + "placeholder": "smtp.gmail.com" + }, + "port": { + "label": "Email port - Port to connect to", + "placeholder": "587" + }, + "address": { + "label": "Email address - Used for authentication", + "placeholder": "you@example.com" + }, + "user": { + "label": "Email user - Username for authentication, overrides email address if specified", + "placeholder": "Leave empty if not required" + }, + "password": { + "label": "Email password - Password for authentication", + "labelSet": "Password is set. Click Reset to change it.", + "placeholder": "Enter your password" + }, + "tlsServername": { + "label": "TLS Servername - Optional Hostname for TLS Validation when host is an IP", + "placeholder": "example.com" + }, + "connectionHost": { + "label": "Email connection host - Hostname to use in the HELO/EHLO greeting", + "placeholder": "localhost" + }, + "secure": { + "label": "Use SSL (recommended): Encrypt the connection using SSL/TLS" + }, + "pool": { + "label": "Enable connection pooling: Reuse existing connections to improve performance" + }, + "ignoreTLS": { + "label": "Disable STARTTLS: Don't use TLS even if the server supports it" + }, + "requireTLS": { + "label": "Force STARTTLS: Require TLS upgrade, fail if not supported" + }, + "rejectUnauthorized": { + "label": "Reject invalid certificates: Reject connections with self-signed or untrusted certificates" + } + } + }, + "demoMonitors": { + "title": "Demo monitors", + "description": "Add sample monitors for demonstration purposes." + }, + "removeMonitors": { + "title": "System reset", + "description": "Remove all monitors from your system.", + "dialog": { + "title": "Remove all monitors?", + "description": "This cannot be undone." + } + }, + "exportMonitors": { + "title": "Export monitors", + "description": "Export your monitors data as a JSON file for backup or transfer." + }, + "about": { + "title": "About", + "developedBy": "Developed by Bluewave Labs" + } } }, - "statusPages": { "deleteSuccess": "Status page deleted successfully", "fallback": { diff --git a/client/src/main.tsx b/client/src/main.tsx index f075886d3..1f5d4d2ef 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -5,7 +5,7 @@ import { BrowserRouter as Router } from "react-router-dom"; import { Provider } from "react-redux"; import { persistor, store } from "@/store.js"; import { PersistGate } from "redux-persist/integration/react"; -import I18nLoader from "./Components/v2/i18nLoader"; +import I18nLoader from "./Components/i18nLoader/index.js"; import { initApiClient } from "./Utils/ApiClient.js"; initApiClient(store); diff --git a/client/vite.config.ts b/client/vite.config.ts index f381eb5ed..ffa4f544d 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -7,7 +7,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export default defineConfig(({}) => { - let version = "3.3"; + let version = "3.4.0"; return { base: "/",