Resolved conflict

This commit is contained in:
Daniel Cojocea
2024-06-21 19:31:27 -04:00
36 changed files with 1348 additions and 72 deletions

View File

@@ -19,6 +19,7 @@
"@mui/x-date-pickers": "7.3.2",
"@reduxjs/toolkit": "2.2.5",
"axios": "1.7.2",
"chart.js": "^4.4.3",
"dayjs": "1.11.11",
"joi": "17.13.1",
"jwt-decode": "^4.0.0",
@@ -27,7 +28,8 @@
"react-redux": "9.1.2",
"react-router": "^6.23.0",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5"
"react-toastify": "^10.0.5",
"redux-persist": "6.0.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
@@ -1089,6 +1091,11 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
@@ -2352,6 +2359,17 @@
"node": ">=0.8.0"
}
},
"node_modules/chart.js": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
"integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -4745,6 +4763,15 @@
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"node_modules/redux-persist": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
"license": "MIT",
"peerDependencies": {
"redux": ">4.0.0"
}
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",

View File

@@ -21,6 +21,7 @@
"@mui/x-date-pickers": "7.3.2",
"@reduxjs/toolkit": "2.2.5",
"axios": "1.7.2",
"chart.js": "^4.4.3",
"dayjs": "1.11.11",
"joi": "17.13.1",
"jwt-decode": "^4.0.0",
@@ -29,7 +30,8 @@
"react-redux": "9.1.2",
"react-router": "^6.23.0",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5"
"react-toastify": "^10.0.5",
"redux-persist": "6.0.0"
},
"devDependencies": {
"@types/react": "^18.2.66",

View File

@@ -0,0 +1,23 @@
import React from "react";
import "./barChart.css";
import PropTypes from "prop-types";
const BarChart = ({ checks = [] }) => {
return (
<div className="bar-chart">
{checks.map((value, index) => (
<div
key={index}
className={`bar ${value.status ? "green" : "red"}`}
style={{ height: `${value.responseTime / 3}%` }}
/>
))}
</div>
);
};
BarChart.propTypes = {
checks: PropTypes.array,
};
export default BarChart;

View File

@@ -0,0 +1,20 @@
.bar-chart {
display: flex;
justify-content: space-around;
align-items: flex-end;
height: 50px;
width: 300px;
}
.bar {
width: 7%;
transition: height 0.3s ease;
}
.green {
background-color: var(--env-var-color-23);
}
.red {
background-color: var(--env-var-color-24);
}

View File

@@ -12,7 +12,7 @@
}
.server-status-tile {
width: 230px;
width: 310px;
padding: var(--spacing-general-1);
border: 1px solid var(--color-border-0);
border-radius: var(--border-radius-0);

View File

@@ -0,0 +1,24 @@
.config-box {
display: flex;
padding: 30px 40px;
border: 1px solid var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
justify-content: space-between;
padding-bottom: 50px;
}
.config-box-desc {
width: 35%;
}
.config-box-desc-title {
color: var(--env-var-color-5);
font-size: var(--env-var-font-size-large);
font-weight: 700;
margin-bottom: var(--env-var-spacing-1);
}
.config-box-desc-text {
color: var(--env-var-color-5);
font-size: var(--env-var-font-size-medium);
}

View File

@@ -0,0 +1,13 @@
import "./index.css";
import React from "react";
const ConfigBox = ({ leftLayout, rightLayout }) => {
return (
<div className="config-box">
{leftLayout}
{rightLayout}
</div>
);
};
export default ConfigBox;

View File

@@ -8,8 +8,110 @@
display: flex;
}
.current-monitors-bar {
display: flex;
justify-content: space-between;
}
.current-monitors-title {
font-size: 1rem;
font-weight: bold;
margin-right: var();
margin-right: var(--env-var-spacing-1);
}
.current-monitors-counter {
width: var(--env-var-spacing-2);
height: var(--env-var-spacing-2);
background-color: var(--env-var-color-15);
border: 1px solid var(--env-var-color-6);
border-radius: 50%;
text-align: center;
align-items: center;
}
.monitors-v-gaping {
height: var(--env-var-spacing-3);
}
.current-monitors-table {
width: 100%;
text-align: left;
}
.current-monitors-table {
width: 100%;
text-align: left;
border-collapse: collapse;
border: 1px solid var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
}
.current-monitors-table th,
.current-monitors-table td {
border: 1px solid var(--env-var-color-16);
border-left: none;
border-right: none;
padding: 8px;
}
.theader-row {
background-color: var(--env-var-color-13);
color: var(--env-var-color-2);
font-weight: 500;
}
.theader-row td {
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
font-size: var(--env-var-font-size-medium);
}
.host-list-item {
display: flex;
align-items: center;
cursor: pointer;
}
.host-list-item-open {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
margin-right: 8px;
}
.host-list-item-name {
font-size: var(--env-var-font-size-medium);
font-weight: bold;
margin-right: var(--env-var-spacing-1);
}
.host-list-item-precentage {
font-size: var(--env-var-font-size-medium);
}
.tbody-row td {
padding: var(--env-var-spacing-2) var(--env-var-img-width-2);
}
.host-status-box {
display: flex;
width: fit-content;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-2);
align-items: center;
padding: calc(var(--env-var-spacing-1) / 2);
}
.host-status-box-circle {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: calc(var(--env-var-spacing-1) / 2);
}
.host-actions {
margin: auto;
cursor: pointer;
}
.tbody-row-cell.actions-cell {
padding-left: 30px;
}

