Merge pull request #365 from bluewave-labs/feat/the-reckoning

Cleaned up the codebase
This commit is contained in:
Alexander Holliday
2024-07-17 15:47:48 -07:00
committed by GitHub
48 changed files with 196 additions and 1138 deletions

View File

@@ -1,9 +0,0 @@
import "./backgroundPattern.css";
import React from "react";
import Pattern from "../../assets/Images/background_pattern_decorative.png";
const BackgroundPattern = () => {
return <img className="background-pattern" src={Pattern} alt="Pattern" />;
};
export default BackgroundPattern;

View File

@@ -1,7 +0,0 @@
.background-pattern {
position: absolute;
top: -5%;
left: 50%;
transform: translate(-50%, -50%);
z-index: -1;
}

View File

@@ -2,7 +2,6 @@
display: flex;
align-items: center;
}
.MuiTable-root .host-row a {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
@@ -18,6 +17,12 @@
font-size: var(--env-var-font-size-small);
}
.MuiTable-root .label {
border-color: var(--env-var-color-4);
border-radius: var(--env-var-radius-2);
padding: calc(var(--env-var-spacing-1) / 2);
}
.MuiTable-root .host-name {
width: fit-content;
margin-right: 10px;

View File

@@ -1,152 +0,0 @@
import { styled, useTheme } from '@mui/material/styles';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { StatusLabel } from "../../Components/Label/";
import PropTypes from 'prop-types';
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.action.hover,
color: theme.palette.common.black,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.hover,
},
'&:nth-of-type(even)': {
backgroundColor: theme.palette.common.white,
},
'&:last-child td, &:last-child th': {
border: 0,
},
}));
function createData(name, date, message) {
return { name, date, message };
}
const CustomizedTables = ({ monitor }) => {
const theme = useTheme();
const rows = (monitor && monitor.checks) ? monitor.checks.map(check => createData(
<StatusLabel status={check.status ? "Up" : "Down"} customStyles={{ backgroundColor: check.status ? '#f2f4f7' : '#fff9f9', borderColor: check.status ? '#d2d6de' : '#ffcac6', color: '#344054' }} />,
new Date(check.createdAt).toLocaleString(),
`HTTP ${check.statusCode} - ${check.status ? 'OK' : 'NOK'}`
)) : [];
return (
<Box
sx={{
marginTop: theme.spacing(4),
marginX: 'auto',
width: '80%',
paddingX: theme.spacing(6),
[theme.breakpoints.down('md')]: {
width: '100%',
},
}}
>
<Box sx={{ display: 'flex', justifyContent: 'space-around', marginBottom: theme.spacing(4) }}>
{[...Array(4)].map((_, index) => (
<Box
key={index}
sx={{
width: '223.35px',
height: '87px',
borderRadius: '4px',
border: '1px solid',
borderColor: '#EAECF0',
padding: theme.spacing(1),
textAlign: 'left',
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
backdropFilter: 'blur(2px)',
}}
>
<Typography
variant="body2"
sx={{
fontFamily: 'Roboto',
fontWeight: 400,
fontSize: '16px',
lineHeight: '24px',
color: '#1570EF',
marginBottom: theme.spacing(1),
}}
>
Currently up for
</Typography>
<Typography
variant="body1"
sx={{
fontFamily: 'Roboto',
fontWeight: 600,
fontSize: '13px',
lineHeight: '20px',
color: '#344054',
}}
>
4h 30m 2s
</Typography>
</Box>
))}
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing(2) }}>
<Typography variant="h6" component="div" sx={{ fontWeight: 600, fontSize: 16 }}>
History
</Typography>
</Box>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell>Status</StyledTableCell>
<StyledTableCell align="right">Date & Time</StyledTableCell>
<StyledTableCell align="right">Message</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<StyledTableRow key={row.name.props.status.key}>
<StyledTableCell component="th" scope="row">
{row.name}
</StyledTableCell>
<StyledTableCell align="right">{row.date}</StyledTableCell>
<StyledTableCell align="right">{row.message}</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
);
};
CustomizedTables.propTypes = {
monitor: PropTypes.shape({
checks: PropTypes.arrayOf(
PropTypes.shape({
status: PropTypes.bool.isRequired,
createdAt: PropTypes.string.isRequired,
statusCode: PropTypes.number.isRequired,
})
),
}),
};
CustomizedTables.defaultProps = {
monitor: { checks: [] },
};
export default CustomizedTables;

View File

