Merge pull request #256 from bluewave-labs/feat/details-pg
Details page implementation.
This commit is contained in:
5 files changed
+321
No files matched your search
@@ -18,8 +18,10 @@ import CheckEmail from "./Pages/CheckEmail";
|
||||
import SetNewPassword from "./Pages/SetNewPassword";
|
||||
import NewPasswordConfirmed from "./Pages/NewPasswordConfirmed";
|
||||
import ProtectedRoute from "./Components/ProtectedRoute";
|
||||
import Details from "./Pages/Details";
|
||||
import Maintenance from "./Pages/Maintenance";
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
@@ -42,6 +44,10 @@ function App() {
|
||||
path="incidents"
|
||||
element={<ProtectedRoute Component={Incidents} />}
|
||||
/>
|
||||
<Route
|
||||
path="details"
|
||||
element={<ProtectedRoute Component={Details} />}
|
||||
/>
|
||||
<Route
|
||||
path="status"
|
||||
element={<ProtectedRoute Component={Status} />}
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,152 @@
|
||||
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;
|
||||
Whitespace-only changes.
@@ -0,0 +1,163 @@
|
||||
import React from 'react';
|
||||
import CustomizedTables from '../../Components/CustomizedTables';
|
||||
import { Box, Typography, useTheme } from '@mui/material';
|
||||
|
||||
/**
|
||||
* Details page component displaying monitor details and related information.
|
||||
* @component
|
||||
*/
|
||||
const DetailsPage = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
// Example monitor data (replace with actual data or fetch from API)
|
||||
const monitorData = {
|
||||
"_id": "66863aa2437936b46e225d1f",
|
||||
"userId": "66863a8f437936b46e225d1a",
|
||||
"name": "Google",
|
||||
"description": "Google",
|
||||
"status": false,
|
||||
"type": "http",
|
||||
"url": "https://www.google.com/404",
|
||||
"isActive": true,
|
||||
"interval": 60000,
|
||||
"createdAt": "2024-07-04T06:01:06.527Z",
|
||||
"updatedAt": "2024-07-04T06:02:00.250Z",
|
||||
"__v": 0,
|
||||
"checks": [
|
||||
{
|
||||
"_id": "66863ad8437936b46e225d6d",
|
||||
"monitorId": "66863aa2437936b46e225d1f",
|
||||
"status": false,
|
||||
"responseTime": 144,
|
||||
"expiry": "2024-07-04T06:02:00.247Z",
|
||||
"statusCode": 404,
|
||||
"createdAt": "2024-07-04T06:02:00.248Z",
|
||||
"updatedAt": "2024-07-04T06:02:00.248Z",
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": "66863b14437936b46e225d79",
|
||||
"monitorId": "66863aa2437936b46e225d1f",
|
||||
"status": false,
|
||||
"responseTime": 140,
|
||||
"expiry": "2024-07-04T06:03:00.167Z",
|
||||
"statusCode": 404,
|
||||
"createdAt": "2024-07-04T06:03:00.168Z",
|
||||
"updatedAt": "2024-07-04T06:03:00.168Z",
|
||||
"__v": 0
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to calculate uptime duration based on the most recent check.
|
||||
* @param {Array} checks Array of check objects.
|
||||
* @returns {string} Uptime duration in hours, minutes, and seconds.
|
||||
* TODO - NEED TO REVISIT THIS FOR PROPER LOGIC.
|
||||
*/
|
||||
const calculateUptimeDuration = (checks) => {
|
||||
if (!checks || checks.length === 0) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
const mostRecentCheck = checks[0];
|
||||
const currentTime = new Date();
|
||||
const lastCheckedTime = new Date(mostRecentCheck.createdAt);
|
||||
let uptimeDuration = currentTime - lastCheckedTime;
|
||||
|
||||
// Calculate hours, minutes, and seconds from uptime duration
|
||||
// TODO - NEED TO REVISIT THIS FOR PROPER LOGIC.
|
||||
let uptimeHours = Math.floor(uptimeDuration / (1000 * 60 * 60));
|
||||
uptimeDuration %= (1000 * 60 * 60);
|
||||
let uptimeMinutes = Math.floor(uptimeDuration / (1000 * 60));
|
||||
uptimeDuration %= (1000 * 60);
|
||||
let uptimeSeconds = Math.floor(uptimeDuration / 1000);
|
||||
|
||||
return `${uptimeHours}h ${uptimeMinutes}m ${uptimeSeconds}s`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get timestamp of the most recent check.
|
||||
* @param {Array} checks Array of check objects.
|
||||
* @returns {string} Timestamp of the most recent check.
|
||||
* TODO - NEED TO REVISIT THIS FOR PROPER LOGIC.
|
||||
*/
|
||||
const getLastCheckedTimestamp = (checks) => {
|
||||
if (!checks || checks.length === 0) {
|
||||
return 'N/A'; // Handle case when no checks are available
|
||||
}
|
||||
|
||||
const mostRecentCheck = checks[0];
|
||||
return new Date(mostRecentCheck.createdAt).toLocaleString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to count incidents (checks with status === false).
|
||||
* @param {Array} checks Array of check objects.
|
||||
* @returns {number} Number of incidents.
|
||||
* TODO - NEED TO REVISIT THIS FOR PROPER LOGIC.
|
||||
*/
|
||||
const countIncidents = (checks) => {
|
||||
if (!checks || checks.length === 0) {
|
||||
return 0; // Handle case when no checks are available
|
||||
}
|
||||
|
||||
// Count checks with status === false
|
||||
// TODO - NEED TO REVISIT THIS FOR PROPER LOGIC.
|
||||
const incidentCount = checks.filter(check => !check.status).length;
|
||||
return incidentCount;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* Customized Tables Component */}
|
||||
<CustomizedTables monitor={monitorData} />
|
||||
|
||||
{/* Uptime duration */}
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: 600,
|
||||
fontSize: '13px',
|
||||
lineHeight: '20px',
|
||||
color: '#344054',
|
||||
marginTop: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
Up for: {calculateUptimeDuration(monitorData.checks)}
|
||||
</Typography>
|
||||
|
||||
{/* Last checked */}
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: 400,
|
||||
fontSize: '16px',
|
||||
lineHeight: '24px',
|
||||
color: '#1570EF',
|
||||
marginBottom: theme.spacing(1),
|
||||
}}
|
||||
>
|
||||
Last checked: {getLastCheckedTimestamp(monitorData.checks)}
|
||||
</Typography>
|
||||
|
||||
{/* Incidents */}
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: 600,
|
||||
fontSize: '13px',
|
||||
lineHeight: '20px',
|
||||
color: '#344054',
|
||||
}}
|
||||
>
|
||||
Incidents: {countIncidents(monitorData.checks)}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailsPage;
|
||||
Reference in new issue
Block a user