View File

@@ -1,7 +1,10 @@
import SearchTextField from "../TextFields/Search/SearchTextField";
import "./index.css";
import React from "react";
import HostsTable from "../HostsTable";
import Pagination from "../Pagination";
const CurrentMonitors = () => {
const CurrentMonitors = ({ monitors }) => {
return (
<div className="current-monitors">
<div className="current-monitors-bar">
@@ -9,8 +12,14 @@ const CurrentMonitors = () => {
<div className="current-monitors-title">Current monitors</div>
<div className="current-monitors-counter">5</div>
</div>
<div className="current-monitors-search-bar"></div>
<div className="current-monitors-search-bar">
<SearchTextField />
</div>
</div>
<div className="monitors-v-gaping"></div>
<HostsTable monitors={monitors} />
<div className="monitors-v-gaping"></div>
<Pagination />
</div>
);
};

View File

@@ -0,0 +1,13 @@
#dashboard-setting-menu-item {
width: 135px;
padding: 6px;
margin: 5px 12px;
}
#dashboard-setting-menu-item:hover {
color: var(--env-var-color-26);
}
#menu-appbar {
padding: var(--env-var-spacing-1);
}

View File

@@ -1,15 +1,22 @@
import { useState } from 'react';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import Menu from '@mui/material/Menu';
import Tooltip from '@mui/material/Tooltip';
import MenuItem from '@mui/material/MenuItem';
import Typography from '@mui/material/Typography';
import SvgIcon from '@mui/material/SvgIcon';
import SettingsIcon from '@mui/icons-material/Settings';
import { useTheme } from '@mui/material/styles';
import "./index.css";
import { useState } from "react";
import Box from "@mui/material/Box";
import IconButton from "@mui/material/IconButton";
import Menu from "@mui/material/Menu";
import Tooltip from "@mui/material/Tooltip";
import MenuItem from "@mui/material/MenuItem";
import Typography from "@mui/material/Typography";
import SvgIcon from "@mui/material/SvgIcon";
import SettingsIcon from "@mui/icons-material/Settings";
import { useTheme } from "@mui/material/styles";
const settings = ['Open Site', 'Detailed View', 'Incidents', 'Configure', 'Remove'];
const settings = [
"Open Site",
"Detailed View",
"Incidents",
"Configure",
"Remove",
];
/**
* DashboardSettings component
@@ -54,20 +61,29 @@ function DashboardSettings() {
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
vertical: "top",
horizontal: "right",
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{settings.map((setting) => (
<MenuItem key={setting} onClick={handleCloseUserMenu}>
<Typography textAlign="center">{setting}</Typography>
<MenuItem
id="dashboard-setting-menu-item"
key={setting}
onClick={handleCloseUserMenu}
>
<Typography
style={{ fontSize: "var(--env-var-font-size-medium)" }}
textAlign="center"
>
{setting}
</Typography>
</MenuItem>
))}
</Menu>

View File

@@ -0,0 +1,18 @@
import React from "react";
import OpenIt from "../../assets/Images/Icon-open-in-tab-gray.png";
function Host(title, precentage, statusColor, link) {
return (
<div className="host-list-item">
<a href={link} target="_blank">
<img className="host-list-item-open" src={OpenIt} alt="OpenIt" />
</a>
<div className="host-list-item-name">{title}</div>
<div className="host-list-item-precentage" style={{ color: statusColor }}>
{precentage}%
</div>
</div>
);
}
export default Host;

View File

@@ -0,0 +1,8 @@
import React from "react";
import DashboardSettings from "../DashboardSettingsIcon";
function HostActions() {
return <DashboardSettings />;
}
export default HostActions;

View File

@@ -0,0 +1,18 @@
import React from "react";
function HostStatus(backgroundColor, status, dotColor) {
return (
<div
className="host-status-box"
style={{ backgroundColor: backgroundColor }}
>
<div
className="host-status-box-circle"
style={{ backgroundColor: dotColor }}
></div>
<div className="host-status-box-text">{status}</div>
</div>
);
}
export default HostStatus;

View File

@@ -0,0 +1,30 @@
import React from "react";
const HostsTable = ({ monitors }) => {
return (
<div className="current-monitors-table-holder">
<table className="current-monitors-table">
<thead>
<tr className="theader-row">
<td className="theader-row-cell">Host</td>
<td className="theader-row-cell">Status</td>
<td className="theader-row-cell">Team</td>
<td className="theader-row-cell">Actions</td>
</tr>
</thead>
<tbody>
{monitors.map((monitor, index) => (
<tr className="tbody-row" key={index}>
<td className="tbody-row-cell">{monitor.host}</td>
<td className="tbody-row-cell">{monitor.status}</td>
<td className="tbody-row-cell">{monitor.team}</td>
<td className="tbody-row-cell actions-cell">{monitor.actions}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default HostsTable;

View File

@@ -0,0 +1,33 @@
.pagination-holder {
display: flex;
width: 100%;
justify-content: center;
}
.MuiPagination-root {
.MuiPagination-ul {
flex-wrap: nowrap;
li {
&:first-child {
flex-basis: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
> button::after {
margin-left: 10px;
content: "previous";
}
}
&:last-child {
flex-basis: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
> button::before {
margin-right: 10px;
content: "next";
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
import React from "react";
import { Pagination as MuiPagination } from "@mui/material";
import "./index.css";
const Pagination = () => {
return (
<div className="pagination-holder">
<MuiPagination count={10} variant="outlined" shape="rounded" />
</div>
);
};
export default Pagination;

View File

@@ -0,0 +1,52 @@
/**
* RadioButton component.
*
* @component
* @example
* // Usage:
* <RadioButton
* title="Radio Button Title"
* desc="Radio Button Description"
* size="small"
* />
*
* @param {Object} props - The component props.
* @param {string} props.title - The title of the radio button.
* @param {string} [props.desc] - The description of the radio button.
* @param {string} [props.size="small"] - The size of the radio button.
* @returns {JSX.Element} - The rendered RadioButton component.
*/
import { FormControlLabel, Radio } from "@mui/material";
import React from "react";
import PropTypes from "prop-types";
function RadioButton(props) {
return (
<div className="custom-radio-button">
<FormControlLabel
value="check"
control={<Radio size={props.size} />}
label={
<div>
<div className="service-check-list-title">{props.title}</div>
<div className="service-check-list-desc">{props.desc}</div>
</div>
}
labelPlacement="end"
/>
</div>
);
}
RadioButton.propTypes = {
title: PropTypes.string.isRequired,
desc: PropTypes.string,
size: PropTypes.string,
};
RadioButton.defaultProps = {
size: "small",
};
export default RadioButton;

