From a9507585581beb1567be494180c7bda059afe52e Mon Sep 17 00:00:00 2001 From: heejung1180 Date: Wed, 19 Jun 2024 20:44:52 -0700 Subject: [PATCH 1/7] submit database migration and route --- client/src/components/Button/SubmitButton.js | 32 +++++++++++++++++++ .../ProjectWizard/TdmCalculationWizard.js | 11 +++++++ .../components/ProjectWizard/WizardFooter.js | 8 +++++ .../components/Projects/ProjectTableRow.js | 8 +++++ .../src/components/Projects/ProjectsPage.js | 1 + client/src/services/project.service.js | 4 +++ server/app/controllers/project.controller.js | 16 ++++++++++ server/app/routes/project.routes.js | 1 + server/app/services/project.service.js | 18 +++++++++++ ...29.1948__add_dateSubmitted_column_1704.sql | 1 + 10 files changed, 100 insertions(+) create mode 100644 client/src/components/Button/SubmitButton.js create mode 100644 server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql diff --git a/client/src/components/Button/SubmitButton.js b/client/src/components/Button/SubmitButton.js new file mode 100644 index 00000000..5b19a65a --- /dev/null +++ b/client/src/components/Button/SubmitButton.js @@ -0,0 +1,32 @@ +import React from "react"; +import PropTypes from "prop-types"; +import Button from "../Button/Button"; + +const SubmitButton = ({ id, onClick, isDisabled, isDisplayed, color }) => { + return ( + <> + + + ); +}; + +SubmitButton.propTypes = { + children: PropTypes.object, + onClick: PropTypes.func.isRequired, + id: PropTypes.string.isRequired, + color: PropTypes.string, + isDisabled: PropTypes.bool, + isDisplayed: PropTypes.bool.isRequired +}; + +export default SubmitButton; diff --git a/client/src/components/ProjectWizard/TdmCalculationWizard.js b/client/src/components/ProjectWizard/TdmCalculationWizard.js index 355eec20..8bd72abb 100644 --- a/client/src/components/ProjectWizard/TdmCalculationWizard.js +++ b/client/src/components/ProjectWizard/TdmCalculationWizard.js @@ -55,6 +55,7 @@ const TdmCalculationWizard = props => { projectIsValid, dateModified, dateSnapshotted, + dateSubmitted, contentContainerRef, inapplicableStrategiesModal, closeStrategiesModal @@ -197,6 +198,13 @@ const TdmCalculationWizard = props => { return false; }; + const setDisplaySubmitButton = () => { + if (page === 5) { + return true; + } + return false; + }; + const handleValidate = () => { const { page } = params; const validations = { @@ -325,9 +333,11 @@ const TdmCalculationWizard = props => { setDisabledSaveButton={setDisabledSaveButton} setDisplaySaveButton={setDisplaySaveButton} setDisplayPrintButton={setDisplayPrintButton} + setDisplaySubmitButton={setDisplaySubmitButton} onSave={onSave} dateModified={dateModified} dateSnapshotted={dateSnapshotted} + dateSubmitted={dateSubmitted} /> @@ -374,6 +384,7 @@ TdmCalculationWizard.propTypes = { projectIsValid: PropTypes.func, dateModified: PropTypes.string, dateSnapshotted: PropTypes.string, + dateSubmitted: PropTypes.string, inapplicableStrategiesModal: PropTypes.bool, closeStrategiesModal: PropTypes.func }; diff --git a/client/src/components/ProjectWizard/WizardFooter.js b/client/src/components/ProjectWizard/WizardFooter.js index 13e9b11f..10a9d4b7 100644 --- a/client/src/components/ProjectWizard/WizardFooter.js +++ b/client/src/components/ProjectWizard/WizardFooter.js @@ -2,6 +2,7 @@ import React, { useRef } from "react"; import PropTypes from "prop-types"; import NavButton from "../Button/NavButton"; import SaveButton from "../Button/SaveButton"; +import SubmitButton from "../Button/SubmitButton"; import { createUseStyles } from "react-jss"; import PrintButton from "../Button/PrintButton"; import ReactToPrint from "react-to-print"; @@ -47,6 +48,7 @@ const WizardFooter = ({ setDisabledSaveButton, setDisplaySaveButton, setDisplayPrintButton, + setDisplaySubmitButton, onSave, dateModified, dateSnapshotted @@ -124,6 +126,11 @@ const WizardFooter = ({ isDisplayed={setDisplaySaveButton()} onClick={onSave} /> + ) : null} @@ -161,6 +168,7 @@ WizardFooter.propTypes = { setDisabledSaveButton: PropTypes.any, setDisplaySaveButton: PropTypes.any, setDisplayPrintButton: PropTypes.any, + setDisplaySubmitButton: PropTypes.any, onSave: PropTypes.any, onDownload: PropTypes.any, dateModified: PropTypes.any, diff --git a/client/src/components/Projects/ProjectTableRow.js b/client/src/components/Projects/ProjectTableRow.js index 74638f2a..dd02eb07 100644 --- a/client/src/components/Projects/ProjectTableRow.js +++ b/client/src/components/Projects/ProjectTableRow.js @@ -108,6 +108,13 @@ const ProjectTableRow = ({ return {formatDate(project.dateModified)}; }; + const dateSubmittedDisplay = () => { + if (project.dateSubmitted) { + return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + } + return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + }; + return ( @@ -151,6 +158,7 @@ const ProjectTableRow = ({ {dateModifiedDisplay()} + {dateSubmittedDisplay()} {projectRules && ( diff --git a/client/src/components/Projects/ProjectsPage.js b/client/src/components/Projects/ProjectsPage.js index 5bf42be6..1d68e7ef 100644 --- a/client/src/components/Projects/ProjectsPage.js +++ b/client/src/components/Projects/ProjectsPage.js @@ -598,6 +598,7 @@ const ProjectsPage = ({ contentContainerRef }) => { { id: "firstName", label: "Created By" }, { id: "dateCreated", label: "Created On" }, { id: "dateModified", label: "Last Modified" }, + { id: "dateSubmitted", label: "Submitted" }, { id: "contextMenu", label: "" diff --git a/client/src/services/project.service.js b/client/src/services/project.service.js index cdeb587c..31018db1 100644 --- a/client/src/services/project.service.js +++ b/client/src/services/project.service.js @@ -26,6 +26,10 @@ export function del(id) { return axios.delete(baseUrl + "/" + id); } +export function submit(id) { + return axios.put(baseUrl + "/submit", id); +} + export function snapshot(id) { return axios.put(baseUrl + "/snapshot", id); } diff --git a/server/app/controllers/project.controller.js b/server/app/controllers/project.controller.js index e040f199..70c2ffc7 100644 --- a/server/app/controllers/project.controller.js +++ b/server/app/controllers/project.controller.js @@ -107,6 +107,21 @@ const trash = async (req, res) => { } }; +const submit = async (req, res) => { + try { + const { id, name } = req.body; + + const result = await projectService.submit(id, req.user.id, name); + if (result === 1) { + res.sendStatus(403); + } else { + res.sendStatus(204); + } + } catch (err) { + res.status(500).send(err); + } +}; + const snapshot = async (req, res) => { try { const { id, name } = req.body; @@ -160,6 +175,7 @@ module.exports = { del, hide, trash, + submit, snapshot, renameSnapshot, getAllArchivedProjects diff --git a/server/app/routes/project.routes.js b/server/app/routes/project.routes.js index 3b86ac21..c4ece2c2 100644 --- a/server/app/routes/project.routes.js +++ b/server/app/routes/project.routes.js @@ -15,6 +15,7 @@ router.post("/", jwtSession.validateUser, projectController.post); router.put("/hide", jwtSession.validateUser, projectController.hide); router.put("/trash", jwtSession.validateUser, projectController.trash); router.put("/snapshot", jwtSession.validateUser, projectController.snapshot); +router.put("/submit", jwtSession.validateUser, projectController.submit); router.put( "/renameSnapshot", jwtSession.validateUser, diff --git a/server/app/services/project.service.js b/server/app/services/project.service.js index e2b0d62a..14260be1 100644 --- a/server/app/services/project.service.js +++ b/server/app/services/project.service.js @@ -139,6 +139,23 @@ const snapshot = async (id, loginId, name) => { } }; +const submit = async (id, loginId, name) => { + try { + await poolConnect; + const request = pool.request(); + + request.input("id", id); + request.input("loginId", loginId); + request.input("name", name); + + const response = await request.execute("Project_Submit"); + return response.returnValue; + } catch (err) { + console.log("err:", err); + return Promise.reject(err); + } +}; + const renameSnapshot = async (id, loginId, name) => { try { await poolConnect; @@ -175,6 +192,7 @@ module.exports = { del, hide, trash, + submit, snapshot, renameSnapshot, getAllArchivedProjects diff --git a/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql new file mode 100644 index 00000000..348f9877 --- /dev/null +++ b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql @@ -0,0 +1 @@ +ALTER TABLE tdmdev.dbo.Project ADD dateSubmitted datetime2(0) NULL; From e59aeb31bea1b7fc3b9baab5fc60622e8560440d Mon Sep 17 00:00:00 2001 From: heejung1180 Date: Wed, 19 Jun 2024 20:44:52 -0700 Subject: [PATCH 2/7] submit database migration and route --- client/src/components/Button/SubmitButton.js | 32 +++++++++++++++++++ .../ProjectWizard/TdmCalculationWizard.js | 26 +++++++++++++++ .../components/ProjectWizard/WizardFooter.js | 8 +++++ .../components/Projects/ProjectTableRow.js | 8 +++++ .../src/components/Projects/ProjectsPage.js | 1 + client/src/services/project.service.js | 4 +++ server/app/controllers/project.controller.js | 16 ++++++++++ server/app/routes/project.routes.js | 1 + server/app/services/project.service.js | 18 +++++++++++ ...29.1948__add_dateSubmitted_column_1704.sql | 2 +- 10 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 client/src/components/Button/SubmitButton.js diff --git a/client/src/components/Button/SubmitButton.js b/client/src/components/Button/SubmitButton.js new file mode 100644 index 00000000..5b19a65a --- /dev/null +++ b/client/src/components/Button/SubmitButton.js @@ -0,0 +1,32 @@ +import React from "react"; +import PropTypes from "prop-types"; +import Button from "../Button/Button"; + +const SubmitButton = ({ id, onClick, isDisabled, isDisplayed, color }) => { + return ( + <> + + + ); +}; + +SubmitButton.propTypes = { + children: PropTypes.object, + onClick: PropTypes.func.isRequired, + id: PropTypes.string.isRequired, + color: PropTypes.string, + isDisabled: PropTypes.bool, + isDisplayed: PropTypes.bool.isRequired +}; + +export default SubmitButton; diff --git a/client/src/components/ProjectWizard/TdmCalculationWizard.js b/client/src/components/ProjectWizard/TdmCalculationWizard.js index 75ca82ee..727f4d70 100644 --- a/client/src/components/ProjectWizard/TdmCalculationWizard.js +++ b/client/src/components/ProjectWizard/TdmCalculationWizard.js @@ -53,9 +53,15 @@ const TdmCalculationWizard = props => { schoolPackageSelected, formIsDirty, projectIsValid, +<<<<<<< HEAD // dateModified, // dateSnapshotted, // dateSubmitted, +======= + dateModified, + dateSnapshotted, + dateSubmitted, +>>>>>>> a9507585 (submit database migration and route) contentContainerRef, inapplicableStrategiesModal, closeStrategiesModal, @@ -200,6 +206,13 @@ const TdmCalculationWizard = props => { return false; }; + const setDisplaySubmitButton = () => { + if (page === 5) { + return true; + } + return false; + }; + const handleValidate = () => { const { page } = params; const validations = { @@ -329,12 +342,19 @@ const TdmCalculationWizard = props => { setDisabledSaveButton={setDisabledSaveButton} setDisplaySaveButton={setDisplaySaveButton} setDisplayPrintButton={setDisplayPrintButton} + setDisplaySubmitButton={setDisplaySubmitButton} onSave={onSave} +<<<<<<< HEAD project={project} // dateModified={dateModified} // dateSnapshotted={dateSnapshotted} // dateSubmitted={dateSubmitted} // loginId={loginId} +======= + dateModified={dateModified} + dateSnapshotted={dateSnapshotted} + dateSubmitted={dateSubmitted} +>>>>>>> a9507585 (submit database migration and route) /> @@ -379,9 +399,15 @@ TdmCalculationWizard.propTypes = { schoolPackageSelected: PropTypes.func, formIsDirty: PropTypes.bool, projectIsValid: PropTypes.func, +<<<<<<< HEAD // dateModified: PropTypes.string, // dateSnapshotted: PropTypes.string, // dateSubmitted: PropTypes.string, +======= + dateModified: PropTypes.string, + dateSnapshotted: PropTypes.string, + dateSubmitted: PropTypes.string, +>>>>>>> a9507585 (submit database migration and route) inapplicableStrategiesModal: PropTypes.bool, closeStrategiesModal: PropTypes.func, project: PropTypes.shape diff --git a/client/src/components/ProjectWizard/WizardFooter.js b/client/src/components/ProjectWizard/WizardFooter.js index eb4c049c..fa7dc021 100644 --- a/client/src/components/ProjectWizard/WizardFooter.js +++ b/client/src/components/ProjectWizard/WizardFooter.js @@ -2,6 +2,7 @@ import React, { useRef, useContext } from "react"; import PropTypes from "prop-types"; import NavButton from "../Button/NavButton"; import SaveButton from "../Button/SaveButton"; +import SubmitButton from "../Button/SubmitButton"; import { createUseStyles } from "react-jss"; import PrintButton from "../Button/PrintButton"; import ReactToPrint from "react-to-print"; @@ -48,6 +49,7 @@ const WizardFooter = ({ setDisabledSaveButton, setDisplaySaveButton, setDisplayPrintButton, + setDisplaySubmitButton, onSave, project }) => { @@ -115,6 +117,11 @@ const WizardFooter = ({ isDisplayed={setDisplaySaveButton()} onClick={onSave} /> + ) : null} @@ -163,6 +170,7 @@ WizardFooter.propTypes = { setDisabledSaveButton: PropTypes.any, setDisplaySaveButton: PropTypes.any, setDisplayPrintButton: PropTypes.any, + setDisplaySubmitButton: PropTypes.any, onSave: PropTypes.any, onDownload: PropTypes.any, project: PropTypes.shape diff --git a/client/src/components/Projects/ProjectTableRow.js b/client/src/components/Projects/ProjectTableRow.js index 0e72f379..5896fde6 100644 --- a/client/src/components/Projects/ProjectTableRow.js +++ b/client/src/components/Projects/ProjectTableRow.js @@ -98,6 +98,13 @@ const ProjectTableRow = ({ return {formatDate(project.dateModified)}; }; + const dateSubmittedDisplay = () => { + if (project.dateSubmitted) { + return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + } + return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + }; + return ( @@ -141,6 +148,7 @@ const ProjectTableRow = ({ {dateModifiedDisplay()} + {dateSubmittedDisplay()} {projectRules && ( diff --git a/client/src/components/Projects/ProjectsPage.js b/client/src/components/Projects/ProjectsPage.js index d12f6780..18a0d59d 100644 --- a/client/src/components/Projects/ProjectsPage.js +++ b/client/src/components/Projects/ProjectsPage.js @@ -598,6 +598,7 @@ const ProjectsPage = ({ contentContainerRef }) => { { id: "firstName", label: "Created By" }, { id: "dateCreated", label: "Created On" }, { id: "dateModified", label: "Last Modified" }, + { id: "dateSubmitted", label: "Submitted" }, { id: "contextMenu", label: "" diff --git a/client/src/services/project.service.js b/client/src/services/project.service.js index cdeb587c..31018db1 100644 --- a/client/src/services/project.service.js +++ b/client/src/services/project.service.js @@ -26,6 +26,10 @@ export function del(id) { return axios.delete(baseUrl + "/" + id); } +export function submit(id) { + return axios.put(baseUrl + "/submit", id); +} + export function snapshot(id) { return axios.put(baseUrl + "/snapshot", id); } diff --git a/server/app/controllers/project.controller.js b/server/app/controllers/project.controller.js index e040f199..70c2ffc7 100644 --- a/server/app/controllers/project.controller.js +++ b/server/app/controllers/project.controller.js @@ -107,6 +107,21 @@ const trash = async (req, res) => { } }; +const submit = async (req, res) => { + try { + const { id, name } = req.body; + + const result = await projectService.submit(id, req.user.id, name); + if (result === 1) { + res.sendStatus(403); + } else { + res.sendStatus(204); + } + } catch (err) { + res.status(500).send(err); + } +}; + const snapshot = async (req, res) => { try { const { id, name } = req.body; @@ -160,6 +175,7 @@ module.exports = { del, hide, trash, + submit, snapshot, renameSnapshot, getAllArchivedProjects diff --git a/server/app/routes/project.routes.js b/server/app/routes/project.routes.js index 3b86ac21..c4ece2c2 100644 --- a/server/app/routes/project.routes.js +++ b/server/app/routes/project.routes.js @@ -15,6 +15,7 @@ router.post("/", jwtSession.validateUser, projectController.post); router.put("/hide", jwtSession.validateUser, projectController.hide); router.put("/trash", jwtSession.validateUser, projectController.trash); router.put("/snapshot", jwtSession.validateUser, projectController.snapshot); +router.put("/submit", jwtSession.validateUser, projectController.submit); router.put( "/renameSnapshot", jwtSession.validateUser, diff --git a/server/app/services/project.service.js b/server/app/services/project.service.js index e2b0d62a..14260be1 100644 --- a/server/app/services/project.service.js +++ b/server/app/services/project.service.js @@ -139,6 +139,23 @@ const snapshot = async (id, loginId, name) => { } }; +const submit = async (id, loginId, name) => { + try { + await poolConnect; + const request = pool.request(); + + request.input("id", id); + request.input("loginId", loginId); + request.input("name", name); + + const response = await request.execute("Project_Submit"); + return response.returnValue; + } catch (err) { + console.log("err:", err); + return Promise.reject(err); + } +}; + const renameSnapshot = async (id, loginId, name) => { try { await poolConnect; @@ -175,6 +192,7 @@ module.exports = { del, hide, trash, + submit, snapshot, renameSnapshot, getAllArchivedProjects diff --git a/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql index 32285571..ada57c8e 100644 --- a/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql +++ b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql @@ -1 +1 @@ -ALTER TABLE Project ADD dateSubmitted datetime2(0) NULL; \ No newline at end of file +ALTER TABLE Project ADD dateSubmitted datetime2(0) NULL; From f14653f414f8bde98912bb071ff2f018d98e1750 Mon Sep 17 00:00:00 2001 From: John Darragh Date: Wed, 7 Aug 2024 16:52:03 -0700 Subject: [PATCH 3/7] Update date format for Submit Date --- .../ProjectWizard/TdmCalculationWizard.js | 18 ------------------ .../src/components/Projects/ProjectTableRow.js | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/client/src/components/ProjectWizard/TdmCalculationWizard.js b/client/src/components/ProjectWizard/TdmCalculationWizard.js index 727f4d70..fff5c7a8 100644 --- a/client/src/components/ProjectWizard/TdmCalculationWizard.js +++ b/client/src/components/ProjectWizard/TdmCalculationWizard.js @@ -53,15 +53,9 @@ const TdmCalculationWizard = props => { schoolPackageSelected, formIsDirty, projectIsValid, -<<<<<<< HEAD // dateModified, // dateSnapshotted, // dateSubmitted, -======= - dateModified, - dateSnapshotted, - dateSubmitted, ->>>>>>> a9507585 (submit database migration and route) contentContainerRef, inapplicableStrategiesModal, closeStrategiesModal, @@ -344,17 +338,11 @@ const TdmCalculationWizard = props => { setDisplayPrintButton={setDisplayPrintButton} setDisplaySubmitButton={setDisplaySubmitButton} onSave={onSave} -<<<<<<< HEAD project={project} // dateModified={dateModified} // dateSnapshotted={dateSnapshotted} // dateSubmitted={dateSubmitted} // loginId={loginId} -======= - dateModified={dateModified} - dateSnapshotted={dateSnapshotted} - dateSubmitted={dateSubmitted} ->>>>>>> a9507585 (submit database migration and route) /> @@ -399,15 +387,9 @@ TdmCalculationWizard.propTypes = { schoolPackageSelected: PropTypes.func, formIsDirty: PropTypes.bool, projectIsValid: PropTypes.func, -<<<<<<< HEAD // dateModified: PropTypes.string, // dateSnapshotted: PropTypes.string, // dateSubmitted: PropTypes.string, -======= - dateModified: PropTypes.string, - dateSnapshotted: PropTypes.string, - dateSubmitted: PropTypes.string, ->>>>>>> a9507585 (submit database migration and route) inapplicableStrategiesModal: PropTypes.bool, closeStrategiesModal: PropTypes.func, project: PropTypes.shape diff --git a/client/src/components/Projects/ProjectTableRow.js b/client/src/components/Projects/ProjectTableRow.js index 5896fde6..7d833bd8 100644 --- a/client/src/components/Projects/ProjectTableRow.js +++ b/client/src/components/Projects/ProjectTableRow.js @@ -100,9 +100,9 @@ const ProjectTableRow = ({ const dateSubmittedDisplay = () => { if (project.dateSubmitted) { - return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + return {formatDate(project.dateSubmitted)}; } - return {moment(project.dateSubmitted).format("YYYY-MM-DD")}; + return {formatDate(project.dateSubmitted)}; }; return ( From b086be4cbf7ce42a73c8d516fe98949ae54f136d Mon Sep 17 00:00:00 2001 From: heejung1180 Date: Wed, 14 Aug 2024 20:23:40 -0700 Subject: [PATCH 4/7] made slight modifications to frontend code --- client/src/components/Button/SubmitButton.js | 4 +--- client/src/components/Feedback/ProjectList.js | 4 ++++ client/src/components/Projects/ProjectTableRow.js | 5 +---- package-lock.json | 9 +++++++++ package.json | 1 + 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/src/components/Button/SubmitButton.js b/client/src/components/Button/SubmitButton.js index 5b19a65a..710b3334 100644 --- a/client/src/components/Button/SubmitButton.js +++ b/client/src/components/Button/SubmitButton.js @@ -2,7 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import Button from "../Button/Button"; -const SubmitButton = ({ id, onClick, isDisabled, isDisplayed, color }) => { +const SubmitButton = ({ id, onClick, isDisplayed, color }) => { return ( <> + + + + ); +}; + +DatePopup.propTypes = { + close: PropTypes.func, + header: PropTypes.any, + criteria: PropTypes.any, + setCriteria: PropTypes.func, + order: PropTypes.string, + orderBy: PropTypes.string, + setSort: PropTypes.func, + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func +}; + +export default DatePopup; diff --git a/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js b/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js new file mode 100644 index 00000000..6fe7f4ca --- /dev/null +++ b/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js @@ -0,0 +1,117 @@ +import React from "react"; +import PropTypes from "prop-types"; +import "react-datepicker/dist/react-datepicker.css"; +import { faFilter } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import Popup from "reactjs-popup"; +import DatePopup from "./DatePopup"; +import TextPopup from "./TextPopup"; +import VisibilityPopup from "./VisibilityPopup"; +import StatusPopup from "./StatusPopup"; + +const ProjectTableColumnHeader = ({ + header, + criteria, + setCriteria, + order, + orderBy, + setSort, + setCheckedProjectIds, + setSelectAllChecked +}) => { + return ( +
+ {header.id !== "checkAllProjects" && header.id !== "contextMenu" ? ( + + {header.label} + +
+ } + position="bottom center" + offsetY={10} + arrow={false} + contentStyle={{ width: "auto" }} + > + {close => { + return !header.popupType ? null : header.popupType === + "datetime" ? ( + + ) : header.popupType === "text" ? ( + + ) : header.popupType === "visibility" ? ( + + ) : header.popupType === "status" ? ( + + ) : null; + }} + + ) : ( + {header.label} + )} + + ); +}; + +ProjectTableColumnHeader.propTypes = { + header: PropTypes.any, + criteria: PropTypes.any, + setCriteria: PropTypes.func, + order: PropTypes.string, + orderBy: PropTypes.string, + setSort: PropTypes.func, + + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func +}; + +export default ProjectTableColumnHeader; diff --git a/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js b/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js new file mode 100644 index 00000000..efea469e --- /dev/null +++ b/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js @@ -0,0 +1,87 @@ +import React from "react"; +import PropTypes from "prop-types"; +import Button from "../../Button/Button"; +import RadioButton from "../../UI/RadioButton"; +import "react-datepicker/dist/react-datepicker.css"; +import { faX } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +const StatusPopup = ({ + close, + header, + criteria, + setCriteria, + order, + orderBy, + setSort, + setCheckedProjectIds, + setSelectAllChecked +}) => { + const setDefault = () => { + setCriteria({ + ...criteria, + [header.id]: "" + }); + setCheckedProjectIds([]); + setSelectAllChecked(false); + }; + + return ( +
+
+ +
+
+ {/* If there is a dateSnapshotted (i.e., project is snapshot), property value is 1 */} + setSort(header.id, "asc")} + /> + setSort(header.id, "desc")} + /> +
+
+
(Under Construction)
+ +
+ +
+ ); +}; + +StatusPopup.propTypes = { + close: PropTypes.func, + header: PropTypes.any, + criteria: PropTypes.any, + setCriteria: PropTypes.func, + order: PropTypes.string, + orderBy: PropTypes.string, + setSort: PropTypes.func, + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func +}; + +export default StatusPopup; diff --git a/client/src/components/Projects/ColumnHeaderPopups/TextPopup.js b/client/src/components/Projects/ColumnHeaderPopups/TextPopup.js new file mode 100644 index 00000000..9b34b9c8 --- /dev/null +++ b/client/src/components/Projects/ColumnHeaderPopups/TextPopup.js @@ -0,0 +1,110 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import Button from "../../Button/Button"; +import RadioButton from "../../UI/RadioButton"; +import "react-datepicker/dist/react-datepicker.css"; +import { faX } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +const TextPopup = ({ + close, + header, + criteria, + setCriteria, + order, + orderBy, + setSort, + setCheckedProjectIds, + setSelectAllChecked +}) => { + const [newOrder, setNewOrder] = useState( + header.id !== orderBy ? null : order + ); + const [newSearchString, setNewSearchString] = useState(criteria[header.id]); + + const applyChanges = () => { + setCriteria({ ...criteria, [header.id]: newSearchString }); + if (newOrder) { + setSort(header.id, newOrder); + } + setCheckedProjectIds([]); + setSelectAllChecked(false); + close(); + }; + + const setDefault = () => { + setNewSearchString(""); + setCheckedProjectIds([]); + setSelectAllChecked(false); + }; + + return ( +
+
+ +
+
+ setNewOrder("asc")} + /> + setNewOrder("desc")} + /> +
+
+ { + setNewSearchString(e.target.value); + }} + value={newSearchString} + /> + +
+
+ + +
+
+ ); +}; + +TextPopup.propTypes = { + close: PropTypes.func, + header: PropTypes.any, + criteria: PropTypes.any, + setCriteria: PropTypes.func, + order: PropTypes.string, + orderBy: PropTypes.string, + setSort: PropTypes.func, + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func +}; + +export default TextPopup; diff --git a/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js b/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js new file mode 100644 index 00000000..091a3c71 --- /dev/null +++ b/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js @@ -0,0 +1,87 @@ +import React from "react"; +import PropTypes from "prop-types"; +import Button from "../../Button/Button"; +import RadioButton from "../../UI/RadioButton"; +import "react-datepicker/dist/react-datepicker.css"; +import { faX } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +const VisibilityPopup = ({ + close, + header, + criteria, + setCriteria, + order, + orderBy, + setSort, + setCheckedProjectIds, + setSelectAllChecked +}) => { + const setDefault = () => { + setCriteria({ + ...criteria, + [header.id]: "" + }); + setCheckedProjectIds([]); + setSelectAllChecked(false); + }; + + return ( +
+
+ +
+
+ {/* If there is a dateHidden, property value is 1 */} + setSort(header.id, "asc")} + /> + setSort(header.id, "desc")} + /> +
+
+
(Under Construction)
+ +
+ +
+ ); +}; + +VisibilityPopup.propTypes = { + close: PropTypes.func, + header: PropTypes.any, + criteria: PropTypes.any, + setCriteria: PropTypes.func, + order: PropTypes.string, + orderBy: PropTypes.string, + setSort: PropTypes.func, + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func +}; + +export default VisibilityPopup; diff --git a/client/src/components/Projects/ProjectTableRow.js b/client/src/components/Projects/ProjectTableRow.js index 7d833bd8..90146827 100644 --- a/client/src/components/Projects/ProjectTableRow.js +++ b/client/src/components/Projects/ProjectTableRow.js @@ -192,7 +192,7 @@ const ProjectTableRow = ({ }; ProjectTableRow.propTypes = { - project: PropTypes.object.isRequired, + project: PropTypes.any, handleCsvModalOpen: PropTypes.func.isRequired, handleCopyModalOpen: PropTypes.func.isRequired, handleDeleteModalOpen: PropTypes.func.isRequired, diff --git a/client/src/components/Projects/ProjectsPage.js b/client/src/components/Projects/ProjectsPage.js index 18a0d59d..baa55376 100644 --- a/client/src/components/Projects/ProjectsPage.js +++ b/client/src/components/Projects/ProjectsPage.js @@ -5,8 +5,8 @@ import { createUseStyles } from "react-jss"; import UserContext from "../../contexts/UserContext.js"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { - faSortUp, - faSortDown, + // faSortUp, + // faSortDown, faFilter } from "@fortawesome/free-solid-svg-icons"; import SearchIcon from "../../images/search.png"; @@ -26,6 +26,7 @@ import ProjectTableRow from "./ProjectTableRow"; import FilterDrawer from "./FilterDrawer.js"; import MultiProjectToolbarMenu from "./MultiProjectToolbarMenu.js"; import fetchEngineRules from "./fetchEngineRules.js"; +import ProjectTableColumnHeader from "./ColumnHeaderPopups/ProjectTableColumnHeader.js"; const useStyles = createUseStyles({ outerDiv: { @@ -422,7 +423,7 @@ const ProjectsPage = ({ contentContainerRef }) => { setSelectAllChecked(!selectAllChecked); }; - const descCompareBy = (a, b, orderBy) => { + const ascCompareBy = (a, b, orderBy) => { let projectA, projectB; if (orderBy === "VERSION_NO") { @@ -446,6 +447,13 @@ const ProjectsPage = ({ contentContainerRef }) => { ) { projectA = a[orderBy] ? 1 : 0; projectB = b[orderBy] ? 1 : 0; + } else if ( + orderBy === "dateSubmitted" || + orderBy === "dateCreated" || + orderBy === "dateModified" + ) { + projectA = a[orderBy] ? a[orderBy] : "2000-01-01"; + projectB = b[orderBy] ? b[orderBy] : "2000-01-01"; } else { projectA = a[orderBy].toLowerCase(); projectB = b[orderBy].toLowerCase(); @@ -461,9 +469,9 @@ const ProjectsPage = ({ contentContainerRef }) => { }; const getComparator = (order, orderBy) => { - return order === "desc" - ? (a, b) => descCompareBy(a, b, orderBy) - : (a, b) => -descCompareBy(a, b, orderBy); + return order === "asc" + ? (a, b) => ascCompareBy(a, b, orderBy) + : (a, b) => -ascCompareBy(a, b, orderBy); }; const stableSort = (array, comparator) => { @@ -476,13 +484,9 @@ const ProjectsPage = ({ contentContainerRef }) => { return stabilizedList.map(el => el[0]); }; - const handleSort = property => { - // disable sorting for header checkbox - if (property === "checkAllProjects") return; - - const isAsc = orderBy === property && order === "asc"; - setOrder(isAsc ? "desc" : "asc"); - setOrderBy(property); + const setSort = (orderBy, order) => { + setOrder(order); + setOrderBy(orderBy); }; const handleFilterTextChange = text => { @@ -586,19 +590,39 @@ const ProjectsPage = ({ contentContainerRef }) => { }, { id: "dateHidden", - label: "Visibility" + label: "Visibility", + popupType: "visibility" }, { id: "dateSnapshotted", - label: "Status" + label: "Status", + popupType: "status" + }, + { id: "name", label: "Name", popupType: "text" }, + { id: "address", label: "Address", popupType: "text" }, + { id: "alternative", label: "Alternative Number", popupType: "text" }, + { id: "author", label: "Created By", popupType: "text" }, + { + id: "dateCreated", + label: "Created On", + popupType: "datetime", + startDatePropertyName: "startDateCreated", + endDatePropertyName: "endDateCreated" + }, + { + id: "dateModified", + label: "Last Modified", + popupType: "datetime", + startDatePropertyName: "startDateModified", + endDatePropertyName: "endDateModified" + }, + { + id: "dateSubmitted", + label: "Submitted", + popupType: "datetime", + startDatePropertyName: "startDateSubmitted", + endDatePropertyName: "endDateSubmitted" }, - { id: "name", label: "Name" }, - { id: "address", label: "Address" }, - { id: "VERSION_NO", label: "Alternative Number" }, - { id: "firstName", label: "Created By" }, - { id: "dateCreated", label: "Created On" }, - { id: "dateModified", label: "Last Modified" }, - { id: "dateSubmitted", label: "Submitted" }, { id: "contextMenu", label: "" @@ -706,41 +730,55 @@ const ProjectsPage = ({ contentContainerRef }) => { {headerData.map(header => { - const label = header.label; return ( - handleSort(header.id) - } - > - {orderBy === header.id ? ( - - {label}{" "} - {order === "asc" ? ( - - ) : ( - - )} - - ) : ( - {label} - )} + + ); + // const label = header.label; + // return ( + // handleSort(header.id) + // } + // > + // {orderBy === header.id ? ( + // + // {label}{" "} + // {order === "asc" ? ( + // + // ) : ( + // + // )} + // + // ) : ( + // {label} + // )} + // + // ); })} @@ -844,6 +882,7 @@ const ProjectsPage = ({ contentContainerRef }) => { )} +
{JSON.stringify(criteria, null, 2)}
diff --git a/client/src/components/UI/DatePickerCustomInput.jsx b/client/src/components/UI/DatePickerCustomInput.jsx index 13007023..31a099c3 100644 --- a/client/src/components/UI/DatePickerCustomInput.jsx +++ b/client/src/components/UI/DatePickerCustomInput.jsx @@ -2,7 +2,7 @@ import React, { forwardRef } from "react"; import PropTypes from "prop-types"; const DatePickerCustomInput = forwardRef( - ({ value, onClick, onChange }, ref) => ( + ({ value, onClick, onChange, placeholder }, ref) => ( ) ); @@ -23,7 +24,8 @@ DatePickerCustomInput.displayName = "DatePickerCustomInput"; DatePickerCustomInput.propTypes = { value: PropTypes.any, onClick: PropTypes.func, - onChange: PropTypes.func + onChange: PropTypes.func, + placeholder: PropTypes.string }; export default DatePickerCustomInput; diff --git a/client/src/components/UI/DateRangePicker.jsx b/client/src/components/UI/DateRangePicker.jsx index f4682125..89c082fc 100644 --- a/client/src/components/UI/DateRangePicker.jsx +++ b/client/src/components/UI/DateRangePicker.jsx @@ -3,7 +3,14 @@ import PropTypes from "prop-types"; import DatePicker from "react-datepicker"; import DatePickerCustomInput from "./DatePickerCustomInput"; -const DateRangePicker = ({ startDate, endDate, setStartDate, setEndDate }) => { +const DateRangePicker = ({ + startDate, + endDate, + setStartDate, + setEndDate, + startDatePlaceholder, + endDatePlaceholder +}) => { return (
{ endDate={endDate} customInput={} dateFormat="yyyy-MM-dd" + placeholderText={startDatePlaceholder || ""} />
 to 
{ endDate={endDate} customInput={} dateFormat="yyyy-MM-dd" + placeholderText={endDatePlaceholder || ""} />
); @@ -38,7 +47,9 @@ DateRangePicker.propTypes = { startDate: PropTypes.any, endDate: PropTypes.any, setStartDate: PropTypes.func, - setEndDate: PropTypes.func + setEndDate: PropTypes.func, + startDatePlaceholder: PropTypes.string, + endDatePlaceholder: PropTypes.string }; export default DateRangePicker; diff --git a/server/app/controllers/project.controller.js b/server/app/controllers/project.controller.js index 2a1ab87d..e9450cc1 100644 --- a/server/app/controllers/project.controller.js +++ b/server/app/controllers/project.controller.js @@ -56,7 +56,7 @@ const put = async (req, res) => { const del = async (req, res) => { try { const project = await getProject(req, res); - if (project.loginId !== req.user.id) { + if (project.loginId !== req.user.id && !req.user.isAdmin) { res.status(403).send("You can only delete your own projects."); return; } From be302ed5196e3db5c841858f8a92b324f6f05d1c Mon Sep 17 00:00:00 2001 From: John Darragh Date: Tue, 27 Aug 2024 16:54:03 -0700 Subject: [PATCH 7/7] My Project Page filtering changes --- .../ProjectTableColumnHeader.js | 22 ++++++--- .../ColumnHeaderPopups/StatusPopup.js | 45 ++++++++++++++----- .../ColumnHeaderPopups/VisibilityPopup.js | 45 ++++++++++++++----- 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js b/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js index 6fe7f4ca..fc42f0ef 100644 --- a/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js +++ b/client/src/components/Projects/ColumnHeaderPopups/ProjectTableColumnHeader.js @@ -1,8 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import "react-datepicker/dist/react-datepicker.css"; -import { faFilter } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdFilterAlt } from "react-icons/md"; import Popup from "reactjs-popup"; import DatePopup from "./DatePopup"; import TextPopup from "./TextPopup"; @@ -24,17 +23,30 @@ const ProjectTableColumnHeader = ({ {header.id !== "checkAllProjects" && header.id !== "contextMenu" ? ( +
{header.label} - + {/* */}
} position="bottom center" diff --git a/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js b/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js index efea469e..dc1bf01e 100644 --- a/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js +++ b/client/src/components/Projects/ColumnHeaderPopups/StatusPopup.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import PropTypes from "prop-types"; import Button from "../../Button/Button"; import RadioButton from "../../UI/RadioButton"; @@ -17,6 +17,12 @@ const StatusPopup = ({ setCheckedProjectIds, setSelectAllChecked }) => { + const [newOrder, setNewOrder] = useState( + header.id !== orderBy ? null : order + ); + + // TODO More state variables for status filtering go here + const setDefault = () => { setCriteria({ ...criteria, @@ -26,6 +32,16 @@ const StatusPopup = ({ setSelectAllChecked(false); }; + const applyChanges = () => { + // Set Criteria for status + if (newOrder) { + setSort(header.id, newOrder); + } + setCheckedProjectIds([]); + setSelectAllChecked(false); + close(); + }; + return (
@@ -47,27 +63,32 @@ const StatusPopup = ({ setSort(header.id, "asc")} + checked={newOrder == "asc"} + onChange={() => setNewOrder("asc")} /> setSort(header.id, "desc")} + checked={newOrder === "desc"} + onChange={() => setNewOrder("desc")} />
(Under Construction)

- +
+ + +
); }; diff --git a/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js b/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js index 091a3c71..48c3eeec 100644 --- a/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js +++ b/client/src/components/Projects/ColumnHeaderPopups/VisibilityPopup.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import PropTypes from "prop-types"; import Button from "../../Button/Button"; import RadioButton from "../../UI/RadioButton"; @@ -17,6 +17,12 @@ const VisibilityPopup = ({ setCheckedProjectIds, setSelectAllChecked }) => { + const [newOrder, setNewOrder] = useState( + header.id !== orderBy ? null : order + ); + + // TODO More state variables for visibility filtering go here + const setDefault = () => { setCriteria({ ...criteria, @@ -26,6 +32,16 @@ const VisibilityPopup = ({ setSelectAllChecked(false); }; + const applyChanges = () => { + // Set Criteria for status + if (newOrder) { + setSort(header.id, newOrder); + } + setCheckedProjectIds([]); + setSelectAllChecked(false); + close(); + }; + return (
@@ -47,27 +63,32 @@ const VisibilityPopup = ({ setSort(header.id, "asc")} + checked={newOrder === "asc"} + onChange={() => setNewOrder("asc")} /> setSort(header.id, "desc")} + checked={newOrder === "desc"} + onChange={() => setNewOrder("desc")} />
(Under Construction)

- +
+ + +
); };