Skip to content

Commit

Permalink
Merge branch 'develop' into 1703-implement-shared-snapshot-view
Browse files Browse the repository at this point in the history
  • Loading branch information
bilalbg authored Dec 9, 2024
2 parents 6144948 + 5c9e5d5 commit e85dcbb
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 322 deletions.
340 changes: 126 additions & 214 deletions client/package-lock.json

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions client/src/components/Faq/FaqConfirmDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from "react";
import PropTypes from "prop-types";
import ModalDialog from "../UI/AriaModal/ModalDialog";
import Button from "../Button/Button";
import { createUseStyles } from "react-jss";
import { MdWarning } from "react-icons/md";

const useStyles = createUseStyles({
title: {
textAlign: "center"
},
warningWrapper: {
color: "#B64E38",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
warningMessage: {
verticalAlign: "middle",
display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: "1em"
},
modalActions: {
display: "flex",
justifyContent: "center"
},
proceedButton: {
color: "white",
fontWeight: "bold"
}
});

const FaqConfirmDialog = ({ blocker }) => {
const classes = useStyles();
return (
<ModalDialog
mounted={blocker.state === "blocked"}
onClose={blocker.reset}
showCloseBox={false}
>
<div className={classes.warningWrapper}>
<MdWarning alt="Warning" size={70} />
</div>
<h2 className={classes.title}>
<strong>Unsaved changes will be lost</strong>
</h2>
<p className={classes.warningMessage}>
<span>
&nbsp; Leaving this page will permanently delete any unsaved changes
to the FAQ.
</span>
</p>
<div className={classes.modalActions}>
<Button
color="colorCancel"
variant="outlined"
id="modalCancel"
data-testid="transitionCancel"
onClick={() => blocker.reset()}
>
<p style={{ fontWeight: "bold" }}>Cancel</p>
</Button>
<Button
color="colorError"
id="modalProceed"
data-testid="transitionProceed"
onClick={() => blocker.proceed()}
>
<p className={classes.proceedButton}>Proceed</p>
</Button>
</div>
</ModalDialog>
);
};

FaqConfirmDialog.propTypes = {
blocker: PropTypes.object.isRequired
};

export default FaqConfirmDialog;
44 changes: 43 additions & 1 deletion client/src/components/Faq/FaqView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import AddNewCategoryButton from "../Button/AddNewCategory";
import { createUseStyles } from "react-jss";
import DeleteFaqModal from "./DeleteFaqModal";
import SaveConfirmationModal from "./SaveConfirmationModal";
import FaqConfirmDialog from "./FaqConfirmDialog";
import { matchPath, unstable_useBlocker as useBlocker } from "react-router-dom";

const useStyles = createUseStyles(theme => ({
headerContainer: {
Expand All @@ -35,6 +37,7 @@ const FaqView = () => {
const [highestFaqId, setHighestFaqId] = useState(0);
const [highestCategoryId, setHighestCategoryId] = useState(0);
const [expanded, setExpanded] = useState(false);
const [formHasSaved, setFormHasSaved] = useState(false);
const [admin, setAdmin] = useState(false);
const [categoryToDelete, setCategoryToDelete] = useState(null);
const [faqToDelete, setFaqToDelete] = useState(null);
Expand All @@ -47,6 +50,36 @@ const FaqView = () => {
fetchFaqData();
}, []);

const calculationPath = "/faqs";

const shouldBlock = React.useCallback(
({ currentLocation, nextLocation }) => {
const isSamePage = (currentLocation, nextLocation) => {
const currentMatch = matchPath(
{
path: calculationPath,
exact: true
},
currentLocation.pathname
);
const nextMatch = matchPath(
{
path: calculationPath,
exact: true
},
nextLocation.pathname
);

return currentMatch && nextMatch;
};

return formHasSaved && !isSamePage(currentLocation, nextLocation);
},
[formHasSaved]
);

const blocker = useBlocker(shouldBlock);

const handleDragEnd = result => {
const { type, destination, source } = result;

Expand Down Expand Up @@ -161,12 +194,14 @@ const FaqView = () => {
};
setHighestCategoryId(updatedHighestCategoryId);
setFaqCategoryList(prevState => [...prevState, newCategory]);
setFormHasSaved(true);
};

const onDeleteCategory = categoryId => {
setFaqCategoryList(prevState =>
prevState.filter(category => category.id !== categoryId)
);
setFormHasSaved(true);
};

const handleAddFAQ = (category, question, answer) => {
Expand Down Expand Up @@ -198,6 +233,7 @@ const FaqView = () => {
return category;
})
);
setFormHasSaved(true);
};

const handleEditFAQ = (categoryId, faqId, question, answer) => {
Expand All @@ -224,6 +260,7 @@ const FaqView = () => {
return category;
})
);
setFormHasSaved(true);
};

const handleEditCategory = (category, name) => {
Expand All @@ -240,6 +277,7 @@ const FaqView = () => {
return cat;
})
);
setFormHasSaved(true);
};