@@ -1,15 +1,15 @@
import "./index.css";
import DashboardMenuButton from "../DashboardMenuButton";
import Monitors from "../../assets/Images/Icon-monitor-gray.png";
import Incidents from "../../assets/Images/Icon-warning-gray.png";
import SensorsIcon from "../../assets/Images/Icon-signal-gray.png";
import AllInclusiveIcon from "../../assets/Images/Icon-link-gray.png";
import MaintenanceIcon from "../../assets/Images/Icon-maintenance-gray.png";
import SettingsIcon from "../../assets/Images/Icon-setting-gray.png";
import Monitors from "../../../assets/Images/Icon-monitor-gray.png";
import Incidents from "../../../assets/Images/Icon-warning-gray.png";
import SensorsIcon from "../../../assets/Images/Icon-signal-gray.png";
import AllInclusiveIcon from "../../../assets/Images/Icon-link-gray.png";
import MaintenanceIcon from "../../../assets/Images/Icon-maintenance-gray.png";
import SettingsIcon from "../../../assets/Images/Icon-setting-gray.png";
import React, { useState, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import pathMap from "../../Utils/PathMap";
import pathMap from "../../../Utils/PathMap";
/**
* @component

View File

@@ -1,7 +1,7 @@
import DashboardMenu from "../../Components/DashboardMenu";
import DashboardMenu from "../DashboardMenu";
import SvgIcon from "@mui/material/SvgIcon";
import "./index.css";
import SupportIcon from "../../assets/Images/Icon-support-gray.png";
import SupportIcon from "../../../assets/Images/Icon-support-gray.png";
/**
* @component

View File

@@ -1 +0,0 @@
/* dropdown styles*/

View File

@@ -1,29 +0,0 @@
import PropTypes from "prop-types";
import Autocomplete from "@mui/material/Autocomplete";
import TextField from "@mui/material/TextField";
const Dropdown = (props) => {
return (
<Autocomplete
id={props.id}
options={props.options}
getOptionLabel={(option) => (option.name ? option.name : "")}
value={props.value}
onChange={props.onChange}
// Add isOptionEqualToValue prop
isOptionEqualToValue={(option, value) => option.name === value}
renderInput={(params) => <TextField {...params} label={props.label} />}
/>
);
};
// Define PropTypes for DropDown component
Dropdown.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
options: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
};
export default Dropdown;

View File

@@ -1 +0,0 @@
/*Dropdown Team Member styles*/

View File

@@ -1,39 +0,0 @@
import { useState } from 'react'
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
// import DropDown from './Dropdown';
import DropDown from '../Dropdown'
const DropdownTeamMember = () => {
const [selectedTeamMember, setSelectedTeamMember] = useState(null);
const teamMembers = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
{ id: 3, name: 'Alex Johnson' },
// more team members...
];
const handleTeamMemberChange = (event, value) => {
setSelectedTeamMember(value);
};
return (
<>
<div className="app-container" style={{ width: '500px' }}>
<DropDown
id="team-member-autocomplete"
options={teamMembers}
label="Select team member"
value={selectedTeamMember}
onChange={handleTeamMemberChange}
/>
</div>
</>
);
}
export default DropdownTeamMember

View File

@@ -1,36 +0,0 @@
import "./complexAlert.css";
import React from "react";
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
import PropTypes from "prop-types";
/**
* @component
* @param {Object} props
* @param {"red" | "green"} props.theme - The color theme for the alert (required) - must be either "red" or "green"
* @returns {JSX.Element} - Renders the complex alert component with dynamic color theme
*/
const ComplexAlert = ({ theme }) => {
if (theme === "red") {
return (
<div className="icon-holder-outer-red">
<div className="icon-holder-inner-red">
<ErrorOutlineIcon className="icon-core" style={{ fill: "#D92D20" }} />
</div>
</div>
);
} else if (theme === "green") {
return (
<div className="icon-holder-outer-green">
<div className="icon-holder-inner-green">
<ErrorOutlineIcon className="icon-core" style={{ fill: "#079455" }} />
</div>
</div>
);
}
};
ComplexAlert.propTypes = {
theme: PropTypes.string.isRequired,
};
export default ComplexAlert;

View File

@@ -1,44 +0,0 @@
.icon-holder-outer-red {
width: 38px;
height: 38px;
border-radius: 50%;
border: 2px solid #d92d2019;
display: flex;
justify-content: center;
align-items: center;
}
.icon-holder-inner-red {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid #d92d204d;
display: flex;
justify-content: center;
align-items: center;
}
.icon-holder-outer-green {
width: 38px;
height: 38px;
border-radius: 50%;
border: 2px solid #07945519;
display: flex;
justify-content: center;
align-items: center;
}
.icon-holder-inner-green {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid #0794554d;
display: flex;
justify-content: center;
align-items: center;
}
.icon-core {
width: 20px;
height: 20px;
}

View File

@@ -1,14 +0,0 @@
import "./searchTextField.css";
import React from "react";
import SearchSvg from "../../../assets/icons/search.svg?react";
const SearchTextField = () => {
return (
<div className="search-field-holder">
<SearchSvg style={{ marginRight: "5px" }} />
<input className="search-field" type="text" placeholder="Search" />
</div>
);
};
export default SearchTextField;

View File

@@ -1,26 +0,0 @@
.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

