diff --git a/frontend/package.json b/frontend/package.json index 72ff31d..5f10ef8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "i18next-http-backend": "^3.0.2", "idb": "^8.0.0", "react": "^18.3.1", + "react-cool-onclickoutside": "^1.7.0", "react-dom": "^18.3.1", "react-i18next": "^15.4.1", "react-project-template": "file:", diff --git a/frontend/public/locales/fr/item.json b/frontend/public/locales/fr/item.json index c7d785e..009080e 100644 --- a/frontend/public/locales/fr/item.json +++ b/frontend/public/locales/fr/item.json @@ -4,6 +4,7 @@ "object": "Objet", "objects": "Objets", + "add_object": "Ajouter un objet", "edit_object": "Modifier l'objet", "delete_object": "Supprimer l'objet", diff --git a/frontend/src/index.js b/frontend/src/index.js index e726098..a46402b 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -6,6 +6,7 @@ import MainLayout from './layouts/MainLayout'; import Home from './pages/Home'; import ItemCommonDetails from './pages/ItemCommonDetails'; +import ItemCommonCreation from './pages/ItemCommonCreation'; import Loading from './ui/Loading'; @@ -34,6 +35,11 @@ root.render( element={} /> + } + /> + { const { t } = useTranslation(["buttons", "filters", "item", "misc"]); + const navigate = useNavigate(); const [expandFilters, setExpandFilters] = useState(false); const [filterButtonIcon, setFilterButtonIcon] = useState(faFilter); @@ -221,11 +224,13 @@ const Filters = ({ actions={[ { icon: faPlus, - label: t("new", { ns: "buttons" }) + label: t("new", { ns: "buttons" }), + action: () => navigate("/objects/add") }, { icon: faFileExport, - label: t("export", { ns: "buttons" }) + label: t("export", { ns: "buttons" }), + action: () => notDevelopedFeature() } ]} className={"sm:flex-col-reverse"} diff --git a/frontend/src/modules/Item.jsx b/frontend/src/modules/Item.jsx index 67e881d..21dbbe9 100644 --- a/frontend/src/modules/Item.jsx +++ b/frontend/src/modules/Item.jsx @@ -2,7 +2,6 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; - import Heading from "../ui/Heading"; import HTMLLink from "../ui/HTMLLink"; import Image from "../ui/Image"; diff --git a/frontend/src/modules/ItemCommonDetailedCard.jsx b/frontend/src/modules/ItemCommonDetailedCard.jsx index e504f32..98bc3c0 100644 --- a/frontend/src/modules/ItemCommonDetailedCard.jsx +++ b/frontend/src/modules/ItemCommonDetailedCard.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { faPen, faTrash } from "@fortawesome/free-solid-svg-icons"; @@ -6,7 +6,6 @@ import { faPen, faTrash } from "@fortawesome/free-solid-svg-icons"; import ItemCommonForm from "../modules/ItemCommonForm"; import Image from "../ui/Image"; -import Button from "../ui/Button"; import MeatballsMenu from "../ui/MeatballsMenu"; import Tag from "../ui/Tag"; @@ -20,11 +19,11 @@ import { notDevelopedFeature } from "../utils/devUtils"; */ const ItemCommonDetailedCard = ({ itemCommon, - updateItemCommon = false + updateItemCommon = false, + showButtonsAndOptions = true, + setShowButtonsAndOptions = null }) => { - // FIX : Multiselect not displaying the correct selected count when editing an object from the Home page. - const { t } = useTranslation(["item", "misc"]); const [isUpdated, setIsUpdated] = useState(updateItemCommon); @@ -43,26 +42,37 @@ const ItemCommonDetailedCard = ({ setIsUpdated(false); } + /** + * Toggles the display of buttons and options for item and exemplar + * when the item common form is opened or closed. + */ + useEffect(() => + { + setShowButtonsAndOptions(!isUpdated); + }, [isUpdated]) + return (
{!isUpdated ? ( -
-
- setIsUpdated((prev) => !prev) - }, - { - isLink: false, - label: t("delete_object", { ns: "item" }), - icon: faTrash, - action: () => notDevelopedFeature() - }, - ]}/> -
+
+ {showButtonsAndOptions && +
+ setIsUpdated((prev) => !prev) + }, + { + isLink: false, + label: t("delete_object", { ns: "item" }), + icon: faTrash, + action: () => notDevelopedFeature() + }, + ]}/> +
+ } { if(!editExemplarFunction) @@ -68,26 +69,28 @@ const ItemDetailedCard = ({ )} >
- editExemplarFunction(item) - }, - { - isLink: false, - label: t("delete_exemplar", { ns: "item" }), - icon: faTrash, - action: () => notDevelopedFeature() - } - ]}/> + {showButtonsAndOptions && + editExemplarFunction(item) + }, + { + isLink: false, + label: t("delete_exemplar", { ns: "item" }), + icon: faTrash, + action: () => notDevelopedFeature() + } + ]}/> + }