View File

@@ -0,0 +1,17 @@
import "./flexibileTextField.css";
import React from "react";
const FlexibileTextField = ({ title, type, placeholder }) => {
return (
<div className="flexibile-textfield">
<div className="flexibile-textfield-title">{title}</div>
<input
type={type}
className="flexibile-textfield-input"
placeholder={placeholder}
/>
</div>
);
};
export default FlexibileTextField;

View File

@@ -0,0 +1,22 @@
.flexibile-textfield-title {
color: var(--env-var-color-5);
font-size: var(--env-var-font-size-medium);
}
.flexibile-textfield-input {
width: 292.8px;
height: 6.8px;
padding: var(--env-var-spacing-1);
margin-top: 10px;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-1);
}
.flexibile-textfield-input:focus {
outline: none;
}
.flexibile-textfield-input::placeholder {
color: var(--env-var-color-25);
font-size: var(--env-var-font-size-medium);
}

View File

@@ -0,0 +1,14 @@
import "./searchTextField.css";
import React from "react";
import Search from "../../../assets/Images/Icon-search-gray.png";
const SearchTextField = () => {
return (
<div className="search-field-holder">
<img className="search-field-icon" src={Search} alt="Search" />
<input className="search-field" type="text" placeholder="Search" />
</div>
);
};
export default SearchTextField;

View File

@@ -0,0 +1,26 @@
.search-field-holder {
display: flex;
align-items: center;
width: 180px;
height: 9px;
padding: var(--env-var-spacing-1);
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-1);
}
.search-field-icon {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
margin-right: calc(var(--env-var-img-width-2) / 2);
}
.search-field {
width: 150px;
border: none;
outline: none;
font-size: var(--env-var-font-size-medium);
}
.search-field::placeholder {
color: var(--env-var-color-14);
}

View File