@@ -27,10 +27,10 @@ const BaseLabel = ({ label, styles, children }) => {
const padding = theme.spacing(1 * 0.75, 2);
return (
<div
<Box
className="label"
style={{
borderRadius: borderRadius,
sx={{
borderRadius: `${borderRadius}px`,
borderColor: theme.palette.tertiary.main,
color: theme.palette.tertiary.main,
padding: padding,
@@ -39,7 +39,7 @@ const BaseLabel = ({ label, styles, children }) => {
>
{children}
{label}
</div>
</Box>
);
};
@@ -118,13 +118,14 @@ ColoredLabel.propTypes = {
* @component
* @param {Object} props
* @param {'Seen' | 'Waiting' | 'New' | 'Active'} props.status - The status for the label
* @param {string} props.dot - The color of the dot
* @returns {JSX.Element}
* @example
* // Render an active label
* <StatusLabel status="Active" />
*/
const StatusLabel = ({ status, customStyles }) => {
const StatusLabel = ({ status, dot, customStyles }) => {
const theme = useTheme();
const colorLookup = {
@@ -132,8 +133,7 @@ const StatusLabel = ({ status, customStyles }) => {
Waiting: theme.palette.labelRed.color,
New: theme.palette.labelOrange.color,
Active: theme.palette.labelGreen.color,
Down: theme.palette.error.main, // Assuming theme.palette.error.main is red
Down: theme.palette.error.main, // Assuming theme.palette.error.main is red
};
// Look up the color for the status, default to labelGray if not found
@@ -142,18 +142,26 @@ const StatusLabel = ({ status, customStyles }) => {
return (
<BaseLabel label={status} styles={customStyles}>
<Box
width={12}
height={12}
bgcolor={color}
width={7}
height={7}
bgcolor={dot || color}
borderRadius="50%"
marginRight={1}
marginRight="5px"
/>
</BaseLabel>
);
};
StatusLabel.propTypes = {
status: PropTypes.oneOf(["Seen", "Waiting", "New", "Active", "Down", "Cannot resolve"]),
status: PropTypes.oneOf([
"Seen",
"Waiting",
"New",
"Active",
"Up",
"Down",
"Cannot resolve",
]),
customStyles: PropTypes.object,
};

View File

@@ -1,112 +0,0 @@
/* .host-row {
display: flex;
align-items: center;
}
.host-row a {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
color: var(--env-var-color-5);
margin-right: 10px;
margin-bottom: 2px;
}
.host-row a svg {
width: var(--env-var-font-size-large);
height: var(--env-var-font-size-large);
}
.host-row .host-percentage {
font-size: var(--env-var-font-size-small);
}
.host-name {
width: fit-content;
margin-right: 10px;
font-weight: 700;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.monitors .MuiPaper-root:has(table.MuiTable-root) {
box-shadow: none;
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
}
.monitors .MuiTableBody-root .MuiTableRow-root:last-child .MuiTableCell-root {
border: none;
}
.monitors .MuiTableHead-root,
.monitors .MuiTableRow-root:hover {
background-color: var(--env-var-color-13);
}
.monitors .MuiTableHead-root .MuiTableCell-root,
.monitors .MuiTableBody-root .MuiTableCell-root {
font-size: var(--env-var-font-size-medium);
}
.monitors .MuiTableHead-root .MuiTableCell-root {
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
font-weight: 500;
color: var(--env-var-color-2);
}
.monitors .MuiTableHead-root span {
display: inline-block;
height: 17px;
width: 20px;
overflow: hidden;
margin-bottom: -2px;
margin-left: 2px;
}
.monitors .MuiTableHead-root span svg {
width: 20px;
height: 20px;
}
.monitors .MuiTableBody-root .MuiTableCell-root {
color: var(--env-var-color-5);
padding: 6px var(--env-var-spacing-2);
}
.monitors .MuiPagination-root {
flex: 1;
margin-top: 35px;
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
}
.monitors .MuiPagination-root ul {
justify-content: center;
}
.monitors .MuiPagination-root ul li button {
font-size: var(--env-var-font-size-medium);
color: var(--env-var-color-5);
font-weight: 500;
}
.monitors .MuiPagination-root ul li:first-child {
margin-right: auto;
}
.monitors .MuiPagination-root ul li:last-child {
margin-left: auto;
}
.monitors .MuiPagination-root ul li:first-child button,
.monitors .MuiPagination-root ul li:last-child button {
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
}
.monitors .MuiPagination-root ul li:first-child button {
padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus);
}
.monitors .MuiPagination-root ul li:last-child button {
padding: 0 var(--env-var-spacing-1-plus) 0 var(--env-var-spacing-1);
}
.monitors .MuiPagination-root ul li:first-child button::after,
.monitors .MuiPagination-root ul li:last-child button::before {
position: relative;
display: inline-block;
}
.monitors .MuiPagination-root ul li:first-child button::after {
content: "Previous";
margin-left: 15px;
}
.monitors .MuiPagination-root ul li:last-child button::before {
content: "Next";
margin-right: 15px;
} */

View File

@@ -1,116 +0,0 @@
import "./index.css";
import PropTypes from "prop-types";
import ResponseTimeChart from "../Charts/ResponseTimeChart";
import BasicTable from "../BasicTable";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
import { useNavigate } from "react-router-dom";
import StatusLabel from "../StatusLabel";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
/**
* Host component.
* This subcomponent receives a params object and displays the host details.
*
* @component
* @param {Object} params - An object containing the following properties:
* @param {string} params.url - The URL of the host.
* @param {string} params.title - The name of the host.
* @param {string} params.percentageColor - The color of the percentage text.
* @param {number} params.precentage - The percentage to display.
* @returns {React.ElementType} Returns a div element with the host details.
*/
const Host = ({ params }) => {
return (
<div className="host-row">
<a href={params.url} target="_blank">
<OpenInNewPage />
</a>
<div className="host-name">{params.title}</div>
<div
className="host-percentage"
style={{ color: params.percentageColor }}
>
{params.precentage}%
</div>
</div>
);
};
/**
* MonitorTable component.
* Takes an array of monitor objects and displays them in a table.
* Each row in the table represents a monitor and includes the host, status, response time, and action.
*
* @component
* @param {Object[]} monitors - An array of monitor objects. Each object should have the following properties:
* @param {string} monitors[].url - The URL of the monitor.
* @param {string} monitors[].name - The name of the monitor.
* @param {boolean} monitors[].status - The status of the monitor. True if the monitor is up, false otherwise.
* @param {Object[]} monitors[].checks - An array of check objects for the response time chart.
* @returns {React.Component} Returns a table with the monitor data.
*/
const MonitorTable = ({ monitors = [] }) => {
const navigate = useNavigate();
const data = {
cols: [
{ id: 1, name: "Host" },
{
id: 2,
name: (
<>
Status
<span>
<ArrowDownwardRoundedIcon />
</span>
</>
),
},
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
],
rows: [],
};
data.rows = monitors.map((monitor, idx) => {
const params = {
url: monitor.url,
title: monitor.name,
precentage: 100,
percentageColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
status: monitor.status === true ? "up" : "down",
backgroundColor:
monitor.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
return {
id: monitor._id,
handleClick: () => navigate(`/monitors/${monitor._id}`),
data: [
{ id: idx, data: <Host params={params} /> },
{ id: idx + 1, data: <StatusLabel params={params} /> },
{ id: idx + 2, data: <ResponseTimeChart checks={monitor.checks} /> },
{ id: idx + 3, data: "TODO" },
],
};
});
return <BasicTable data={data} paginated={true} />;
};
MonitorTable.propTypes = {
monitors: PropTypes.arrayOf(PropTypes.object).isRequired,
};
Host.propTypes = { params: PropTypes.object.isRequired };
export default MonitorTable;

View File

@@ -1,33 +0,0 @@
.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

@@ -1,13 +0,0 @@
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

@@ -1,55 +0,0 @@
import "./dualButtonPopupModal.css";
import React from "react";
import PropTypes from "prop-types";
import { useTheme } from "@mui/material";
/**
* @component
* @param {Object} props
* @param {boolean} [props.open=true] - Controls the visibility of the modal (defaults to true)
* @param {string} props.subject - The title text for the modal (required)
* @param {string} props.description - The description text for the modal (required)
* @param {string} props.esc - The text for the discard button (usually "Cancel", "Dismiss" or "Discard") (required)
* @param {string} props.save - The text for the save button (required)
* @returns {JSX.Element} - Renders the dual button popup modal component
*/
const DualButtonPopupModal = ({
open = true,
subject,
description,
esc,
save,
}) => {
const theme = useTheme();
const fontLookup = {
default: theme.font.default.font,
};
const fontFamily = fontLookup["default"];
return (
<div
className="dual-button-popup-modal-holder"
style={{ fontFamily: fontFamily }}
>
<div className="dual-button-popup-modal-subject">{subject}</div>
<div className="dual-button-popup-modal-description">{description}</div>
<div className="dual-modal-spacing"></div>
<div className="dual-button-popup-modal-controllers">
<button className="transparent-discard-button">{esc}</button>
<button className="blue-save-button">{save}</button>
</div>
</div>
);
};
DualButtonPopupModal.propTypes = {
open: PropTypes.bool,
subject: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
esc: PropTypes.string.isRequired,
save: PropTypes.string.isRequired,
};
export default DualButtonPopupModal;

View File

@@ -1,52 +0,0 @@
.dual-button-popup-modal-holder {
width: 380px;
height: 130px;
margin: 10px 20px;
padding: 30px;
box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
0px 20px 24px -4px rgba(16, 24, 40, 0.08);
border: 1px solid var(--env-var-color-9);
border-radius: var(--env-var-radius-1);
}
.dual-button-popup-modal-subject {
color: var(--env-var-color-1);
font-weight: bolder;
font-size: 16px;
}
.dual-button-popup-modal-description {
color: var(--env-var-color-2);
font-size: var(--env-var-font-size-medium);
margin-top: 10px;
}
.dual-modal-spacing {
height: 40px;
}
.dual-button-popup-modal-controllers {
display: flex;
justify-content: end;
}
.blue-save-button {
width: 145px;
height: 35px;
padding: 5px 20px;
background-color: var(--env-var-color-3);
border: 1px solid var(--env-var-color-10);
border-radius: var(--env-var-radius-1);
margin: 5px 0;
margin-left: 20px;
color: var(--env-var-color-8);
cursor: pointer;
font-size: var(--env-var-font-size-medium);
}
.transparent-discard-button {
border: none;
background-color: transparent;
margin: 5px 10px;
font-size: var(--env-var-font-size-medium);
}

View File

@@ -1,73 +0,0 @@
import "./dualButtonPopupModalWithTextfields.css";
import React from "react";
import CloseIcon from "@mui/icons-material/Close";
import PropTypes from "prop-types";
import { useTheme } from "@mui/material";
/**
* @component
* @param {Object} props
* @param {string} props.title - The title text for the modal (required)
* @returns {JSX.Element} - Renders a single text field component within a popup modal
*/
const PopupModalTextfield = ({ title }) => {
const theme = useTheme();
const fontLookup = {
default: theme.font.default.font,
};
const fontFamily = fontLookup["default"];
return (
<div className="popup-modal-input" style={{ fontFamily: fontFamily }}>
<div className="popup-modal-input-title">{title}</div>
<input type="text" className="popup-modal-text-field" />
</div>
);
};
/**
* @component
* @param {Object} props
* @param {string} props.title - The title text for the modal (required)
* @param {string} props.esc - The text for the cancel button (required)
* @param {string} props.save - The text for the save button (required)
* @returns {JSX.Element} - Renders the dual button popup modal component with text fields
*/
const DualButtonPopupModalWithTextfields = ({ title, esc, save }) => {
const theme = useTheme();
const fontLookup = {
default: theme.font.default.font,
};
const fontFamily = fontLookup["default"];
return (
<div className="popup-modal-holder" style={{ fontFamily: fontFamily }}>
<div className="popup-modal-header">
<div className="popup-modal-title">{title}</div>
<div className="popup-modal-close">
<CloseIcon style={{ fill: "#98A2B3" }} />
</div>
</div>
<div className="spacing-height"></div>
<PopupModalTextfield title="Name" />
<div className="spacing-height"></div>
<div className="spacing-height"></div>
<div className="popup-modal-controllers">
<button className="white-cancel-button">{esc}</button>
<button className="blue-save-button">{save}</button>
</div>
</div>
);
};
DualButtonPopupModalWithTextfields.propTypes = {
title: PropTypes.string.isRequired,
esc: PropTypes.string.isRequired,
save: PropTypes.string.isRequired,
};
export default DualButtonPopupModalWithTextfields;

View File

@@ -1,55 +0,0 @@
.popup-modal-holder {
width: 480px;
font-family: var(--popup-font-family-0);
padding: 50px 40px;
border-radius: var(--env-var-radius-1);
margin: 10px 20px;
border: 1px solid var(--env-var-color-9);
box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
0px 20px 24px -4px rgba(16, 24, 40, 0.08);
}
.popup-modal-header {
display: flex;
justify-content: space-between;
font-weight: bold;
}
.popup-modal-close {
cursor: pointer;
}
.spacing-height {
height: 30px;
}
.popup-modal-input-title {
margin-bottom: 15px;
color: var(--env-var-color-5);
}
.popup-modal-text-field {
width: 100%;
height: 35px;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-1);
}
.popup-modal-controllers {
display: flex;
justify-content: end;
}
.white-cancel-button {
border: none;
background-color: transparent;
font-family: var(--popup-font-family-0);
margin: 5px 10px;
width: 145px;
height: 35px;
padding: 5px 20px;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-1);
cursor: pointer;
font-size: var(--env-var-font-size-medium);
}

View File

@@ -1,132 +0,0 @@
import PropTypes from "prop-types";
import { useState, useEffect } from "react";
import {
Box,
Container,
useTheme,
Typography,
TextField,
Switch,
} from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import Button from "../Button/";
import AddIcon from "@mui/icons-material/Add";
/**
* @component
* @param {Object} props
* @param { Array} props.monitors - Array of monitors associated with the section
* @returns {JSX.Element}
* @example
* // Renders a section component with a list of monitors
* <Section monitors={monitors} />
*/
const Section = ({ monitors }) => {
const [monitorStates, setMonitorStates] = useState(
monitors.map((monitor) => monitor.isActive)
);
useEffect(() => {
// Update DB here
}, [monitorStates]);
const handleMonitor = (monitorIndex) => {
setMonitorStates((prevStates) => {
const newStates = [...prevStates];
newStates[monitorIndex] = !newStates[monitorIndex];
return newStates;
// Need to update DB with new monitor state
});
};
const theme = useTheme();
return (
<>
<Container
disableGutters
sx={{
border: `1px solid ${theme.palette.section.borderColor}`,
borderRadius: `${theme.shape.borderRadius}px`,
}}
>
<Box
sx={{
textAlign: "left",
padding: `${theme.spacing(2)}`,
bgcolor: `${theme.palette.section.bgColor}`,
borderBottom: `1px solid ${theme.palette.section.borderColor}`,
}}
>
<Typography>Section Name</Typography>
<TextField
placeholder="Service Name"
sx={{
"& input": {
width: "320px",
height: "34px",
padding: "10px 14px 10px 14px",
},
}}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
textAlign: "left",
padding: `${theme.spacing(2)}`,
gap: 2,
}}
>
<Typography>Servers List</Typography>
<Button level="primary" label="Add new" />
{monitors.map((monitor, index) => {
return (
<Box
key={monitor.id}
sx={{
boxSizing: "border-box",
width: "100%",
padding: "10px 14px 10px 14px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
border: `1px solid ${theme.palette.section.borderColor}`,
borderRadius: `${theme.shape.borderRadius}px`,
bgcolor: `${theme.palette.section.bgColor}`,
}}
>
<MenuIcon
sx={{ color: `${theme.palette.section.borderColor}` }}
/>
<Switch
checked={monitorStates[index]}
onChange={() => handleMonitor(index)}
/>
<Typography sx={{ flexGrow: 1 }}>{monitor.name}</Typography>
<DeleteOutlineIcon
sx={{ color: `${theme.palette.section.borderColor}` }}
/>
</Box>
);
})}
</Box>
</Container>
<Button
sx={{ marginTop: theme.spacing(2) }}
level="imageSecondary"
label="Add new section"
img={<AddIcon />}
/>
</>
);
};
Section.propTypes = {
monitors: PropTypes.array,
};
export default Section;

View File

@@ -1,22 +0,0 @@
.host-status {
width: fit-content;
}
.host-status-details {
display: flex;
align-items: center;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-2);
padding: calc(var(--env-var-spacing-1) / 2);
}
.host-status-text {
font-size: var(--env-var-font-size-medium);
line-height: var(--env-var-font-size-medium);
}
.host-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: calc(var(--env-var-spacing-1) / 2);
}

View File

@@ -1,39 +0,0 @@
import "./index.css";
import PropTypes from "prop-types";
/**
* Status component.
* This subcomponent receives a params object and displays the status details of a monitor.
*
* @component
* @param {Object} params - An object containing the following properties:
* @param {string} params.backgroundColor - The background color of the status box.
* @param {string} params.statusDotColor - The color of the status dot.
* @param {string} params.status - The status text to display.
* @returns {React.ElementType} Returns a div element with the host status.
*/
const StatusLabel = ({ params }) => {
return (
<div className="host-status">
<div
className="host-status-details"
style={{ backgroundColor: params.backgroundColor }}
>
<div
className="host-status-dot"
style={{ backgroundColor: params.statusDotColor }}
/>
<span
className="host-status-text"
style={{ textTransform: "capitalize" }}
>
{params.status}
</span>
</div>
</div>
);
};
export default StatusLabel;
StatusLabel.propTypes = { params: PropTypes.object.isRequired };

View File

@@ -1,6 +1,6 @@
import NavBar from "../../Components/NavBar";
import { Outlet } from "react-router";
import DashboardSidebar from "../../Components/DashboardSidebar";
import DashboardSidebar from "../../Components/Dashboard/DashboardSidebar";
import "./index.css";
const HomeLayout = () => {

View File

@@ -1,5 +1,5 @@
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import "./index.css";
import background from "../../assets/Images/background_pattern_decorative.png";
import React, { useEffect, useState } from "react";
import EmailIcon from "../../assets/icons/email.svg?react";
import Button from "../../Components/Button";
@@ -95,7 +95,11 @@ const CheckEmail = () => {
return (
<div className="check-email-page">
<BackgroundPattern />
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="check-email-form">
<Stack direction="column" alignItems="center" gap={theme.gap.small}>
<EmailIcon alt="EmailIcon" style={{ fill: "white" }} />

View File

@@ -21,7 +21,6 @@ import { ColoredLabel, StatusLabel } from "../../Components/Label/";
import Avatar from "../../Components/Avatar/";
import ProgressStepper from "../../Components/ProgressStepper";
import avatarImage from "../../assets/Images/avatar_placeholder.png";
import Section from "../../Components/Section";
import { DataGrid } from "@mui/x-data-grid";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
@@ -204,8 +203,6 @@ const Demo = () => {
}, 4000);
};
//fields
const [visibility, setVisibility] = useState(false);
return (
<div>
<div style={{ padding: "4rem", border: "1px solid black" }}>
@@ -287,6 +284,7 @@ const Demo = () => {
<StatusLabel status="Waiting" />
<StatusLabel status="New" />
<StatusLabel status="Active" />
<StatusLabel status="Up" />
</div>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<h4 id="avatar">Avatar</h4>
@@ -375,9 +373,6 @@ const Demo = () => {
<ProgressStepper steps={steps}></ProgressStepper>
</div>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<h4 id="section">Section</h4>
<Section monitors={demoMonitors} />
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<h4 id="section">Loading Buttons</h4>
<Stack direction="row" justifyContent="center" spacing={3}>
<ButtonSpinner

View File

@@ -6,7 +6,7 @@ import { useParams } from "react-router-dom";
import axiosInstance from "../../Utils/axiosConfig";
import BasicTable from "../../Components/BasicTable";
import MonitorDetailsAreaChart from "../../Components/Charts/MonitorDetailsAreaChart";
import StatusLabel from "../../Components/StatusLabel";
import { StatusLabel } from "../../Components/Label";
const formatDuration = (ms) => {
const seconds = Math.floor(ms / 1000);
@@ -60,7 +60,7 @@ const DetailsPage = () => {
],
rows: res.data.data.checks.map((check, idx) => {
const params = {
status: check.status === true ? "up" : "down",
status: check.status === true ? "Up" : "Down",
backgroundColor:
check.status === true
? "var(--env-var-color-20)"
@@ -74,7 +74,18 @@ const DetailsPage = () => {
return {
id: check._id,
data: [
{ id: idx, data: <StatusLabel params={params} /> },
{
id: idx,
data: (
<StatusLabel
status={params.status}
dot={params.statusDotColor}
customStyles={{
backgroundColor: params.backgroundColor,
}}
/>
),
},
{ id: idx + 1, data: new Date(check.createdAt).toLocaleString() },
{ id: idx + 2, data: check.statusCode },
],

View File

@@ -1,6 +1,5 @@
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import "./index.css";
import React from "react";
import background from "../../assets/Images/background_pattern_decorative.png";
import Logomark from "../../assets/icons/key.svg?react";
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
import { useState } from "react";
@@ -100,7 +99,11 @@ const ForgotPassword = () => {
return (
<div className="forgot-password-page">
<BackgroundPattern></BackgroundPattern>
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="forgot-password-form" onSubmit={handleSubmit}>
<Stack direction="column" alignItems="center" gap={theme.gap.small}>
<Logomark alt="Logomark" style={{ fill: "white" }} />

View File

@@ -2,10 +2,10 @@ import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import "./index.css";
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import Logomark from "../../assets/Images/bwl-logo-2.svg?react";
import Button from "../../Components/Button";
import Google from "../../assets/Images/Google.png";
import background from "../../assets/Images/background_pattern_decorative.png";
import axiosInstance from "../../Utils/axiosConfig";
import { credentials } from "../../Validation/validation";
import { login } from "../../Features/Auth/authSlice";
@@ -122,7 +122,11 @@ const Login = () => {
return (
<div className="login-page">
<BackgroundPattern></BackgroundPattern>
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="login-form" onSubmit={handleSubmit}>
<Stack gap={theme.gap.large} direction="column">
<Logomark alt="BlueWave Uptime Icon" />

View File

@@ -6,9 +6,41 @@ import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice";
import { useNavigate } from "react-router-dom";
import Button from "../../Components/Button";
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
import SearchTextField from "../../Components/Inputs/Search/SearchTextField";
import MonitorTable from "../../Components/MonitorTable";
import { useTheme } from "@emotion/react";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
import BasicTable from "../../Components/BasicTable";
import { StatusLabel } from "../../Components/Label";
import ResponseTimeChart from "../../Components/Charts/ResponseTimeChart";
/**
* Host component.
* This subcomponent receives a params object and displays the host details.
*
* @component
* @param {Object} params - An object containing the following properties:
* @param {string} params.url - The URL of the host.
* @param {string} params.title - The name of the host.
* @param {string} params.percentageColor - The color of the percentage text.
* @param {number} params.precentage - The percentage to display.
* @returns {React.ElementType} Returns a div element with the host details.
*/
const Host = ({ params }) => {
return (
<div className="host-row">
<a href={params.url} target="_blank">
<OpenInNewPage />
</a>
<div className="host-name">{params.title}</div>
<div
className="host-percentage"
style={{ color: params.percentageColor }}
>
{params.precentage}%
</div>
</div>
);
};
const Monitors = () => {
const theme = useTheme();
@@ -26,6 +58,70 @@ const Monitors = () => {
}, 0);
const down = monitorState.monitors.length - up;
const data = {
cols: [
{ id: 1, name: "Host" },
{
id: 2,
name: (
<>
Status
<span>
<ArrowDownwardRoundedIcon />
</span>
</>
),
},
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
],
rows: [],
};
data.rows = monitorState.monitors.map((monitor, idx) => {
const params = {
url: monitor.url,
title: monitor.name,
precentage: 100,
percentageColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
status: monitor.status === true ? "Up" : "Down",
backgroundColor:
monitor.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
return {
id: monitor._id,
handleClick: () => navigate(`/monitors/${monitor._id}`),
data: [
{ id: idx, data: <Host params={params} /> },
{
id: idx + 1,
data: (
<StatusLabel
status={params.status}
dot={params.statusDotColor}
customStyles={{
backgroundColor: params.backgroundColor,
}}
/>
),
},
{ id: idx + 2, data: <ResponseTimeChart checks={monitor.checks} /> },
{ id: idx + 3, data: "TODO" },
],
};
});
return (
<div
className="monitors"
@@ -62,11 +158,11 @@ const Monitors = () => {
</div>
</div>
<div className="current-monitors-search-bar">
<SearchTextField />
{/* TODO - add search bar */}
</div>
</div>
<div className="monitors-v-gaping" />
<MonitorTable monitors={monitorState.monitors} />
<BasicTable data={data} paginated={true} />
</div>
</div>
);

View File

@@ -1,5 +1,5 @@
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import "./index.css";
import background from "../../assets/Images/background_pattern_decorative.png";
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
import ConfirmIcon from "../../assets/icons/confirm-icon.svg?react";
import Button from "../../Components/Button";
@@ -23,7 +23,11 @@ const NewPasswordConfirmed = () => {
return (
<div className="password-confirmed-page">
<BackgroundPattern />
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="password-confirmed-form">
<Stack direction="column" alignItems="center" gap={theme.gap.small}>
<ConfirmIcon alt="confirm icon" style={{ fill: "white" }} />

View File

@@ -1,25 +0,0 @@
import React from "react";
import DualButtonPopupModal from "../../Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal";
import DualButtonPopupModalWithTextfields from "../../Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields";
function PlayGroundPopupModals() {
return (
<div style={{ display: "flex" }}>
<DualButtonPopupModal
subject="Unsaved changes"
description="Do you want to save or discard changes?"
esc="Discard"
save="Save changes"
/>
<br />
<br />
<DualButtonPopupModalWithTextfields
title="Create new organization"
esc="Cancel"
save="Save"
/>
</div>
);
}
export default PlayGroundPopupModals;

View File

@@ -1,6 +1,5 @@
import React from "react";
import PlayGroundCharts from "./PlayGround-Charts";
import PlayGroundPopupModals from "./PlayGround-Popup-Modals";
import PlayGroundTooltips from "./PlayGround-Tooltips";
import Field from "../../Components/Inputs/Field";
@@ -79,8 +78,6 @@ function PlayGround() {
<hr />
<PlayGroundCharts />
<hr />
<PlayGroundPopupModals />
<hr />
<PlayGroundTooltips />
</div>
);

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import "./index.css";
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import background from "../../assets/Images/background_pattern_decorative.png";
import Logomark from "../../assets/Images/bwl-logo-2.svg?react";
import Check from "../../Components/Check/Check";
import Button from "../../Components/Button";
@@ -132,7 +132,11 @@ const Register = () => {
return (
<div className="register-page">
<BackgroundPattern></BackgroundPattern>
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="register-form" onSubmit={handleSubmit} noValidate>
<Stack gap={theme.gap.large} direction="column">
<Logomark alt="BlueWave Uptime Icon" />

View File

@@ -1,5 +1,5 @@
import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern";
import "./index.css";
import background from "../../assets/Images/background_pattern_decorative.png";
import LockIcon from "../../assets/icons/lock-button-icon.svg?react";
import Check from "../../Components/Check/Check";
import ButtonSpinner from "../../Components/ButtonSpinner";
@@ -114,7 +114,11 @@ const SetNewPassword = () => {
return (
<div className="set-new-password-page">
<BackgroundPattern />
<img
className="background-pattern-svg"
src={background}
alt="background pattern"
/>
<form className="set-new-password-form" onSubmit={handleSubmit}>
<Stack direction="column" alignItems="center" gap={theme.gap.small}>
<LockIcon alt="lock icon" style={{ fill: "white" }} />

View File

@@ -128,7 +128,7 @@ button:focus-visible {
.Toastify__toast-container {
width: auto;
}
.Toastify__toast-body .alert{
.Toastify__toast-body .alert {
min-width: 150px;
}
.Toastify [class^="Toastify__toast"] {
@@ -140,6 +140,14 @@ button:focus-visible {
border-radius: 4px;
}
.background-pattern-svg {
position: absolute;
top: -5%;
left: 50%;
transform: translate(-50%, -50%);
z-index: -1;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;