@@ -152,33 +155,35 @@ const ItemDetailedCard = ({

-
- {item.loan_state !== loanStates[0]?.name ? ( - - {t("return_loan", { ns: "item" })} - - ) : ( - - {t("add_loan", { ns: "item" })} - - )} - - - {t("add_control", { ns: "item" })} - -
+ {showButtonsAndOptions && +
+ {item.loan_state !== loanStates[0]?.name ? ( + + {t("return_loan", { ns: "item" })} + + ) : ( + + {t("add_loan", { ns: "item" })} + + )} + + + {t("add_control", { ns: "item" })} + +
+ }
) } diff --git a/frontend/src/modules/ItemsList.jsx b/frontend/src/modules/ItemsList.jsx index f97bd83..ac1d743 100644 --- a/frontend/src/modules/ItemsList.jsx +++ b/frontend/src/modules/ItemsList.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import NoResults from "../ui/NoResults"; diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index d4a7ea2..c5fa1e6 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -8,7 +8,6 @@ import { getAllGroups } from "../services/api/groups"; import { getAllItemConditions } from "../services/api/item_conditions"; import { getAllStockingPlaces } from "../services/api/stocking_places"; - import Loading from "../ui/Loading"; import Filters from "../modules/Filters"; @@ -202,11 +201,7 @@ const Home = () => /** * Filters the list of items common when a filter is updated. */ - useEffect(() => { - //debugFilters(); - filterItems(); - - }, [displayMode, searchBar, selectedObjectTypes, + useEffect(() => filterItems(), [displayMode, searchBar, selectedObjectTypes, selectedLoanStates, selectedExemplarConditions, selectedGroups, selectedStockingPlaces, selectedFilterOption, filterByAscOrder diff --git a/frontend/src/pages/ItemCommonCreation.jsx b/frontend/src/pages/ItemCommonCreation.jsx new file mode 100644 index 0000000..68b9ccb --- /dev/null +++ b/frontend/src/pages/ItemCommonCreation.jsx @@ -0,0 +1,73 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; + +import ItemCommonForm from "../modules/ItemCommonForm"; +import ItemForm from "../modules/ItemForm"; + +import Heading from "../ui/Heading"; +import HTMLLink from "../ui/HTMLLink"; + +const ItemCommonCreation = () => +{ + const { t } = useTranslation(["buttons", "item", "titles"]); + const navigate = useNavigate() + + const handleNewObjectForm = (event) => + { + event.preventDefault(); + + const formData = Object.fromEntries(new FormData(event.target).entries()); + console.log(formData); + + // ============================================== // + // Future POST request to backend will go here... // + // ============================================== // + + navigate("/objects/1/exemplars"); + } + + return ( + <> + + + + {t("back_to_list", { ns: "buttons" })} + + +
+
+ + + +
+ +
+ + + navigate("/")} + /> +
+
+ + + ) +} + +export default ItemCommonCreation; \ No newline at end of file diff --git a/frontend/src/pages/ItemCommonDetails.jsx b/frontend/src/pages/ItemCommonDetails.jsx index 04e4cbe..e95ae15 100644 --- a/frontend/src/pages/ItemCommonDetails.jsx +++ b/frontend/src/pages/ItemCommonDetails.jsx @@ -38,6 +38,8 @@ const ItemCommonDetails = () => const [exemplarFormData, setExemplarFormData] = useState(null) const [itemCommon, setItemCommon] = useState({}); + const [filteredExemplars, setFilteredExemplars] = useState([]); + const [showButtonsAndOptions, setShowButtonsAndOptions] = useState(true); /** * Fetches itemComment data on mount. @@ -48,6 +50,7 @@ const ItemCommonDetails = () => { const data = await getItemCommon(parseInt(itemCommonId)); setItemCommon(data); + setFilteredExemplars(data.items) }; fetchItemCommonData(); @@ -60,7 +63,7 @@ const ItemCommonDetails = () => useEffect(() => { if(isExemplarAddMode) - setDisplayExemplarForm(true); + displayForm(); else if(itemId && isExemplarEditMode) { @@ -78,6 +81,17 @@ const ItemCommonDetails = () => jumpToAnchor(itemId); }, [itemCommon]) + /** + * Displays the form. + * + * @return {void} + * + */ + const displayForm = () => + { + setDisplayExemplarForm(true); + setShowButtonsAndOptions(false); + } /** * Hides the exemplar form and empty all form values. @@ -88,7 +102,8 @@ const ItemCommonDetails = () => const cancelForm = () => { setDisplayExemplarForm(false); - setExemplarFormData(null) + setShowButtonsAndOptions(true); + setExemplarFormData(null); } /** @@ -103,7 +118,7 @@ const ItemCommonDetails = () => const editExemplar = async (exemplarData) => { if(displayExemplarForm) await cancelForm(); - setDisplayExemplarForm(true); + displayForm(); setExemplarFormData(exemplarData); } @@ -126,18 +141,26 @@ const ItemCommonDetails = () => // Future POST request to backend will go here... // // ============================================== // - setDisplayExemplarForm(false); - setExemplarFormData(null) + cancelForm(); } /** * When the exemplar form is opened, scroll to the top of it. */ useEffect(() => { - if (displayExemplarForm) + if(displayExemplarForm) jumpToAnchor("exemplar-form"); }, [displayExemplarForm]); + /** + * Filters the item common exemplars to hide the exemplar being updated. + */ + useEffect(() => + { + setFilteredExemplars(itemCommon.items?.filter(item => + exemplarFormData === null || item.id !== exemplarFormData.id)); + }, [exemplarFormData]) + return ( <> {displayExemplarForm && ( @@ -194,19 +219,20 @@ const ItemCommonDetails = () => {!displayExemplarForm && (