From ac461ec869e9ca660bfb386e5547c9ac6fe12d2e Mon Sep 17 00:00:00 2001 From: MiraGeowerkstatt Date: Thu, 18 Jul 2024 11:50:09 +0200 Subject: [PATCH 01/56] Add button tooltips to sidebar --- CHANGELOG.md | 2 +- src/client/public/icons/help.svg | 2 +- .../src/commons/menu/mainView/mainSideNav.tsx | 162 ++++++++++++++---- 3 files changed, 128 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..731328d62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ - Added borehole geometry panel. - Added secondary header to borehole detail view. -- Added new codelist entries for `casing_type` and `backfill_material`. +- Added new codelist entries for `casing_type` and `backfill_material`.git ### Changed diff --git a/src/client/public/icons/help.svg b/src/client/public/icons/help.svg index 5efd24d9e..2c5d0a9a2 100644 --- a/src/client/public/icons/help.svg +++ b/src/client/public/icons/help.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index c3c11c29c..a7ce41865 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -1,7 +1,7 @@ import { useContext, useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; -import { Badge, IconButton, Stack } from "@mui/material"; +import { Badge, IconButton, Stack, Typography } from "@mui/material"; import { ImportErrorModal } from "./menuComponents/importErrorModal"; import FilterIcon from "../../../../public/icons/filter.svg?react"; import AddIcon from "../../../../public/icons/add.svg?react"; @@ -16,13 +16,20 @@ import { DrawerContentTypes } from "../../../pages/editor/editorComponentInterfa import { ErrorResponse, MainSideNavProps } from "./menuComponents/menuComponentInterfaces"; import { ReduxRootState, User } from "../../../ReduxStateInterfaces"; import { FilterContext } from "../../../components/filter/filterContext"; +import { useTranslation } from "react-i18next"; const StyledIconButton = styled(IconButton)({ padding: "10px", marginBottom: "25px", color: theme.palette.neutral.contrastText, "&:hover": { - backgroundColor: theme.palette.background.lightgrey, + backgroundColor: `${theme.palette.secondary.main} !important`, + color: `${theme.palette.secondary.contrastText} !important`, + width: "fit-content", + whiteSpace: "nowrap", + zIndex: 6000, + paddingLeft: "16px", + justifyContent: "flex-start", }, borderRadius: "10px", }); @@ -44,6 +51,7 @@ const MainSideNav = ({ }: MainSideNavProps) => { const history = useHistory(); const menuRef = useRef(null); + const { t } = useTranslation(); const [creating, setCreating] = useState(false); const [modal, setModal] = useState(false); const [upload, setUpload] = useState(false); @@ -52,6 +60,7 @@ const MainSideNav = ({ const [selectedBoreholeAttachments, setSelectedBoreholeAttachments] = useState(null); const [selectedLithologyFile, setSelectedLithologyFile] = useState(null); const [errorsResponse, setErrorsResponse] = useState(null); + const [hoveredButtonId, setHoveredButtonId] = useState(null); const filterContext = useContext(FilterContext); // Redux state @@ -96,6 +105,99 @@ const MainSideNav = ({ const isLayersPanelVisible = drawerOpen && sideDrawerContent === DrawerContentTypes.CustomLayers; const activeFilterCount = filterContext.activeFilterLength + (filterContext.filterPolygon === null ? 0 : 1); + function getTooltipWrapper(content: JSX.Element) { + return ( + + {content} + + ); + } + + interface ButtonContentInterface { + id: string; + onClick: () => void; + sx: object; + defaultContent: JSX.Element; + hoverContent: JSX.Element; + } + + const topButtonData: ButtonContentInterface[] = [ + { + id: "show-filter-button", + onClick: handleToggleFilter, + sx: isFilterPanelVisible ? selectedButtonStyle : {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + {t("searchfilters")} + , + ), + }, + { + id: "new-borehole-button", + onClick: handleToggleAdd, + sx: isAddPanelVisible ? selectedButtonStyle : {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + {t("add")} + , + ), + }, + { + id: "import-borehole-button", + onClick: () => { + toggleDrawer(false); + setModal(true); + setUpload(true); + }, + sx: {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + {t("upload")} + , + ), + }, + { + id: "layers-button", + onClick: handleToggleLayers, + sx: isLayersPanelVisible ? selectedButtonStyle : {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + + {t("usersMap")} + , + ), + }, + ]; + + const bottomButtonData: ButtonContentInterface[] = [ + { + id: "settings-button", + onClick: () => history.push(`/setting`), + sx: {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + {t("header_settings")} + , + ), + }, + { + id: "help-button", + onClick: () => window.open(`/help`), + sx: {}, + defaultContent: , + hoverContent: getTooltipWrapper( + <> + {t("header_help")} + , + ), + }, + ]; + return ( {activeFilterCount > 0 && } - - - - - - - { - setModal(true); - setUpload(true); - }} - disabled={user.data.roles.indexOf("EDIT") === -1}> - - - - - + {topButtonData.map(button => ( + setHoveredButtonId(button.id)} + onMouseLeave={() => setHoveredButtonId(null)} + sx={button.sx}> + {hoveredButtonId === button.id ? button.hoverContent : button.defaultContent} + + ))} - history.push(`/setting`)}> - - - - window.open(`/help`)} /> - + {bottomButtonData.map(button => ( + setHoveredButtonId(button.id)} + onMouseLeave={() => setHoveredButtonId(null)} + sx={button.sx}> + {hoveredButtonId === button.id ? button.hoverContent : button.defaultContent} + + ))} Date: Thu, 18 Jul 2024 11:51:12 +0200 Subject: [PATCH 02/56] Fix changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 731328d62..c081c1943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ - Added borehole geometry panel. - Added secondary header to borehole detail view. -- Added new codelist entries for `casing_type` and `backfill_material`.git +- Added new codelist entries for `casing_type` and `backfill_material`. +- Added tooltips to main side navigation. ### Changed From 628159a27b8af18927b105b6432f384045644df2 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 08:32:31 +0200 Subject: [PATCH 03/56] Fix label id --- src/client/src/commons/menu/detailView/detailSideNav.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/src/commons/menu/detailView/detailSideNav.jsx b/src/client/src/commons/menu/detailView/detailSideNav.jsx index 0396efa7f..b4465fb19 100644 --- a/src/client/src/commons/menu/detailView/detailSideNav.jsx +++ b/src/client/src/commons/menu/detailView/detailSideNav.jsx @@ -190,7 +190,7 @@ const DetailSideNav = ({ borehole, history, match }) => { - + From f18d8a92c251541793836ec1a9d3c1a97992d297 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 08:34:01 +0200 Subject: [PATCH 04/56] Delete duplicate code --- src/client/src/commons/form/borehole/boreholeForm.jsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/client/src/commons/form/borehole/boreholeForm.jsx b/src/client/src/commons/form/borehole/boreholeForm.jsx index cf2b18adb..13f83bea1 100644 --- a/src/client/src/commons/form/borehole/boreholeForm.jsx +++ b/src/client/src/commons/form/borehole/boreholeForm.jsx @@ -386,11 +386,6 @@ class BoreholeForm extends React.Component { path={"/:id/hydrogeology/hydrotest"} render={() => } /> - } - /> { From b94d4489661e999609ec57f89e3652e1f85a9b4e Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 09:13:40 +0200 Subject: [PATCH 05/56] Add hash to tabs --- .../form/borehole/borehole/boreholePanel.jsx | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/client/src/commons/form/borehole/borehole/boreholePanel.jsx b/src/client/src/commons/form/borehole/borehole/boreholePanel.jsx index 0f98e9971..e785f59ce 100644 --- a/src/client/src/commons/form/borehole/borehole/boreholePanel.jsx +++ b/src/client/src/commons/form/borehole/borehole/boreholePanel.jsx @@ -1,22 +1,55 @@ import BoreholeGeneralSegment from "../segments/boreholeGeneralSegment"; import BoreholeDetailSegment from "../segments/boreholeDetailSegment"; import { useTranslation } from "react-i18next"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import Sections from "./sections"; import Geometry from "./geometry"; -import { BdmsTabContentBox, BdmsTabs, BdmsTab } from "../../../../components/styledTabComponents"; +import { BdmsTab, BdmsTabContentBox, BdmsTabs } from "../../../../components/styledTabComponents"; +import { useHistory, useLocation } from "react-router-dom"; const BoreholePanel = ({ size, boreholeId, borehole, updateChange, updateNumber, isEditable }) => { const { t } = useTranslation(); - + const history = useHistory(); + const location = useLocation(); const [activeIndex, setActiveIndex] = useState(0); + const tabs = [ + { + label: t("general"), + hash: "general", + }, + { label: t("sections"), hash: "sections" }, + { label: t("boreholeGeometry"), hash: "geometry" }, + ]; + + const handleIndexChange = (event, index) => { + setActiveIndex(index); + const newLocation = location.pathname + "#" + tabs[index].hash; + if (location.pathname + location.hash !== newLocation) { + history.push(newLocation); + } + }; + + useEffect(() => { + const newTabIndex = tabs.findIndex(t => t.hash === location.hash.replace("#", "")); + if (newTabIndex > -1 && activeIndex !== newTabIndex) { + handleIndexChange(null, newTabIndex); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [location.hash]); + + useEffect(() => { + if (!location.hash) { + history.replace(location.pathname + "#" + tabs[activeIndex].hash); + } + // eslint-disable-next-line + }, []); return ( <> - setActiveIndex(newValue)}> - - - + + {tabs.map((tab, index) => { + return ; + })} {activeIndex === 0 && ( From 33823c971d00ee033975837f000eb366d85fd262 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 09:14:04 +0200 Subject: [PATCH 06/56] Update tests --- src/client/cypress/e2e/editor/geometry.cy.js | 5 +---- src/client/cypress/e2e/editor/sections.cy.js | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/client/cypress/e2e/editor/geometry.cy.js b/src/client/cypress/e2e/editor/geometry.cy.js index 5853c1a58..bc7d3f475 100644 --- a/src/client/cypress/e2e/editor/geometry.cy.js +++ b/src/client/cypress/e2e/editor/geometry.cy.js @@ -8,14 +8,11 @@ describe("Geometry crud tests", () => { // open section editor cy.get("@borehole_id").then(id => { loginAsAdmin(); - cy.visit(`/${id}/borehole`); + cy.visit(`/${id}/borehole#geometry`); }); // start editing session startBoreholeEditing(); - - cy.get('[data-cy="geometry-tab"]').click(); - cy.wait("@boreholegeometry_GET"); }); it("adds and deletes borehole geometry", () => { diff --git a/src/client/cypress/e2e/editor/sections.cy.js b/src/client/cypress/e2e/editor/sections.cy.js index f01cefe60..132f12804 100644 --- a/src/client/cypress/e2e/editor/sections.cy.js +++ b/src/client/cypress/e2e/editor/sections.cy.js @@ -9,14 +9,11 @@ describe("Section crud tests", () => { // open section editor cy.get("@borehole_id").then(id => { loginAsAdmin(); - cy.visit(`/${id}/borehole`); + cy.visit(`/${id}/borehole#sections`); }); // start editing session startBoreholeEditing(); - - cy.get('[data-cy="sections-tab"]').click(); - cy.wait("@get-sections-by-boreholeId"); }); it("adds, edits and deletes sections", () => { From 95e6c1d03b0577918d882715d8fd1928c8b84909 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 09:14:19 +0200 Subject: [PATCH 07/56] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..d1f8c595a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Added +- Location hash for tabs in borehole detail view. + ### Changed - Renamed technical attributes `kind_id_cli` to `borehole_type_id`, `top_bedrock` to `top_bedrock_fresh` and `qt_top_bedrock` to `top_bedrock_weathered`. From 12c6cc6f82cfb385d15a4900bf53bf1812b9b560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roswita=20Tsch=C3=BCmperlin?= <108136714+tschumpr@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:04:32 +0200 Subject: [PATCH 08/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..e9a451c46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. +- Fixed label for water ingress menu item ## v2.1.772 - 2024-06-27 From 118cfabdca8e2f19f616793ee2c582efc78cc4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roswita=20Tsch=C3=BCmperlin?= <108136714+tschumpr@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:04:45 +0200 Subject: [PATCH 09/56] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a451c46..d1243af1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. -- Fixed label for water ingress menu item +- Fixed label for water ingress menu item. ## v2.1.772 - 2024-06-27 From e6c471a239fe51086b817b2e27f63730c2811654 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 10:17:07 +0200 Subject: [PATCH 10/56] Add test --- .../cypress/e2e/editor/boreholeform.cy.js | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/client/cypress/e2e/editor/boreholeform.cy.js b/src/client/cypress/e2e/editor/boreholeform.cy.js index 607a21d60..21c52f929 100644 --- a/src/client/cypress/e2e/editor/boreholeform.cy.js +++ b/src/client/cypress/e2e/editor/boreholeform.cy.js @@ -1,4 +1,4 @@ -import { newEditableBorehole } from "../helpers/testHelpers"; +import { createBorehole, loginAsAdmin, newEditableBorehole } from "../helpers/testHelpers"; describe("Test for the borehole form.", () => { it("Creates a borehole and fills dropdowns.", () => { @@ -43,4 +43,32 @@ describe("Test for the borehole form.", () => { expect(boreholeDropdownValues).to.deep.eq(["borehole", "geotechnics", "open, no completion", "2"]); }); }); + + it("switches tabs", () => { + let boreholeId; + createBorehole({ "extended.original_name": "LSENALZE" }).as("borehole_id"); + cy.get("@borehole_id").then(id => { + boreholeId = id; + loginAsAdmin(); + cy.visit(`/${id}/borehole`); + }); + cy.location().should(location => { + expect(location.pathname).to.eq(`/${boreholeId}/borehole`); + expect(location.hash).to.eq("#general"); + }); + + cy.get('[data-cy="sections-tab"]').click(); + cy.wait("@get-sections-by-boreholeId"); + cy.location().should(location => { + expect(location.pathname).to.eq(`/${boreholeId}/borehole`); + expect(location.hash).to.eq("#sections"); + }); + + cy.get('[data-cy="geometry-tab"]').click(); + cy.wait("@boreholegeometry_GET"); + cy.location().should(location => { + expect(location.pathname).to.eq(`/${boreholeId}/borehole`); + expect(location.hash).to.eq("#geometry"); + }); + }); }); From a0bb4c8f3d49ff924e7f3ef347d0efaa78f35284 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 11:10:49 +0200 Subject: [PATCH 11/56] Use correct column --- src/api-legacy/v1/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-legacy/v1/action.py b/src/api-legacy/v1/action.py index f225de367..393b44838 100644 --- a/src/api-legacy/v1/action.py +++ b/src/api-legacy/v1/action.py @@ -536,7 +536,7 @@ def filterBorehole(self, filter={}): 'location_precision'] not in ['', None]: params.append(int(filter['location_precision'])) where.append(""" - location_precision_id_cli = %s + qt_location_id_cli = %s """ % self.getIdx()) if 'reference_elevation_from' in keys and filter['reference_elevation_from'] not in ['', None]: From 8daf729fb9b2a2f96d4bf14f5c18880bd059b31d Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 11:12:09 +0200 Subject: [PATCH 12/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..2f27a300c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. +- Location precision filter caused internal error. ## v2.1.772 - 2024-06-27 From 88f3c391b597df2ed26a68776ff53e96e164244f Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 13:10:03 +0200 Subject: [PATCH 13/56] Open different section as workaround --- src/client/cypress/e2e/editor/lithologicalDescription.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/cypress/e2e/editor/lithologicalDescription.cy.js b/src/client/cypress/e2e/editor/lithologicalDescription.cy.js index 9580f6314..9ce1c930b 100644 --- a/src/client/cypress/e2e/editor/lithologicalDescription.cy.js +++ b/src/client/cypress/e2e/editor/lithologicalDescription.cy.js @@ -44,7 +44,7 @@ describe("Tests for the lithological description column.", () => { cy.get('[data-cy="styled-layer-2"] [data-testid="ClearIcon"]').click(); // workaround because close button of profile attributes is sometimes not clickable - cy.get('[data-cy="borehole-menu-item"]').click(); + cy.get('[data-cy="location-menu-item"]').click(); cy.get('[data-cy="stratigraphy-menu-item"]').click(); cy.get('[data-cy="lithology-menu-item"]').click(); From 9193f705a771cf501443794d3d397ea452ad0f5a Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 13:28:17 +0200 Subject: [PATCH 14/56] Disable buttons for view-only users --- src/client/src/commons/menu/mainView/mainSideNav.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index a7ce41865..fab651e90 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -119,6 +119,7 @@ const MainSideNav = ({ sx: object; defaultContent: JSX.Element; hoverContent: JSX.Element; + disabled?: boolean; } const topButtonData: ButtonContentInterface[] = [ @@ -138,6 +139,7 @@ const MainSideNav = ({ onClick: handleToggleAdd, sx: isAddPanelVisible ? selectedButtonStyle : {}, defaultContent: , + disabled: user.data.roles.indexOf("EDIT") === -1, hoverContent: getTooltipWrapper( <> {t("add")} @@ -153,6 +155,7 @@ const MainSideNav = ({ }, sx: {}, defaultContent: , + disabled: user.data.roles.indexOf("EDIT") === -1, hoverContent: getTooltipWrapper( <> {t("upload")} @@ -222,6 +225,7 @@ const MainSideNav = ({ onClick={button.onClick} onMouseEnter={() => setHoveredButtonId(button.id)} onMouseLeave={() => setHoveredButtonId(null)} + disabled={button.disabled} sx={button.sx}> {hoveredButtonId === button.id ? button.hoverContent : button.defaultContent} From d94a906486c84c2eff9d38afa7fb792a683b7351 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 14:34:03 +0200 Subject: [PATCH 15/56] Add navButton component --- .../src/commons/menu/mainView/mainSideNav.tsx | 189 +++++------------- .../src/components/buttons/navButton.tsx | 53 +++++ 2 files changed, 100 insertions(+), 142 deletions(-) create mode 100644 src/client/src/components/buttons/navButton.tsx diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index fab651e90..9e79a8963 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -1,7 +1,7 @@ import { useContext, useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; -import { Badge, IconButton, Stack, Typography } from "@mui/material"; +import { Badge, Stack } from "@mui/material"; import { ImportErrorModal } from "./menuComponents/importErrorModal"; import FilterIcon from "../../../../public/icons/filter.svg?react"; import AddIcon from "../../../../public/icons/add.svg?react"; @@ -10,34 +10,13 @@ import SettingsIcon from "../../../../public/icons/settings.svg?react"; import HelpIcon from "../../../../public/icons/help.svg?react"; import LayersIcon from "../../../../public/icons/layers.svg?react"; import { theme } from "../../../AppTheme"; -import { styled } from "@mui/system"; import ImportModal from "./actions/importModal"; import { DrawerContentTypes } from "../../../pages/editor/editorComponentInterfaces"; import { ErrorResponse, MainSideNavProps } from "./menuComponents/menuComponentInterfaces"; import { ReduxRootState, User } from "../../../ReduxStateInterfaces"; import { FilterContext } from "../../../components/filter/filterContext"; import { useTranslation } from "react-i18next"; - -const StyledIconButton = styled(IconButton)({ - padding: "10px", - marginBottom: "25px", - color: theme.palette.neutral.contrastText, - "&:hover": { - backgroundColor: `${theme.palette.secondary.main} !important`, - color: `${theme.palette.secondary.contrastText} !important`, - width: "fit-content", - whiteSpace: "nowrap", - zIndex: 6000, - paddingLeft: "16px", - justifyContent: "flex-start", - }, - borderRadius: "10px", -}); - -const selectedButtonStyle = { - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.background.menuItemActive + " !important", -}; +import { NavButton } from "../../../components/buttons/navButton"; const MainSideNav = ({ toggleDrawer, @@ -105,102 +84,6 @@ const MainSideNav = ({ const isLayersPanelVisible = drawerOpen && sideDrawerContent === DrawerContentTypes.CustomLayers; const activeFilterCount = filterContext.activeFilterLength + (filterContext.filterPolygon === null ? 0 : 1); - function getTooltipWrapper(content: JSX.Element) { - return ( - - {content} - - ); - } - - interface ButtonContentInterface { - id: string; - onClick: () => void; - sx: object; - defaultContent: JSX.Element; - hoverContent: JSX.Element; - disabled?: boolean; - } - - const topButtonData: ButtonContentInterface[] = [ - { - id: "show-filter-button", - onClick: handleToggleFilter, - sx: isFilterPanelVisible ? selectedButtonStyle : {}, - defaultContent: , - hoverContent: getTooltipWrapper( - <> - {t("searchfilters")} - , - ), - }, - { - id: "new-borehole-button", - onClick: handleToggleAdd, - sx: isAddPanelVisible ? selectedButtonStyle : {}, - defaultContent: , - disabled: user.data.roles.indexOf("EDIT") === -1, - hoverContent: getTooltipWrapper( - <> - {t("add")} - , - ), - }, - { - id: "import-borehole-button", - onClick: () => { - toggleDrawer(false); - setModal(true); - setUpload(true); - }, - sx: {}, - defaultContent: , - disabled: user.data.roles.indexOf("EDIT") === -1, - hoverContent: getTooltipWrapper( - <> - {t("upload")} - , - ), - }, - { - id: "layers-button", - onClick: handleToggleLayers, - sx: isLayersPanelVisible ? selectedButtonStyle : {}, - defaultContent: , - hoverContent: getTooltipWrapper( - <> - - {t("usersMap")} - , - ), - }, - ]; - - const bottomButtonData: ButtonContentInterface[] = [ - { - id: "settings-button", - onClick: () => history.push(`/setting`), - sx: {}, - defaultContent: , - hoverContent: getTooltipWrapper( - <> - {t("header_settings")} - , - ), - }, - { - id: "help-button", - onClick: () => window.open(`/help`), - sx: {}, - defaultContent: , - hoverContent: getTooltipWrapper( - <> - {t("header_help")} - , - ), - }, - ]; - return ( {activeFilterCount > 0 && } - {topButtonData.map(button => ( - setHoveredButtonId(button.id)} - onMouseLeave={() => setHoveredButtonId(null)} - disabled={button.disabled} - sx={button.sx}> - {hoveredButtonId === button.id ? button.hoverContent : button.defaultContent} - - ))} + } + label={t("searchfilters")} + selected={isFilterPanelVisible} + onClick={handleToggleFilter} + /> + } + label={t("add")} + selected={isAddPanelVisible} + disabled={user.data.roles.indexOf("EDIT") === -1} + onClick={handleToggleAdd} + /> + } + label={t("upload")} + disabled={user.data.roles.indexOf("EDIT") === -1} + onClick={() => { + toggleDrawer(false); + setModal(true); + setUpload(true); + }} + /> + } + label={t("usersMap")} + selected={isLayersPanelVisible} + onClick={handleToggleLayers} + /> - {bottomButtonData.map(button => ( - setHoveredButtonId(button.id)} - onMouseLeave={() => setHoveredButtonId(null)} - sx={button.sx}> - {hoveredButtonId === button.id ? button.hoverContent : button.defaultContent} - - ))} + } + label={t("header_settings")} + onClick={() => history.push(`/setting`)} + /> + } + label={t("header_help")} + onClick={() => window.open(`/help`)} + /> ((props, ref) => { + const [isHovered, setIsHovered] = useState(false); + + const handleMouseEnter = () => setIsHovered(true); + const handleMouseLeave = () => setIsHovered(false); + + const defaultContent = props.icon; + const hoverContent = ( + <> + {props.icon} {props.label} + + ); + + return ( + + + {isHovered ? hoverContent : defaultContent} + + + ); +}); From 37ed5e9e3d3b04600a1ea200a758e17d0e5988af Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 14:45:14 +0200 Subject: [PATCH 16/56] Remove unused code --- src/client/src/commons/menu/mainView/mainSideNav.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index 9e79a8963..4a3ad3e19 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -39,7 +39,6 @@ const MainSideNav = ({ const [selectedBoreholeAttachments, setSelectedBoreholeAttachments] = useState(null); const [selectedLithologyFile, setSelectedLithologyFile] = useState(null); const [errorsResponse, setErrorsResponse] = useState(null); - const [hoveredButtonId, setHoveredButtonId] = useState(null); const filterContext = useContext(FilterContext); // Redux state From 6888e409b99941e1268e98906b207f56c70745ed Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 19 Jul 2024 15:00:48 +0200 Subject: [PATCH 17/56] Fix setting data-cy --- src/client/src/commons/menu/mainView/mainSideNav.tsx | 12 ++++++------ src/client/src/components/buttons/navButton.tsx | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index 4a3ad3e19..f21b501ca 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -101,14 +101,14 @@ const MainSideNav = ({ }}> {activeFilterCount > 0 && } } label={t("searchfilters")} selected={isFilterPanelVisible} onClick={handleToggleFilter} /> } label={t("add")} selected={isAddPanelVisible} @@ -116,7 +116,7 @@ const MainSideNav = ({ onClick={handleToggleAdd} /> } label={t("upload")} disabled={user.data.roles.indexOf("EDIT") === -1} @@ -127,7 +127,7 @@ const MainSideNav = ({ }} /> } label={t("usersMap")} selected={isLayersPanelVisible} @@ -140,13 +140,13 @@ const MainSideNav = ({ padding: "1em", }}> } label={t("header_settings")} onClick={() => history.push(`/setting`)} /> } label={t("header_help")} onClick={() => window.open(`/help`)} diff --git a/src/client/src/components/buttons/navButton.tsx b/src/client/src/components/buttons/navButton.tsx index c9aa83cfb..2fd5aee87 100644 --- a/src/client/src/components/buttons/navButton.tsx +++ b/src/client/src/components/buttons/navButton.tsx @@ -4,7 +4,8 @@ import { IconButton, Stack, Typography } from "@mui/material"; import { theme } from "../../AppTheme"; export interface NavButtonProps extends ButtonProps { - key: string; + label: string; + icon: JSX.Element; selected?: boolean; } @@ -20,13 +21,10 @@ export const NavButton = forwardRef((props, r {props.icon} {props.label} ); - return ( Date: Mon, 22 Jul 2024 08:44:54 +0200 Subject: [PATCH 18/56] Fix filter paddings --- .../src/commons/menu/mainView/menuComponents/menuItems.tsx | 2 +- src/client/src/commons/menu/mainView/sideDrawer.tsx | 2 +- src/client/src/commons/search/editor/FilterChips.tsx | 2 +- src/client/src/commons/search/editor/filterComponent.jsx | 7 +++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/client/src/commons/menu/mainView/menuComponents/menuItems.tsx b/src/client/src/commons/menu/mainView/menuComponents/menuItems.tsx index 9c5b1b518..ff0ac0007 100644 --- a/src/client/src/commons/menu/mainView/menuComponents/menuItems.tsx +++ b/src/client/src/commons/menu/mainView/menuComponents/menuItems.tsx @@ -4,7 +4,7 @@ import { Button, Stack } from "@mui/material"; export const MenuItems = ({ reset }: MenuItemsProps) => { return ( - + + + + {languages.map(language => ( + { + onLanguageChanged(language); + }} + sx={{ + cursor: "pointer", + "&:hover": { backgroundColor: theme.palette.background.lightgrey }, + }}> + {selectedLanguage === language && ( + + + + )} + + {language.toUpperCase()} + + + ))} + + + + ); +} diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json index 55d6d284f..3192c59ac 100644 --- a/src/client/tsconfig.json +++ b/src/client/tsconfig.json @@ -42,7 +42,7 @@ ] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ From 1eb9d9f6cd3b9ec7a25f7e8104b4af939ddea79d Mon Sep 17 00:00:00 2001 From: tschumpr Date: Tue, 23 Jul 2024 15:10:00 +0200 Subject: [PATCH 31/56] Simplify code --- src/client/src/commons/menu/profilePopup.tsx | 30 +++++++------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/client/src/commons/menu/profilePopup.tsx b/src/client/src/commons/menu/profilePopup.tsx index 8bf546361..569d318f4 100644 --- a/src/client/src/commons/menu/profilePopup.tsx +++ b/src/client/src/commons/menu/profilePopup.tsx @@ -1,29 +1,27 @@ import { useAuth } from "react-oidc-context"; import { Button, IconButton, Popover, Stack, Typography } from "@mui/material"; -import { theme } from "../../AppTheme.js"; +import { theme } from "../../AppTheme.ts"; import ProfileIcon from "../../../public/icons/profile.svg?react"; import { UserData } from "../../ReduxStateInterfaces"; -import { useState } from "react"; +import { MouseEvent, useState } from "react"; export function ProfilePopup({ user }: { user: UserData }) { const auth = useAuth(); - const [anchorEl, setAnchorEl] = useState(null); + const [anchorEl, setAnchorEl] = useState(); - const handleClick = (event: React.MouseEvent) => { + const handleClick = (event: MouseEvent) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { - setAnchorEl(null); + setAnchorEl(undefined); }; const open = Boolean(anchorEl); const id = open ? "simple-popover" : undefined; - const popoverTop = anchorEl?.getBoundingClientRect().bottom || 0; - const popoverLeft = anchorEl?.getBoundingClientRect().left || 0; - function ProfileButton() { - return ( + return ( + <> - ); - } - - return ( - <> - {user !== null && ( <> From 9b14b2f3519882823a5c1184e090aba17ef1e49d Mon Sep 17 00:00:00 2001 From: tschumpr Date: Tue, 23 Jul 2024 15:10:42 +0200 Subject: [PATCH 32/56] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..5cc8730b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Language dropdown in the header. + ### Changed - Renamed technical attributes `kind_id_cli` to `borehole_type_id`, `top_bedrock` to `top_bedrock_fresh` and `qt_top_bedrock` to `top_bedrock_weathered`. From da416e62c2b33e1531dbf98eb56af644ff800111 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Tue, 23 Jul 2024 15:40:00 +0200 Subject: [PATCH 33/56] Fix tests --- src/client/cypress/e2e/editor/fieldMeasurement.cy.js | 5 ++--- .../cypress/e2e/editor/groundwaterLevelMeasurement.cy.js | 5 ++--- src/client/cypress/e2e/editor/hydrotest.cy.js | 5 ++--- src/client/cypress/e2e/editor/waterIngress.cy.js | 5 ++--- src/client/cypress/e2e/helpers/testHelpers.js | 6 ++++++ src/client/src/commons/menu/languagePopup.tsx | 5 ++++- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/client/cypress/e2e/editor/fieldMeasurement.cy.js b/src/client/cypress/e2e/editor/fieldMeasurement.cy.js index 077a39a87..d908ab1d9 100644 --- a/src/client/cypress/e2e/editor/fieldMeasurement.cy.js +++ b/src/client/cypress/e2e/editor/fieldMeasurement.cy.js @@ -5,6 +5,7 @@ import { createFieldMeasurement, handlePrompt, loginAsAdmin, + selectLanguage, startBoreholeEditing, } from "../helpers/testHelpers"; import { evaluateDisplayValue, setInput, setSelect } from "../helpers/formHelpers"; @@ -31,9 +32,7 @@ describe("Tests for the field measurement editor.", () => { cy.visit(`/${id}/hydrogeology/fieldmeasurement`); startBoreholeEditing(); - // switch to german - cy.contains("span", "DE").click({ force: true }); - cy.wait(1000); + selectLanguage("de"); // create field measurement addItem("addFieldMeasurement"); diff --git a/src/client/cypress/e2e/editor/groundwaterLevelMeasurement.cy.js b/src/client/cypress/e2e/editor/groundwaterLevelMeasurement.cy.js index 425d83b9a..91e200e19 100644 --- a/src/client/cypress/e2e/editor/groundwaterLevelMeasurement.cy.js +++ b/src/client/cypress/e2e/editor/groundwaterLevelMeasurement.cy.js @@ -5,6 +5,7 @@ import { createGroundwaterLevelMeasurement, handlePrompt, loginAsAdmin, + selectLanguage, startBoreholeEditing, } from "../helpers/testHelpers"; import { evaluateDisplayValue, setInput, setSelect } from "../helpers/formHelpers"; @@ -36,9 +37,7 @@ describe("Tests for the groundwater level measurement editor.", () => { force: true, }); - // switch to german - cy.contains("span", "DE").click({ force: true }); - cy.wait(1000); + selectLanguage("de"); // create groundwater level measurement addItem("addGroundwaterLevelMeasurement"); diff --git a/src/client/cypress/e2e/editor/hydrotest.cy.js b/src/client/cypress/e2e/editor/hydrotest.cy.js index adfa63490..236b72740 100644 --- a/src/client/cypress/e2e/editor/hydrotest.cy.js +++ b/src/client/cypress/e2e/editor/hydrotest.cy.js @@ -5,6 +5,7 @@ import { createHydrotest, handlePrompt, loginAsAdmin, + selectLanguage, startBoreholeEditing, } from "../helpers/testHelpers"; import { @@ -41,9 +42,7 @@ describe("Tests for the hydrotest editor.", () => { cy.get('[data-cy="hydrogeology-menu-item"]').click({ force: true }); cy.get('[data-cy="hydrotest-menu-item"]').click({ force: true }); - // switch to german - cy.contains("span", "DE").click({ force: true }); - cy.wait(1000); + selectLanguage("de"); // create hydrotest addItem("addHydrotest"); diff --git a/src/client/cypress/e2e/editor/waterIngress.cy.js b/src/client/cypress/e2e/editor/waterIngress.cy.js index c26bf706b..ce3bfeff7 100644 --- a/src/client/cypress/e2e/editor/waterIngress.cy.js +++ b/src/client/cypress/e2e/editor/waterIngress.cy.js @@ -5,6 +5,7 @@ import { createWateringress, handlePrompt, loginAsAdmin, + selectLanguage, startBoreholeEditing, } from "../helpers/testHelpers"; import { evaluateDisplayValue, setInput, setSelect } from "../helpers/formHelpers"; @@ -39,9 +40,7 @@ describe("Tests for the wateringress editor.", () => { cy.wait("@wateringress_GET"); - // switch to german - cy.contains("span", "DE").click({ force: true }); - cy.wait(1000); + selectLanguage("de"); // create wateringress addItem("addWaterIngress"); diff --git a/src/client/cypress/e2e/helpers/testHelpers.js b/src/client/cypress/e2e/helpers/testHelpers.js index aeec33644..81d7adcd3 100644 --- a/src/client/cypress/e2e/helpers/testHelpers.js +++ b/src/client/cypress/e2e/helpers/testHelpers.js @@ -594,3 +594,9 @@ export const createBaseSelector = parent => { return ""; } }; + +export const selectLanguage = language => { + cy.get('[data-cy="language-selector"]').click({ force: true }); + cy.get(`[data-cy="language-${language.toLowerCase()}"]`).click({ force: true }); + cy.wait(1000); +}; diff --git a/src/client/src/commons/menu/languagePopup.tsx b/src/client/src/commons/menu/languagePopup.tsx index b52fcbb7d..c7658f866 100644 --- a/src/client/src/commons/menu/languagePopup.tsx +++ b/src/client/src/commons/menu/languagePopup.tsx @@ -50,6 +50,7 @@ export function LanguagePopup() { const onLanguageChanged = (language: string) => { i18n.changeLanguage(language); + handleClose(); }; return ( @@ -64,7 +65,8 @@ export function LanguagePopup() { color: theme.palette.primary.main, fontWeight: "500", ...(isOpen && { backgroundColor: theme.palette.background.lightgrey }), - }}> + }} + data-cy="language-selector"> {selectedLanguage.toUpperCase()} ( { onLanguageChanged(language); }} From b5fbff6007d9bf58c21f2ae6aa0e9dedcfa8af8a Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 11:05:59 +0200 Subject: [PATCH 34/56] Update toggle icon --- src/client/public/icons/arrow_down.svg | 14 ++++++++++++++ src/client/public/icons/arrow_up.svg | 14 ++++++++++++++ src/client/src/commons/menu/languagePopup.tsx | 7 +++---- .../src/commons/menu/mainView/bottomBar.tsx | 18 +++++------------- .../commons/search/editor/filterComponent.jsx | 5 +++-- 5 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 src/client/public/icons/arrow_down.svg create mode 100644 src/client/public/icons/arrow_up.svg diff --git a/src/client/public/icons/arrow_down.svg b/src/client/public/icons/arrow_down.svg new file mode 100644 index 000000000..cd0753eb6 --- /dev/null +++ b/src/client/public/icons/arrow_down.svg @@ -0,0 +1,14 @@ + + + + diff --git a/src/client/public/icons/arrow_up.svg b/src/client/public/icons/arrow_up.svg new file mode 100644 index 000000000..f2c46f207 --- /dev/null +++ b/src/client/public/icons/arrow_up.svg @@ -0,0 +1,14 @@ + + + + diff --git a/src/client/src/commons/menu/languagePopup.tsx b/src/client/src/commons/menu/languagePopup.tsx index c7658f866..8baeac9b7 100644 --- a/src/client/src/commons/menu/languagePopup.tsx +++ b/src/client/src/commons/menu/languagePopup.tsx @@ -1,9 +1,9 @@ import { Button, List, ListItem, ListItemIcon, ListItemText, Popover } from "@mui/material"; import { theme } from "../../AppTheme.ts"; import { MouseEvent, useEffect, useState } from "react"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import CheckIcon from "@mui/icons-material/Check"; +import ArrowDownIcon from "../../../public/icons/arrow_down.svg?react"; +import ArrowUpIcon from "../../../public/icons/arrow_up.svg?react"; import i18n from "../../i18n"; const languages = ["de", "fr", "it", "en"]; @@ -57,11 +57,10 @@ export function LanguagePopup() { <> ); diff --git a/src/client/src/commons/search/editor/filterComponent.jsx b/src/client/src/commons/search/editor/filterComponent.jsx index 14929512b..a04496930 100644 --- a/src/client/src/commons/search/editor/filterComponent.jsx +++ b/src/client/src/commons/search/editor/filterComponent.jsx @@ -14,7 +14,7 @@ import { lithostratigraphySearchData } from "../data/lithostratigraphySearchData import { MenuItems } from "../../menu/mainView/menuComponents/menuItems"; import { Accordion, AccordionDetails, AccordionSummary, Badge, Box, Button, Stack, Typography } from "@mui/material"; import { styled } from "@mui/material/styles"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ArrowDownIcon from "../../../../public/icons/arrow_down.svg?react"; import ListFilter from "../components/listFilter.jsx"; import { SideDrawerHeader } from "../../menu/mainView/sideDrawerHeader.tsx"; import Polygon from "../../../../public/icons/polygon.svg?react"; @@ -24,6 +24,7 @@ import { FilterContext } from "../../../components/filter/filterContext.tsx"; class FilterComponent extends React.Component { static contextType = FilterContext; + constructor(props) { super(props); this.handleFilterReset = this.handleFilterReset.bind(this); @@ -237,7 +238,7 @@ class FilterComponent extends React.Component { return ( } + expandIcon={} onClick={() => { this.setState(prevState => ({ ...prevState, From 821c0b5083b734f460f3ce20365c3c3cc0a83457 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 13:37:49 +0200 Subject: [PATCH 35/56] Remove faulty property --- src/client/src/commons/search/data/lithologySearchData.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/src/commons/search/data/lithologySearchData.js b/src/client/src/commons/search/data/lithologySearchData.js index 8982f4ae9..d03725ddb 100644 --- a/src/client/src/commons/search/data/lithologySearchData.js +++ b/src/client/src/commons/search/data/lithologySearchData.js @@ -30,7 +30,6 @@ export const lithologySearchData = [ inputType: "number", hasTwoFields: true, isVisibleValue: "layer.depth_to", - isVisible: true, placeholder: "from", }, { @@ -42,7 +41,6 @@ export const lithologySearchData = [ inputType: "number", hasTwoFields: true, isVisibleValue: "layer.depth_to", - isVisible: true, placeholder: "to", }, { From 4222587dfb2744e477dfe75b41623de15dbf930c Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 13:45:58 +0200 Subject: [PATCH 36/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..2675d0f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. +- `layer_depth_to` was displayed in filter even though it was not selected in the filter settings. ## v2.1.772 - 2024-06-27 From 2d52cb910987cee32a7a1bb47cb9574868b7a3c0 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 14:24:25 +0200 Subject: [PATCH 37/56] Restrict import and adding --- src/client/src/ReduxStateInterfaces.ts | 6 ++++-- src/client/src/commons/menu/mainView/mainSideNav.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/client/src/ReduxStateInterfaces.ts b/src/client/src/ReduxStateInterfaces.ts index ca048eb74..ee5995e6f 100644 --- a/src/client/src/ReduxStateInterfaces.ts +++ b/src/client/src/ReduxStateInterfaces.ts @@ -8,10 +8,12 @@ export interface User { data: UserData; } +export type Role = "PUBLIC" | "VIEW" | "VALID" | "EDIT" | "CONTROL"; + export interface UserData { // Incomplete type definition, add other properties as needed workgroups: Workgroup[]; - roles: string[]; + roles: Role[]; id: number; name: string; username: string; @@ -22,7 +24,7 @@ export interface Workgroup { disabled: null; id: number; workgroup: string; - roles: string[]; + roles: Role[]; } interface Workflow { diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index c3c11c29c..9314c0442 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -63,7 +63,7 @@ const MainSideNav = ({ }; useEffect(() => { - const wgs = user.data.workgroups.filter(w => w.disabled === null && !w.supplier); + const wgs = user.data.workgroups.filter(w => w.disabled === null && !w.supplier && w.roles.includes("EDIT")); setEnabledWorkgroups(wgs); setWorkgroup(wgs.length > 0 ? wgs[0].id : null); }, [setEnabledWorkgroups, setWorkgroup, user.data.workgroups]); From d44cccb890c49957f9b79c544bbcc542201dde38 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 14:24:37 +0200 Subject: [PATCH 38/56] Remove unused code --- src/client/src/pages/editor/editorComponent.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/src/pages/editor/editorComponent.jsx b/src/client/src/pages/editor/editorComponent.jsx index 4d210a6b6..ad1247b7a 100644 --- a/src/client/src/pages/editor/editorComponent.jsx +++ b/src/client/src/pages/editor/editorComponent.jsx @@ -64,7 +64,6 @@ const EditorComponent = props => { workgroup={workgroup} setWorkgroup={setWorkgroup} enabledWorkgroups={enabledWorkgroups} - setEnabledWorkgroups={setEnabledWorkgroups} /> ), customLayers: , From 964b835692a5678fde6b9b326f8cce7454f069b8 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 14:24:45 +0200 Subject: [PATCH 39/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..6ad71cc6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. +- Users can now only import or add new boreholes to workgroups where they have an editor role. ## v2.1.772 - 2024-06-27 From 57820135530bd843ca09a8a7e4679b961adc47af Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 14:35:59 +0200 Subject: [PATCH 40/56] Set role type --- src/client/src/ReduxStateInterfaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/src/ReduxStateInterfaces.ts b/src/client/src/ReduxStateInterfaces.ts index ee5995e6f..1ce05e5e2 100644 --- a/src/client/src/ReduxStateInterfaces.ts +++ b/src/client/src/ReduxStateInterfaces.ts @@ -30,7 +30,7 @@ export interface Workgroup { interface Workflow { started: string; finished: string; - role: string; + role: Role; username: string; workflow: number; } @@ -43,7 +43,7 @@ export interface Borehole { workflow: Workflow; id: number; spatial_reference_system: number; - role: string; + role: Role; lock: { id: number; }; From 74ced3da05f81dd277a800eed205ddfd4afd166f Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 24 Jul 2024 16:40:03 +0200 Subject: [PATCH 41/56] Add file upload button --- src/client/package-lock.json | 64 ++++++----- src/client/package.json | 1 + src/client/public/locale/de/common.json | 1 - src/client/public/locale/en/common.json | 1 - src/client/public/locale/fr/common.json | 1 - src/client/public/locale/it/common.json | 1 - .../files/table/editorBoreholeFilesTable.jsx | 107 ++++++++++-------- 7 files changed, 95 insertions(+), 81 deletions(-) diff --git a/src/client/package-lock.json b/src/client/package-lock.json index 5e1077e36..3e5f89c67 100644 --- a/src/client/package-lock.json +++ b/src/client/package-lock.json @@ -12,6 +12,7 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.19", "@mui/material": "^5.14.20", + "@mui/system": "^5.16.4", "@react-hook/resize-observer": "^1.2.6", "@types/ol": "^7.0.0", "axios": "^1.6.2", @@ -1493,12 +1494,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.15.11", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.11.tgz", - "integrity": "sha512-jY/696SnSxSzO1u86Thym7ky5T9CgfidU3NFJjguldqK4f3Z5S97amZ6nffg8gTD0HBjY9scB+4ekqDEUmxZOA==", + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.4.tgz", + "integrity": "sha512-ZsAm8cq31SJ37SVWLRlu02v9SRthxnfQofaiv14L5Bht51B0dz6yQEoVU/V8UduZDCCIrWkBHuReVfKhE/UuXA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.11", + "@mui/utils": "^5.16.4", "prop-types": "^15.8.1" }, "engines": { @@ -1519,9 +1520,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.11", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz", - "integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==", + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", + "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -1550,15 +1551,15 @@ } }, "node_modules/@mui/system": { - "version": "5.15.11", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.11.tgz", - "integrity": "sha512-9j35suLFq+MgJo5ktVSHPbkjDLRMBCV17NMBdEQurh6oWyGnLM4uhU4QGZZQ75o0vuhjJghOCA1jkO3+79wKsA==", + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.4.tgz", + "integrity": "sha512-ET1Ujl2/8hbsD611/mqUuNArMCGv/fIWO/f8B3ZqF5iyPHM2aS74vhTNyjytncc4i6dYwGxNk+tLa7GwjNS0/w==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.11", - "@mui/styled-engine": "^5.15.11", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.11", + "@mui/private-theming": "^5.16.4", + "@mui/styled-engine": "^5.16.4", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.4", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1589,9 +1590,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", - "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -1602,14 +1603,15 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.11", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.11.tgz", - "integrity": "sha512-D6bwqprUa9Stf8ft0dcMqWyWDKEo7D+6pB1k8WajbqlYIRA8J8Kw9Ra7PSZKKePGBGWO+/xxrX1U8HpG/aXQCw==", + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.4.tgz", + "integrity": "sha512-nlppYwq10TBIFqp7qxY0SvbACOXeOjeVL3pOcDsK0FT8XjrEXh9/+lkg8AEIzD16z7YfiJDQjaJG2OLkE7BxNg==", "dependencies": { "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^18.3.1" }, "engines": { "node": ">=12.0.0" @@ -2298,9 +2300,9 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.2.63", @@ -3630,9 +3632,9 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -8522,9 +8524,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/react-number-format": { "version": "5.3.3", diff --git a/src/client/package.json b/src/client/package.json index 0024531df..108482c78 100644 --- a/src/client/package.json +++ b/src/client/package.json @@ -27,6 +27,7 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.19", "@mui/material": "^5.14.20", + "@mui/system": "^5.16.4", "@react-hook/resize-observer": "^1.2.6", "@types/ol": "^7.0.0", "axios": "^1.6.2", diff --git a/src/client/public/locale/de/common.json b/src/client/public/locale/de/common.json index 247a53479..69b2b1b07 100644 --- a/src/client/public/locale/de/common.json +++ b/src/client/public/locale/de/common.json @@ -436,7 +436,6 @@ "updateDate": "Bearbeitungsdatum", "updatedBy": "Aktualisiert durch", "upload": "Hochladen", - "uploadNewFile": "Hochladen einer neuen Datei", "uploaded": "Hochgeladen", "uscs_1": "USCS 1", "uscs_2": "USCS 2", diff --git a/src/client/public/locale/en/common.json b/src/client/public/locale/en/common.json index 8b09c1035..88bc13a40 100644 --- a/src/client/public/locale/en/common.json +++ b/src/client/public/locale/en/common.json @@ -437,7 +437,6 @@ "updateDate": "Update date", "updatedBy": "Updated by", "upload": "Upload", - "uploadNewFile": "Upload a new file", "uploaded": "Uploaded", "uscs_1": "USCS 1", "uscs_2": "USCS 2", diff --git a/src/client/public/locale/fr/common.json b/src/client/public/locale/fr/common.json index c5eed6015..da15ccd1a 100644 --- a/src/client/public/locale/fr/common.json +++ b/src/client/public/locale/fr/common.json @@ -436,7 +436,6 @@ "updateDate": "Date de modification", "updatedBy": "Mis à jour par", "upload": "Télécharger", - "uploadNewFile": "Télécharger un nouveau fichier", "uploaded": "Téléchargé", "uscs_1": "USCS 1", "uscs_2": "USCS 2", diff --git a/src/client/public/locale/it/common.json b/src/client/public/locale/it/common.json index 427634a7a..975a77c60 100644 --- a/src/client/public/locale/it/common.json +++ b/src/client/public/locale/it/common.json @@ -436,7 +436,6 @@ "updateDate": "Data di aggiornamento", "updatedBy": "Aggiornato da", "upload": "Caricare", - "uploadNewFile": "Caricare un nuovo file", "uploaded": "Caricamento", "uscs_1": "USCS 1", "uscs_2": "USCS 2", diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx index b74a04498..debad3989 100644 --- a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx +++ b/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx @@ -2,17 +2,30 @@ import { Component } from "react"; import PropTypes from "prop-types"; import { - uploadBoreholeAttachment, detachBoreholeAttachment, getBoreholeAttachments, updateBoreholeAttachment, + uploadBoreholeAttachment, } from "../../../api/fetchApiV2"; import FilesTableComponent from "./filesTableComponent"; -import TranslationText from "../../form/translationText"; -import { Button } from "semantic-ui-react"; +import { Box, Button, Input } from "@mui/material"; +import UploadFileIcon from "@mui/icons-material/UploadFile"; import { AlertContext } from "../../../components/alert/alertContext"; import { withTranslation } from "react-i18next"; +import { styled } from "@mui/system"; + +export const VisuallyHiddenInput = styled(Input)({ + clip: "rect(0 0 0 0)", + clipPath: "inset(50%)", + height: 1, + overflow: "hidden", + position: "absolute", + bottom: 0, + left: 0, + whiteSpace: "nowrap", + width: 1, +}); class EditorBoreholeFilesTable extends Component { static propTypes = { @@ -32,7 +45,6 @@ class EditorBoreholeFilesTable extends Component { patching: null, upload: false, files: [], - file: null, }; this.loadFiles = this.loadFiles.bind(this); this.patch = this.patch.bind(this); @@ -116,42 +128,45 @@ class EditorBoreholeFilesTable extends Component { overflowY: "hidden", }}> {this.props.unlocked === true ? ( -
- :   - { - const file = e.target.files[0]; - const maxSizeInBytes = 210_000_000; // 1024 x 1024 x 200 = 209715200 bytes (as stated in the import docs) - - // If file size is less than max allowed size, upload file. Otherwise, display error message. - if (file && file.size <= maxSizeInBytes) { - const formData = new FormData(); - formData.append("file", file); - this.setState({ - file: formData, - }); - } else { - this.context.error(t("maxfileSizeExceeded") + " (200 MB)"); - this.input.value = null; // Reset the input to clear the selected file - } - }} - ref={e => (this.input = e)} - style={{ - fontFamily: "inherit", - }} - type="file" - /> + -
+ ) : null} { From d183018560c0a1d591ddc04fd29516c9a50b0500 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 09:38:41 +0200 Subject: [PATCH 42/56] Add file upload button --- CHANGELOG.md | 1 + .../menu/mainView/actions/importModal.tsx | 10 +-- .../sidePanelContent/newBoreholePanel.tsx | 4 +- .../src/components/alert/alertBanner.jsx | 28 --------- .../src/components/alert/alertBanner.tsx | 20 ++++++ .../src/components/alert/alertContext.tsx | 61 ++++++++++--------- .../src/components/alert/alertInterfaces.ts | 18 ++++-- 7 files changed, 72 insertions(+), 70 deletions(-) delete mode 100644 src/client/src/components/alert/alertBanner.jsx create mode 100644 src/client/src/components/alert/alertBanner.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c74de19..2aeafe3ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Moved groundwater radio buttons in borehole form to the bottom. - Removed title from prompt dialog. - Use standard prompt dialog for deleting boreholes. +- Updated standard alert. ### Fixed diff --git a/src/client/src/commons/menu/mainView/actions/importModal.tsx b/src/client/src/commons/menu/mainView/actions/importModal.tsx index 0c94f50c5..da2bc376e 100644 --- a/src/client/src/commons/menu/mainView/actions/importModal.tsx +++ b/src/client/src/commons/menu/mainView/actions/importModal.tsx @@ -27,7 +27,7 @@ const ImportModal = ({ setSelectedLithologyFile, refresh, }: ImportModalProps) => { - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const handleBoreholeImport = () => { const combinedFormData = new FormData(); @@ -53,7 +53,7 @@ const ImportModal = ({ setUpload(false); (async () => { if (response.ok) { - alertContext.success(`${await response.text()} ${t("boreholesImported")}.`); + showAlert(`${await response.text()} ${t("boreholesImported")}.`, "success"); refresh(); } else { let responseBody = await response.text(); @@ -71,12 +71,12 @@ const ImportModal = ({ // If response is of type ProblemDetails, show error message. else { - alertContext.error(`${responseBody.detail}`); + showAlert(responseBody.detail, "error"); } } else if (response.status === 504) { - alertContext.error(`${t("boreholesImportLongRunning")}`); + showAlert(t("boreholesImportLongRunning"), "error"); } else { - alertContext.error(`${t("boreholesImportError")}`); + showAlert(t("boreholesImportError"), "error"); } } } diff --git a/src/client/src/commons/menu/mainView/sidePanelContent/newBoreholePanel.tsx b/src/client/src/commons/menu/mainView/sidePanelContent/newBoreholePanel.tsx index 48d8b8951..45984efce 100644 --- a/src/client/src/commons/menu/mainView/sidePanelContent/newBoreholePanel.tsx +++ b/src/client/src/commons/menu/mainView/sidePanelContent/newBoreholePanel.tsx @@ -10,7 +10,7 @@ import { NewBoreholeProps } from "../actions/actionsInterfaces"; const NewBoreholePanel = ({ workgroup, enabledWorkgroups, setWorkgroup, toggleDrawer }: NewBoreholeProps) => { const history = useHistory(); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const { t } = useTranslation(); const handleBoreholeCreate = () => { // @ts-expect-error : The createBorehole function is not typed @@ -20,7 +20,7 @@ const NewBoreholePanel = ({ workgroup, enabledWorkgroups, setWorkgroup, toggleDr if (response.data.success) { history.push("/" + response.data.id); } else { - alertContext.error(response.data.message); + showAlert(response.data.message, "error"); window.location.reload(); } }) diff --git a/src/client/src/components/alert/alertBanner.jsx b/src/client/src/components/alert/alertBanner.jsx deleted file mode 100644 index c20cc0b4c..000000000 --- a/src/client/src/components/alert/alertBanner.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useContext } from "react"; -import Alert from "@mui/material/Alert"; -import Snackbar from "@mui/material/Snackbar"; -import { AlertContext } from "./alertContext"; - -export const AlertBanner = () => { - const alertContext = useContext(AlertContext); - return ( - alertContext.text && ( - { - alertContext.clear(); - }}> - { - alertContext.clear(); - }}> - {alertContext.text} - - - ) - ); -}; diff --git a/src/client/src/components/alert/alertBanner.tsx b/src/client/src/components/alert/alertBanner.tsx new file mode 100644 index 000000000..d4f7f04ca --- /dev/null +++ b/src/client/src/components/alert/alertBanner.tsx @@ -0,0 +1,20 @@ +import { useContext } from "react"; +import { AlertContext } from "./alertContext"; +import { Alert, Snackbar } from "@mui/material"; + +export const AlertBanner = () => { + const alertContext = useContext(AlertContext); + return ( + alertContext.alertIsOpen && ( + + + {alertContext.text} + + + ) + ); +}; diff --git a/src/client/src/components/alert/alertContext.tsx b/src/client/src/components/alert/alertContext.tsx index af4d1000e..1692e8daa 100644 --- a/src/client/src/components/alert/alertContext.tsx +++ b/src/client/src/components/alert/alertContext.tsx @@ -1,44 +1,45 @@ -import React from "react"; -import { useState, createContext } from "react"; -import { AlertContextInterface, AlertProviderProps } from "./alertInterfaces"; +import { createContext, FC, useEffect, useState } from "react"; +import { AlertColor } from "@mui/material"; +import { AlertContextInterface, AlertOptions, AlertProviderProps } from "./alertInterfaces"; -const defaultState: AlertContextInterface = { - success: () => {}, - error: () => {}, - clear: () => {}, - text: null, - severity: null, -}; - -export const AlertContext = createContext(defaultState); +export const AlertContext = createContext({ + alertIsOpen: false, + text: undefined, + severity: undefined, + autoHideDuration: null, + showAlert: () => {}, + closeAlert: () => {}, +}); -export const AlertProvider: React.FC = ({ children }) => { - const [text, setText] = useState(null); - const [severity, setSeverity] = useState<"success" | "error" | null>(null); +export const AlertProvider: FC = ({ children }) => { + const [currentAlert, setCurrentAlert] = useState(); + const [alerts, setAlerts] = useState([]); - const success = (text: string) => { - setText(text); - setSeverity("success"); + const showAlert = (text: string, severity: AlertColor | undefined, allowAutoHide: boolean | undefined) => { + const newAlert = { text, severity: severity ?? "info", allowAutoHide: allowAutoHide ?? false }; + setAlerts(prevAlerts => [...prevAlerts, newAlert]); }; - const error = (text: string) => { - setText(text); - setSeverity("error"); + const closeAlert = () => { + setCurrentAlert(undefined); }; - const clear = () => { - setText(null); - setSeverity(null); - }; + useEffect(() => { + if (alerts.length > 0 && !currentAlert) { + setCurrentAlert(alerts[0]); + setAlerts(prevAlerts => prevAlerts.slice(1)); + } + }, [alerts, currentAlert]); return ( {children} diff --git a/src/client/src/components/alert/alertInterfaces.ts b/src/client/src/components/alert/alertInterfaces.ts index fbd1e31de..8f6690b1e 100644 --- a/src/client/src/components/alert/alertInterfaces.ts +++ b/src/client/src/components/alert/alertInterfaces.ts @@ -1,11 +1,19 @@ import { ReactNode } from "react"; +import { AlertColor } from "@mui/material"; export interface AlertContextInterface { - success: (text: string) => void; - error: (text: string) => void; - clear: () => void; - text: string | null; - severity: "success" | "error" | null; + alertIsOpen: boolean; + text: string | undefined; + severity: AlertColor | undefined; + autoHideDuration: number | null; + showAlert: (text: string, severity?: AlertColor, allowAutoHide?: boolean) => void; + closeAlert: () => void; +} + +export interface AlertOptions { + text: string; + severity?: AlertColor; + allowAutoHide?: boolean; } export interface AlertProviderProps { From a094adae3a3ec20289a3240ff9aa24fbc0f6eb1b Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 10:39:58 +0200 Subject: [PATCH 43/56] Set default for autoHide to true --- src/client/src/components/alert/alertContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/src/components/alert/alertContext.tsx b/src/client/src/components/alert/alertContext.tsx index 1692e8daa..8fd4c1b07 100644 --- a/src/client/src/components/alert/alertContext.tsx +++ b/src/client/src/components/alert/alertContext.tsx @@ -16,7 +16,7 @@ export const AlertProvider: FC = ({ children }) => { const [alerts, setAlerts] = useState([]); const showAlert = (text: string, severity: AlertColor | undefined, allowAutoHide: boolean | undefined) => { - const newAlert = { text, severity: severity ?? "info", allowAutoHide: allowAutoHide ?? false }; + const newAlert = { text, severity: severity ?? "info", allowAutoHide: allowAutoHide ?? true }; setAlerts(prevAlerts => [...prevAlerts, newAlert]); }; From 963123a7a4a9259d543dfb29aa0d267a4bab0423 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 10:42:31 +0200 Subject: [PATCH 44/56] Update alert calls --- .../files/table/editorBoreholeFilesTable.jsx | 8 ++++---- .../form/borehole/borehole/geometryImport.jsx | 4 ++-- .../src/commons/form/borehole/boreholeForm.jsx | 7 ++++--- .../borehole/segments/indentifierSegment.jsx | 4 ++-- .../profileAttributes/profileAttributes.jsx | 6 +++--- .../components/profileInfo/profileInfo.jsx | 6 +++--- .../profileLayersError/profileLayersError.jsx | 12 ++++++------ .../components/profileLayers/profileLayers.jsx | 6 +++--- .../src/commons/form/workflow/workflowForm.jsx | 3 ++- .../src/commons/table/boreholeEditorTable.jsx | 2 -- src/client/src/pages/editor/editorComponent.jsx | 6 ++++-- .../src/pages/settings/admin/adminSettings.jsx | 17 +++++++++-------- .../editorSettingList/mapSettings.jsx | 5 +++-- .../src/pages/settings/editorSettings.jsx | 6 ++++-- 14 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx index b74a04498..86dd27398 100644 --- a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx +++ b/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx @@ -2,10 +2,10 @@ import { Component } from "react"; import PropTypes from "prop-types"; import { - uploadBoreholeAttachment, detachBoreholeAttachment, getBoreholeAttachments, updateBoreholeAttachment, + uploadBoreholeAttachment, } from "../../../api/fetchApiV2"; import FilesTableComponent from "./filesTableComponent"; @@ -131,7 +131,7 @@ class EditorBoreholeFilesTable extends Component { file: formData, }); } else { - this.context.error(t("maxfileSizeExceeded") + " (200 MB)"); + this.context.showAlert(t("maxfileSizeExceeded") + " (200 MB)", "error"); this.input.value = null; // Reset the input to clear the selected file } }} @@ -154,9 +154,9 @@ class EditorBoreholeFilesTable extends Component { uploadBoreholeAttachment(this.props.id, this.state.file).then(r => { if (r.ok === false) { if (r.status === 400) { - this.context.error(t("errorDuplicatedUploadPerBorehole")); + this.context.showAlert(t("errorDuplicatedUploadPerBorehole"), "error"); } else { - this.context.error(t("errorDuringBoreholeFileUpload")); + this.context.showAlert(t("errorDuringBoreholeFileUpload"), "error"); } } this.input.value = ""; diff --git a/src/client/src/commons/form/borehole/borehole/geometryImport.jsx b/src/client/src/commons/form/borehole/borehole/geometryImport.jsx index c5173a8e1..219b49271 100644 --- a/src/client/src/commons/form/borehole/borehole/geometryImport.jsx +++ b/src/client/src/commons/form/borehole/borehole/geometryImport.jsx @@ -28,7 +28,7 @@ import { DevTool } from "../../../../../hookformDevtools.ts"; const GeometryImport = ({ boreholeId }) => { const { t } = useTranslation(); - const { error } = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const { set: { mutate: setBoreholeGeometry, isLoading: isUpdatingBoreholeGeometry }, @@ -59,7 +59,7 @@ const GeometryImport = ({ boreholeId }) => { // fetch does not fail promises on 4xx or 5xx responses // ¯\_(ツ)_/¯ if (!data.ok) { - data.json().then(msg => error(msg.detail ?? t("errorDuringBoreholeFileUpload"))); + data.json().then(msg => showAlert(msg.detail ?? t("errorDuringBoreholeFileUpload"), "error")); } }, }, diff --git a/src/client/src/commons/form/borehole/boreholeForm.jsx b/src/client/src/commons/form/borehole/boreholeForm.jsx index cf2b18adb..e69455f78 100644 --- a/src/client/src/commons/form/borehole/boreholeForm.jsx +++ b/src/client/src/commons/form/borehole/boreholeForm.jsx @@ -108,15 +108,16 @@ class BoreholeForm extends React.Component { checkLock() { const { t } = this.props; if (this.props.borehole.data.role !== "EDIT") { - this.context.error( + this.context.showAlert( t("common:errorStartEditingWrongStatus", { status: this.props.borehole.data.role, }), + "error", ); return false; } if (this.props.borehole.data.lock === null || this.props.borehole.data.lock.id !== this.props.user.data.id) { - this.context.error(t("common:errorStartEditing")); + this.context.showAlert(t("common:errorStartEditing"), "error"); return false; } return true; @@ -207,7 +208,7 @@ class BoreholeForm extends React.Component { }, ); } else if (response.status === 200) { - this.context.error(response.data.message); + this.context.showAlert(response.data.message, "error"); if (response.data.error === "errorLocked") { this.setState( { diff --git a/src/client/src/commons/form/borehole/segments/indentifierSegment.jsx b/src/client/src/commons/form/borehole/segments/indentifierSegment.jsx index 207ad3bf0..1fcf2d3a1 100644 --- a/src/client/src/commons/form/borehole/segments/indentifierSegment.jsx +++ b/src/client/src/commons/form/borehole/segments/indentifierSegment.jsx @@ -12,7 +12,7 @@ import { useTranslation } from "react-i18next"; const IdentifierSegment = props => { const { borehole, identifier, identifierValue, updateBorehole, setState, user } = props; const { t } = useTranslation(); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const isEditable = borehole?.data.role === "EDIT" && borehole?.data.lock !== null && borehole?.data.lock?.id === user?.data.id; @@ -147,7 +147,7 @@ const IdentifierSegment = props => { : []; if (alreadySet.includes(identifier)) { - alertContext.error(t("msgIdentifierAlreadyUsed")); + showAlert(t("msgIdentifierAlreadyUsed"), "error"); } else { addIdentifier(borehole.data.id, identifier, identifierValue).then(response => { if (response.data.success) { diff --git a/src/client/src/commons/form/profile/components/profileAttributes/profileAttributes.jsx b/src/client/src/commons/form/profile/components/profileAttributes/profileAttributes.jsx index 4fc3dfd77..9bf23414b 100644 --- a/src/client/src/commons/form/profile/components/profileAttributes/profileAttributes.jsx +++ b/src/client/src/commons/form/profile/components/profileAttributes/profileAttributes.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useCallback, useContext } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import * as Styled from "./styles"; import { Checkbox } from "semantic-ui-react"; import TranslationText from "../../../translationText"; @@ -57,7 +57,7 @@ const ProfileAttributes = props => { }); const { t } = useTranslation(); const queryClient = useQueryClient(); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const mounted = useRef(false); @@ -90,7 +90,7 @@ const ProfileAttributes = props => { const updateChange = (attribute, value, isNumber = false) => { if (!isEditable) { - alertContext.error(t("common:errorStartEditing")); + showAlert(t("common:errorStartEditing"), "error"); return; } diff --git a/src/client/src/commons/form/profile/components/profileInfo/profileInfo.jsx b/src/client/src/commons/form/profile/components/profileInfo/profileInfo.jsx index afd59732f..ef5782db1 100644 --- a/src/client/src/commons/form/profile/components/profileInfo/profileInfo.jsx +++ b/src/client/src/commons/form/profile/components/profileInfo/profileInfo.jsx @@ -1,4 +1,4 @@ -import { useRef, useState, useCallback, useEffect, useContext } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import * as Styled from "./styles"; import InfoList from "./components/infoList"; import InfoCheckBox from "./components/infoCheckBox"; @@ -13,7 +13,7 @@ const ProfileInfo = props => { const mounted = useRef(false); const { t } = useTranslation(); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const [state, setState] = useState({ isPatching: false, updateAttributeDelay: {}, @@ -46,7 +46,7 @@ const ProfileInfo = props => { const updateChange = (attribute, value, isNumber = false) => { if (!isEditable) { - alertContext.error(t("common:errorStartEditing")); + showAlert(t("common:errorStartEditing"), "error"); return; } setState(prevState => ({ ...prevState, isPatching: true })); diff --git a/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.jsx b/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.jsx index 06a9688a6..2c3955fe0 100644 --- a/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.jsx +++ b/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.jsx @@ -1,9 +1,9 @@ -import { useState, useEffect, useContext } from "react"; +import { useContext, useEffect, useState } from "react"; import * as Styled from "./styles"; import { Icon, Radio } from "semantic-ui-react"; import TranslationText from "../../../../../translationText"; -import { gapLayer, deleteLayer } from "../../../../../../../api-lib/index"; -import { fetchLayerById, addBedrock } from "../../../../../../../api/fetchApiV2"; +import { deleteLayer, gapLayer } from "../../../../../../../api-lib/index"; +import { addBedrock, fetchLayerById } from "../../../../../../../api/fetchApiV2"; import ErrorTypes from "./errorTypes"; import { AlertContext } from "../../../../../../../components/alert/alertContext"; @@ -14,7 +14,7 @@ const ProfileLayersError = props => { const [error, setError] = useState(); const [resolvingAction, setResolvingAction] = useState(null); const [isDelete, setIsDelete] = useState(false); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); useEffect(() => { let e; @@ -79,7 +79,7 @@ const ProfileLayersError = props => { if (response.data.success) { onUpdated("fixErrors"); } else { - alertContext.error(response.data.message); + showAlert(response.data.message, "error"); } }) .catch(error => { @@ -107,7 +107,7 @@ const ProfileLayersError = props => { if (response.data.success) { onUpdated("deleteLayer"); } else { - alertContext.error(response.data.message); + showAlert(response.data.message, "error"); } }) .catch(function (error) { diff --git a/src/client/src/commons/form/profile/components/profileLayers/profileLayers.jsx b/src/client/src/commons/form/profile/components/profileLayers/profileLayers.jsx index 7f7d53146..79e6dbd71 100644 --- a/src/client/src/commons/form/profile/components/profileLayers/profileLayers.jsx +++ b/src/client/src/commons/form/profile/components/profileLayers/profileLayers.jsx @@ -42,7 +42,7 @@ const ProfileLayers = props => { const [layersWithValidation, setLayersWithValidation] = useState(null); const [selecteDescription, setSelectedDescription] = useState(null); const [showDelete, setShowDelete] = useState(); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const [deleteParams, setDeleteParams] = useState(null); const layers = useLayers(selectedStratigraphyID); @@ -173,7 +173,7 @@ const ProfileLayers = props => { const addDescription = (query, mutation) => { if (query?.data && query?.data?.length && query?.data[query?.data?.length - 1]?.toDepth == null) { - alertContext.error(t("first_add_layer_to_depth")); + showAlert(t("first_add_layer_to_depth"), "error"); } else { setSelectedDescription(null); const newFromDepth = query?.data?.at(-1)?.toDepth ?? 0; @@ -183,7 +183,7 @@ const ProfileLayers = props => { fromDepth: newFromDepth, toDepth: layersWithValidation?.data.find(l => l.depth_from === newFromDepth)?.depth_to, }) - : alertContext.error(t("first_add_lithology")); + : showAlert(t("first_add_lithology"), "error"); } }; diff --git a/src/client/src/commons/form/workflow/workflowForm.jsx b/src/client/src/commons/form/workflow/workflowForm.jsx index 42a92fadb..299d18b6a 100644 --- a/src/client/src/commons/form/workflow/workflowForm.jsx +++ b/src/client/src/commons/form/workflow/workflowForm.jsx @@ -25,6 +25,7 @@ import { Stack } from "@mui/material"; class WorkflowForm extends React.Component { static contextType = AlertContext; + constructor(props) { super(props); this.load = this.load.bind(this); @@ -75,7 +76,7 @@ class WorkflowForm extends React.Component { handleChange(value) { const { t } = this.props; if (this.props.borehole.data.lock === null || this.props.borehole.data.lock.id !== this.props.user.data.id) { - this.context.error(t("common:errorStartEditing")); + this.context.showAlert(t("common:errorStartEditing"), "error"); } else { this.props.updateWorkflow(value); if (this.updateAttributeDelay !== false) { diff --git a/src/client/src/commons/table/boreholeEditorTable.jsx b/src/client/src/commons/table/boreholeEditorTable.jsx index c48d3c55a..b194c664a 100644 --- a/src/client/src/commons/table/boreholeEditorTable.jsx +++ b/src/client/src/commons/table/boreholeEditorTable.jsx @@ -9,11 +9,9 @@ import { copyBorehole } from "../../api/fetchApiV2"; import TTable from "./table"; import { Button, Checkbox, Dropdown, Header, Icon, Modal, Segment, Table } from "semantic-ui-react"; import { deleteBoreholes, getdBoreholeIds, loadEditingBoreholes } from "../../api-lib/index"; -import { AlertContext } from "../../components/alert/alertContext"; import { theme } from "../../AppTheme"; class BoreholeEditorTable extends TTable { - static contextType = AlertContext; constructor(props) { super(props); diff --git a/src/client/src/pages/editor/editorComponent.jsx b/src/client/src/pages/editor/editorComponent.jsx index 4d210a6b6..be41fb642 100644 --- a/src/client/src/pages/editor/editorComponent.jsx +++ b/src/client/src/pages/editor/editorComponent.jsx @@ -46,7 +46,7 @@ const EditorComponent = props => { const [workgroup, setWorkgroup] = useState(0); const [enabledWorkgroups, setEnabledWorkgroups] = useState([]); const [sideDrawerContent, setSideDrawerContent] = useState(DrawerContentTypes.Filters); - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const toggleSideDrawer = open => { setSideDrawerOpen(open); @@ -114,7 +114,9 @@ const EditorComponent = props => { setSort={setSort} toggleBottomDrawer={toggleBottomDrawer} bottomDrawerOpen={bottomDrawerOpen} - displayErrorMessage={alertContext.error} + displayErrorMessage={message => { + showAlert(message, "error"); + }} /> )} /> diff --git a/src/client/src/pages/settings/admin/adminSettings.jsx b/src/client/src/pages/settings/admin/adminSettings.jsx index 61ec73e20..8f6e70582 100644 --- a/src/client/src/pages/settings/admin/adminSettings.jsx +++ b/src/client/src/pages/settings/admin/adminSettings.jsx @@ -5,21 +5,21 @@ import { withTranslation } from "react-i18next"; import { AlertContext } from "../../../components/alert/alertContext"; import { fetchUsers } from "../../../api/fetchApiV2"; -import { Button, Checkbox, Icon, Input, Label, Modal, Table, Form, Loader } from "semantic-ui-react"; +import { Button, Checkbox, Form, Icon, Input, Label, Loader, Modal, Table } from "semantic-ui-react"; import { createWorkgroup, - enableWorkgroup, - disableWorkgroup, - deleteWorkgroup, - updateWorkgroup, deleteUser, + deleteWorkgroup, disableUser, + disableWorkgroup, enableUser, - updateUser, - setRole, + enableWorkgroup, listWorkgroups, reloadUser, + setRole, + updateUser, + updateWorkgroup, } from "../../../api-lib/index"; import DateText from "../../../commons/form/dateText"; @@ -28,6 +28,7 @@ import { WorkgroupRoleSettings } from "./workgroupRoleSettings"; class AdminSettings extends React.Component { static contextType = AlertContext; + constructor(props) { super(props); this.setRole = this.setRole.bind(this); @@ -243,7 +244,7 @@ class AdminSettings extends React.Component { onClick={() => { updateUser(this.state.uId, this.state.uAdmin).then(response => { if (response.data.success === false) { - this.context.error(response.data.message); + this.context.showAlert(response.data.message, "error"); } else { this.listUsers(); } diff --git a/src/client/src/pages/settings/components/editorSettingList/mapSettings.jsx b/src/client/src/pages/settings/components/editorSettingList/mapSettings.jsx index bfbba2350..cf63bbe02 100644 --- a/src/client/src/pages/settings/components/editorSettingList/mapSettings.jsx +++ b/src/client/src/pages/settings/components/editorSettingList/mapSettings.jsx @@ -11,7 +11,7 @@ import WMSCapabilities from "ol/format/WMSCapabilities"; import { theme } from "../../../../AppTheme"; const MapSettings = props => { - const alertContext = useContext(AlertContext); + const { showAlert } = useContext(AlertContext); const { setting, i18n, rmExplorerMap, addExplorerMap, handleAddItem, handleOnChange, state, setState } = props; return ( @@ -122,8 +122,9 @@ const MapSettings = props => { wms: null, wmts: null, }); - alertContext.error( + showAlert( "Sorry, only Web Map Services (WMS) and " + "Web Map Tile Service (WMTS) are supported", + "error", ); } }); diff --git a/src/client/src/pages/settings/editorSettings.jsx b/src/client/src/pages/settings/editorSettings.jsx index 548c4003d..2ccb6d42c 100644 --- a/src/client/src/pages/settings/editorSettings.jsx +++ b/src/client/src/pages/settings/editorSettings.jsx @@ -106,6 +106,7 @@ class EditorSettings extends React.Component { } return false; } + handleButtonSelected(name, isSelected) { let selectedData; if (name === "location" && isSelected) { @@ -123,6 +124,7 @@ class EditorSettings extends React.Component { } return selectedData; } + render() { const { addExplorerMap, @@ -284,7 +286,7 @@ const mapDispatchToProps = dispatch => { addExplorerMap: (layer, type, result, position = 0) => { if (type === "WMS") { if (!layer.CRS.includes("EPSG:2056")) { - this.context.error("Only EPSG:2056 is supported"); + this.context.showAlert("Only EPSG:2056 is supported", "error"); } else { dispatch( patchSettings( @@ -309,7 +311,7 @@ const mapDispatchToProps = dispatch => { layer: layer.Identifier, }); if (Object.prototype.hasOwnProperty.call(conf, "matrixSet") && !conf.matrixSet.includes("2056")) { - this.context.error("Only EPSG:2056 is supported"); + this.context.showAlert("Only EPSG:2056 is supported", "error"); } else { dispatch( patchSettings( From 259d7c88f62eeef71531deafcb4e63235399167b Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 11:02:17 +0200 Subject: [PATCH 45/56] Update badge style to match figma --- src/client/src/AppTheme.ts | 10 +++++++++- src/client/src/commons/menu/mainView/mainSideNav.tsx | 2 +- .../src/commons/search/editor/filterComponent.jsx | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/client/src/AppTheme.ts b/src/client/src/AppTheme.ts index 7ec3f81a2..9fffda9b8 100644 --- a/src/client/src/AppTheme.ts +++ b/src/client/src/AppTheme.ts @@ -43,7 +43,7 @@ export const theme = createTheme({ lightgrey: "#f1f3f5", darkgrey: "#787878", dark: "rgba(0, 0, 0, 0.5)", - menuItemActive: "#D92B04", + menuItemActive: "#A65462", filterItemActive: "#1C2834", }, @@ -135,5 +135,13 @@ export const theme = createTheme({ }, }, }, + MuiBadge: { + styleOverrides: { + badge: { + backgroundColor: "#FF0000", + color: "#FFFFFF", + }, + }, + }, }, }); diff --git a/src/client/src/commons/menu/mainView/mainSideNav.tsx b/src/client/src/commons/menu/mainView/mainSideNav.tsx index f21b501ca..225d47fd4 100644 --- a/src/client/src/commons/menu/mainView/mainSideNav.tsx +++ b/src/client/src/commons/menu/mainView/mainSideNav.tsx @@ -99,7 +99,7 @@ const MainSideNav = ({ padding: "1em", flex: "1 1 100%", }}> - {activeFilterCount > 0 && } + {activeFilterCount > 0 && } } diff --git a/src/client/src/commons/search/editor/filterComponent.jsx b/src/client/src/commons/search/editor/filterComponent.jsx index 14929512b..99af9ec6d 100644 --- a/src/client/src/commons/search/editor/filterComponent.jsx +++ b/src/client/src/commons/search/editor/filterComponent.jsx @@ -226,7 +226,7 @@ class FilterComponent extends React.Component { {t("polygon_selection")} {filterPolygon !== null && ( - + )} {this.state?.searchList?.map((filter, idx) => { @@ -248,7 +248,7 @@ class FilterComponent extends React.Component { }}> {t(filter?.translationId)}{" "} - +
{filter?.name === "workgroup" && filter?.isSelected && ( From 35a86229115ade1c4f045a8e29cd8204f37ef76c Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 13:23:11 +0200 Subject: [PATCH 46/56] Refactor to functional component --- .../cypress/e2e/editor/attachments.cy.js | 45 ++-- .../src/commons/files/fileInterfaces.ts | 20 ++ .../files/table/editorBoreholeFilesTable.jsx | 220 ------------------ .../files/table/editorBoreholeFilesTable.tsx | 166 +++++++++++++ .../commons/form/borehole/boreholeForm.jsx | 2 +- 5 files changed, 214 insertions(+), 239 deletions(-) create mode 100644 src/client/src/commons/files/fileInterfaces.ts delete mode 100644 src/client/src/commons/files/table/editorBoreholeFilesTable.jsx create mode 100644 src/client/src/commons/files/table/editorBoreholeFilesTable.tsx diff --git a/src/client/cypress/e2e/editor/attachments.cy.js b/src/client/cypress/e2e/editor/attachments.cy.js index 37ce1bb9e..3cbe166f4 100644 --- a/src/client/cypress/e2e/editor/attachments.cy.js +++ b/src/client/cypress/e2e/editor/attachments.cy.js @@ -18,19 +18,22 @@ describe("Tests for 'Attachments' edit page.", () => { // navigate to attachments tab cy.get('[data-cy="attachments-menu-item"]').click(); - // create file "LOUDSPATULA.pdf" for input - cy.get("input[type=file]").selectFile({ - contents: Cypress.Buffer.from(Math.random().toString()), - fileName: "LOUDSPATULA.txt", - mimeType: "text/plain", - }); - // intercept get all Attachments for borehole request cy.intercept("/api/v2/boreholefile/getAllForBorehole?boreholeId=**").as("getAllAttachments"); // intercept upload file request cy.intercept("/api/v2/boreholefile/upload?boreholeId=**").as("upload-files"); - // upload file + // create file "LOUDSPATULA.pdf" for input + cy.get("input[type=file]").selectFile( + { + contents: Cypress.Buffer.from(Math.random().toString()), + fileName: "LOUDSPATULA.txt", + mimeType: "text/plain", + }, + { force: true }, + ); + + // // upload file cy.get('[data-cy="attachments-upload-button"]').should("be.visible").click(); cy.wait(["@upload-files"]); cy.wait(["@getAllAttachments"]); @@ -41,11 +44,14 @@ describe("Tests for 'Attachments' edit page.", () => { // create file "IRATETRINITY.pdf" for input let fileContent = Math.random().toString(); - cy.get("input[type=file]").selectFile({ - contents: Cypress.Buffer.from(fileContent), - fileName: "IRATETRINITY.pdf", - mimeType: "application/pdf", - }); + cy.get("input[type=file]").selectFile( + { + contents: Cypress.Buffer.from(fileContent), + fileName: "IRATETRINITY.pdf", + mimeType: "application/pdf", + }, + { force: true }, + ); // upload and verify file IRATETRINITY.pdf cy.get('[data-cy="attachments-upload-button"]').should("be.visible").click(); @@ -56,11 +62,14 @@ describe("Tests for 'Attachments' edit page.", () => { cy.get("tbody").children().contains("td", "application/pdf"); // Select "IRATETRINITY.pdf" second time. - cy.get("input[type=file]").selectFile({ - contents: Cypress.Buffer.from(fileContent), - fileName: "IRATETRINITY.pdf", - mimeType: "application/pdf", - }); + cy.get("input[type=file]").selectFile( + { + contents: Cypress.Buffer.from(fileContent), + fileName: "IRATETRINITY.pdf", + mimeType: "application/pdf", + }, + { force: true }, + ); // Upload "IRATETRINITY.pdf" second time. Should not be uploaded. cy.get('[data-cy="attachments-upload-button"]').should("be.visible").click(); diff --git a/src/client/src/commons/files/fileInterfaces.ts b/src/client/src/commons/files/fileInterfaces.ts new file mode 100644 index 000000000..350e50c33 --- /dev/null +++ b/src/client/src/commons/files/fileInterfaces.ts @@ -0,0 +1,20 @@ +import { Borehole } from "../../ReduxStateInterfaces.ts"; + +export interface File { + id: number; + createdById: number; + updatedById: number; + updated: string; + name: string; + nameUuid: string; + hash: string; + type: string; + created: string; +} + +export interface FileResponse { + boreholeId: number; + borehole?: Borehole; + fileId: number; + file: File; +} diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx deleted file mode 100644 index a581abb28..000000000 --- a/src/client/src/commons/files/table/editorBoreholeFilesTable.jsx +++ /dev/null @@ -1,220 +0,0 @@ -import { Component } from "react"; -import PropTypes from "prop-types"; - -import { - detachBoreholeAttachment, - getBoreholeAttachments, - updateBoreholeAttachment, - uploadBoreholeAttachment, -} from "../../../api/fetchApiV2"; - -import FilesTableComponent from "./filesTableComponent"; -import { Box, Button, Input } from "@mui/material"; -import UploadFileIcon from "@mui/icons-material/UploadFile"; -import { AlertContext } from "../../../components/alert/alertContext"; -import { withTranslation } from "react-i18next"; -import { styled } from "@mui/system"; - -export const VisuallyHiddenInput = styled(Input)({ - clip: "rect(0 0 0 0)", - clipPath: "inset(50%)", - height: 1, - overflow: "hidden", - position: "absolute", - bottom: 0, - left: 0, - whiteSpace: "nowrap", - width: 1, -}); - -class EditorBoreholeFilesTable extends Component { - static propTypes = { - id: PropTypes.number, - unlocked: PropTypes.bool, - t: PropTypes.func, - }; - - static contextType = AlertContext; - - constructor(props, context) { - super(props, context); - this.input = null; - this.state = { - creating: false, - fetching: false, - patching: null, - upload: false, - files: [], - }; - this.loadFiles = this.loadFiles.bind(this); - this.patch = this.patch.bind(this); - this.patchQueued = false; - } - - componentDidMount() { - this.loadFiles(); - } - - patch(id, fid, currentDescription, currentIsPublic, field, value) { - this.setState( - { - files: this.state.files.map(el => { - if (el.fileId === fid) { - var val = {}; - val[field] = value; - return Object.assign({}, el, val); - } - return el; - }), - patching: fid, - }, - () => { - if (field === "public") { - // Patch immediately - updateBoreholeAttachment(id, fid, currentDescription, value).then(() => { - this.setState({ - patching: null, - }); - }); - } else { - // Apply delay - if (this.patchQueued) { - clearTimeout(this.patchQueued); - this.patchQueued = false; - } - this.patchQueued = setTimeout(() => { - updateBoreholeAttachment(id, fid, value, currentIsPublic).then(() => { - this.setState({ - patching: null, - }); - }); - }, 250); - } - }, - ); - } - - loadFiles() { - if (this.props.id) { - this.setState( - { - fetching: true, - files: [], - }, - () => { - getBoreholeAttachments(this.props.id) - .then(response => { - if (response) { - this.setState({ - fetching: false, - files: response, - }); - } - }) - .catch(function (error) { - console.log(error); - }); - }, - ); - } - } - - render() { - const { t } = this.props; - return this.props.id ? ( -
- {this.props.unlocked === true ? ( - - - - ) : null} - { - detachBoreholeAttachment(id, fid).then(() => { - this.loadFiles(); - }); - }} - editor - files={this.state.files} - id={this.props.id} - patchFile={this.patch} - reload={() => { - this.loadFiles(); - }} - unlocked={this.props.unlocked} - /> -
- ) : ( - "nothing selected" - ); - } -} - -const TranslatedEditorBoreholeFilesTable = withTranslation(["common"])(EditorBoreholeFilesTable); -export default TranslatedEditorBoreholeFilesTable; diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx new file mode 100644 index 000000000..c89ef8c77 --- /dev/null +++ b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx @@ -0,0 +1,166 @@ +import { ChangeEvent, FC, useContext, useEffect, useRef, useState } from "react"; + +import { + detachBoreholeAttachment, + getBoreholeAttachments, + updateBoreholeAttachment, + uploadBoreholeAttachment, +} from "../../../api/fetchApiV2"; + +import FilesTableComponent from "./filesTableComponent"; +import { Box, Button, Input } from "@mui/material"; +import UploadFileIcon from "@mui/icons-material/UploadFile"; +import { AlertContext } from "../../../components/alert/alertContext"; +import { FileResponse } from "../fileInterfaces.ts"; +import { useTranslation } from "react-i18next"; + +export interface EditorBoreholeFilesTable2Props { + id: number; + unlocked: boolean; +} + +const EditorBoreholeFilesTable: FC = ({ + id, + unlocked, +}: EditorBoreholeFilesTable2Props) => { + const { t } = useTranslation(); + const formRef = useRef(null); + const [files, setFiles] = useState([]); + const [patchQueued, setPatchQueued] = useState(); + const { showAlert } = useContext(AlertContext); + + useEffect(() => { + loadFiles(); + }, [id]); + + const loadFiles = async () => { + if (id) { + const response = await getBoreholeAttachments(id); + if (response) { + setFiles(response); + } + } + }; + + const uploadFile = async (e: ChangeEvent) => { + if (e.target?.files && e.target?.files.length > 0) { + const file = e.target?.files[0]; + const maxSizeInBytes = 210_000_000; // 200 MB + + if (file && file.size <= maxSizeInBytes) { + const formData = new FormData(); + formData.append("file", file); + + const uploadResponse = await uploadBoreholeAttachment(id, formData); + // console.log("uploadBoreholeAttachment response:", uploadResponse); + if (!uploadResponse.ok) { + if (uploadResponse.status === 400) { + showAlert(t("errorDuplicatedUploadPerBorehole"), "error"); + } else { + showAlert(t("errorDuringBoreholeFileUpload"), "error"); + } + } else { + loadFiles(); + } + } else { + showAlert(t("maxfileSizeExceeded") + " (200 MB)", "error"); + } + } + formRef.current?.reset(); + }; + + const patch = ( + id: number, + fid: number, + currentDescription: string, + currentIsPublic: boolean, + field: string, + value: string | boolean, + ) => { + setFiles( + files.map(file => { + if (file.fileId === fid) { + const val: { [key: string]: string | boolean } = {}; + val[field] = value; + return Object.assign({}, file, val); + } + return file; + }), + ); + if (field === "public") { + updateBoreholeAttachment(id, fid, currentDescription, value); + } else { + if (patchQueued) { + clearTimeout(patchQueued); + setPatchQueued(undefined); + } + setPatchQueued( + setTimeout(() => { + updateBoreholeAttachment(id, fid, value, currentIsPublic); + }, 250), + ); + } + }; + + return id ? ( +
+ {unlocked && ( + +
+ +
+
+ )} + { + detachBoreholeAttachment(id, fid).then(() => { + loadFiles(); + }); + }} + editor + files={files} + id={id} + patchFile={patch} + reload={loadFiles} + unlocked={unlocked} + /> +
+ ) : ( + "nothing selected" + ); +}; + +export default EditorBoreholeFilesTable; diff --git a/src/client/src/commons/form/borehole/boreholeForm.jsx b/src/client/src/commons/form/borehole/boreholeForm.jsx index e69455f78..be5c8c2cb 100644 --- a/src/client/src/commons/form/borehole/boreholeForm.jsx +++ b/src/client/src/commons/form/borehole/boreholeForm.jsx @@ -5,7 +5,7 @@ import { withTranslation } from "react-i18next"; import _ from "lodash"; import { Redirect, Route, Switch, withRouter } from "react-router-dom"; import { loadBorehole, patchBorehole, updateBorehole } from "../../../api-lib/index"; -import EditorBoreholeFilesTable from "../../files/table/editorBoreholeFilesTable"; +import EditorBoreholeFilesTable from "../../files/table/editorBoreholeFilesTable.tsx"; import TranslationText from "../translationText"; import { Dimmer, Loader } from "semantic-ui-react"; import Profile from "../profile"; From 50c58187e5faafd29b52922d974e31d8c36d1c7a Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 13:33:06 +0200 Subject: [PATCH 47/56] Fix warning --- src/client/src/commons/files/table/editorBoreholeFilesTable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx index c89ef8c77..e408cd996 100644 --- a/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx +++ b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx @@ -31,6 +31,7 @@ const EditorBoreholeFilesTable: FC = ({ useEffect(() => { loadFiles(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); const loadFiles = async () => { From 40a508a397cbf0b02ec4116e3d5225a7ecf8161a Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 25 Jul 2024 15:16:53 +0200 Subject: [PATCH 48/56] Center badge number --- src/client/src/commons/search/editor/filterComponent.jsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/client/src/commons/search/editor/filterComponent.jsx b/src/client/src/commons/search/editor/filterComponent.jsx index 99af9ec6d..a16e32c38 100644 --- a/src/client/src/commons/search/editor/filterComponent.jsx +++ b/src/client/src/commons/search/editor/filterComponent.jsx @@ -24,6 +24,7 @@ import { FilterContext } from "../../../components/filter/filterContext.tsx"; class FilterComponent extends React.Component { static contextType = FilterContext; + constructor(props) { super(props); this.handleFilterReset = this.handleFilterReset.bind(this); @@ -226,7 +227,7 @@ class FilterComponent extends React.Component { {t("polygon_selection")} {filterPolygon !== null && ( - + )} {this.state?.searchList?.map((filter, idx) => { @@ -246,10 +247,8 @@ class FilterComponent extends React.Component { ), })); }}> - - {t(filter?.translationId)}{" "} - - + {t(filter?.translationId)} + {filter?.name === "workgroup" && filter?.isSelected && ( From 9d1b227f407d59d67ed63fe65ed2884d3348f9cf Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 08:43:29 +0200 Subject: [PATCH 49/56] Update label --- src/client/src/commons/search/data/lithologySearchData.js | 2 +- src/client/src/pages/settings/data/lithologyFieldEditorData.js | 2 +- src/client/src/pages/settings/data/lithologyFilterEditorData.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/src/commons/search/data/lithologySearchData.js b/src/client/src/commons/search/data/lithologySearchData.js index 8982f4ae9..f733dbe10 100644 --- a/src/client/src/commons/search/data/lithologySearchData.js +++ b/src/client/src/commons/search/data/lithologySearchData.js @@ -48,7 +48,7 @@ export const lithologySearchData = [ { id: 4, type: "Dropdown", - label: "description_quality", + label: "completeness", value: "description_quality", schema: "description_quality", multiple: false, diff --git a/src/client/src/pages/settings/data/lithologyFieldEditorData.js b/src/client/src/pages/settings/data/lithologyFieldEditorData.js index 8425aed31..4b954bd29 100644 --- a/src/client/src/pages/settings/data/lithologyFieldEditorData.js +++ b/src/client/src/pages/settings/data/lithologyFieldEditorData.js @@ -6,7 +6,7 @@ export const lithologyFieldEditorData = [ }, { id: 1, - label: "description_quality", + label: "completeness", value: "description_quality", }, { diff --git a/src/client/src/pages/settings/data/lithologyFilterEditorData.js b/src/client/src/pages/settings/data/lithologyFilterEditorData.js index 1c6898f35..fe1c89262 100644 --- a/src/client/src/pages/settings/data/lithologyFilterEditorData.js +++ b/src/client/src/pages/settings/data/lithologyFilterEditorData.js @@ -11,7 +11,7 @@ export const lithologyFilterEditorData = [ }, { id: 2, - label: "description_quality", + label: "completeness", value: "layer.description_quality", }, { From 7049927d7ed61c18248125da03cabb1020648758 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 08:44:54 +0200 Subject: [PATCH 50/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7326a15c..7706f180e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. +- Label for description quality was wrong in lithology filter and settings. ## v2.1.772 - 2024-06-27 From de0ff254c0eec5b684fdffbc864c2eb9427f88a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roswita=20Tsch=C3=BCmperlin?= <108136714+tschumpr@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:30:48 +0200 Subject: [PATCH 51/56] Update CHANGELOG.md Co-authored-by: Daniel Jovanovic --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f27a300c..7830cca4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. -- Location precision filter caused internal error. +- Location precision filter caused an internal error. ## v2.1.772 - 2024-06-27 From 2ae78befdd6019ed60ef25e0cb9d2be933222996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roswita=20Tsch=C3=BCmperlin?= <108136714+tschumpr@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:38:50 +0200 Subject: [PATCH 52/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73190c13d..e32294362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Made `startTime` and `reliability` optional for hydrogeology. ### Fixed + - Attribute `top_bedrock_weathered` could not be imported. - Badge with number of active filters on sidebar did not include polygon filter. From 408ccf6925331a1e33f3cfb0d8397e65202c3afd Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 13:55:53 +0200 Subject: [PATCH 53/56] Simplify code --- src/client/src/commons/menu/languagePopup.tsx | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/client/src/commons/menu/languagePopup.tsx b/src/client/src/commons/menu/languagePopup.tsx index 8baeac9b7..22e4513a2 100644 --- a/src/client/src/commons/menu/languagePopup.tsx +++ b/src/client/src/commons/menu/languagePopup.tsx @@ -23,21 +23,11 @@ export function LanguagePopup() { useEffect(() => { const handleLanguageChange = () => { - switch (i18n.language) { - case "de": - setSelectedLanguage(languages[0]); - break; - case "fr": - setSelectedLanguage(languages[1]); - break; - case "it": - setSelectedLanguage(languages[2]); - break; - case "en": - setSelectedLanguage(languages[3]); - break; - default: - setSelectedLanguage(languages[0]); + const languageIndex = languages.indexOf(i18n.language); + if (languageIndex !== -1) { + setSelectedLanguage(languages[languageIndex]); + } else { + setSelectedLanguage(languages[0]); } }; From aec702225fef1a2662fa97235fdc20fcc5281356 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 13:57:12 +0200 Subject: [PATCH 54/56] Place change in unreleased section --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de607b3c6..125125ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [Unreleased] ### Added + +- Added tooltips to main side navigation. - Location hash for tabs in borehole detail view. ### Changed @@ -24,7 +26,6 @@ - Added borehole geometry panel. - Added secondary header to borehole detail view. - Added new codelist entries for `casing_type` and `backfill_material`. -- Added tooltips to main side navigation. ### Changed From e7fd85d67a09d9af04e45e99941ffdd999302e27 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 14:43:49 +0200 Subject: [PATCH 55/56] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aeafe3ec..a4689164a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Removed title from prompt dialog. - Use standard prompt dialog for deleting boreholes. - Updated standard alert. +- Updated styling of attachment upload button. ### Fixed From 5b45c8cfeaa9ff1f6bfa6155818c9e5614ca9bb7 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 29 Jul 2024 14:44:12 +0200 Subject: [PATCH 56/56] Remove unnecessary comment --- src/client/src/commons/files/table/editorBoreholeFilesTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx index e408cd996..d299081ac 100644 --- a/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx +++ b/src/client/src/commons/files/table/editorBoreholeFilesTable.tsx @@ -53,7 +53,6 @@ const EditorBoreholeFilesTable: FC = ({ formData.append("file", file); const uploadResponse = await uploadBoreholeAttachment(id, formData); - // console.log("uploadBoreholeAttachment response:", uploadResponse); if (!uploadResponse.ok) { if (uploadResponse.status === 400) { showAlert(t("errorDuplicatedUploadPerBorehole"), "error");