From 6569ab573ec4f7404b2bd41ac3913fabb9d4fb85 Mon Sep 17 00:00:00 2001 From: Dervey Dylan <114241438+derveydylan@users.noreply.github.com> Date: Thu, 8 May 2025 09:52:54 +0200 Subject: [PATCH 1/8] feat: itemCommonCreation page Fixes an image being displayed if src prop equals "". Other minor changes. --- frontend/public/locales/fr/item.json | 1 + frontend/src/index.js | 6 ++ frontend/src/modules/Filters.jsx | 16 ++-- frontend/src/modules/Item.jsx | 1 - .../src/modules/ItemCommonDetailedCard.jsx | 1 - frontend/src/modules/ItemsList.jsx | 2 +- frontend/src/pages/Home.jsx | 7 +- frontend/src/pages/ItemCommonCreation.jsx | 75 +++++++++++++++++++ frontend/src/ui/Image.jsx | 5 +- frontend/src/ui/MeatballsMenu.jsx | 1 + 10 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 frontend/src/pages/ItemCommonCreation.jsx 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); @@ -216,16 +219,19 @@ const Filters = ({ )} + {/* TODO : The menu should be in the header instead. */}
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..a74c7b0 100644 --- a/frontend/src/modules/ItemCommonDetailedCard.jsx +++ b/frontend/src/modules/ItemCommonDetailedCard.jsx @@ -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"; 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..2d3f86b --- /dev/null +++ b/frontend/src/pages/ItemCommonCreation.jsx @@ -0,0 +1,75 @@ +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 = () => +{ + // BUG : Image not loading the onError placeholder when adding a itemCommon. + + 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/ui/Image.jsx b/frontend/src/ui/Image.jsx index b212449..4172584 100644 --- a/frontend/src/ui/Image.jsx +++ b/frontend/src/ui/Image.jsx @@ -22,7 +22,7 @@ const Image = ({ }) => { if(src && !alt) - console.warn("For accessibility reasons, it's preferable to add an alternative text to the image"); + console.warn("For accessibility reasons, it's preferable to add an alternative text to the image."); const [insertImagePlaceholder, setInsertImagePlaceholder] = useState(false) @@ -33,6 +33,9 @@ const Image = ({ { if(src) setInsertImagePlaceholder(false) + + else if(src === "") + setInsertImagePlaceholder(true) }, [src]) return ( diff --git a/frontend/src/ui/MeatballsMenu.jsx b/frontend/src/ui/MeatballsMenu.jsx index 52d529c..f4ade07 100644 --- a/frontend/src/ui/MeatballsMenu.jsx +++ b/frontend/src/ui/MeatballsMenu.jsx @@ -11,6 +11,7 @@ import HTMLLink from "./HTMLLink"; * UI component to add a meatballs menu with personalized actions. * * @param {Array} actions List of objects with a label, an icon, and an action. \ + * isLink: Define whether the action is a link or not. * label: Text of the action. \ * icon: Icon (FontAwesome) of the action. \ * action: Function to execute when the action is clicked. From fa167b0bc6ea26cc81c6d67d4f86a8de07ad8449 Mon Sep 17 00:00:00 2001 From: Dervey Dylan <114241438+derveydylan@users.noreply.github.com> Date: Fri, 9 May 2025 08:15:46 +0200 Subject: [PATCH 2/8] fix: fileInputImage Image alt text --- frontend/src/ui/InputFileImage.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/ui/InputFileImage.jsx b/frontend/src/ui/InputFileImage.jsx index 956d9cb..198ef62 100644 --- a/frontend/src/ui/InputFileImage.jsx +++ b/frontend/src/ui/InputFileImage.jsx @@ -123,6 +123,7 @@ const InputFileImage = ({ > {"Image From 93c9baabe6d897fcce5b7afb347885b2026effde Mon Sep 17 00:00:00 2001 From: Dervey Dylan <114241438+derveydylan@users.noreply.github.com> Date: Fri, 9 May 2025 08:38:09 +0200 Subject: [PATCH 3/8] fix: internal state of MultiSelect The internal state wasn't synchronized when we updated the selectedValues prop outside of the component. --- frontend/src/pages/ItemCommonCreation.jsx | 2 -- frontend/src/ui/MultiSelect.jsx | 13 +++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/ItemCommonCreation.jsx b/frontend/src/pages/ItemCommonCreation.jsx index 2d3f86b..68b9ccb 100644 --- a/frontend/src/pages/ItemCommonCreation.jsx +++ b/frontend/src/pages/ItemCommonCreation.jsx @@ -10,8 +10,6 @@ import HTMLLink from "../ui/HTMLLink"; const ItemCommonCreation = () => { - // BUG : Image not loading the onError placeholder when adding a itemCommon. - const { t } = useTranslation(["buttons", "item", "titles"]); const navigate = useNavigate() diff --git a/frontend/src/ui/MultiSelect.jsx b/frontend/src/ui/MultiSelect.jsx index 29429da..97de47e 100644 --- a/frontend/src/ui/MultiSelect.jsx +++ b/frontend/src/ui/MultiSelect.jsx @@ -43,8 +43,6 @@ const MultiSelect = ({ className = null }) => { - // BUG : Internal state not resetting when clicking the reset filters on the Home page. - if(!name) { console.error("MultiSelect must have a name."); @@ -79,6 +77,17 @@ const MultiSelect = ({ ); }; + /** + * Synchronizes the internal state when we update the selectedValues prop outside of this component. + */ + useEffect(() => { + if (onChangeFunction !== null) + setSelectedOptions(selectedValues); + }, [selectedValues, onChangeFunction]); + + /** + * Updates the count of selected items when the selectedOptions list is updated. + */ useEffect(() => setSelectedCount(selectedOptions.length), [selectedOptions]); return ( From 2db5fed0d080d3d068de67e10221e2ef0bf2186c Mon Sep 17 00:00:00 2001 From: Dervey Dylan <114241438+derveydylan@users.noreply.github.com> Date: Wed, 14 May 2025 08:37:39 +0200 Subject: [PATCH 4/8] fix: Mutlitselect selectedOptions count Uncontrolled Multiselects having default values now correctly displays the length of the defaultValues array prop. --- frontend/src/modules/Filters.jsx | 1 - frontend/src/modules/ItemCommonDetailedCard.jsx | 2 -- frontend/src/ui/MultiSelect.jsx | 9 +++++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/Filters.jsx b/frontend/src/modules/Filters.jsx index f279e9e..ee582ff 100644 --- a/frontend/src/modules/Filters.jsx +++ b/frontend/src/modules/Filters.jsx @@ -219,7 +219,6 @@ const Filters = ({ )}
- {/* TODO : The menu should be in the header instead. */}
{ - // 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); diff --git a/frontend/src/ui/MultiSelect.jsx b/frontend/src/ui/MultiSelect.jsx index 97de47e..afa6845 100644 --- a/frontend/src/ui/MultiSelect.jsx +++ b/frontend/src/ui/MultiSelect.jsx @@ -85,6 +85,15 @@ const MultiSelect = ({ setSelectedOptions(selectedValues); }, [selectedValues, onChangeFunction]); + /** + * Synchronizes the internal state when a uncontrolled multiselect have default values. + */ + useEffect(() => + { + if(onChangeFunction === null && defaultValues.length > 0) + setSelectedOptions(defaultValues) + }, [defaultValues]); + /** * Updates the count of selected items when the selectedOptions list is updated. */ From cc43002aad7765f6dc8854ad329f09603df3b20e Mon Sep 17 00:00:00 2001 From: Dervey Dylan <114241438+derveydylan@users.noreply.github.com> Date: Wed, 14 May 2025 09:28:15 +0200 Subject: [PATCH 5/8] feat: hides items options and buttons when a form is opened When a exemplar is edited, it is removed from the exemplar list. --- .../src/modules/ItemCommonDetailedCard.jsx | 45 ++++---- frontend/src/modules/ItemDetailedCard.jsx | 101 +++++++++--------- frontend/src/pages/ItemCommonDetails.jsx | 42 ++++++-- 3 files changed, 114 insertions(+), 74 deletions(-) diff --git a/frontend/src/modules/ItemCommonDetailedCard.jsx b/frontend/src/modules/ItemCommonDetailedCard.jsx index 43924fc..c129fdb 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"; @@ -19,7 +19,9 @@ import { notDevelopedFeature } from "../utils/devUtils"; */ const ItemCommonDetailedCard = ({ itemCommon, - updateItemCommon = false + updateItemCommon = false, + showButtonsAndOptions = true, + setShowButtonsAndOptions = null }) => { const { t } = useTranslation(["item", "misc"]); @@ -40,26 +42,33 @@ const ItemCommonDetailedCard = ({ setIsUpdated(false); } + 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/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 && (