From c13abb5cde2cb6dfd755f76c1a497f537b9800d7 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 10:49:13 -0700 Subject: [PATCH 01/37] Made files for status table component. --- Client/src/Components/StatusTable/index.css | 0 Client/src/Components/StatusTable/index.jsx | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Client/src/Components/StatusTable/index.css create mode 100644 Client/src/Components/StatusTable/index.jsx 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..e69de29bb From 322ad651b098ffb1bcd1f581e23ec4c0b290f17f Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:16:43 -0700 Subject: [PATCH 02/37] Make the incident history table into a component. --- Client/src/Components/StatusTable/index.jsx | 111 +++++++++++++++ Client/src/Pages/Incidents/index.jsx | 144 +++++--------------- 2 files changed, 143 insertions(+), 112 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index e69de29bb..9b8f752e8 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +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/"; + +/** + * 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, + }, +})); + +/** + * 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 IncidentHistoryTable() { + 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} + + ))} + +
+
+
+ ); +} diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index b9778cbe9..30c56f661 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -1,133 +1,23 @@ 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 IncidentHistoryTable from '../../Components/StatusTable'; // Make sure to import the correct path to your IncidentHistoryTable component 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 +55,33 @@ function Filter() { /> ); } + +/** + * 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 + + + + + + ); +} From cb510ef56f9300faca0f0ba2a7c648aec276bdfb Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:18:19 -0700 Subject: [PATCH 03/37] Got rid of extra text at the top. --- Client/src/Components/StatusTable/index.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index 9b8f752e8..af2081f04 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -82,7 +82,6 @@ export default function IncidentHistoryTable() { }} > - Incident History From 9e0719a8f9f57bbb3fec4b61e115dea531fab88a Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:19:45 -0700 Subject: [PATCH 04/37] Got rid of extra typography code. --- Client/src/Components/StatusTable/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index af2081f04..108a9f1dd 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -81,8 +81,6 @@ export default function IncidentHistoryTable() { }, }} > - -
From 01ac1f0bb9c940d12185967e53c9f42aa9cbfc81 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:21:43 -0700 Subject: [PATCH 05/37] Changed the name of the component to be more meaningful. --- Client/src/Components/StatusTable/index.jsx | 2 +- Client/src/Pages/Incidents/index.jsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index 108a9f1dd..11632b588 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -58,7 +58,7 @@ function createData(name, date, message) { * Customized table component displaying incident history. * @returns {JSX.Element} The JSX element representing the customized table. */ -export default function IncidentHistoryTable() { +export default function StatusTable() { const theme = useTheme(); const rows = [ diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 30c56f661..45ee052f1 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -5,7 +5,7 @@ import { useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import TuneIcon from '@mui/icons-material/Tune'; -import IncidentHistoryTable from '../../Components/StatusTable'; // Make sure to import the correct path to your IncidentHistoryTable component +import StatusTable from '../../Components/StatusTable'; // Make sure to import the correct path to your IncidentHistoryTable component const filterOptions = createFilterOptions({ matchFrom: 'start', @@ -81,7 +81,7 @@ export default function CustomizedTables() { - + ); } From be8651076a18482ab49493e4cc10515b53d7474d Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:31:23 -0700 Subject: [PATCH 06/37] Got rid of extra imports. --- Client/src/Components/StatusTable/index.jsx | 2 -- Client/src/Pages/Incidents/index.jsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index 11632b588..c45cb8012 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { styled, useTheme } from '@mui/material/styles'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; @@ -8,7 +7,6 @@ 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/"; /** diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 45ee052f1..ba17cadfa 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -5,7 +5,7 @@ import { useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import TuneIcon from '@mui/icons-material/Tune'; -import StatusTable from '../../Components/StatusTable'; // Make sure to import the correct path to your IncidentHistoryTable component +import StatusTable from '../../Components/StatusTable'; const filterOptions = createFilterOptions({ matchFrom: 'start', From a617b5f27317e463e9b31995b3846c132263f7e9 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:37:55 -0700 Subject: [PATCH 07/37] Give the table the arbitrary prop data functionality. --- Client/src/Components/StatusTable/index.jsx | 35 +++++++++------------ Client/src/Pages/Incidents/index.jsx | 13 ++++++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index c45cb8012..cebaa7a35 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -1,3 +1,5 @@ +import * as 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'; @@ -41,32 +43,15 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({ }, })); -/** - * 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. + * @param {object} props - The props object. + * @param {array} props.data - The array of data objects containing name, date, and message. * @returns {JSX.Element} The JSX element representing the customized table. */ -export default function StatusTable() { +export default function StatusTable({ data }) { 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 ( - {rows.map((row, index) => ( + {data.map((row, index) => ( {row.name} @@ -104,3 +89,11 @@ export default function StatusTable() { ); } + +StatusTable.propTypes = { + data: PropTypes.arrayOf(PropTypes.shape({ + name: PropTypes.node.isRequired, + date: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + })).isRequired, +}; diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index ba17cadfa..599950eea 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -5,7 +5,8 @@ import { useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import TuneIcon from '@mui/icons-material/Tune'; -import StatusTable from '../../Components/StatusTable'; +import StatusTable from '../../Components/StatusTable'; +import { StatusLabel } from "../../Components/Label/"; const filterOptions = createFilterOptions({ matchFrom: 'start', @@ -56,6 +57,14 @@ 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' }, +]; + /** * Customized table component displaying incident history with a filter. * @returns {JSX.Element} The JSX element representing the customized table with a filter. @@ -81,7 +90,7 @@ export default function CustomizedTables() { - + ); } From 14bd0da891fa1f19606d85fff697f1a639153898 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 11:38:53 -0700 Subject: [PATCH 08/37] Get rid of unnecessary imports. --- Client/src/Components/StatusTable/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index cebaa7a35..71d2b7641 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import PropTypes from 'prop-types'; import { styled, useTheme } from '@mui/material/styles'; import Table from '@mui/material/Table'; @@ -9,7 +8,6 @@ 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 { StatusLabel } from "../../Components/Label/"; /** * Creates a styled TableCell component. From c9a3f54a658c8be749dcb92619434052c8f2d73b Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 13:20:15 -0700 Subject: [PATCH 09/37] Table now renders an arbitrary number of columns. --- Client/src/Components/StatusTable/index.jsx | 46 +++++++++++++-------- Client/src/Pages/Incidents/index.jsx | 8 +++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index 71d2b7641..ffe0b6e8a 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -44,10 +44,11 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({ /** * Customized table component displaying incident history. * @param {object} props - The props object. - * @param {array} props.data - The array of data objects containing name, date, and message. + * @param {array} props.data - The array of data objects containing JSX elements for name, and strings for date and message. + * @param {array} props.columns - The array of column headers and alignments. * @returns {JSX.Element} The JSX element representing the customized table. */ -export default function StatusTable({ data }) { +export default function StatusTable({ data, columns }) { const theme = useTheme(); return ( @@ -66,19 +67,27 @@ export default function StatusTable({ data }) {
- Status - Date & Time - Message + {columns.map((column, index) => ( + + {column.header} + + ))} - {data.map((row, index) => ( - - - {row.name} - - {row.date} - {row.message} + {data.map((row, rowIndex) => ( + + {columns.map((column, colIndex) => ( + + {column.id === 'name' ? ( +
+ {row.name} +
+ ) : ( + row[column.id] + )} +
+ ))}
))}
@@ -89,9 +98,12 @@ export default function StatusTable({ data }) { } StatusTable.propTypes = { - data: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.node.isRequired, - date: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - })).isRequired, + 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/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 599950eea..4bb75a53d 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -65,6 +65,12 @@ const sampleData = [ { 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. @@ -90,7 +96,7 @@ export default function CustomizedTables() { - + ); } From 9a99f89b89cf7d1d58406ec692a5fa4f7bdc859e Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 11 Jul 2024 13:35:47 -0700 Subject: [PATCH 10/37] Provided optional pagination for the table. --- Client/src/Components/StatusTable/index.jsx | 41 ++++++++++++--------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Client/src/Components/StatusTable/index.jsx b/Client/src/Components/StatusTable/index.jsx index ffe0b6e8a..9546d5742 100644 --- a/Client/src/Components/StatusTable/index.jsx +++ b/Client/src/Components/StatusTable/index.jsx @@ -1,3 +1,4 @@ +import React from 'react'; import PropTypes from 'prop-types'; import { styled, useTheme } from '@mui/material/styles'; import Table from '@mui/material/Table'; @@ -8,12 +9,8 @@ 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'; -/** - * 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, @@ -24,11 +21,6 @@ const StyledTableCell = styled(TableCell)(({ theme }) => ({ }, })); -/** - * 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, @@ -41,15 +33,21 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({ }, })); -/** - * Customized table component displaying incident history. - * @param {object} props - The props object. - * @param {array} props.data - The array of data objects containing JSX elements for name, and strings for date and message. - * @param {array} props.columns - The array of column headers and alignments. - * @returns {JSX.Element} The JSX element representing the customized table. - */ +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 ( - {data.map((row, rowIndex) => ( + {paginatedData.map((row, rowIndex) => ( {columns.map((column, colIndex) => ( @@ -93,6 +91,13 @@ export default function StatusTable({ data, columns }) {
+ {totalPages > 1 && ( + + )} ); } From 5ca6850b4ad14cd2f2fd4577df88a4c66b856123 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 16:45:29 -0400 Subject: [PATCH 11/37] Refactored alert component --- Client/src/Components/Alert/index.css | 8 ++--- Client/src/Components/Alert/index.jsx | 52 +++++++++++++++++++++++++-- Client/src/Pages/Demo/Demo.jsx | 29 +++++++++++++-- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/Alert/index.css b/Client/src/Components/Alert/index.css index 9f0c397e3..c65bfa38c 100644 --- a/Client/src/Components/Alert/index.css +++ b/Client/src/Components/Alert/index.css @@ -1,9 +1,7 @@ .alert { margin: 0; width: fit-content; - padding: var(--env-var-spacing-1-plus); +} +.alert, .alert button{ font-size: var(--env-var-font-size-medium); -} -.alert .MuiStack-root { - max-width: 85%; -} +} \ No newline at end of file diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index bf2531bba..cacd0f058 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -1,10 +1,12 @@ import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import { Box, Stack } from "@mui/material"; +import { Box, IconButton, Stack } from "@mui/material"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined"; import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined"; +import CloseIcon from "@mui/icons-material/Close"; import "./index.css"; +import Button from "../Button"; /** * Icons mapping for different alert variants. @@ -25,7 +27,7 @@ const icons = { * @returns {JSX.Element} */ -const Alert = ({ variant, title, body }) => { +const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { const theme = useTheme(); const { bg, border, color } = theme.alert[variant]; const icon = icons[variant]; @@ -34,19 +36,63 @@ const Alert = ({ variant, title, body }) => { - {icon && {icon}} + {hasIcon && {icon}} {title && {title}} {body && {body}} + {hasIcon && toast && ( + + )} + {toast && ( + + + + )} ); }; diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index 42e30b6d2..5163b51be 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -421,11 +421,20 @@ const Demo = () => { - + console.log("changed")} + /> - } label="image.jpg" size="2 MB" /> + } + label="image.jpg" + size="2 MB" + progress={50} + onClick={() => console.log("click")} + /> @@ -449,6 +458,22 @@ const Demo = () => { body="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." /> + + + + + + From e9cb0aa339f691410b7c5b239839a40799953768 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 16:54:25 -0400 Subject: [PATCH 12/37] Removed ripple effect from icon button and renamed bools --- Client/src/Components/Alert/index.jsx | 10 +++++++--- Client/src/Pages/Demo/Demo.jsx | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index cacd0f058..7cdc972a5 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -27,7 +27,7 @@ const icons = { * @returns {JSX.Element} */ -const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { +const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => { const theme = useTheme(); const { bg, border, color } = theme.alert[variant]; const icon = icons[variant]; @@ -50,7 +50,7 @@ const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { {title && {title}} {body && {body}} - {hasIcon && toast && ( + {hasIcon && isToast && ( )} - {toast && ( + {isToast && ( { "&:focus": { outline: "none", }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, }} > { From 92d4caa801cc15dfb4e1f3492817ae3063ae3ebf Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:05:41 -0400 Subject: [PATCH 13/37] Added create toast function --- Client/src/Pages/Demo/Demo.jsx | 18 +++++++++++++++++- Client/src/Utils/Theme.js | 6 +++--- Client/src/Utils/toastUtils.jsx | 26 ++++++++++++++++++++++++++ Client/src/index.css | 9 +++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 Client/src/Utils/toastUtils.jsx diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index c5f83d2a8..6a7a07e11 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -40,6 +40,8 @@ import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice"; import ImageField from "../../Components/TextFields/Image"; import ProgressUpload from "../../Components/ProgressBars"; import Alert from "../../Components/Alert"; +import { createToast } from "../../Utils/toastUtils"; +import { ToastContainer } from "react-toastify"; const cols = [ { @@ -463,7 +465,7 @@ const Demo = () => { variant="info" title="We've just released a new feature" body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid pariatur, ipsum dolor." - toast={true} + isToast={true} /> @@ -474,8 +476,22 @@ const Demo = () => { hasIcon={false} /> + + - - -
- -
-
{header}
-
- - -
-
- - ); -}; - -AnnouncementUpdateSubscription.propTypes = { - title: PropTypes.string.isRequired, - text: PropTypes.string.isRequired, - cancel: PropTypes.string.isRequired, - positive: PropTypes.string.isRequired, - header: PropTypes.string.isRequired, - button: PropTypes.string.isRequired, -}; - -export default AnnouncementUpdateSubscription; diff --git a/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css b/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css deleted file mode 100644 index 6064a86cd..000000000 --- a/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css +++ /dev/null @@ -1,86 +0,0 @@ -.update-subscription { - margin: 20px; -} - -.update-subscription, -.update-subscription-dialog { - max-width: 770px; - border-radius: 4px; - border: 1px solid #eaecf0; -} - -.update-subscription-dialog { - padding: 15px 30px; -} - -.subscription-dialog-title { - font-size: 1rem; - font-weight: 700; - color: var(--env-var-color-1); -} - -.v-spacing-small { - height: 10px; -} - -.v-spacing-medium { - height: 15px; -} - -.subscription-dialog-text { - font-size: 13px; - color: var(--env-var-color-2); -} - -.subscription-dialog-controls { - display: flex; -} - -.controls-negative { - padding: 12px 16px; - margin-right: 12px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-6); - background-color: var(--env-var-color-8); - font-size: var(--env-var-font-size-medium); -} - -.controls-positive, -.subscribe-button { - padding: 12px 16px; - margin-right: 12px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-7); - background-color: var(--env-var-color-3); - color: var(--env-var-color-8); - font-size: var(--env-var-font-size-medium); -} - -.update-subscription-title { - color: var(--env-var-color-5); - padding: 0 20px; - font-weight: 700; - font-size: 13px; - margin-bottom: 10px; -} - -.subscription-email { - display: flex; - padding: 0 20px; -} - -.subscription-email-field { - width: 290px; - height: 24px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-4); - box-shadow: 0px 1px 2px 0px rgba(101, 182, 28, 0.05); - padding: 5px 15px; - margin-right: 20px; -} - -.subscribe-button { - width: 100px; - height: 34px; - padding: 2px 15px; -} diff --git a/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx b/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx deleted file mode 100644 index 2abbadb1e..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import "./announcementsDualButton.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.subject - The announcement subject text - * @param {string} props.body - The announcement body text content - * @param {string} props.esc - The text for the escape button - * @param {string} props.primary - The text for the primary button - * @returns {JSX.Element} - Renders the announcement dual button component - */ - -const AnnouncementsDualButton = ({ subject, body, esc, primary }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
-
{subject}
-
-
{body}
-
-
{esc}
-
{primary}
-
-
-
-
- -
-
- ); -}; - -AnnouncementsDualButton.propTypes = { - subject: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - primary: PropTypes.string.isRequired, -}; - -export default AnnouncementsDualButton; diff --git a/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css b/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css deleted file mode 100644 index aff02ab46..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css +++ /dev/null @@ -1,25 +0,0 @@ -.announcement-without-tile { - margin: 20px; - max-width: 343px; - width: fit-content; - display: flex; - border: 1px solid var(--env-var-color-4); - padding: 16px; - border-radius: var(--env-var-radius-1); -} - -.h-spacing { - width: 20px; -} - -.announcement-without-content-subject { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-5); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-without-content-body { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-2); -} diff --git a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx b/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx deleted file mode 100644 index bd2675f4c..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import "./announcementsDualButtonWithIcon.css"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {React.ReactNode} props.icon - Optional React node for an announcement icon - * @param {string} props.subject - The announcement subject text - * @param {string} props.body - The announcement body text content - * @param {string} props.esc - The text for the escape button (usually "Close") - * @param {string} props.primary - The text for the primary button - * @param {function} props.closeToast - Function to close the toast notification - * @returns {JSX.Element} - Renders the announcement dual button with icon component - */ - -const AnnouncementsDualButtonWithIcon = ({ - icon, - subject, - body, - esc, - primary, - closeToast, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
- {icon &&
{icon}
} -
- {subject && ( -
{subject}
- )} - {body &&
{body}
} - {(esc || primary) && ( -
- {esc && ( -
- {esc} -
- )} - {primary && ( -
{primary}
- )} -
- )} -
-
- -
-
- ); -}; - -AnnouncementsDualButtonWithIcon.propTypes = { - icon: PropTypes.object, - subject: PropTypes.string, - body: PropTypes.string, - esc: PropTypes.string, - primary: PropTypes.string, - closeToast: PropTypes.func.isRequired, -}; - -export default AnnouncementsDualButtonWithIcon; diff --git a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css b/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css deleted file mode 100644 index 02f132374..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css +++ /dev/null @@ -1,58 +0,0 @@ -.announcement-tile { - margin: 20px; - width: fit-content; - display: flex; - border: 1px solid var(--env-var-color-4); - padding: 16px; - border-radius: var(--env-var-radius-1); -} - -.info-icon-frame { - display: flex; - width: var(--env-var-default-2); - height: var(--env-var-default-2); - border: 1px solid var(--env-var-color-6); - border-radius: var(--env-var-radius-2); - justify-content: center; - align-items: center; -} - -.announcement-content { - padding: 0 16px; -} - -.announcement-content-subject { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-5); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-content-body { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-2); - font-weight: 300; -} - -.announcement-content-controllers { - display: flex; - font-size: var(--env-var-font-size-medium); - margin-top: 15px; -} - -.controllers-button-esc { - font-size: var(--env-var-font-size-medium); - font-weight: 600; - margin-right: 12px; -} - -.controllers-button-primary { - font-size: var(--env-var-font-size-medium); - font-weight: 600; - color: var(--env-var-color-3); -} - -.announcement-close { - width: var(--env-var-default-1); - height: var(--env-var-default-1); -} diff --git a/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx b/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx deleted file mode 100644 index 20a179052..000000000 --- a/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import "./announcementsMessageBar.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.message - The announcement message text content (required) - * @returns {JSX.Element} - Renders the announcement message bar component - */ -const AnnouncementsMessageBar = ({ message }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{message}
-
- -
-
- ); -}; - -AnnouncementsMessageBar.propTypes = { - message: PropTypes.string.isRequired, -}; - -export default AnnouncementsMessageBar; diff --git a/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css b/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css deleted file mode 100644 index 7a6e30b0a..000000000 --- a/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css +++ /dev/null @@ -1,8 +0,0 @@ -.announcement-messagebar-body { - margin: 20px; - font-size: var(--env-var-font-size-medium); - padding: 0 16px 0 0; - justify-content: center; - align-items: center; - margin: auto; -} diff --git a/Client/src/Components/Toast/index.css b/Client/src/Components/Toast/index.css deleted file mode 100644 index c778d22f5..000000000 --- a/Client/src/Components/Toast/index.css +++ /dev/null @@ -1,3 +0,0 @@ -.Toastify__toast-container { - width: auto; -} \ No newline at end of file diff --git a/Client/src/Components/Toast/index.jsx b/Client/src/Components/Toast/index.jsx deleted file mode 100644 index 41367ec11..000000000 --- a/Client/src/Components/Toast/index.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from "prop-types"; -import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert"; -import AnnouncementsDualButtonWithIcon from "./AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon"; -import "react-toastify/dist/ReactToastify.css"; -import { toast, ToastContainer } from "react-toastify"; -import "./index.css"; - -/** - * ToastComponent displays a notification that is triggered by error messages. - * - * @component - * @param {Object} props - The component props - * @param {string} props.subject - The subject or title of the toast message - * @param {string} props.body - The body content of the toast message - * @param {string} props.esc - The text for the dismiss action button - * @param {string} props.primary - The text for the primary action button - * @returns {JSX.Element} JSX element representing the ToastComponent - */ - -const ToastComponent = ({ subject, body, esc, primary }) => { - const displayMsg = () => { - toast( - ({ closeToast, toastProps }) => ( - } - subject={subject} - body={body} - esc={esc} - primary={primary} - closeToast={closeToast} - /> - ), - { closeButton: false } - ); - }; - - return ( -
- - -
- ); -}; - -ToastComponent.propTypes = { - subject: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - primary: PropTypes.string.isRequired, -}; - -export default ToastComponent; diff --git a/Client/src/Pages/PlayGround/PlayGround.jsx b/Client/src/Pages/PlayGround/PlayGround.jsx index bde0b6761..247146362 100644 --- a/Client/src/Pages/PlayGround/PlayGround.jsx +++ b/Client/src/Pages/PlayGround/PlayGround.jsx @@ -4,16 +4,9 @@ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; import WebsiteTextField from "../../Components/TextFields/Website/WebsiteTextField"; import DescriptionTextField from "../../Components/TextFields/Description/DescriptionTextField"; -import AnnouncementsDualButton from "../../Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton"; -import AnnouncementsDualButtonWithIcon from "../../Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert"; -import AnnouncementsMessageBar from "../../Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar"; -import AnnouncementUpdateSubscription from "../../Components/Toast/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription"; import PlayGroundCharts from "./PlayGround-Charts"; import PlayGroundPopupModals from "./PlayGround-Popup-Modals"; import PlayGroundTooltips from "./PlayGround-Tooltips"; -import Toast from "../../Components/Toast"; // This Component is just for the development and test // purposes and just to see what my components look like while development @@ -53,7 +46,6 @@ function PlayGround() { hasCopyButton={true} hintText="This is a hint text to help user." /> -
{/* Now, illustration of the Description text fields */} @@ -66,55 +58,6 @@ function PlayGround() { hasError={true} hintText="This is a hint text to help user." /> - - - - } - subject="We’ve just released a new feature" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="View changes" - /> - - } - subject="There was a problem with that action" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="Learn more" - /> - - } - subject="Successfully updated profile" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="Learn more" - /> - - - - - -

From 7e1ccff00df698b4187e444c5a60f05095193446 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:53:14 -0400 Subject: [PATCH 15/37] Added toast alerts to profile panel --- .../TabPanels/Account/ProfilePanel.jsx | 30 +++++++++++++------ Client/src/Pages/Account/index.jsx | 3 ++ Client/src/Utils/toastUtils.jsx | 2 +- Client/src/index.css | 3 ++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index ab44b34b3..2fdbba2e6 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -22,7 +22,7 @@ import ImageIcon from "@mui/icons-material/Image"; import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; import { clearMonitorState } from "../../../Features/Monitors/monitorsSlice"; - +import { createToast } from "../../../Utils/toastUtils"; /** * ProfilePanel component displays a form for editing user profile information * and allows for actions like updating profile picture, credentials, @@ -153,12 +153,8 @@ const ProfilePanel = () => { setIsOpen(""); }; - console.log( - localData.deleteProfileImage === false && localData.file === undefined - ); - // Handles form submission to update user profile - const handleSaveProfile = (event) => { + const handleSaveProfile = async (event) => { event.preventDefault(); if ( localData.firstname === user.firstname && @@ -166,12 +162,28 @@ const ProfilePanel = () => { localData.deleteProfileImage === undefined && localData.file === undefined ) { - // TODO: Add toast/notification for unchanged profile data + createToast({ + variant: "error", + body: "Unable to update profile: No changes detected.", + hasIcon: false, + }); return; } - dispatch(update({ authToken, localData })); - // TODO: Add toast/notification for profile update success + const action = await dispatch(update({ authToken, localData })); + if (action.payload.success) { + createToast({ + variant: "info", + body: "Your profile data was changed successfully.", + hasIcon: false, + }); + } else { + createToast({ + variant: "error", + body: "There was an error updating your profile data.", + hasIcon: false, + }); + } }; // Removes current profile image from UI diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index 2bb4dc018..ec010f006 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -8,6 +8,8 @@ import ProfilePanel from "../../Components/TabPanels/Account/ProfilePanel"; import PasswordPanel from "../../Components/TabPanels/Account/PasswordPanel"; import TeamPanel from "../../Components/TabPanels/Account/TeamPanel"; import { useNavigate } from "react-router"; +import "react-toastify/dist/ReactToastify.css"; +import { ToastContainer } from "react-toastify"; /** * Account component renders a settings page with tabs for Profile, Password, and Team settings. @@ -73,6 +75,7 @@ const Account = ({ open = "profile" }) => { +
); }; diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 01beaeaea..7fb29ff6d 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -3,7 +3,7 @@ import Alert from "../Components/Alert"; export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { const toastConfig = { - position: "top-right", + position: "bottom-right", autoClose: 3000, hideProgressBar: true, closeButton: false, diff --git a/Client/src/index.css b/Client/src/index.css index 1ba3b2239..086692678 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -124,6 +124,9 @@ button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } +.Toastify__toast-container { + width: auto; +} .Toastify [class^="Toastify__toast"] { padding: 0; margin: 0; From e9bab07309fcdcaa8d256aebc1fd791b77ecbb0d Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:55:26 -0400 Subject: [PATCH 16/37] Added small fix to prevent spamming the save button --- Client/src/Components/TabPanels/Account/ProfilePanel.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 2fdbba2e6..27949be8a 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -58,6 +58,7 @@ const ProfilePanel = () => { // Handles input field changes and performs validation const handleChange = (event) => { + errors["unchanged"] && clearError("unchanged"); const { value, id } = event.target; const name = idToName[id]; setLocalData((prev) => ({ @@ -163,10 +164,11 @@ const ProfilePanel = () => { localData.file === undefined ) { createToast({ - variant: "error", + variant: "warning", body: "Unable to update profile: No changes detected.", hasIcon: false, }); + setErrors({ unchanged: "unable to update profile" }); return; } From 8bd8c292b8e549b178b912071464d61a9fc05825 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 19:00:31 -0400 Subject: [PATCH 17/37] Added toast alerts to password panel --- .../Components/TabPanels/Account/PasswordPanel.jsx | 13 +++++++++++-- .../Components/TabPanels/Account/ProfilePanel.jsx | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx index d0f6e6f62..0e035f5cf 100644 --- a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -8,6 +8,7 @@ import { editPasswordValidation } from "../../../Validation/validation"; import Alert from "../../Alert"; import { update } from "../../../Features/Auth/authSlice"; import { useDispatch, useSelector } from "react-redux"; +import { createToast } from "../../../Utils/toastUtils"; /** * PasswordPanel component manages the form for editing password. @@ -78,15 +79,23 @@ const PasswordPanel = () => { } else { const action = await dispatch(update({ authToken, localData })); if (action.payload.success) { - // TODO: Add toast/notification for password update success + createToast({ + variant: "info", + body: "Your password was changed successfully.", + hasIcon: false, + }); setLocalData({ password: "", newPassword: "", confirm: "", }); } else { - // TODO: Add toast/notification for password update rejected // TODO: Check for other errors? + createToast({ + variant: "info", + body: "Your password input was incorrect.", + hasIcon: false, + }); setErrors({ password: "*" + action.payload.msg + "." }); } } diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 27949be8a..52d10a2f5 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -86,7 +86,7 @@ const ProfilePanel = () => { delete: false, }); - //TODO - potentitally remove once image compression functions are implemented + //TODO - potentitally remove, will revisit in the future intervalRef.current = setInterval(() => { const buffer = 12; setProgress((prev) => { From 91abe374f6e50fdf952f3c2dac4ccbafb64ee070 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 20:30:11 -0400 Subject: [PATCH 18/37] Added jsdocs and proptypes --- Client/src/Components/Alert/index.jsx | 13 +++++++++++++ Client/src/Utils/toastUtils.jsx | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index 7cdc972a5..3fa0d82ab 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -24,6 +24,9 @@ const icons = { * @param {'info' | 'error' | 'warning'} props.variant - The type of alert. * @param {string} [props.title] - The title of the alert. * @param {string} [props.body] - The body text of the alert. + * @param {boolean} [props.isToast] - Indicates if the alert is used as a toast notification. + * @param {boolean} [props.hasIcon] - Whether to display an icon in the alert. + * @param {function} props.onClick - Toast dismiss function. * @returns {JSX.Element} */ @@ -105,6 +108,16 @@ Alert.propTypes = { variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, title: PropTypes.string, body: PropTypes.string, + isToast: PropTypes.bool, + hasIcon: PropTypes.bool, + onClick: function (props, propName, componentName) { + if (props.isToast && !props[propName]) { + return new Error( + `Prop '${propName}' is required when 'isToast' is true in '${componentName}'.` + ); + } + return null; + }, }; export default Alert; diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 7fb29ff6d..73c21c260 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -1,6 +1,16 @@ +import PropTypes from "prop-types"; import { toast } from "react-toastify"; import Alert from "../Components/Alert"; +/** + * @param {object} props + * @param {'info' | 'error' | 'warning'} - The variant of the alert (e.g., "info", "error"). + * @param {string} props.title - The title of the alert. + * @param {string} props.body - The body/content of the alert. + * @param {boolean} props.hasIcon - Whether the alert should include an icon. + * @param {object} [props.config] - Additional configuration props for the toast. + */ + export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { const toastConfig = { position: "bottom-right", @@ -24,3 +34,11 @@ export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { toastConfig ); }; + +createToast.propTypes = { + variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, + title: PropTypes.string, + body: PropTypes.string, + hasIcon: PropTypes.bool, + config: PropTypes.object, +}; From e2b9a6fa161a84cd75487ed81c07fc35f91a07ce Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 11:33:34 -0400 Subject: [PATCH 19/37] Clear unchanged error when picture is modified --- Client/src/Components/TabPanels/Account/ProfilePanel.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 52d10a2f5..e96f507d9 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -23,6 +23,7 @@ import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; import { clearMonitorState } from "../../../Features/Monitors/monitorsSlice"; import { createToast } from "../../../Utils/toastUtils"; + /** * ProfilePanel component displays a form for editing user profile information * and allows for actions like updating profile picture, credentials, @@ -152,6 +153,7 @@ const ProfilePanel = () => { deleteProfileImage: false, })); setIsOpen(""); + errors["unchanged"] && clearError("unchanged"); }; // Handles form submission to update user profile @@ -194,6 +196,7 @@ const ProfilePanel = () => { ...prev, deleteProfileImage: true, })); + errors["unchanged"] && clearError("unchanged"); }; // Initiates the account deletion process From caadc6c9209c64fedfe858fd2990ea4c21b5425f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 10:02:39 -0700 Subject: [PATCH 20/37] Added config --- .coderabbit.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 000000000..dbfba6220 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,15 @@ +language: "en-CA" +tone_instructions: "Tale as old as time (ooh-ooh) Tune as old as song Bittersweet and strange Finding you can change Learning you were wrong Certain as the sun Rising in the east Tale as old as time Song as old as rhyme Beauty and the beast" +early_access: false +reviews: + profile: "assertive" + request_changes_workflow: false + high_level_summary: false + poem: false + review_status: true + collapse_walkthrough: true + auto_review: + enabled: true + drafts: false +chat: + auto_reply: false From 2257c717f3ce1a43d7ed0248426093b4ceca402d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 10:07:03 -0700 Subject: [PATCH 21/37] Removed walkthrough --- .coderabbit.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index dbfba6220..fbe457426 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -7,7 +7,6 @@ reviews: high_level_summary: false poem: false review_status: true - collapse_walkthrough: true auto_review: enabled: true drafts: false From 12e85aed03ed5feb143cbeb58bd73a6a02486b56 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 10:24:46 -0700 Subject: [PATCH 22/37] Fixed non functioning code --- Client/src/Features/Monitors/monitorsSlice.js | 13 ++++++++----- Server/db/MongoDB.js | 14 +++++++------- Server/routes/monitorRoute.js | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) 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/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( From b0cbea156cf94a6d75bd963ac1cb28652b49ed17 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 10:57:39 -0700 Subject: [PATCH 23/37] Add check for admin acount --- Server/controllers/authController.js | 15 +++++++++++++++ Server/utils/messages.js | 1 + 2 files changed, 16 insertions(+) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 184158e55..979a0b87e 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -60,6 +60,21 @@ const registerController = async (req, res, next) => { return; } + // Check if an admin user exists, if so, error + try { + const admin = await req.db.checkAdmin(req, res); + console.log(admin); + if (admin === true) { + throw new Error(errorMessages.AUTH_ADMIN_EXISTS); + } + } catch (error) { + console.log("WEEEEEEE", error.message); + error.service = SERVICE_NAME; + error.status = 403; + next(error); + return; + } + // Create a new user try { const newUser = await req.db.insertUser(req, res); diff --git a/Server/utils/messages.js b/Server/utils/messages.js index aded84d59..787adc8f6 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -4,6 +4,7 @@ const errorMessages = { UNKNOWN_ERROR: "An unknown error occurred", // Auth Controller UNAUTHORIZED: "Unauthorized access", + AUTH_ADMIN_EXISTS: "Admin already exists", //Error handling middleware UNKNOWN_SERVICE: "Unknown service", From e4c179f63d4ab3bb41fe572563fd5d8204e8e718 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 14:00:16 -0400 Subject: [PATCH 24/37] Added toast alerts to login/register pages --- Client/src/App.jsx | 3 +++ Client/src/Pages/Account/index.jsx | 3 --- Client/src/Pages/Login/index.jsx | 27 +++++++++++++++++++-------- Client/src/Pages/Register/index.jsx | 27 +++++++++++++++++++-------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Client/src/App.jsx b/Client/src/App.jsx index abd2d37b7..f1c24049a 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -20,6 +20,8 @@ import NewPasswordConfirmed from "./Pages/NewPasswordConfirmed"; import ProtectedRoute from "./Components/ProtectedRoute"; import Details from "./Pages/Details"; import Maintenance from "./Pages/Maintenance"; +import "react-toastify/dist/ReactToastify.css"; +import { ToastContainer } from "react-toastify"; function App() { return ( @@ -92,6 +94,7 @@ function App() { element={} /> + ); } diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index ec010f006..2bb4dc018 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -8,8 +8,6 @@ import ProfilePanel from "../../Components/TabPanels/Account/ProfilePanel"; import PasswordPanel from "../../Components/TabPanels/Account/PasswordPanel"; import TeamPanel from "../../Components/TabPanels/Account/TeamPanel"; import { useNavigate } from "react-router"; -import "react-toastify/dist/ReactToastify.css"; -import { ToastContainer } from "react-toastify"; /** * Account component renders a settings page with tabs for Profile, Password, and Team settings. @@ -75,7 +73,6 @@ const Account = ({ open = "profile" }) => { - ); }; diff --git a/Client/src/Pages/Login/index.jsx b/Client/src/Pages/Login/index.jsx index a9fcde0d5..cd96ea9bd 100644 --- a/Client/src/Pages/Login/index.jsx +++ b/Client/src/Pages/Login/index.jsx @@ -13,6 +13,7 @@ import axiosInstance from "../../Utils/axiosConfig"; import { loginValidation } from "../../Validation/validation"; import { login } from "../../Features/Auth/authSlice"; import { useDispatch } from "react-redux"; +import { createToast } from "../../Utils/toastUtils"; const Login = () => { const dispatch = useDispatch(); @@ -74,16 +75,26 @@ const Login = () => { } } catch (error) { if (error.name === "ValidationError") { - // TODO Handle validation errors - console.log(error.details); - alert(error); + // validation errors + createToast({ + variant: "info", + body: error.details[0].message, + hasIcon: false, + }); } else if (error.response) { - // TODO handle dispatch errors - alert(error.response.msg); + // dispatch errors + createToast({ + variant: "info", + body: error.response.msg, + hasIcon: false, + }); } else { - // TODO handle unknown errors - console.log(error); - alert("Unknown error"); + // unknown errors + createToast({ + variant: "info", + body: "Unknown error.", + hasIcon: false, + }); } } }; diff --git a/Client/src/Pages/Register/index.jsx b/Client/src/Pages/Register/index.jsx index 617e04edd..098c00bf0 100644 --- a/Client/src/Pages/Register/index.jsx +++ b/Client/src/Pages/Register/index.jsx @@ -14,6 +14,7 @@ import { registerValidation } from "../../Validation/validation"; import axiosInstance from "../../Utils/axiosConfig"; import { useDispatch } from "react-redux"; import { register } from "../../Features/Auth/authSlice"; +import { createToast } from "../../Utils/toastUtils"; const Register = () => { const dispatch = useDispatch(); @@ -91,16 +92,26 @@ const Register = () => { } } catch (error) { if (error.name === "ValidationError") { - // TODO Handle validation errors - console.log(error); - alert(error); + // validation errors + createToast({ + variant: "info", + body: error.details[0].message, + hasIcon: false, + }); } else if (error.response) { - // TODO handle dispatch errors - alert(error.response.msg); + // dispatch errors + createToast({ + variant: "info", + body: error.response.msg, + hasIcon: false, + }); } else { - // TODO handle unknown errors - console.log(error); - alert("Unknown error"); + // unknown errors + createToast({ + variant: "info", + body: "Unknown error.", + hasIcon: false, + }); } } }; From f4bd0e7a097a57dc911e55211f69b6409c864c81 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 14:08:30 -0400 Subject: [PATCH 25/37] Added check to error details --- Client/src/Pages/Login/index.jsx | 5 ++++- Client/src/Pages/Register/index.jsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Client/src/Pages/Login/index.jsx b/Client/src/Pages/Login/index.jsx index cd96ea9bd..85d1c8664 100644 --- a/Client/src/Pages/Login/index.jsx +++ b/Client/src/Pages/Login/index.jsx @@ -78,7 +78,10 @@ const Login = () => { // validation errors createToast({ variant: "info", - body: error.details[0].message, + body: + error && error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data.", hasIcon: false, }); } else if (error.response) { diff --git a/Client/src/Pages/Register/index.jsx b/Client/src/Pages/Register/index.jsx index 098c00bf0..8ca92d5d8 100644 --- a/Client/src/Pages/Register/index.jsx +++ b/Client/src/Pages/Register/index.jsx @@ -95,7 +95,10 @@ const Register = () => { // validation errors createToast({ variant: "info", - body: error.details[0].message, + body: + error && error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data.", hasIcon: false, }); } else if (error.response) { From 7eacc542b55e45a143281b90960d1cd59ce18d02 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 11:21:33 -0700 Subject: [PATCH 26/37] Changed text for create account --- Client/src/Pages/Register/index.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 +
From 38102d1e7b93b4f0759769272fdf6c128507d94a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 11:30:36 -0700 Subject: [PATCH 27/37] Removed 404 error thrown when user has no monitors to display --- Server/controllers/monitorController.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 3cb1711c7..52433c8d7 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -87,12 +87,6 @@ const getMonitorsByUserId = async (req, res, next) => { const userId = req.params.userId; const monitors = await req.db.getMonitorsByUserId(req, res); - if (monitors && monitors.length === 0) { - const err = new Error(errorMessages.MONITOR_GET_BY_USER_ID); - err.status = 404; - throw err; - } - return res.json({ success: true, msg: successMessages.MONITOR_GET_BY_USER_ID(userId), From d840b05f17d0e34a7040193bdb674b12a10c6733 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 12 Jul 2024 14:30:39 -0700 Subject: [PATCH 28/37] Implemented a basic table that takes a standard arrangement of data --- Client/src/Components/BasicTable/index.css | 0 Client/src/Components/BasicTable/index.jsx | 146 +++++++++++++++++++ Client/src/Components/MonitorTable/index.jsx | 143 +++++------------- 3 files changed, 181 insertions(+), 108 deletions(-) create mode 100644 Client/src/Components/BasicTable/index.css create mode 100644 Client/src/Components/BasicTable/index.jsx diff --git a/Client/src/Components/BasicTable/index.css b/Client/src/Components/BasicTable/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx new file mode 100644 index 000000000..a80c7bfb6 --- /dev/null +++ b/Client/src/Components/BasicTable/index.jsx @@ -0,0 +1,146 @@ +import { + TableContainer, + Paper, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + TablePagination, +} from "@mui/material"; +import React, { useState } from "react"; +import PropTypes from "prop-types"; + +/** + * BasicTable Component + * Renders a table with optional pagination. + * + * @component + * @param {Object} props - Component props. + * @param {Array} props.headers - Array of header titles for the table. + * @param {Array} props.rowItems - Array of row data objects for the table. + * @param {boolean} props.paginated - Flag to enable pagination. + * + * @example + * + * Headers is an array of objets that have a unique ID and a name + * Rows is an array of objects that have a unique ID and some data to display + * Row data can be any valid React node + * Rows are split into groups according to the number of headers, so if you have 3 headers then + * Rows will go under the index % 3 column. + * ie: Row 0 -> col 0 % 3 = 0 + * ie: Row 1 -> col 1 % 3 = 1 + * ie: Row 2 -> col 2 % 3 = 2 + * ie: Row 3 -> col 3 % 3 = 0 + * + * Thus items are put in their correct columns + * + * + * const headers = [ + * { id: 1, name: "ID" }, + * { id: 2, name: "Name" }, + * { id: 3, name: "Age" }, + * ]; + * const rows = [ + * { id: 1, data: 999 }, + * { id: 2, data:
Alex Holliday
}, + * { id: 3, data:
5
}, + * // More rows... + * ]; + * + */ + +const BasicTable = ({ headers, rowItems, paginated }) => { + // Add headers to props validation + + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); // Reset to first page after changing rows per page + }; + + const numOfCols = headers.length; + + const groupedRowItems = rowItems.reduce((acc, item, idx) => { + const rowIndex = Math.floor((item.id - 1) / numOfCols); + if (!acc[rowIndex]) acc[rowIndex] = { items: [], idx }; + acc[rowIndex].items.push({ item }); + return acc; + }, {}); + + const paginatedRowsKeys = Object.keys(groupedRowItems).slice( + page * rowsPerPage, + page * rowsPerPage + rowsPerPage + ); + + return ( + <> + + + + + {headers.map((header) => { + return {header.name}; + })} + + + + {paginated === false && + Object.keys(groupedRowItems).map((key) => { + return ( + + {groupedRowItems[key].items.map(({ item }) => { + return {item.data}; + })} + + ); + })} + {paginated === true && + paginatedRowsKeys.map((key) => ( + + {groupedRowItems[key].items.map(({ item }) => ( + {item.data} + ))} + + ))} + +
+
+ {paginated === true && ( + + )} + + ); +}; + +BasicTable.propTypes = { + headers: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + name: PropTypes.string.isRequired, + }) + ).isRequired, + rowItems: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + data: PropTypes.node, + }) + ).isRequired, + paginated: PropTypes.bool, +}; + +export default BasicTable; diff --git a/Client/src/Components/MonitorTable/index.jsx b/Client/src/Components/MonitorTable/index.jsx index 5bb06935b..338ebb793 100644 --- a/Client/src/Components/MonitorTable/index.jsx +++ b/Client/src/Components/MonitorTable/index.jsx @@ -1,23 +1,8 @@ import "./index.css"; -import { useState } from "react"; import PropTypes from "prop-types"; import ResponseTimeChart from "../Charts/ResponseTimeChart"; -import { - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Paper, - Pagination, - PaginationItem, -} from "@mui/material"; -import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded"; +import BasicTable from "../BasicTable"; import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react"; -import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded"; -import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded"; -import { useNavigate } from "react-router-dom"; /** * Host component. @@ -95,100 +80,42 @@ const Status = ({ params }) => { * @returns {React.Component} Returns a table with the monitor data. */ const MonitorTable = ({ monitors = [] }) => { - const navigate = useNavigate(); - const [page, setPage] = useState(0); - const rowsPerPage = 5; - - const handleChangePage = (event, newPage) => { - setPage(newPage); - }; - - const mappedRows = monitors - .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - .map((monitor) => { - 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 ( - { - navigate(`/monitors/${monitor._id}`); - }} - > - - - - - - - - - - TODO Add Actions - - ); + const headers = [ + { id: 1, name: "Host" }, + { id: 2, name: "Status" }, + { id: 3, name: "Response Time" }, + { id: 4, name: "Actions" }, + ]; + const data = []; + monitors.forEach((monitor) => { + 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)", + }; + data.push({ id: data.length + 1, data: }); + data.push({ id: data.length + 1, data: }); + data.push({ + id: data.length + 1, + data: , }); + data.push({ id: data.length + 1, data: "TODO" }); + }); - return ( - <> - - - - - Host - - Status - - - - - Response Time - Actions - - - {mappedRows} -
-
- handleChangePage(event, value - 1)} - shape="rounded" - renderItem={(item) => ( - - )} - /> - - ); + return ; }; MonitorTable.propTypes = { From 3855d4e4d5a66aea6d263a6dec5e8896da676d74 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 18:45:23 -0400 Subject: [PATCH 29/37] Refactored textfield component --- .../src/Components/TextFields/Field/index.css | 49 ++++++++++ .../src/Components/TextFields/Field/index.jsx | 93 +++++++++++++++++++ Client/src/Pages/Demo/Demo.jsx | 40 ++++++-- Client/src/Utils/Theme.js | 5 +- Client/src/index.css | 1 + 5 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 Client/src/Components/TextFields/Field/index.css create mode 100644 Client/src/Components/TextFields/Field/index.jsx diff --git a/Client/src/Components/TextFields/Field/index.css b/Client/src/Components/TextFields/Field/index.css new file mode 100644 index 000000000..10d4c6720 --- /dev/null +++ b/Client/src/Components/TextFields/Field/index.css @@ -0,0 +1,49 @@ +.field { + min-width: 250px; +} +.field h1.MuiTypography-root, +.field h5.MuiTypography-root, +.field input, +.field .input-error { + font-size: var(--env-var-font-size-medium); +} +.field h1.MuiTypography-root, +.field h5.MuiTypography-root, +.field input { + color: var(--env-var-color-5); +} +.field h1.MuiTypography-root { + font-weight: 600; +} +.field h1.MuiTypography-root span { + color: var(--env-var-color-19); + margin-left: 1px; +} +.field h5.MuiTypography-root { + position: relative; + opacity: 0.8; + padding: var(--env-var-spacing-1-minus); + padding-left: 0; + border-right: solid 1px var(--env-var-color-29); +} +.field .MuiInputBase-root { + height: 39px; + padding-right: var(--env-var-spacing-1-minus); +} +.field input { + padding: var(--env-var-spacing-1-minus); +} +.field .MuiOutlinedInput-root fieldset { + border-color: var(--env-var-color-29); + border-radius: var(--env-var-radius-1); +} +.field .MuiOutlinedInput-root:hover fieldset { + border-color: var(--env-var-color-25); +} +.field .input-error { + color: var(--env-var-color-24); + opacity: 0.8; +} +.field:has(.input-error) .MuiOutlinedInput-root fieldset { + border-color: #fda29b; +} diff --git a/Client/src/Components/TextFields/Field/index.jsx b/Client/src/Components/TextFields/Field/index.jsx new file mode 100644 index 000000000..811146faa --- /dev/null +++ b/Client/src/Components/TextFields/Field/index.jsx @@ -0,0 +1,93 @@ +import { useTheme } from "@emotion/react"; +import { + IconButton, + InputAdornment, + Stack, + TextField, + Typography, +} from "@mui/material"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; +import Visibility from "@mui/icons-material/Visibility"; +import "./index.css"; + +const Field = ({ + type, + id, + label, + isRequired, + isVisible, + setVisibility, + autoComplete, + placeholder, + value, + onChange, + error, + disabled, +}) => { + const theme = useTheme(); + + return ( + + {label && ( + + {label} + {isRequired ? * : ""} + + )} + http://, + } + : type === "password" && { + endAdornment: ( + + setVisibility((show) => !show)} + tabIndex={-1} + sx={{ + color: theme.palette.section.borderColor, + padding: theme.gap.xs, + "&:focus": { + outline: "none", + }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, + }} + > + {!isVisible ? : } + + + ), + } + } + /> + {error && ( + + {error} + + )} + + ); +}; + +export default Field; diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index 6a7a07e11..9d3cea52d 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -41,7 +41,7 @@ import ImageField from "../../Components/TextFields/Image"; import ProgressUpload from "../../Components/ProgressBars"; import Alert from "../../Components/Alert"; import { createToast } from "../../Utils/toastUtils"; -import { ToastContainer } from "react-toastify"; +import Field from "../../Components/TextFields/Field"; const cols = [ { @@ -203,6 +203,9 @@ const Demo = () => { setIsLoading(false); }, 4000); }; + + //fields + const [visibility, setVisibility] = useState(false); return (
@@ -439,7 +442,7 @@ const Demo = () => { /> - + { body="Since you logged in via SSO, you cannot reset or modify your password." /> - + - + - + { isToast={true} /> - + { - + + + + + + +
); }; diff --git a/Client/src/Utils/Theme.js b/Client/src/Utils/Theme.js index 4b345c9de..f3ed11fc3 100644 --- a/Client/src/Utils/Theme.js +++ b/Client/src/Utils/Theme.js @@ -91,6 +91,7 @@ const theme = createTheme({ pY: "40px", }, gap: { + xs: "4px", small: "8px", medium: "12px", ml: "16px", @@ -101,7 +102,7 @@ const theme = createTheme({ info: { color: secondaryMain, bg: otherColorsWhite, - border: "#d0d5dd", + border: sectionBorder, }, error: { color: "#d92d20", @@ -111,7 +112,7 @@ const theme = createTheme({ warning: { color: "#DC6803", bg: "#fffcf5", - border: "#fec84b" + border: "#fec84b", }, }, }); diff --git a/Client/src/index.css b/Client/src/index.css index 086692678..674acc13e 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -59,6 +59,7 @@ --env-var-spacing-1: 12px; --env-var-spacing-1-plus: 16px; + --env-var-spacing-1-minus: 10px; --env-var-spacing-2: 24px; --env-var-spacing-3: 32px; --env-var-spacing-4: 40px; From 3adb73fe9627537fdc6b592ac82d73e35c06500e Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 19:27:47 -0400 Subject: [PATCH 30/37] Replaced textfield components in profile and password panels --- .../TabPanels/Account/PasswordPanel.jsx | 95 +++++++------------ .../TabPanels/Account/ProfilePanel.jsx | 83 +++++----------- .../src/Components/TextFields/Field/index.css | 20 ++-- .../src/Components/TextFields/Field/index.jsx | 73 ++++++-------- Client/src/Pages/Account/index.css | 8 +- Client/src/Pages/Demo/Demo.jsx | 2 +- 6 files changed, 105 insertions(+), 176 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx index 0e035f5cf..1ef7a07e3 100644 --- a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -3,7 +3,7 @@ import React, { useState } from "react"; import { useTheme } from "@emotion/react"; import { Box, Divider, Stack, Typography } from "@mui/material"; import ButtonSpinner from "../../ButtonSpinner"; -import PasswordTextField from "../../TextFields/Password/PasswordTextField"; +import Field from "../../TextFields/Field"; import { editPasswordValidation } from "../../../Validation/validation"; import Alert from "../../Alert"; import { update } from "../../../Features/Auth/authSlice"; @@ -120,76 +120,49 @@ const PasswordPanel = () => { Current password - - - {errors[idToName["edit-current-password"]] ? ( - - {errors[idToName["edit-current-password"]]} - - ) : ( - "" - )} - +
New password - - - {errors[idToName["edit-new-password"]] ? ( - - {errors[idToName["edit-new-password"]]} - - ) : ( - "" - )} - +
Confirm new password - - - {errors[idToName["edit-confirm-password"]] ? ( - - {errors[idToName["edit-confirm-password"]]} - - ) : ( - "" - )} - +
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index e96f507d9..d56bd5f4e 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -4,9 +4,8 @@ import TabPanel from "@mui/lab/TabPanel"; import { Box, Divider, Modal, Stack, Typography } from "@mui/material"; import ButtonSpinner from "../../ButtonSpinner"; import Button from "../../Button"; -import EmailTextField from "../../TextFields/Email/EmailTextField"; -import StringTextField from "../../TextFields/Text/TextField"; import Avatar from "../../Avatar"; +import Field from "../../TextFields/Field"; import { editProfileValidation, imageValidation, @@ -219,47 +218,27 @@ const ProfilePanel = () => { First name - - - {errors[idToName["edit-first-name"]] ? ( - - {errors[idToName["edit-first-name"]]} - - ) : ( - "" - )} - +
Last name - - - {errors[idToName["edit-last-name"]] ? ( - - {errors[idToName["edit-last-name"]]} - - ) : ( - "" - )} - +
@@ -268,25 +247,15 @@ const ProfilePanel = () => { This is your current email address — it cannot be changed. - - - {errors[idToName["edit-email"]] ? ( - - {errors[idToName["edit-email"]]} - - ) : ( - "" - )} - +
diff --git a/Client/src/Components/TextFields/Field/index.css b/Client/src/Components/TextFields/Field/index.css index 10d4c6720..8c51c81ee 100644 --- a/Client/src/Components/TextFields/Field/index.css +++ b/Client/src/Components/TextFields/Field/index.css @@ -1,21 +1,21 @@ .field { min-width: 250px; } -.field h1.MuiTypography-root, +.field h3.MuiTypography-root, .field h5.MuiTypography-root, .field input, .field .input-error { font-size: var(--env-var-font-size-medium); } -.field h1.MuiTypography-root, +.field h3.MuiTypography-root, .field h5.MuiTypography-root, .field input { color: var(--env-var-color-5); } -.field h1.MuiTypography-root { - font-weight: 600; +.field h3.MuiTypography-root { + font-weight: 500; } -.field h1.MuiTypography-root span { +.field h3.MuiTypography-root span { color: var(--env-var-color-19); margin-left: 1px; } @@ -27,19 +27,19 @@ border-right: solid 1px var(--env-var-color-29); } .field .MuiInputBase-root { - height: 39px; + height: 35px; + overflow: hidden; +} +.field .MuiInputBase-root:has(.MuiInputAdornment-root) { padding-right: var(--env-var-spacing-1-minus); } .field input { - padding: var(--env-var-spacing-1-minus); + padding: 0 var(--env-var-spacing-1-minus); } .field .MuiOutlinedInput-root fieldset { border-color: var(--env-var-color-29); border-radius: var(--env-var-radius-1); } -.field .MuiOutlinedInput-root:hover fieldset { - border-color: var(--env-var-color-25); -} .field .input-error { color: var(--env-var-color-24); opacity: 0.8; diff --git a/Client/src/Components/TextFields/Field/index.jsx b/Client/src/Components/TextFields/Field/index.jsx index 811146faa..b02f9891b 100644 --- a/Client/src/Components/TextFields/Field/index.jsx +++ b/Client/src/Components/TextFields/Field/index.jsx @@ -11,7 +11,7 @@ import Visibility from "@mui/icons-material/Visibility"; import "./index.css"; const Field = ({ - type, + type = "text", id, label, isRequired, @@ -29,60 +29,49 @@ const Field = ({ return ( {label && ( - + {label} {isRequired ? * : ""} )} http://, - } - : type === "password" && { - endAdornment: ( - - setVisibility((show) => !show)} - tabIndex={-1} - sx={{ - color: theme.palette.section.borderColor, - padding: theme.gap.xs, - "&:focus": { - outline: "none", - }, - "& .MuiTouchRipple-root": { - pointerEvents: "none", - display: "none", - }, - }} - > - {!isVisible ? : } - - - ), - } - } + InputProps={{ + startAdornment: type === "url" && ( + https:// + ), + endAdornment: type === "password" && ( + + setVisibility((show) => !show)} + tabIndex={-1} + sx={{ + color: theme.palette.section.borderColor, + padding: theme.gap.xs, + "&:focus": { + outline: "none", + }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, + }} + > + {!isVisible ? : } + + + ), + }} /> {error && ( - + {error} )} diff --git a/Client/src/Pages/Account/index.css b/Client/src/Pages/Account/index.css index acf71988b..608e08b94 100644 --- a/Client/src/Pages/Account/index.css +++ b/Client/src/Pages/Account/index.css @@ -139,7 +139,7 @@ } } -.account .MuiStack-root:has(p.MuiTypography-root.input-error) { +.account .MuiStack-root:has(span.MuiTypography-root.input-error) { position: relative; } .account .text-field-icon { @@ -150,11 +150,9 @@ height: 18px !important; opacity: 0.8; } -.account p.MuiTypography-root.input-error { - color: var(--env-var-color-24); - opacity: 0.8; +.account span.MuiTypography-root.input-error { position: absolute; - top: calc(100% + 5px); + top: 100%; } .account .email-text-field, .account .password-text-field { diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index 9d3cea52d..c95dcc7a1 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -512,7 +512,7 @@ const Demo = () => { placeholder="Create a password" /> From 20f04ab63e001dd34f244a0a68403f064964bc4a Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 20:01:41 -0400 Subject: [PATCH 31/37] Replaced textfield components in new monitor page --- .../src/Components/TextFields/Field/index.css | 17 +++++++++----- .../src/Components/TextFields/Field/index.jsx | 15 +++++++++++-- Client/src/Pages/CreateNewMonitor/index.css | 7 ++++++ Client/src/Pages/CreateNewMonitor/index.jsx | 22 +++++++++---------- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/Client/src/Components/TextFields/Field/index.css b/Client/src/Components/TextFields/Field/index.css index 8c51c81ee..ade346e30 100644 --- a/Client/src/Components/TextFields/Field/index.css +++ b/Client/src/Components/TextFields/Field/index.css @@ -15,31 +15,36 @@ .field h3.MuiTypography-root { font-weight: 500; } -.field h3.MuiTypography-root span { +.field h3.MuiTypography-root span.field-required { color: var(--env-var-color-19); margin-left: 1px; } +.field h3.MuiTypography-root span.field-optional { + opacity: 0.6; + margin-left: 4px; +} .field h5.MuiTypography-root { position: relative; opacity: 0.8; - padding: var(--env-var-spacing-1-minus); - padding-left: 0; - border-right: solid 1px var(--env-var-color-29); + padding-right: var(--env-var-spacing-1-minus); } .field .MuiInputBase-root { - height: 35px; - overflow: hidden; + height: 34px; } .field .MuiInputBase-root:has(.MuiInputAdornment-root) { padding-right: var(--env-var-spacing-1-minus); } .field input { + height: 100%; padding: 0 var(--env-var-spacing-1-minus); } .field .MuiOutlinedInput-root fieldset { border-color: var(--env-var-color-29); border-radius: var(--env-var-radius-1); } +.field .MuiOutlinedInput-root:hover:not(:has(input:focus)) fieldset { + border-color: var(--env-var-color-29); +} .field .input-error { color: var(--env-var-color-24); opacity: 0.8; diff --git a/Client/src/Components/TextFields/Field/index.jsx b/Client/src/Components/TextFields/Field/index.jsx index b02f9891b..54cb5ee57 100644 --- a/Client/src/Components/TextFields/Field/index.jsx +++ b/Client/src/Components/TextFields/Field/index.jsx @@ -15,6 +15,7 @@ const Field = ({ id, label, isRequired, + isOptional, isVisible, setVisibility, autoComplete, @@ -31,7 +32,8 @@ const Field = ({ {label && ( {label} - {isRequired ? * : ""} + {isRequired ? * : ""} + {isOptional ? (optional) : ""} )} https:// + + https:// + ), endAdornment: type === "password" && ( diff --git a/Client/src/Pages/CreateNewMonitor/index.css b/Client/src/Pages/CreateNewMonitor/index.css index 4702127f5..8ea9b2529 100644 --- a/Client/src/Pages/CreateNewMonitor/index.css +++ b/Client/src/Pages/CreateNewMonitor/index.css @@ -55,6 +55,13 @@ outline: none; border: none; } +.create-monitor-form .MuiStack-root:has(span.MuiTypography-root.input-error) { + position: relative; +} +.create-monitor-form span.MuiTypography-root.input-error { + position: absolute; + top: 100%; +} .monitors-gaps-medium { height: var(--env-var-spacing-2); diff --git a/Client/src/Pages/CreateNewMonitor/index.jsx b/Client/src/Pages/CreateNewMonitor/index.jsx index 805aa6426..9860d879d 100644 --- a/Client/src/Pages/CreateNewMonitor/index.jsx +++ b/Client/src/Pages/CreateNewMonitor/index.jsx @@ -1,6 +1,5 @@ import "./index.css"; import ConfigBox from "../../Components/ConfigBox"; -import FlexibileTextField from "../../Components/TextFields/Flexibile/FlexibileTextField"; import React, { useState } from "react"; import RadioButton from "../../Components/RadioButton"; import CustomizableCheckBox from "../../Components/Checkbox/CustomizableCheckbox"; @@ -12,6 +11,7 @@ import { createMonitor } from "../../Features/Monitors/monitorsSlice"; import { useNavigate } from "react-router-dom"; import { useTheme } from "@emotion/react"; import WestRoundedIcon from "@mui/icons-material/WestRounded"; +import Field from "../../Components/TextFields/Field"; const CreateNewMonitor = () => { const MS_PER_MINUTE = 60000; @@ -86,6 +86,7 @@ const CreateNewMonitor = () => { //obj to submit let monitor = { ...generalSettings, + url: "https://" + generalSettings.url, ...checks, }; @@ -162,25 +163,22 @@ const CreateNewMonitor = () => { } rightLayout={ - handleChange(event, "url", setGeneralSettings) } error={errors["url"]} /> - - Friendly name (optional) - - } + From a84b340d6e4caccee6231ffa89b8684476c782ef Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Sat, 13 Jul 2024 13:11:03 -0400 Subject: [PATCH 32/37] Replaced textfield components in login and register pages --- .../src/Components/TextFields/Field/index.css | 15 ++- .../src/Components/TextFields/Field/index.jsx | 2 +- Client/src/Pages/Login/index.css | 14 +-- Client/src/Pages/Login/index.jsx | 32 +++--- Client/src/Pages/Register/index.css | 11 -- Client/src/Pages/Register/index.jsx | 100 +++++++++++------- 6 files changed, 95 insertions(+), 79 deletions(-) diff --git a/Client/src/Components/TextFields/Field/index.css b/Client/src/Components/TextFields/Field/index.css index ade346e30..08a22a384 100644 --- a/Client/src/Components/TextFields/Field/index.css +++ b/Client/src/Components/TextFields/Field/index.css @@ -17,7 +17,7 @@ } .field h3.MuiTypography-root span.field-required { color: var(--env-var-color-19); - margin-left: 1px; + margin-left: 2px; } .field h3.MuiTypography-root span.field-optional { opacity: 0.6; @@ -42,7 +42,9 @@ border-color: var(--env-var-color-29); border-radius: var(--env-var-radius-1); } -.field .MuiOutlinedInput-root:hover:not(:has(input:focus)) fieldset { +.field:not(:has(.input-error)) + .MuiOutlinedInput-root:hover:not(:has(input:focus)) + fieldset { border-color: var(--env-var-color-29); } .field .input-error { @@ -50,5 +52,12 @@ opacity: 0.8; } .field:has(.input-error) .MuiOutlinedInput-root fieldset { - border-color: #fda29b; + border-color: var(--env-var-color-19); +} + +.register-page .field .MuiOutlinedInput-root fieldset, +.register-page .field input, +.login-page .field .MuiOutlinedInput-root fieldset, +.login-page .field input { + border-radius: var(--env-var-radius-2); } diff --git a/Client/src/Components/TextFields/Field/index.jsx b/Client/src/Components/TextFields/Field/index.jsx index 54cb5ee57..7b4ad77af 100644 --- a/Client/src/Components/TextFields/Field/index.jsx +++ b/Client/src/Components/TextFields/Field/index.jsx @@ -65,7 +65,7 @@ const Field = ({ tabIndex={-1} sx={{ color: theme.palette.section.borderColor, - padding: theme.gap.xs, + padding: `calc(${theme.gap.xs} / 2)`, "&:focus": { outline: "none", }, diff --git a/Client/src/Pages/Login/index.css b/Client/src/Pages/Login/index.css index 195b87001..770f41134 100644 --- a/Client/src/Pages/Login/index.css +++ b/Client/src/Pages/Login/index.css @@ -39,17 +39,9 @@ color: var(--env-var-color-1); } -#login-email-input { - width: 308px; - height: 1px; - font-size: var(--env-var-font-size-medium); -} - -#login-password-input { - width: 308px; - height: 1px; - font-size: var(--env-var-font-size-medium); -} +/* .login-form-inputs .field h3.MuiTypography-root{ + font-weight: 600; +} */ .login-form-password-options { display: flex; diff --git a/Client/src/Pages/Login/index.jsx b/Client/src/Pages/Login/index.jsx index 85d1c8664..6b8d93fc8 100644 --- a/Client/src/Pages/Login/index.jsx +++ b/Client/src/Pages/Login/index.jsx @@ -4,16 +4,15 @@ import { useNavigate } from "react-router-dom"; import "./index.css"; import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern"; import Logomark from "../../assets/Images/Logomark.png"; -import EmailTextField from "../../Components/TextFields/Email/EmailTextField"; import CheckBox from "../../Components/Checkbox/Checkbox"; import Button from "../../Components/Button"; import Google from "../../assets/Images/Google.png"; -import PasswordTextField from "../../Components/TextFields/Password/PasswordTextField"; import axiosInstance from "../../Utils/axiosConfig"; import { loginValidation } from "../../Validation/validation"; import { login } from "../../Features/Auth/authSlice"; import { useDispatch } from "react-redux"; import { createToast } from "../../Utils/toastUtils"; +import Field from "../../Components/TextFields/Field"; const Login = () => { const dispatch = useDispatch(); @@ -111,6 +110,8 @@ const Login = () => { navigate("/register"); }; + const [visibility, setVisibility] = useState(false); + return (
@@ -125,23 +126,28 @@ const Login = () => {
Log in to your account
+
-
-
diff --git a/Client/src/Pages/Register/index.css b/Client/src/Pages/Register/index.css index 8d4b99f1b..948f0afd0 100644 --- a/Client/src/Pages/Register/index.css +++ b/Client/src/Pages/Register/index.css @@ -1,5 +1,4 @@ .register-page { - width: var(--env-var-width-1); display: flex; justify-content: center; min-height: var(--env-var-height-1); @@ -40,16 +39,6 @@ font-weight: 600; } -#register-firstname-input, -#register-lastname-input, -#register-email-input, -#register-password-input { - font-size: var(--env-var-font-size-medium); - width: 294px; - height: 11px; - padding: 12px 14px; -} - .register-bottom-spacing { height: 50px; } diff --git a/Client/src/Pages/Register/index.jsx b/Client/src/Pages/Register/index.jsx index 8bd026187..f7c346f69 100644 --- a/Client/src/Pages/Register/index.jsx +++ b/Client/src/Pages/Register/index.jsx @@ -4,9 +4,6 @@ import { useNavigate } from "react-router-dom"; import "./index.css"; import BackgroundPattern from "../../Components/BackgroundPattern/BackgroundPattern"; import Logomark from "../../assets/Images/Logomark.png"; -import EmailTextField from "../../Components/TextFields/Email/EmailTextField"; -import PasswordTextField from "../../Components/TextFields/Password/PasswordTextField"; -import StringTextField from "../../Components/TextFields/Text/TextField"; import Check from "../../Components/Check/Check"; import Button from "../../Components/Button"; import Google from "../../assets/Images/Google.png"; @@ -15,6 +12,7 @@ import axiosInstance from "../../Utils/axiosConfig"; import { useDispatch } from "react-redux"; import { register } from "../../Features/Auth/authSlice"; import { createToast } from "../../Utils/toastUtils"; +import Field from "../../Components/TextFields/Field"; const Register = () => { const dispatch = useDispatch(); @@ -38,18 +36,18 @@ const Register = () => { role: "", }); - useEffect(() => { - axiosInstance - .get("/auth/users/admin") - .then((response) => { - if (response.data.data === true) { - navigate("/login"); - } - }) - .catch((error) => { - console.log(error); - }); - }, [form, navigate]); + // useEffect(() => { + // axiosInstance + // .get("/auth/users/admin") + // .then((response) => { + // if (response.data.data === true) { + // navigate("/login"); + // } + // }) + // .catch((error) => { + // console.log(error); + // }); + // }, [form, navigate]); useEffect(() => { const { error } = registerValidation.validate(form, { @@ -119,6 +117,8 @@ const Register = () => { } }; + const [visibility, setVisibility] = useState(false); + return (
@@ -137,42 +137,62 @@ const Register = () => {
-
-
-
- +
+ {/* TODO - hook up to form state and run checks */} +
From 51ce98f5e91ae2c8afd20d035a63c40f92961fea Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Sat, 13 Jul 2024 14:17:04 -0400 Subject: [PATCH 33/37] Added copy button and description input type --- .../src/Components/TextFields/Field/index.css | 28 ++++- .../src/Components/TextFields/Field/index.jsx | 77 ++++++++++---- Client/src/Pages/PlayGround/PlayGround.jsx | 100 +++++++++++------- 3 files changed, 139 insertions(+), 66 deletions(-) diff --git a/Client/src/Components/TextFields/Field/index.css b/Client/src/Components/TextFields/Field/index.css index 08a22a384..802eff6b0 100644 --- a/Client/src/Components/TextFields/Field/index.css +++ b/Client/src/Components/TextFields/Field/index.css @@ -4,12 +4,14 @@ .field h3.MuiTypography-root, .field h5.MuiTypography-root, .field input, +.field textarea, .field .input-error { font-size: var(--env-var-font-size-medium); } .field h3.MuiTypography-root, .field h5.MuiTypography-root, -.field input { +.field input, +.field textarea { color: var(--env-var-color-5); } .field h3.MuiTypography-root { @@ -28,22 +30,42 @@ opacity: 0.8; padding-right: var(--env-var-spacing-1-minus); } -.field .MuiInputBase-root { +.field .MuiInputBase-root:has(input) { height: 34px; } .field .MuiInputBase-root:has(.MuiInputAdornment-root) { padding-right: var(--env-var-spacing-1-minus); } +.field .MuiInputBase-root:has(.MuiInputAdornment-root > .MuiButtonBase-root) { + padding-right: 0; +} +.field .MuiInputBase-root .MuiInputAdornment-root .MuiButtonBase-root { + padding: 0 var(--env-var-spacing-1-minus); + font-size: var(--env-var-font-size-medium); + border-radius: 0; + height: 34px; +} +.field .MuiInputBase-root .MuiInputAdornment-root .MuiButtonBase-root svg { + width: 20px; + height: 20px; + margin-right: 5px; +} +.field .MuiInputBase-root .MuiInputAdornment-root .MuiButtonBase-root:hover { + border-color: var(--env-var-color-29); +} .field input { height: 100%; padding: 0 var(--env-var-spacing-1-minus); } +.field .MuiInputBase-root:has(textarea) { + padding: var(--env-var-spacing-1-minus); +} .field .MuiOutlinedInput-root fieldset { border-color: var(--env-var-color-29); border-radius: var(--env-var-radius-1); } .field:not(:has(.input-error)) - .MuiOutlinedInput-root:hover:not(:has(input:focus)) + .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset { border-color: var(--env-var-color-29); } diff --git a/Client/src/Components/TextFields/Field/index.jsx b/Client/src/Components/TextFields/Field/index.jsx index 7b4ad77af..8f84dbfec 100644 --- a/Client/src/Components/TextFields/Field/index.jsx +++ b/Client/src/Components/TextFields/Field/index.jsx @@ -8,7 +8,10 @@ import { } from "@mui/material"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; import Visibility from "@mui/icons-material/Visibility"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import Button from "../../Button"; import "./index.css"; +import { useState } from "react"; const Field = ({ type = "text", @@ -18,6 +21,7 @@ const Field = ({ isOptional, isVisible, setVisibility, + hasCopy, autoComplete, placeholder, value, @@ -27,6 +31,13 @@ const Field = ({ }) => { const theme = useTheme(); + // TODO - are we using this feature anywhere ? + const [copy, setCopy] = useState(false); + const handleCopy = () => { + setCopy(true); + setTimeout(() => setCopy(false), 1000); + }; + return ( {label && ( @@ -41,6 +52,8 @@ const Field = ({ id={id} autoComplete={autoComplete} placeholder={placeholder} + multiline={type === "description"} + rows={type === "description" ? 4 : 1} value={value} onChange={onChange} disabled={disabled} @@ -57,28 +70,48 @@ const Field = ({ https:// ), - endAdornment: type === "password" && ( - - setVisibility((show) => !show)} - tabIndex={-1} - sx={{ - color: theme.palette.section.borderColor, - padding: `calc(${theme.gap.xs} / 2)`, - "&:focus": { - outline: "none", - }, - "& .MuiTouchRipple-root": { - pointerEvents: "none", - display: "none", - }, - }} - > - {!isVisible ? : } - - - ), + endAdornment: + type === "password" ? ( + + setVisibility((show) => !show)} + tabIndex={-1} + sx={{ + color: theme.palette.section.borderColor, + padding: `calc(${theme.gap.xs} / 2)`, + "&:focus": { + outline: "none", + }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, + }} + > + {!isVisible ? : } + + + ) : ( + hasCopy && ( + +