Skip to content

Commit

Permalink
1520 multi project toolbar menu (#1639)
Browse files Browse the repository at this point in the history
* added ProjectCheckBoxMenu and set up where check boxes will be

* added checkboxes and menu to project table - hide feature works

* added useHiddenStatus hook for checkbox button

* added code to disable hide button in ProjectCheckBoxMenu & adjust div for missing ProjectCheckBoxmenu

* header checkbox checks/unchecks all project rows

* added filters to handleHeaderCheckbox & uncheck checkboxes when filter applied

* added permissions check in useHiddenStatus & ProjectCheckBoxMenu

* fixed select all for current page only

* multi-select delete feature working. added new multi-project data hook

* refactored handleHide

* delete and hidden buttons working. refactored ProjectCheckBoxMenu

* added tooltips for toolbar menu - delete btn

* added tooltip to hide btn

* added tooltip & msg

* added tooltip style and renamed ProjectCheckBoxMenu to MultiProjectToolbarMenu

* added styles to tooltip. reset checked projects state on page change

* added message types for tooltip. added comments

* fixed tooltip message for delete button

* renamed toolbarmenu component

* fixed bug on handleHide for context menu

* added print btn

* added print pdf button and functionality

* toolbar load on initial. tweaked tooltip for pdf btn
  • Loading branch information
agutiernc authored Mar 28, 2024
1 parent 9723b37 commit 70f0dee
Show file tree
Hide file tree
Showing 8 changed files with 472 additions and 51 deletions.
2 changes: 1 addition & 1 deletion client/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"ecmaVersion": 2020,
"sourceType": "module"
},
"plugins": ["react", "jest", "prettier", "eslint-plugin-react"],
Expand Down
5 changes: 2 additions & 3 deletions client/src/components/Projects/DeleteProjectModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,12 @@ const DeleteProjectModal = ({ mounted, onClose, project }) => {
className={classes.warningIcon}
alt="Warning"
/>
Are you sure you want to delete the project,
Are you sure you want to delete the following?
</div>
</>
)}

<div style={{ ...theme.typography.heading3, marginBottom: "1.5rem" }}>
{project.name}?
{Array.isArray(project.name) ? project.name.join(", ") : project.name}
</div>
<div className={classes.buttonFlexBox}>
<Button onClick={onClose} variant="text" id="cancelButton">
Expand Down
16 changes: 14 additions & 2 deletions client/src/components/Projects/FilterDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ const useStyles = createUseStyles({
}
});