@@ -22,13 +22,28 @@ export const getMonitors = createAsyncThunk(
}
);
export const getMonitorsByUserId = createAsyncThunk(
"montiors/getMonitorsByUserId",
async (token, thunkApi) => {
try {
// TODO get userId and make request
console.log(token);
} catch (error) {
return thunkApi.rejectWithValue(error.response.data);
}
}
);
const monitorsSlice = createSlice({
name: "monitors",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getMonitors.pending, (state, action) => {
// *****************************************************
// All Monitors
// *****************************************************
.addCase(getMonitors.pending, (state) => {
state.isLoading = true;
})
.addCase(getMonitors.fulfilled, (state, action) => {
@@ -42,6 +57,24 @@ const monitorsSlice = createSlice({
state.isLoading = false;
state.success = action.payload.success;
state.msg = action.payload.msg;
})
// *****************************************************
// Monitors by userId
// *****************************************************
.addCase(getMonitorsByUserId.pending, (state) => {
state.isLoading = true;
})
.addCase(getMonitorsByUserId.fulfilled, (state, action) => {
state.isLoading = false;
state.success = action.payload.msg;
console.log(action.payload);
state.monitors = action.payload.data;
})
.addCase(getMonitorsByUserId.rejected, (state, action) => {
state.isLoading = false;
state.success = action.payload.success;
state.msg = action.payload.msg;
});
},
});

View File

@@ -0,0 +1,534 @@
{
"success": true,
"msg": "Got monitor for 666c9146c9bfa20db790b1df successfully\"",
"data": [
{
"_id": "6671d8469ba14f9f260eed23",
"userId": "666c9146c9bfa20db790b1df",
"name": "Google Monitor",
"description": "Google",
"type": "ping",
"url": "127.0.0.1",
"isActive": true,
"interval": 10000,
"createdAt": "2024-06-18T18:56:06.212Z",
"updatedAt": "2024-06-18T18:56:06.212Z",
"__v": 0,
"checks": [
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 143,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 70,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 143,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 70,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d8549ba14f9f260eed2d",
"monitorId": "6671d8469ba14f9f260eed23",
"status": false,
"responseTime": 120,
"createdAt": "2024-06-18T18:56:20.016Z",
"updatedAt": "2024-06-18T18:56:20.016Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 200,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d8549ba14f9f260eed2d",
"monitorId": "6671d8469ba14f9f260eed23",
"status": false,
"responseTime": 250,
"createdAt": "2024-06-18T18:56:20.016Z",
"updatedAt": "2024-06-18T18:56:20.016Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": false,
"responseTime": 180,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d8549ba14f9f260eed2d",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 200,
"createdAt": "2024-06-18T18:56:20.016Z",
"updatedAt": "2024-06-18T18:56:20.016Z",
"__v": 0
},
{
"_id": "6671d84a9ba14f9f260eed27",
"monitorId": "6671d8469ba14f9f260eed23",
"status": false,
"responseTime": 100,
"createdAt": "2024-06-18T18:56:10.082Z",
"updatedAt": "2024-06-18T18:56:10.082Z",
"__v": 0
},
{
"_id": "6671d8549ba14f9f260eed2d",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 210,
"createdAt": "2024-06-18T18:56:20.016Z",
"updatedAt": "2024-06-18T18:56:20.016Z",
"__v": 0
},
{
"_id": "6671d85e9ba14f9f260eed3c",
"monitorId": "6671d8469ba14f9f260eed23",
"status": true,
"responseTime": 200,
"createdAt": "2024-06-18T18:56:30.050Z",
"updatedAt": "2024-06-18T18:56:30.050Z",
"__v": 0
}
]
},
{
"_id": "6671d8529ba14f9f260eed2b",
"userId": "666c9146c9bfa20db790b1df",
"name": "Google Monitor",
"description": "Google",
"type": "http",
"url": "https://www.google.com/404",
"isActive": true,
"interval": 10000,
"createdAt": "2024-06-18T18:56:18.555Z",
"updatedAt": "2024-06-18T18:56:18.555Z",
"__v": 0,
"checks": [
{
"_id": "6671d8549ba14f9f260eed31",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 167,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:20.190Z",
"updatedAt": "2024-06-18T18:56:20.190Z",
"__v": 0
},
{
"_id": "6671d85e9ba14f9f260eed3e",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 228,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:30.279Z",
"updatedAt": "2024-06-18T18:56:30.279Z",
"__v": 0
},
{
"_id": "6671d8669ba14f9f260eed47",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 112,
"statusCode": 200,
"createdAt": "2024-06-18T18:56:38.381Z",
"updatedAt": "2024-06-18T18:56:38.381Z",
"__v": 0
},
{
"_id": "6671d8809ba14f9f260eed6b",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 134,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:04.846Z",
"updatedAt": "2024-06-18T18:57:04.846Z",
"__v": 0
},
{
"_id": "6671d86d9ba14f9f260eed50",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 301,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:45.492Z",
"updatedAt": "2024-06-18T18:56:45.492Z",
"__v": 0
},
{
"_id": "6671d8739ba14f9f260eed59",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 84,
"statusCode": 200,
"createdAt": "2024-06-18T18:56:51.614Z",
"updatedAt": "2024-06-18T18:56:51.614Z",
"__v": 0
},
{
"_id": "6671d87a9ba14f9f260eed62",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 189,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:58.734Z",
"updatedAt": "2024-06-18T18:56:58.734Z",
"__v": 0
},
{
"_id": "6671d8809ba14f9f260eed6b",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 134,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:04.846Z",
"updatedAt": "2024-06-18T18:57:04.846Z",
"__v": 0
},
{
"_id": "6671d8549ba14f9f260eed31",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 167,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:20.190Z",
"updatedAt": "2024-06-18T18:56:20.190Z",
"__v": 0
},
{
"_id": "6671d85e9ba14f9f260eed3e",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 228,
"statusCode": 404,
"createdAt": "2024-06-18T18:56:30.279Z",
"updatedAt": "2024-06-18T18:56:30.279Z",
"__v": 0
},
{
"_id": "6671d8669ba14f9f260eed47",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 112,
"statusCode": 200,
"createdAt": "2024-06-18T18:56:38.381Z",
"updatedAt": "2024-06-18T18:56:38.381Z",
"__v": 0
},
{
"_id": "6671d8879ba14f9f260eed74",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 247,
"statusCode": 404,
"createdAt": "2024-06-18T18:57:11.958Z",
"updatedAt": "2024-06-18T18:57:11.958Z",
"__v": 0
}
]
},
{
"_id": "6671d8619ba14f9f260eed4c",
"userId": "666c9146c9bfa20db790b1df",
"name": "Amazon Monitor",
"description": "Amazon website",
"type": "http",
"url": "https://www.amazon.com",
"isActive": true,
"interval": 15000,
"createdAt": "2024-06-18T18:56:49.419Z",
"updatedAt": "2024-06-18T18:56:49.419Z",
"__v": 0,
"checks": [
{
"_id": "6671d8919ba14f9f260eed7d",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 92,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:21.076Z",
"updatedAt": "2024-06-18T18:57:21.076Z",
"__v": 0
},
{
"_id": "6671d8989ba14f9f260eed86",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 205,
"statusCode": 404,
"createdAt": "2024-06-18T18:57:28.188Z",
"updatedAt": "2024-06-18T18:57:28.188Z",
"__v": 0
},
{
"_id": "6671d89e9ba14f9f260eed8f",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 159,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:34.300Z",
"updatedAt": "2024-06-18T18:57:34.300Z",
"__v": 0
},
{
"_id": "6671d8a59ba14f9f260eed98",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 271,
"statusCode": 404,
"createdAt": "2024-06-18T18:57:41.421Z",
"updatedAt": "2024-06-18T18:57:41.421Z",
"__v": 0
},
{
"_id": "6671d8ac9ba14f9f260eeda1",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 127,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:48.532Z",
"updatedAt": "2024-06-18T18:57:48.532Z",
"__v": 0
},
{
"_id": "6671d8a59ba14f9f260eed98",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 271,
"statusCode": 404,
"createdAt": "2024-06-18T18:57:41.421Z",
"updatedAt": "2024-06-18T18:57:41.421Z",
"__v": 0
},
{
"_id": "6671d8ac9ba14f9f260eeda1",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 127,
"statusCode": 200,
"createdAt": "2024-06-18T18:57:48.532Z",
"updatedAt": "2024-06-18T18:57:48.532Z",
"__v": 0
},
{
"_id": "6671d8b29ba14f9f260eedaa",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 193,
"statusCode": 404,
"createdAt": "2024-06-18T18:57:54.643Z",
"updatedAt": "2024-06-18T18:57:54.643Z",
"__v": 0
},
{
"_id": "6671d8b89ba14f9f260eedb3",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 104,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:00.754Z",
"updatedAt": "2024-06-18T18:58:00.754Z",
"__v": 0
},
{
"_id": "6671d8bf9ba14f9f260eedbc",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 231,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:07.865Z",
"updatedAt": "2024-06-18T18:58:07.865Z",
"__v": 0
},
{
"_id": "6671d8c59ba14f9f260eedc5",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 147,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:13.977Z",
"updatedAt": "2024-06-18T18:58:13.977Z",
"__v": 0
},
{
"_id": "6671d8cc9ba14f9f260eedce",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 283,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:20.088Z",
"updatedAt": "2024-06-18T18:58:20.088Z",
"__v": 0
}
]
},
{
"_id": "6671d8789ba14f9f260eed66",
"userId": "666c9146c9bfa20db790b1df",
"name": "Facebook Monitor",
"description": "Facebook website",
"type": "http",
"url": "https://www.facebook.com",
"isActive": true,
"interval": 20000,
"createdAt": "2024-06-18T18:57:12.798Z",
"updatedAt": "2024-06-18T18:57:12.798Z",
"__v": 0,
"checks": [
{
"_id": "6671d8d29ba14f9f260eedd7",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 81,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:26.199Z",
"updatedAt": "2024-06-18T18:58:26.199Z",
"__v": 0
},
{
"_id": "6671d8d89ba14f9f260eede0",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 218,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:32.311Z",
"updatedAt": "2024-06-18T18:58:32.311Z",
"__v": 0
},
{
"_id": "6671d8df9ba14f9f260eede9",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 138,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:39.422Z",
"updatedAt": "2024-06-18T18:58:39.422Z",
"__v": 0
},
{
"_id": "6671d8e59ba14f9f260eedf2",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 257,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:45.533Z",
"updatedAt": "2024-06-18T18:58:45.533Z",
"__v": 0
},
{
"_id": "6671d8eb9ba14f9f260eedfb",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 116,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:51.644Z",
"updatedAt": "2024-06-18T18:58:51.644Z",
"__v": 0
},
{
"_id": "6671d8f19ba14f9f260eee04",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 184,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:57.755Z",
"updatedAt": "2024-06-18T18:58:57.755Z",
"__v": 0
},
{
"_id": "6671d8f79ba14f9f260eee0d",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 97,
"statusCode": 200,
"createdAt": "2024-06-18T18:59:03.867Z",
"updatedAt": "2024-06-18T18:59:03.867Z",
"__v": 0
},
{
"_id": "6671d8fd9ba14f9f260eee16",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 240,
"statusCode": 404,
"createdAt": "2024-06-18T18:59:09.978Z",
"updatedAt": "2024-06-18T18:59:09.978Z",
"__v": 0
},
{
"_id": "6671d9039ba14f9f260eee1f",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 135,
"statusCode": 200,
"createdAt": "2024-06-18T18:59:16.089Z",
"updatedAt": "2024-06-18T18:59:16.089Z",
"__v": 0
},
{
"_id": "6671d8d89ba14f9f260eede0",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 218,
"statusCode": 404,
"createdAt": "2024-06-18T18:58:32.311Z",
"updatedAt": "2024-06-18T18:58:32.311Z",
"__v": 0
},
{
"_id": "6671d8df9ba14f9f260eede9",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": true,
"responseTime": 138,
"statusCode": 200,
"createdAt": "2024-06-18T18:58:39.422Z",
"updatedAt": "2024-06-18T18:58:39.422Z",
"__v": 0
},
{
"_id": "6671d9089ba14f9f260eee28",
"monitorId": "6671d8529ba14f9f260eed2b",
"status": false,
"responseTime": 269,
"statusCode": 404,
"createdAt": "2024-06-18T18:59:22.200Z",
"updatedAt": "2024-06-18T18:59:22.200Z",
"__v": 0
}
]
}
]
}

View File

@@ -27,13 +27,14 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import AddIcon from "@mui/icons-material/Add";
import Divider from "@mui/material/Divider";
import UploadIcon from '@mui/icons-material/Upload';
import SendIcon from '@mui/icons-material/Send';
import UploadIcon from "@mui/icons-material/Upload";
import SendIcon from "@mui/icons-material/Send";
// Redux
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { getMonitors } from "../../Features/Monitors/monitorsSlice";
import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice";
const cols = [
{
@@ -226,7 +227,7 @@ const Demo = () => {
level="primary"
label="Get Monitors"
onClick={() => {
dispatch(getMonitors());
dispatch(getMonitorsByUserId("User ID Here"));
}}
/>
</div>
@@ -393,7 +394,7 @@ const Demo = () => {
level="primary"
label="Upload"
position="start"
img={<UploadIcon/>}
img={<UploadIcon />}
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
@@ -401,11 +402,11 @@ const Demo = () => {
level="secondary"
label="Send"
position="end"
img={<SendIcon/>}
img={<SendIcon />}
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
<ButtonSpinner
level="error"
label="Disabled"
onClick={handleButtonSpinnerClick}

View File

@@ -0,0 +1,105 @@
import ConfigBox from "../../Components/ConfigBox";
import FlexibileTextField from "../../Components/TextFields/Flexibile/FlexibileTextField";
import "./index.css";
import React from "react";
import RadioButton from "../../Components/RadioButton";
const CreateNewMonitor = () => {
return (
<div>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<div className="new-monitor-header">Create new monitor</div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
<div className="config-box-desc">
<div className="config-box-desc-title">General settings</div>
<div className="config-box-desc-text">
Here you can select the URL of the host, together with the type of
monitor.
</div>
</div>
}
rightLayout={
<div className="config-box-inputs">
<FlexibileTextField
title="URL to monitor"
type="url"
placeholder="https://google.com"
/>
<div className="monitors-gaps-medium"></div>
<FlexibileTextField
title="Friendly name (optional)"
type="text"
placeholder="Google"
/>
</div>
}
/>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
<div className="config-box-desc">
<div className="config-box-desc-title">Checks to perform</div>
<div className="config-box-desc-text">
You can always add or remove checks after adding your site.
</div>
</div>
}
rightLayout={
<div className="service-check-list">
<RadioButton
title="HTTP/website monitoring"
desc="Use HTTP(s) to monitor your website or API endpoint."
/>
<div className="monitors-gaps-medium"></div>
<RadioButton
title="Ping monitoring"
desc="Check whether your server is available or not."
/>
<div className="monitors-gaps-medium"></div>
<RadioButton
title="Port monitoring"
desc="Monitor a specific service on your server."
/>
<div className="monitors-gaps-medium"></div>
</div>
}
/>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
<div className="config-box-desc">
<div className="config-box-desc-title">Incident notifications</div>
<div className="config-box-desc-text">
When there is an incident, notify users.
</div>
</div>
}
/>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
<div className="config-box-desc">
<div className="config-box-desc-title">Advanced settings</div>
</div>
}
/>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
<div className="config-box-desc">
<div className="config-box-desc-title">Proxy settings</div>
</div>
}
/>
</div>
);
};
export default CreateNewMonitor;

View File

@@ -0,0 +1,69 @@
import React, { useEffect, useState } from "react";
import Host from "../../Components/Host";
import HostStatus from "../../Components/HostStatus";
import HostActions from "../../Components/HostActions";
import BarChart from "../../Components/Charts/BarChart/BarChart";
import Button from "../../Components/Button";
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
import CurrentMonitors from "../../Components/CurrentMonitors";
import MockData from "../../Mock/sample_data.json";
const CurrentStats = (mockdata = true) => {
// The hardcoded part is passed as default value for the purpose of demonstration.
// Pass an empty array, [], before fetching the data
const [monitors, setMonitors] = useState([]);
useEffect(() => {
if (mockdata) {
console.log(MockData.data);
setMonitors(
MockData.data.map((item) => ({
host: Host(item.name, 100, "var(--env-var-color-17)", item.url),
status: HostStatus(
item.isActive
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
item.isActive ? "Up" : "Down",
item.isActive
? "var(--env-var-color-17)"
: "var(--env-var-color-19)"
),
team: <BarChart checks={item.checks} />,
actions: HostActions(),
}))
);
}
}, [mockdata]);
// useEffect(() => {
// fetch("API_URL")
// .then((response) => response.json())
// .then((data) => setMonitors(data))
// .catch((error) => console.error(error));
// }, []);
return (
<div>
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<div className="monitors-bar">
<div className="monitors-bar-title">Hello, Jackie</div>
<Button
level="primary"
label="Create new monitor"
sx={{ padding: "10px 20px", fontSize: "13px" }}
/>
</div>
<div className="monitors-gaps-medium"></div>
<div className="monitors-stats">
<ServerStatus title="Up" value="4" state="up" />
<ServerStatus title="Down" value="0" state="down" />
<ServerStatus title="Paused" value="0" state="pause" />
</div>
<div className="monitors-gaps-medium"></div>
<CurrentMonitors monitors={monitors} />
</div>
);
};
export default CurrentStats;

View File

@@ -1,4 +1,5 @@
.monitors {
.monitors,
.new-monitor {
width: 70vw;
padding: 4vw;
}
@@ -16,9 +17,25 @@
.monitors-stats {
display: flex;
justify-content: space-between;
gap: 24px;
}
.monitors-gaps-medium {
height: var(--env-var-spacing-2);
}
.new-monitor-header {
color: var(--env-var-color-1);
font-size: var(--env-var-font-size-large-plus);
font-weight: 600;
}
.service-check-list-title {
color: var(--env-var-color-5);
font-size: var(--env-var-font-size-medium);
}
.service-check-list-desc {
color: var(--env-var-color-25);
font-size: var(--env-var-font-size-small);
}

View File

@@ -1,28 +1,13 @@
import Button from "../../Components/Button";
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
import CurrentMonitors from "../../Components/CurrentMonitors";
// import CreateNewMonitor from "./CreateNewMonitor";
import CurrentStats from "./CurrentStats";
import "./index.css";
import React from "react";
const Monitors = () => {
return (
<div className="monitors">
<div className="monitors-bar">
<div className="monitors-bar-title">Hello, Jackie</div>
<Button
level="primary"
label="Create new monitor"
sx={{ padding: "10px 20px", fontSize: "13px" }}
/>
</div>
<div className="monitors-gaps-medium"></div>
<div className="monitors-stats">
<ServerStatus title="Up" value="4" state="up" />
<ServerStatus title="Down" value="0" state="down" />
<ServerStatus title="Paused" value="0" state="pause" />
</div>
<div className="monitors-gaps-medium"></div>
<CurrentMonitors />
<CurrentStats />
{/* <CreateNewMonitor /> */}
</div>
);
};

View File

@@ -64,9 +64,8 @@
margin-top: var(--env-var-spacing-4);
}
.edit-team-form__wrapper.compact {
margin-top: var(--env-var-spacing-3);
justify-content: flex-start;
align-items: center;
margin-top: 30px;
color: var(--env-var-color-29);
}
.edit-profile-form__wrapper input,
.edit-password-form__wrapper input,
@@ -96,12 +95,10 @@
border-color: var(--env-var-color-27);
background-color: var(--lighter-env-var-color-27);
}
.MuiBox-root > .announcement-tile .announcement-content-body {
.MuiBox-root > .announcement-tile .announcement-content-body,
.MuiBox-root > .announcement-tile .announcement-close svg {
color: var(--env-var-color-28);
}
.MuiBox-root > .announcement-tile .announcement-close svg,
.edit-password-form__wrapper > .announcement-tile .announcement-close svg {
display: none;
fill: var(--env-var-color-28) !important;
}
#org-name-flex-container .Mui-disabled {
color: var(--env-var-color-2) !important;
@@ -119,7 +116,8 @@
padding-left: 20px;
}
.edit-team-form__wrapper td {
color: var(--env-var-color-25);
font-size: (--env-var-font-size-medium);
color: var(--env-var-color-29);
}
#select-actions,

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

