diff --git a/src/client/cypress/e2e/detailPage/boreholeform.cy.js b/src/client/cypress/e2e/detailPage/boreholeform.cy.js index 875e5d316..d3a532648 100644 --- a/src/client/cypress/e2e/detailPage/boreholeform.cy.js +++ b/src/client/cypress/e2e/detailPage/boreholeform.cy.js @@ -7,22 +7,7 @@ describe("Test for the borehole form.", () => { // create boreholes newEditableBorehole().as("borehole_id"); - // fill all legacy dropdowns on location tab - cy.get('[data-cy="domain-dropdown"]') - .should("have.length", 1) - .each(el => cy.wrap(el).click().find('[role="option"]').last().click()); - - const locationDropdownValues = []; - cy.get('[data-cy="domain-dropdown"]') - .each(el => { - const value = el[0].children[1].firstChild.data; - locationDropdownValues.push(value); - }) - .then(() => { - expect(locationDropdownValues).to.deep.eq(["ID Kernlager"]); - }); - - // fills and evaluates all mui dropdowns on location tab + // fills and evaluates all mui dropdowns on location tab (identifiers are tested separately) setSelect("restriction", 2); isDisabled("restriction_until", true); setSelect("restriction", 3); diff --git a/src/client/cypress/e2e/detailPage/location.cy.js b/src/client/cypress/e2e/detailPage/location.cy.js index 7ce901c1c..1c60879a4 100644 --- a/src/client/cypress/e2e/detailPage/location.cy.js +++ b/src/client/cypress/e2e/detailPage/location.cy.js @@ -1,4 +1,5 @@ import { checkRowWithText, showTableAndWaitForData } from "../helpers/dataGridHelpers"; +import { setInput, setSelect } from "../helpers/formHelpers"; import { createBorehole, goToRouteAndAcceptTerms, @@ -69,30 +70,28 @@ describe("Tests for 'Location' edit page.", () => { }); }); - it("removes error highlight of identifier fields if at least one identifier is present.", () => { + it("adds and removes identifiers.", () => { newEditableBorehole().as("borehole_id"); // initial state - cy.get('[data-cy="identifier-dropdown"]').should("have.class", "error"); - cy.get('[data-cy="identifier-value"]').should("have.class", "error"); + cy.get('[data-cy="identifier-add"]').should("be.disabled"); // add identifier - cy.get('[data-cy="identifier-dropdown"]').click(); - cy.get('[data-cy="identifier-dropdown"]').find("div[role='option']").contains("ID Canton").click(); - cy.get('[data-cy="identifier-dropdown"]').should("not.have.class", "error"); - cy.get('[data-cy="identifier-value"]').should("have.class", "error"); + setSelect("borehole_identifier", 5); + cy.get('[data-cy="identifier-add"]').should("be.disabled"); - cy.get('[data-cy="identifier-value"]').type("ECKLERTA"); - cy.get('[data-cy="identifier-dropdown"]').should("not.have.class", "error"); - cy.get('[data-cy="identifier-value"]').should("not.have.class", "error"); + setInput("borehole_identifier_value", "ECKLERTA"); + cy.get('[data-cy="identifier-add"]').should("not.be.disabled"); cy.get('[data-cy="identifier-add"]').click(); - cy.get('[data-cy="identifier-dropdown"]').should("not.have.class", "error"); - cy.get('[data-cy="identifier-value"]').should("not.have.class", "error"); + cy.contains("ID Canton").should("exist"); + cy.contains("ECKLERTA").should("exist"); + + cy.get('[data-cy="identifier-add"]').should("be.disabled"); // delete identifier - cy.get('[data-cy="identifier"]').contains("Delete").click(); - cy.get('[data-cy="identifier-dropdown"]').should("have.class", "error"); - cy.get('[data-cy="identifier-value"]').should("have.class", "error"); + cy.get('[data-cy="identifier-delete"]').click(); + cy.contains("ID Canton").should("not.exist"); + cy.get('[data-cy="identifier-add"]').should("be.disabled"); }); }); diff --git a/src/client/cypress/e2e/filters/identifierFilter.cy.js b/src/client/cypress/e2e/filters/identifierFilter.cy.js index b4cfb76be..f210fda7e 100644 --- a/src/client/cypress/e2e/filters/identifierFilter.cy.js +++ b/src/client/cypress/e2e/filters/identifierFilter.cy.js @@ -1,16 +1,13 @@ import { checkAllVisibleRows, verifyPaginationText } from "../helpers/dataGridHelpers"; +import { setInput, setSelect } from "../helpers/formHelpers"; import { newEditableBorehole, returnToOverview, stopBoreholeEditing } from "../helpers/testHelpers.js"; describe("Tests for filtering data by identifier.", () => { it("can filter by identifier", () => { newEditableBorehole().as("borehole_id"); - let identifierDropdown = cy.get('[data-cy="identifier-dropdown"]'); - identifierDropdown.each(el => - cy.wrap(el).click({ force: true }).find('[role="option"]').eq(1).click({ force: true }), - ); - - cy.get('[data-cy="identifier-value"] input').type(819544732); + setSelect("borehole_identifier", 1); + setInput("borehole_identifier_value", 819544732); cy.get('[data-cy="identifier-add"]').click(); stopBoreholeEditing(); @@ -38,26 +35,19 @@ describe("Tests for filtering data by identifier.", () => { it("can bulk edit boreholes while filter by identifier is set", () => { newEditableBorehole().as("borehole_id"); - let identifierDropdown = cy.get('[data-cy="identifier-dropdown"]'); - - identifierDropdown.each(el => - cy.wrap(el).click({ force: true }).find('[role="option"]').eq(1).click({ force: true }), - ); + setSelect("borehole_identifier", 1); - cy.get('[data-cy="identifier-value"] input').type(64531274); + setInput("borehole_identifier_value", 64531274); cy.get('[data-cy="identifier-add"]').click(); stopBoreholeEditing(); returnToOverview(); newEditableBorehole().as("borehole_id_2"); - identifierDropdown = cy.get('[data-cy="identifier-dropdown"]'); - identifierDropdown.each(el => - cy.wrap(el).click({ force: true }).find('[role="option"]').eq(1).click({ force: true }), - ); + setSelect("borehole_identifier", 1); - cy.get('[data-cy="identifier-value"] input').type(436584127); + setInput("borehole_identifier_value", 436584127); cy.get('[data-cy="identifier-add"]').click(); stopBoreholeEditing(); diff --git a/src/client/cypress/e2e/mainPage/bulkedit.cy.js b/src/client/cypress/e2e/mainPage/bulkedit.cy.js index 922835660..56e453d8c 100644 --- a/src/client/cypress/e2e/mainPage/bulkedit.cy.js +++ b/src/client/cypress/e2e/mainPage/bulkedit.cy.js @@ -44,7 +44,7 @@ function giveAdminUser2workgroups() { } describe("Test the borehole bulk edit feature.", () => { - it.skip("opens the bulk edit dialog with all boreholes selected", () => { + it("opens the bulk edit dialog with all boreholes selected", () => { giveAdminUser1workgroup(); showTableAndWaitForData(); checkAllVisibleRows(); @@ -52,7 +52,7 @@ describe("Test the borehole bulk edit feature.", () => { cy.get("h1").should("have.text", "Bulk editing"); }); - it.skip("displays workgroup accordion only if user has permission for more than one workgroup", () => { + it("displays workgroup accordion only if user has permission for more than one workgroup", () => { giveAdminUser1workgroup(); checkAllVisibleRows(); cy.contains("button", "Bulk editing").click({ force: true }); @@ -77,7 +77,6 @@ describe("Test the borehole bulk edit feature.", () => { it("fills all bulkedit fields and saves.", () => { createBoreholes(); - giveAdminUser1workgroup(); goToRouteAndAcceptTerms(`/`); showTableAndWaitForData(); cy.wait("@borehole"); @@ -110,12 +109,10 @@ describe("Test the borehole bulk edit feature.", () => { cy.wrap($input).scrollIntoView().clear().type(`${index}`); }); - cy.get('[role="combobox"]') - .should("have.length", 14) - .each(el => { - cy.wrap(el).click(); - cy.get('li[role="option"]').last().click(); - }); + cy.get('[role="combobox"]').each(el => { + cy.wrap(el).click(); + cy.get('li[role="option"]').last().click(); + }); // save cy.contains("button", "Save").click(); diff --git a/src/client/src/api-lib/ReduxStateInterfaces.ts b/src/client/src/api-lib/ReduxStateInterfaces.ts index 483eb4a9c..88de39889 100644 --- a/src/client/src/api-lib/ReduxStateInterfaces.ts +++ b/src/client/src/api-lib/ReduxStateInterfaces.ts @@ -57,7 +57,13 @@ interface Workflow { workflow: number; } -interface BoreholeAttributes { +export interface Identifier { + id: number; + identifier: string; + value: string; +} + +export interface BoreholeAttributes { national_interest: boolean; restriction_until: Date; restriction: number; @@ -85,6 +91,7 @@ interface BoreholeAttributes { precision_location_x_lv03: number; precision_location_y_lv03: number; custom: { + identifiers: Identifier[]; country: string; canton: string; municipality: string; diff --git a/src/client/src/components/form/simpleDomainSelect.tsx b/src/client/src/components/form/simpleDomainSelect.tsx index a63ef76c2..1f6667c12 100644 --- a/src/client/src/components/form/simpleDomainSelect.tsx +++ b/src/client/src/components/form/simpleDomainSelect.tsx @@ -20,7 +20,7 @@ interface SimpleDomainSelectProps { selected?: number | boolean | null; sx?: SxProps; className?: string; - onUpdate?: (value: number | boolean | string | null) => void; + onUpdate?: (value: number | null) => void; } export const SimpleDomainSelect: FC = ({ @@ -63,7 +63,7 @@ export const SimpleDomainSelect: FC = ({ name={fieldName} onChange={e => { if (onUpdate) { - onUpdate(e.target.value); + onUpdate(parseInt(e.target.value)); } }} value={selected} diff --git a/src/client/src/pages/detail/detailPageContent.jsx b/src/client/src/pages/detail/detailPageContent.jsx index aa4c43b4d..a9803e59b 100644 --- a/src/client/src/pages/detail/detailPageContent.jsx +++ b/src/client/src/pages/detail/detailPageContent.jsx @@ -16,7 +16,7 @@ import FieldMeasurement from "./form/hydrogeology/fieldMeasurement.jsx"; import GroundwaterLevelMeasurement from "./form/hydrogeology/groundwaterLevelMeasurement.jsx"; import Hydrotest from "./form/hydrogeology/hydrotest.jsx"; import WaterIngress from "./form/hydrogeology/waterIngress.jsx"; -import IdentifierSegment from "./form/location/indentifierSegment.jsx"; +import IdentifierSegment from "./form/location/identifierSegment.tsx"; import LocationSegment from "./form/location/locationSegment.tsx"; import NameSegment from "./form/location/nameSegment.tsx"; import RestrictionSegment from "./form/location/restrictionSegment.tsx"; @@ -55,7 +55,6 @@ class DetailPageContent extends React.Component { this.updateNumber = this.updateNumber.bind(this); this.updateChange = this.updateChange.bind(this); this.patch = this.patch.bind(this); - this.setStateBound = this.setState.bind(this); } componentDidMount() { @@ -226,7 +225,7 @@ class DetailPageContent extends React.Component { } render() { - const { t, borehole, user, editingEnabled } = this.props; + const { t, borehole, editingEnabled } = this.props; if (borehole.error !== null) { return
{t(borehole.error, borehole.data)}
; } @@ -280,11 +279,8 @@ class DetailPageContent extends React.Component { + editingEnabled={editingEnabled}> { borehole: state.core_borehole, workflow: state.core_workflow, domains: state.core_domain_list, - user: state.core_user, }; }; diff --git a/src/client/src/pages/detail/form/location/identifierSegment.tsx b/src/client/src/pages/detail/form/location/identifierSegment.tsx new file mode 100644 index 000000000..6e41d7b6e --- /dev/null +++ b/src/client/src/pages/detail/form/location/identifierSegment.tsx @@ -0,0 +1,145 @@ +import { useContext, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Card, Grid, IconButton, Typography } from "@mui/material"; +import { Save, Trash2 } from "lucide-react"; +import _ from "lodash"; +import { addIdentifier, removeIdentifier } from "../../../../api-lib"; +import { Borehole, BoreholeAttributes, Identifier } from "../../../../api-lib/ReduxStateInterfaces.ts"; +import { AlertContext } from "../../../../components/alert/alertContext.tsx"; +import { FormValueType } from "../../../../components/form/form.ts"; +import { SimpleDomainSelect } from "../../../../components/form/simpleDomainSelect.tsx"; +import { SimpleFormInput } from "../../../../components/form/simpleFormInput.tsx"; +import DomainText from "../../../../components/legacyComponents/domain/domainText.jsx"; +import { FormSegmentBox } from "../../../../components/styledComponents"; + +interface IdentifierSegmentProps { + borehole: Borehole; + editingEnabled: boolean; + updateBorehole: (borehole: BoreholeAttributes) => void; +} +const IdentifierSegment = ({ borehole, editingEnabled, updateBorehole }: IdentifierSegmentProps) => { + const [identifierId, setIdentifierId] = useState(null); + const [identifierValue, setIdentifierValue] = useState(""); + const { t } = useTranslation(); + const { showAlert } = useContext(AlertContext); + + const removeEntry = (identifier: Identifier) => { + //@ts-expect-error // legacy fetch functions not typed + removeIdentifier(borehole.data.id, identifier.id).then(response => { + if (response.data.success) { + const tmp = _.cloneDeep(borehole.data); + if (tmp.custom.identifiers.length === 1) { + tmp.custom.identifiers = []; + } else { + tmp.custom.identifiers = tmp.custom.identifiers.filter(el => el.id !== identifier.id); + } + updateBorehole(tmp); + } + }); + }; + + const addEntry = () => { + // Check duplicate + const alreadySet = borehole.data.custom.identifiers ? borehole.data.custom.identifiers.map(el => el.id) : []; + + if (identifierId && alreadySet.includes(identifierId)) { + showAlert(t("msgIdentifierAlreadyUsed"), "error"); + } else { + //@ts-expect-error // legacy fetch functions not typed + addIdentifier(borehole.data.id, identifierId, identifierValue).then(response => { + if (response.data.success) { + setIdentifierId(null); + setIdentifierValue(""); + const tmp = _.cloneDeep(borehole.data); + if (tmp.custom.identifiers === null) { + tmp.custom.identifiers = []; + } + tmp.custom.identifiers.push(response.data.data); + updateBorehole(tmp); + } + }); + } + }; + + return ( + + + + + {t("borehole_identifier")} + + + {t("borehole_identifier_value")} + + + + {t("borehole_technical_id")} + + + {borehole.data.id} + + + {borehole.data.custom.identifiers?.map(identifier => ( + <> + + + + + {identifier.value} + + + {editingEnabled && ( + { + removeEntry(identifier); + }} + data-cy="identifier-delete"> + {} + + )} + + + ))} + + {editingEnabled && ( + + + { + selected && setIdentifierId(selected); + }} + /> + + + { + setIdentifierValue(text); + }} + /> + + + + {} + + + + )} + + + ); +}; + +export default IdentifierSegment; diff --git a/src/client/src/pages/detail/form/location/indentifierSegment.jsx b/src/client/src/pages/detail/form/location/indentifierSegment.jsx deleted file mode 100644 index 0b3f782a9..000000000 --- a/src/client/src/pages/detail/form/location/indentifierSegment.jsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useContext } from "react"; -import { useTranslation } from "react-i18next"; -import { Card } from "@mui/material"; -import { Form, Icon, Input } from "semantic-ui-react"; -import _ from "lodash"; -import { addIdentifier, removeIdentifier } from "../../../../api-lib"; -import { AlertContext } from "../../../../components/alert/alertContext.tsx"; -import DomainText from "../../../../components/legacyComponents/domain/domainText.jsx"; -import DomainDropdown from "../../../../components/legacyComponents/domain/dropdown/domainDropdown.jsx"; -import { FormSegmentBox } from "../../../../components/styledComponents"; - -const IdentifierSegment = props => { - const { borehole, identifier, identifierValue, updateBorehole, setState, user } = props; - const { t } = useTranslation(); - const { showAlert } = useContext(AlertContext); - - const isEditable = - borehole?.data.role === "EDIT" && borehole?.data.lock !== null && borehole?.data.lock?.id === user?.data.id; - - return ( - - -
-
{t("borehole_identifier")}
-
{t("borehole_identifier_value")}
-
{borehole.data.lock !== null ? t("delete") : null}
-
-
-
{t("borehole_technical_id")}
-
{borehole.data.id}
-
- {borehole.data.lock !== null ? ( -
- {t("delete")} -
- ) : null} -
-
- {borehole.data.custom.identifiers - ? borehole.data.custom.identifiers.map((identifier, idx) => ( -
-
- -
-
{identifier.value}
-
- {borehole.data.lock !== null ? ( -
{ - removeIdentifier(borehole.data.id, identifier.id).then(response => { - if (response.data.success) { - const tmp = _.cloneDeep(borehole.data); - if (tmp.custom.identifiers.length === 1) { - tmp.custom.identifiers = []; - } else { - tmp.custom.identifiers = tmp.custom.identifiers.filter(el => el.id !== identifier.id); - } - updateBorehole(tmp); - } - }); - }}> - {t("delete")} -
- ) : null} -
-
- )) - : null} - {borehole.data.lock !== null ? ( -
- - - - { - setState({ - identifier: selected.id, - }); - }} - schema="borehole_identifier" - selected={identifier} - readOnly={!isEditable} - /> - - - - { - setState({ - identifierValue: e.target.value, - }); - }} - spellCheck="false" - value={identifierValue ?? ""} - readOnly={!isEditable} - /> - -
- { - // Check duplicate - const alreadySet = borehole.data.custom.identifiers - ? borehole.data.custom.identifiers.map(el => el.id) - : []; - - if (alreadySet.includes(identifier)) { - showAlert(t("msgIdentifierAlreadyUsed"), "error"); - } else { - addIdentifier(borehole.data.id, identifier, identifierValue).then(response => { - if (response.data.success) { - setState( - { - identifier: null, - identifierValue: "", - }, - () => { - const tmp = _.cloneDeep(borehole.data); - if (tmp.custom.identifiers === null) { - tmp.custom.identifiers = []; - } - tmp.custom.identifiers.push(response.data.data); - updateBorehole(tmp); - }, - ); - } - }); - } - }} - secondary - size="tiny"> - - -
-
-
- ) : null} -
-
- ); -}; - -export default IdentifierSegment; diff --git a/src/client/src/pages/detail/form/location/segmentInterface.ts b/src/client/src/pages/detail/form/location/segmentInterface.ts index 52a856778..3405ec1f9 100644 --- a/src/client/src/pages/detail/form/location/segmentInterface.ts +++ b/src/client/src/pages/detail/form/location/segmentInterface.ts @@ -2,10 +2,10 @@ import { Borehole } from "../../../../api-lib/ReduxStateInterfaces.ts"; export interface SegmentProps { borehole: Borehole; + editingEnabled: boolean; updateChange: ( fieldName: keyof Borehole["data"] | "location", value: string | number | boolean | null | (number | string | null)[], to?: boolean, ) => void; - editingEnabled: boolean; }