mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-20 16:49:46 -06:00
Merge remote-tracking branch 'upstream/master' into feat/textfield-refactor
This commit is contained in:
0
Client/src/Components/StatusTable/index.css
Normal file
0
Client/src/Components/StatusTable/index.css
Normal file
114
Client/src/Components/StatusTable/index.jsx
Normal file
114
Client/src/Components/StatusTable/index.jsx
Normal file
@@ -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 (
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: theme.spacing(4),
|
||||
marginX: 'auto',
|
||||
width: '80%',
|
||||
paddingX: theme.spacing(6),
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 700 }} aria-label="customized table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{columns.map((column, index) => (
|
||||
<StyledTableCell key={index} align={column.align || 'left'}>
|
||||
{column.header}
|
||||
</StyledTableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{paginatedData.map((row, rowIndex) => (
|
||||
<StyledTableRow key={rowIndex}>
|
||||
{columns.map((column, colIndex) => (
|
||||
<StyledTableCell key={colIndex} align={column.align || 'left'}>
|
||||
{column.id === 'name' ? (
|
||||
<div style={row.name.props.customStyles}>
|
||||
{row.name}
|
||||
</div>
|
||||
) : (
|
||||
row[column.id]
|
||||
)}
|
||||
</StyledTableCell>
|
||||
))}
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{totalPages > 1 && (
|
||||
<Pagination
|
||||
page={currentPage}
|
||||
count={totalPages}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(<StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, '2024-03-14 21:41:09', 'HTTP 350 - NOK'),
|
||||
createData(<StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
|
||||
createData(<StatusLabel status="Cannot resolve" customStyles={{ backgroundColor: '#f2f4f7', borderColor: '#d2d6de', color: '#344054' }} />, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
|
||||
createData(<StatusLabel status="Cannot resolve" customStyles={{ backgroundColor: '#f2f4f7', borderColor: '#d2d6de', color: '#344054' }} />, '2024-03-14 21:41:09', 'timeout of 48000ms exceeded'),
|
||||
createData(<StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, '2024-03-14 21:41:09', 'HTTP 350 - 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-between', alignItems: 'center', marginBottom: theme.spacing(2) }}>
|
||||
<Typography variant="h6" component="div" sx={{ fontWeight: 600, fontSize: 16 }}>
|
||||
Incident History
|
||||
</Typography>
|
||||
<Filter />
|
||||
</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, index) => (
|
||||
<StyledTableRow key={index}>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: <StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, date: '2024-03-14 21:41:09', message: 'HTTP 350 - NOK' },
|
||||
{ name: <StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
|
||||
{ name: <StatusLabel status="Cannot resolve" customStyles={{ backgroundColor: '#f2f4f7', borderColor: '#d2d6de', color: '#344054' }} />, date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
|
||||
{ name: <StatusLabel status="Cannot resolve" customStyles={{ backgroundColor: '#f2f4f7', borderColor: '#d2d6de', color: '#344054' }} />, date: '2024-03-14 21:41:09', message: 'timeout of 48000ms exceeded' },
|
||||
{ name: <StatusLabel status="Down" customStyles={{ backgroundColor: '#fff9f9', borderColor: '#ffcac6', color: '#344054' }} />, 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 (
|
||||
<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-between', alignItems: 'center', marginBottom: theme.spacing(2) }}>
|
||||
<Typography variant="h6" component="div" sx={{ fontWeight: 600, fontSize: 16 }}>
|
||||
Incident History
|
||||
</Typography>
|
||||
<Filter />
|
||||
</Box>
|
||||
<StatusTable data={sampleData} columns={columns} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,9 @@ const Register = () => {
|
||||
alt="Logomark"
|
||||
/>
|
||||
<div className="register-form-v-spacing-large" />
|
||||
<div className="register-form-heading">Create an account</div>
|
||||
<div className="register-form-heading">
|
||||
Create Uptime Manager admin account
|
||||
</div>
|
||||
<div className="register-form-v-spacing-large"></div>
|
||||
</div>
|
||||
<div className="register-form-v-spacing-40px" />
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user