const FilterPopup = ({ criteria, setCriteria, setCollapsed }) => {
const FilterPopup = ({
criteria,
setCriteria,
setCollapsed,
setCheckedProjects,
setSelectAllChecked
}) => {
const userContext = useContext(UserContext);
const account = userContext.account;
const classes = useStyles();
Expand All @@ -62,6 +68,10 @@ const FilterPopup = ({ criteria, setCriteria, setCollapsed }) => {

const handleChange = (e, propertyName) => {
setCriteria({ ...criteria, [propertyName]: e.target.value });

// reset any checked project rows when filter is applied
setCheckedProjects([]);
setSelectAllChecked(false);
};

const handleChangeDate = (property, date) => {
Expand Down Expand Up @@ -178,7 +188,9 @@ FilterPopup.propTypes = {
criteria: PropTypes.any,
setCriteria: PropTypes.func,
collapsed: PropTypes.bool,
setCollapsed: PropTypes.func
setCollapsed: PropTypes.func,
setCheckedProjects: PropTypes.func,
setSelectAllChecked: PropTypes.func
};

export default FilterPopup;
209 changes: 209 additions & 0 deletions client/src/components/Projects/MultiProjectToolbarMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import React, { useContext, useRef } from "react";
import UserContext from "../../contexts/UserContext";
import PropTypes from "prop-types";
import { createUseStyles } from "react-jss";
import {
faEyeSlash,
faEye,
faTrashCan,
faPrint
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "react-tooltip";
import PdfPrint from "../PdfPrint/PdfPrint";
import moment from "moment";
import { useReactToPrint } from "react-to-print";

const useStyles = createUseStyles({
container: {
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "0.5em",
marginBottom: "-1em"
},
list: {
display: "flex",
flexDirection: "row",
listStyleType: "none",
justifyContent: "space-between",
alignItems: "center",
width: "6.5em"
},
button: {
border: "none",
padding: 0,
background: "none"
},
multiStatus: {
color: "#002E6D"
}
});

const MultiProjectToolbarMenu = ({
handleHideBoxes,
handleDeleteModalOpen,
checkedProjects,
criteria,
projects,
pdfProjectData
}) => {
const momentModified = moment(projects.dateModified);
const printRef = useRef(null);
const classes = useStyles();
const userContext = useContext(UserContext);
const account = userContext.account;
const isProjectOwner = account.id === projects.loginId;

const isBtnDisabled = (projProp, criteriaProp) => {
const sameDateVals = projects[projProp] !== false;
const criteriaFilter = criteria[criteriaProp] === "all";

// disable button if current user is not the owner
// or if criteria is "all" and the date values are different
return !isProjectOwner || (criteriaFilter && !sameDateVals);
};

const isHideBtnDisabled = isBtnDisabled("dateHidden", "visibility");
const isDelBtnDisabled = isBtnDisabled("dateTrashed", "status");

const tooltipMsg = (criteriaProp, msg, dateProp) => {
if (checkedProjects.length === 0) return;

if (!isProjectOwner) {
return "You have selected a project that does not belong to you";
}

// show recover message if project is deleted
if (projects.dateTrashed && criteriaProp === "status") {
return "Restore from Trash";
}

// show message when selecting mixed types (e.g. hide & unhide)
if (checkedProjects.length > 1 && projects[dateProp] === false) {
return criteria[criteriaProp] === "all" ? msg : "";
}
};

const hasPdfData = () => {
return pdfProjectData && pdfProjectData.pdf !== null;
};

const handlePrintPdf = useReactToPrint({
content: () => printRef.current,
bodyClass: "printContainer",
pageStyle: ".printContainer {overflow: hidden;}"
});

return (
<div className={classes.container}>
<div className={classes.multiStatus}>
{checkedProjects.length} Projects Selected
</div>
<ul className={classes.list}>
<li>
<button
id="print-btn"
className={classes.button}
onClick={handlePrintPdf}
disabled={checkedProjects.length !== 1}
>
<FontAwesomeIcon icon={faPrint} />
</button>
{checkedProjects.length !== 1 ? (
<Tooltip
style={{
backgroundColor: "#e6e3e3",
color: "#000",
width: "11%",
borderRadius: "10px",
fontWeight: "bold",
textAlign: "center"
}}
anchorSelect="#print-btn"
content="Please select one project"
/>
) : (
""
)}
{hasPdfData() && (
<div style={{ display: "none" }}>
<PdfPrint
ref={printRef}
rules={pdfProjectData.pdf}
dateModified={momentModified.format("MM/DD/YYYY")}
/>
</div>
)}
</li>
<li>
<button
id="hide-btn"
className={classes.button}
disabled={isHideBtnDisabled}
onClick={handleHideBoxes}
>
{!projects.dateHidden ? (
<FontAwesomeIcon icon={faEyeSlash} />
) : (
<FontAwesomeIcon icon={faEye} />
)}

<Tooltip
style={{
backgroundColor: "#e6e3e3",
color: "#000",
width: "10%",
borderRadius: "10px"
}}
anchorSelect="#hide-btn"
content={tooltipMsg(
"visibility",
"Your selection includes both hidden and visible items",
"dateHidden"
)}
/>
</button>
</li>
<li>
<button
id="delete-btn"
className={classes.button}
disabled={isDelBtnDisabled}
onClick={handleDeleteModalOpen}
>
<FontAwesomeIcon
icon={faTrashCan}
color={isDelBtnDisabled ? "#1010104d" : "red"}
/>
<Tooltip
style={{
backgroundColor: "#e6e3e3",
color: "#000",
width: "10%",
borderRadius: "10px"
}}
anchorSelect="#delete-btn"
content={tooltipMsg(
"status",
"Your selection includes both deleted and active items",
"dateTrashed"
)}
/>
</button>
</li>
</ul>
</div>
);
};

MultiProjectToolbarMenu.propTypes = {
handleHideBoxes: PropTypes.func.isRequired,
handleDeleteModalOpen: PropTypes.func.isRequired,
checkedProjects: PropTypes.array.isRequired,
criteria: PropTypes.object.isRequired,
projects: PropTypes.object.isRequired,
pdfProjectData: PropTypes.object
};

export default MultiProjectToolbarMenu;
16 changes: 14 additions & 2 deletions client/src/components/Projects/ProjectTableRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ const ProjectTableRow = ({
handleDeleteModalOpen,
handleSnapshotModalOpen,
handleRenameSnapshotModalOpen,
handleHide
handleHide,
handleCheckboxChange,
checkedProjects
}) => {
const classes = useStyles();
const momentModified = moment(project.dateModified);
Expand Down Expand Up @@ -312,6 +314,14 @@ const ProjectTableRow = ({

return (
<tr key={project.id}>
<td className={classes.tdCenterAlign}>
<input
style={{ height: "15px" }}
type="checkbox"
checked={checkedProjects.includes(project.id)}
onChange={() => handleCheckboxChange(project.id)}
/>
</td>
<td className={classes.tdCenterAlign}>
{project.dateHidden ? (
<FontAwesomeIcon
Expand Down Expand Up @@ -403,7 +413,9 @@ ProjectTableRow.propTypes = {
handleDeleteModalOpen: PropTypes.func.isRequired,
handleSnapshotModalOpen: PropTypes.func.isRequired,
handleRenameSnapshotModalOpen: PropTypes.func.isRequired,
handleHide: PropTypes.func.isRequired
handleHide: PropTypes.func.isRequired,
handleCheckboxChange: PropTypes.func.isRequired,
checkedProjects: PropTypes.array.isRequired
};

export default ProjectTableRow;
Loading

0 comments on commit 70f0dee

Please sign in to comment.