View File

@@ -3,9 +3,9 @@
line-height: 1.5;
font-weight: 400;
/* color-scheme: light dark;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424; */
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -41,10 +41,11 @@
--env-var-color-24: #d92d20;
--env-var-color-25: #667085;
--env-var-color-26: #ff1818;
--env-var-color-27: #fecf60;
--lighter-env-var-color-27: rgba(254, 207, 96, 0.1);
--env-var-color-28: #f79009;
--env-var-color-29: #D0D5DD;
--env-var-color-29: #667085;
--env-var-radius-1: 4px;
--env-var-radius-2: 8px;
@@ -63,9 +64,12 @@
--env-var-font-size-medium: 13px;
--env-var-font-size-medium-plus: 14px;
--env-var-font-size-large: 16px;
--env-var-font-size-large-plus: 24px;
--env-var-font-size-xlarge: 30px;
--env-var-img-width-1: 20px;
--env-var-img-width-2: 16px;
--env-var-img-width-3: 12px;
--env-var-default-1: 24px;
--env-var-default-2: 40px;

View File

@@ -6,16 +6,19 @@ import { BrowserRouter as Router, HashRouter } from "react-router-dom";
import theme from "./Utils/Theme.js";
import { ThemeProvider } from "@mui/material";
import { Provider } from "react-redux";
import store from "./store";
import { persistor, store } from "./store";
import { PersistGate } from "redux-persist/integration/react";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Provider store={store}>
<ThemeProvider theme={theme}>
<Router>
<App />
</Router>
</ThemeProvider>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<Router>
<App />
</Router>
</ThemeProvider>
</PersistGate>
</Provider>
</React.StrictMode>
);

View File

@@ -1,7 +1,34 @@
import { configureStore } from "@reduxjs/toolkit";
import { configureStore, combineReducers } from "@reduxjs/toolkit";
import monitorsReducer from "./Features/Monitors/monitorsSlice";
import authReducer from "./Features/Auth/authSlice";
import storage from "redux-persist/lib/storage";
import { persistReducer, persistStore } from "redux-persist";
export default configureStore({
reducer: { monitors: monitorsReducer, auth: authReducer },
const persistConfig = {
key: "root",
storage,
whitielist: ["auth", "monitors"],
};
const rootReducer = combineReducers({
monitors: monitorsReducer,
auth: authReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [
"persist/PERSIST",
"persist/REHYDRATE",
"persist/REGISTER",
],
},
}),
});
export const persistor = persistStore(store);