const onDeleteFAQ = (categoryId, faqId) => {
Expand All @@ -256,6 +294,7 @@ const FaqView = () => {
return category;
})
);
setFormHasSaved(true);
};

const expandFaq = faq => {
Expand Down Expand Up @@ -312,20 +351,22 @@ const FaqView = () => {
// Submit data and set admin to false
submitFaqData();
setAdmin(false);

setFormHasSaved(false);
// Close the save confirmation modal
closeSaveConfirmationModal();
};

const handleDeleteCategory = categoryId => {
setCategoryToDelete(categoryId);
setIsDeleteConfirmationModalOpen(true);
setFormHasSaved(true);
};

const handleDeleteFAQ = (categoryId, faqId) => {
setCategoryToDelete(categoryId);
setFaqToDelete(faqId);
setIsDeleteConfirmationModalOpen(true);
setFormHasSaved(true);
};

const closeModal = () => {
Expand Down Expand Up @@ -405,6 +446,7 @@ const FaqView = () => {
onClose={closeSaveConfirmationModal}
onYes={handleSaveConfirmationYes}
/>
{blocker ? <FaqConfirmDialog blocker={blocker} /> : null}
</ContentContainer>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ const TdmCalculationWizard = props => {
},
nextLocation.pathname
);

return (
currentMatch &&
nextMatch &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,32 @@ const ProjectTableColumnHeader = ({
orderBy,
setSort,
setCheckedProjectIds,
setSelectAllChecked
setSelectAllChecked,
droOptions
}) => {
const theme = useTheme();
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

// Filter is considered Applied if it is not set
// to the default criteria values.
const isFilterApplied = () => {
let propertyName = header.id;
let propertyName = header.accessor || header.id;
if (header.popupType === "text") {
propertyName += "List";
return criteria[propertyName].length > 0;
const listPropertyName = propertyName + "List";
const listValue = criteria[listPropertyName];

if (propertyName === "droName") {
return Array.isArray(listValue) && listValue.length > 0;
}

const headerValue = criteria[header.id];

const isListFilterApplied =
Array.isArray(listValue) && listValue.length > 0;
const isTextFilterApplied =
typeof headerValue === "string" && headerValue.length > 0;

return isListFilterApplied || isTextFilterApplied;
}
if (header.popupType === "datetime") {
return (
Expand All @@ -101,6 +115,12 @@ const ProjectTableColumnHeader = ({
if (header.id === "dateSnapshotted") {
return criteria.type !== "all" || criteria.status !== "active";
}
if (header.id === "dro") {
return criteria.droList?.length > 0; // Use optional chaining
}
if (header.id === "adminNotes") {
return (criteria.adminNotes || "").length > 0; // Set default empty string
}
return false;
};

Expand Down Expand Up @@ -151,6 +171,7 @@ const ProjectTableColumnHeader = ({
setSelectAllChecked={setSelectAllChecked}
projects={projects}
filter={filter}
droOptions={droOptions}
/>
) : header.popupType === "visibility" ? (
<VisibilityPopup
Expand Down Expand Up @@ -204,7 +225,8 @@ ProjectTableColumnHeader.propTypes = {
setSort: PropTypes.func,

setCheckedProjectIds: PropTypes.func,
setSelectAllChecked: PropTypes.func
setSelectAllChecked: PropTypes.func,
droOptions: PropTypes.array.isRequired
};

export default ProjectTableColumnHeader;
59 changes: 48 additions & 11 deletions client/src/components/Projects/ColumnHeaderPopups/TextPopup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,33 @@ const TextPopup = ({
orderBy,
setSort,
setCheckedProjectIds,
setSelectAllChecked
setSelectAllChecked,
droOptions
}) => {
const property = header.accessor || header.id;
const getDisplayValue = value => {
if (property === "droName" && value === "") {
return "No DRO Assigned";
}
return value;
};

const getInternalValue = displayValue => {
if (property === "droName" && displayValue === "No DRO Assigned") {
return "";
}
return displayValue;
};
const classes = useStyles();

const [newOrder, setNewOrder] = useState(
header.id !== orderBy ? null : order
);
const [selectedListItems, setSelectedListItems] = useState(
criteria[header.id + "List"].map(s => ({ value: s, label: s }))
(criteria[header.id + "List"] || []).map(s => ({
value: getDisplayValue(s),
label: getDisplayValue(s)
}))
);
const [searchString, setSearchString] = useState("");

Expand All @@ -77,13 +95,26 @@ const TextPopup = ({
// are currently selected EXCEPT the criteria we are currently editing.
const listCriteria = { ...criteria, [header.id + "List"]: [] };
const filteredProjects = projects.filter(p => filter(p, listCriteria));
const property = header.id == "author" ? "fullname" : header.id;
const selectOptions = [...new Set(filteredProjects.map(p => p[property]))]
.filter(value => value !== null)
.sort(
(a, b) => (initiallyChecked(b) ? 1 : 0) - (initiallyChecked(a) ? 1 : 0)
);
// const property = header.id == "author" ? "fullname" : header.id;

let selectOptions;

if (property === "droName") {
selectOptions = droOptions.map(dro => dro.name);
selectOptions.push("No DRO Assigned");
} else if (property === "fullname") {
selectOptions = [...new Set(filteredProjects.map(p => p[property]))]
.filter(value => value !== null)
.sort(
(a, b) => (initiallyChecked(b) ? 1 : 0) - (initiallyChecked(a) ? 1 : 0)
);
} else {
selectOptions = [...new Set(filteredProjects.map(p => p[property]))]
.filter(value => value !== null && value !== "")
.sort(
(a, b) => (initiallyChecked(b) ? 1 : 0) - (initiallyChecked(a) ? 1 : 0)
);
}
const filteredOptions = selectOptions
.filter(o => !!o)
.filter(opt => opt.toLowerCase().includes(searchString.toLowerCase()));
Expand All @@ -110,16 +141,21 @@ const TextPopup = ({

const isChecked = optionValue => {
const checked = selectedListItems.find(
option => option.value == optionValue
option => option.value === optionValue
);
return !!checked;
};

const applyChanges = () => {
let selectedValues = selectedListItems.map(sli =>
getInternalValue(sli.value)
);

setCriteria({
...criteria,
[header.id + "List"]: selectedListItems.map(sli => sli.value)
[header.id + "List"]: selectedValues
});

if (newOrder) {
setSort(header.id, newOrder);
}
Expand Down Expand Up @@ -239,7 +275,8 @@ TextPopup.propTypes = {
orderBy: PropTypes.string,
setSort: PropTypes.func,
setCheckedProjectIds: PropTypes.func,
setSelectAllChecked: PropTypes.func
setSelectAllChecked: PropTypes.func,
droOptions: PropTypes.array.isRequired
};

export default TextPopup;
Loading

0 comments on commit e85dcbb

Please sign in to comment.