diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 7dd1404a1..a6581ad81 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -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={} /> + } + /> } diff --git a/Client/src/Components/CustomizedTables/index.css b/Client/src/Components/CustomizedTables/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/Client/src/Components/CustomizedTables/index.jsx b/Client/src/Components/CustomizedTables/index.jsx new file mode 100644 index 000000000..2630d5671 --- /dev/null +++ b/Client/src/Components/CustomizedTables/index.jsx @@ -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( + , + new Date(check.createdAt).toLocaleString(), + `HTTP ${check.statusCode} - ${check.status ? 'OK' : 'NOK'}` + )) : []; + + return ( + + + {[...Array(4)].map((_, index) => ( + + + Currently up for + + + 4h 30m 2s + + + ))} + + + + History + + + + + + + Status + Date & Time + Message + + + + {rows.map((row) => ( + + + {row.name} + + {row.date} + {row.message} + + ))} + +
+
+
+ ); +}; + +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; diff --git a/Client/src/Pages/Details/index.css b/Client/src/Pages/Details/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/Client/src/Pages/Details/index.jsx b/Client/src/Pages/Details/index.jsx new file mode 100644 index 000000000..c257077c1 --- /dev/null +++ b/Client/src/Pages/Details/index.jsx @@ -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 ( + + {/* Customized Tables Component */} + + + {/* Uptime duration */} + + Up for: {calculateUptimeDuration(monitorData.checks)} + + + {/* Last checked */} + + Last checked: {getLastCheckedTimestamp(monitorData.checks)} + + + {/* Incidents */} + + Incidents: {countIncidents(monitorData.checks)} + + + ); +}; + +export default DetailsPage;