diff --git a/Client/src/Components/StatusTable/index.css b/Client/src/Components/StatusTable/index.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx
new file mode 100644
index 000000000..9546d5742
--- /dev/null
+++ b/Client/src/Components/StatusTable/index.jsx
@@ -0,0 +1,114 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+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 Pagination from '../../Components/Pagination';
+
+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,
+ },
+}));
+
+const itemsPerPage = 5; // Number of items per page
+
+export default function StatusTable({ data, columns }) {
+ const theme = useTheme();
+ const [currentPage, setCurrentPage] = React.useState(1);
+
+ const onPageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on data length and items per page
+ const totalPages = Math.ceil(data.length / itemsPerPage);
+
+ // Slice data based on current page and items per page
+ const paginatedData = data.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
+
+ return (
+
+
+
+
+
+ {columns.map((column, index) => (
+
+ {column.header}
+
+ ))}
+
+
+
+ {paginatedData.map((row, rowIndex) => (
+
+ {columns.map((column, colIndex) => (
+
+ {column.id === 'name' ? (
+
+ {row.name}
+
+ ) : (
+ row[column.id]
+ )}
+
+ ))}
+
+ ))}
+
+
+
+ {totalPages > 1 && (
+
+ )}
+
+ );
+}
+
+StatusTable.propTypes = {
+ data: PropTypes.arrayOf(PropTypes.object).isRequired,
+ columns: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ header: PropTypes.node.isRequired,
+ align: PropTypes.oneOf(['left', 'right', 'center']),
+ })
+ ).isRequired,
+};
diff --git a/Client/src/Features/Monitors/monitorsSlice.js b/Client/src/Features/Monitors/monitorsSlice.js
index d5c257021..8086f1eb4 100644
--- a/Client/src/Features/Monitors/monitorsSlice.js
+++ b/Client/src/Features/Monitors/monitorsSlice.js
@@ -50,11 +50,14 @@ export const getMonitorsByUserId = createAsyncThunk(
async (token, thunkApi) => {
const user = jwtDecode(token);
try {
- const res = await axiosInstance.get("/monitors/user/" + user._id, {
- headers: {
- Authorization: `Bearer ${token}`,
- },
- });
+ const res = await axiosInstance.get(
+ `/monitors/user/${user._id}?limit=25`,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
return res.data;
} catch (error) {
if (error.response && error.response.data) {
diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx
index b9778cbe9..4bb75a53d 100644
--- a/Client/src/Pages/Incidents/index.jsx
+++ b/Client/src/Pages/Incidents/index.jsx
@@ -1,133 +1,24 @@
import * as React from 'react';
import TextField from '@mui/material/TextField';
import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
-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 { useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
-import { StatusLabel } from "../../Components/Label/";
import TuneIcon from '@mui/icons-material/Tune';
-
-/**
- * Creates a styled TableCell component.
- * @param {object} theme - The theme object.
- * @returns {JSX.Element} The styled TableCell component.
- */
-const StyledTableCell = styled(TableCell)(({ theme }) => ({
- [`&.${tableCellClasses.head}`]: {
- backgroundColor: theme.palette.action.hover,
- color: theme.palette.common.black,
- },
- [`&.${tableCellClasses.body}`]: {
- fontSize: 14,
- },
-}));
-
-/**
- * Creates a styled TableRow component.
- * @param {object} theme - The theme object.
- * @returns {JSX.Element} The styled TableRow component.
- */
-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,
- },
-}));
+import StatusTable from '../../Components/StatusTable';
+import { StatusLabel } from "../../Components/Label/";
const filterOptions = createFilterOptions({
matchFrom: 'start',
stringify: (option) => option.title,
});
-
const titles = [
{ title: 'Down' },
{ title: 'Cannot resolve' },
{ title: 'Clear / show all' },
];
-/**
- * Creates a data object for the table row.
- * @param {JSX.Element} name - The status label JSX element.
- * @param {string} date - The date and time string.
- * @param {string} message - The message string.
- * @returns {object} The data object containing name, date, and message.
- */
-function createData(name, date, message) {
- return { name, date, message };
-}
-
-/**
- * Customized table component displaying incident history.
- * @returns {JSX.Element} The JSX element representing the customized table.
- */
-export default function CustomizedTables() {
- const theme = useTheme();
-
- const rows = [
- createData(, '2024-03-14 21:41:09', 'HTTP 350 - NOK'),
- createData(, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
- createData(, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
- createData(, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
- createData(, '2024-03-14 21:41:09', 'HTTP 350 - NOK'),
- ];
-
- return (
-
-
-
- Incident History
-
-
-
-
-
-
-
- Status
- Date & Time
- Message
-
-
-
- {rows.map((row, index) => (
-
-
- {row.name}
-
- {row.date}
- {row.message}
-
- ))}
-
-
-
-
- );
-}
-
/**
* Filter component with autocomplete for status filtering.
* @returns {JSX.Element} The JSX element representing the filter component.
@@ -165,3 +56,47 @@ function Filter() {
/>
);
}
+
+const sampleData = [
+ { name: , date: '2024-03-14 21:41:09', message: 'HTTP 350 - NOK' },
+ { name: , date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
+ { name: , date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
+ { name: , date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
+ { name: , date: '2024-03-14 21:41:09', message: 'HTTP 350 - NOK' },
+];
+
+const columns = [
+ { id: 'name', header: 'Status' },
+ { id: 'date', header: 'Date & Time' },
+ { id: 'message', header: 'Message' },
+];
+
+/**
+ * Customized table component displaying incident history with a filter.
+ * @returns {JSX.Element} The JSX element representing the customized table with a filter.
+ */
+export default function CustomizedTables() {
+ const theme = useTheme();
+
+ return (
+
+
+
+ Incident History
+
+
+
+
+
+ );
+}
diff --git a/Client/src/Pages/Register/index.jsx b/Client/src/Pages/Register/index.jsx
index 8ca92d5d8..8bd026187 100644
--- a/Client/src/Pages/Register/index.jsx
+++ b/Client/src/Pages/Register/index.jsx
@@ -130,7 +130,9 @@ const Register = () => {
alt="Logomark"
/>
- Create an account
+
+ Create Uptime Manager admin account
+
diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js
index 36741711c..9b5a485f8 100644
--- a/Server/db/MongoDB.js
+++ b/Server/db/MongoDB.js
@@ -293,19 +293,19 @@ const getMonitorById = async (req, res) => {
*/
const getMonitorsByUserId = async (req, res) => {
try {
- const limit = req.body.limit;
+ const limit = req.query.limit;
const monitors = await Monitor.find({ userId: req.params.userId });
// Map each monitor to include its associated checks
const monitorsWithChecks = await Promise.all(
monitors.map(async (monitor) => {
-
- if(limit) {
+ if (limit) {
// Checks are order oldest -> newest
- const checks = await Check.find({ monitorId: monitor._id }).sort({
- createdAt: 1,
- }).limit(limit);;
+ const checks = await Check.find({ monitorId: monitor._id })
+ .sort({
+ createdAt: 1,
+ })
+ .limit(limit);
return { ...monitor.toObject(), checks };
-
} else {
// Checks are order oldest -> newest
const checks = await Check.find({ monitorId: monitor._id }).sort({
diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js
index d19aedad9..ae7a5852e 100644
--- a/Server/routes/monitorRoute.js
+++ b/Server/routes/monitorRoute.js
@@ -5,7 +5,7 @@ const Monitor = require("../models/Monitor");
router.get("/", monitorController.getAllMonitors);
router.get("/:monitorId", monitorController.getMonitorById);
-router.get("/user/:userId?limit", monitorController.getMonitorsByUserId);
+router.get("/user/:userId", monitorController.getMonitorsByUserId);
router.post("/", monitorController.createMonitor);
router.post(