diff --git a/packages/webapp/.storybook/state.js b/packages/webapp/.storybook/state.js index d3f40594d0..0a76d9e745 100755 --- a/packages/webapp/.storybook/state.js +++ b/packages/webapp/.storybook/state.js @@ -1383,96 +1383,6 @@ export default { }, }, }, - barnReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - ceremonialReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - farmSiteBoundaryReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - fieldReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - gardenReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - greenhouseReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - surfaceWaterReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - naturalAreaReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - residenceReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - bufferZoneReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - watercourseReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - fenceReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - gateReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - waterValveReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, - soilSampleLocationReducer: { - ids: [], - entities: {}, - loading: false, - loaded: false, - }, showedSpotlightReducer: { loaded: true, loading: false, @@ -1708,6 +1618,13 @@ export default { loading: false, loaded: false, }, + irrigationTaskReducer: { + ids: [], + entities: {}, + loading: false, + error: null, + loaded: false, + }, irrigationTaskTypesReducer: { irrigationTaskTypes: [ { @@ -1746,6 +1663,20 @@ export default { ids: [], entities: {}, }, + animalMovementTaskReducer: { + ids: [], + entities: {}, + loading: false, + error: null, + loaded: false, + }, + soilSampleTaskReducer: { + ids: [], + entities: {}, + loading: false, + error: null, + loaded: false, + }, }, persistedStateReducer: { userLogReducer: { diff --git a/packages/webapp/public/locales/en/message.json b/packages/webapp/public/locales/en/message.json index a774f971e1..07ce09cdb0 100644 --- a/packages/webapp/public/locales/en/message.json +++ b/packages/webapp/public/locales/en/message.json @@ -156,6 +156,7 @@ "MAP": { "FAIL_PATCH": "Failed to update", "FAIL_POST": "Failed to add new", + "FAIL_DELETE": "Failed to retire", "SUCCESS_DELETE": "Successfully retired", "SUCCESS_PATCH": "Successfully updated", "SUCCESS_POST": "Successfully saved" diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/AreaDetails.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails.jsx similarity index 66% rename from packages/webapp/src/components/LocationDetailLayout/AreaDetails/AreaDetails.jsx rename to packages/webapp/src/components/LocationDetailLayout/AreaDetails.jsx index f56b6ff33e..07acace652 100644 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/AreaDetails.jsx +++ b/packages/webapp/src/components/LocationDetailLayout/AreaDetails.jsx @@ -1,18 +1,14 @@ -import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useFormContext } from 'react-hook-form'; -import Input from '../../Form/Input'; -import PureWarningBox from '../../WarningBox'; -import { Label } from '../../Typography'; -import Unit from '../../Form/Unit'; -import { fieldEnum as areaEnum } from '../../../containers/constants'; -import { area_perimeter, area_total_area } from '../../../util/convert-units/unit'; -import InputAutoSize from '../../Form/InputAutoSize'; +import Input from '../Form/Input'; +import Unit from '../Form/Unit'; +import { fieldEnum as areaEnum } from '../../containers/constants'; +import { area_perimeter, area_total_area } from '../../util/convert-units/unit'; +import InputAutoSize from '../Form/InputAutoSize'; export default function AreaDetails({ name, showPerimeter, - history, children, system, isCreateLocationPage, @@ -30,31 +26,9 @@ export default function AreaDetails({ control, formState: { errors }, } = useFormContext(); - const [errorMessage, setErrorMessage] = useState(); - useEffect(() => { - const handleOffline = () => setErrorMessage(t('FARM_MAP.AREA_DETAILS.NETWORK')); - const handleOnline = () => setErrorMessage(null); - window.addEventListener('offline', handleOffline); - window.addEventListener('online', handleOnline); - return () => { - window.removeEventListener('offline', handleOffline); - window.removeEventListener('online', handleOnline); - }; - }, []); - - useEffect(() => { - if (history?.location?.state?.error && !history?.location?.state?.error?.retire) { - setErrorMessage(history?.location?.state?.error); - } - }, [history?.location?.state?.error]); return ( <> - {errorMessage && ( - - - - )} - - - ); -} - -export function PureBarn({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const washPackSelection = data[barnEnum.wash_and_pack]; - const coldStorage = data[barnEnum.cold_storage]; - const usedForAnimals = data[barnEnum.used_for_animals]; - data[barnEnum.total_area_unit] = data[barnEnum.total_area_unit]?.value; - data[barnEnum.perimeter_unit] = data[barnEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - type: 'barn', - wash_and_pack: washPackSelection, - cold_storage: coldStorage, - used_for_animals: usedForAnimals, - }); - submitForm({ formData }); - }; - - return ( - } - showPerimeter={false} - /> - ); -} - -export function BarnDetailChildren({ isViewLocationPage }) { - const { t } = useTranslation(); - const { control } = useFormContext(); - return ( - <> -
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
- - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/CeremonialArea/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/CeremonialArea/index.jsx deleted file mode 100644 index 416ecac23c..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/CeremonialArea/index.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ceremonialEnum } from '../../../../containers/constants'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureCeremonialAreaWrapper(props) { - return ( - - - - ); -} - -export function PureCeremonialArea({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - data[ceremonialEnum.total_area_unit] = data[ceremonialEnum.total_area_unit]?.value; - data[ceremonialEnum.perimeter_unit] = data[ceremonialEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - type: 'ceremonial_area', - }); - submitForm({ formData }); - }; - - return ( - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/FarmSiteBoundary/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/FarmSiteBoundary/index.jsx deleted file mode 100644 index d4df9d7579..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/FarmSiteBoundary/index.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import { farmSiteBoundaryEnum } from '../../../../containers/constants'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureFarmSiteBoundaryWrapper(props) { - return ( - - - - ); -} - -export function PureFarmSiteBoundary({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - data[farmSiteBoundaryEnum.total_area_unit] = data[farmSiteBoundaryEnum.total_area_unit]?.value; - data[farmSiteBoundaryEnum.perimeter_unit] = data[farmSiteBoundaryEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'farm_site_boundary', - }); - submitForm({ formData }); - }; - return ( - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Field/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Field/index.jsx deleted file mode 100644 index d561a9ceb4..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Field/index.jsx +++ /dev/null @@ -1,132 +0,0 @@ -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import Leaf from '../../../../assets/images/farmMapFilter/Leaf.svg'; -import Input from '../../../Form/Input'; -import { fieldEnum } from '../../../../containers/constants'; -import { Label } from '../../../Typography'; -import { getDateInputFormat } from '../../../../util/moment'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import RadioGroup from '../../../Form/RadioGroup'; -import { - getFormDataWithoutNulls, - getProcessedFormData, -} from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureFieldWrapper(props) { - return ( - - - - ); -} - -export function PureField({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - getProcessedFormData(); - const getDefaultValues = () => { - return { - [fieldEnum.organic_status]: 'Non-Organic', - ...persistedFormData, - [fieldEnum.transition_date]: getDateInputFormat( - persistedFormData[fieldEnum.transition_date] || new Date(), - ), - }; - }; - const onSubmit = (data) => { - data[fieldEnum.total_area_unit] = data[fieldEnum.total_area_unit]?.value; - data[fieldEnum.perimeter_unit] = data[fieldEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'field', - }); - submitForm({ formData }); - }; - - return ( - } - showPerimeter={true} - /> - ); -} - -export function FieldDetailsChildren({ isViewLocationPage }) { - const { t } = useTranslation(); - const { control, watch, register } = useFormContext(); - const fieldTypeSelection = watch(fieldEnum.organic_status); - const [transitionalDate, setTransitionalDate] = useState(watch(fieldEnum.transition_date)); - return ( -
-
- - -
- - - -
- {fieldTypeSelection === 'Transitional' && ( - setTransitionalDate(e.target.value)} - value={transitionalDate} - /> - )} -
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Garden/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Garden/index.jsx deleted file mode 100644 index 906ab9d7b2..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Garden/index.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import Leaf from '../../../../assets/images/farmMapFilter/Leaf.svg'; -import Input, { getInputErrors } from '../../../Form/Input'; -import { gardenEnum } from '../../../../containers/constants'; -import { Label } from '../../../Typography'; -import { getDateInputFormat } from '../../../../util/moment'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import RadioGroup from '../../../Form/RadioGroup'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureGardenWrapper(props) { - return ( - - - - ); -} - -export function PureGarden({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const getDefaultValues = () => { - return { - [gardenEnum.organic_status]: 'Non-Organic', - ...persistedFormData, - [gardenEnum.transition_date]: getDateInputFormat( - persistedFormData[gardenEnum.transition_date] || new Date(), - ), - }; - }; - const onSubmit = (data) => { - data[gardenEnum.total_area_unit] = data[gardenEnum.total_area_unit]?.value; - data[gardenEnum.perimeter_unit] = data[gardenEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'garden', - }); - submitForm({ formData }); - }; - - return ( - } - showPerimeter={true} - /> - ); -} - -export function GardenDetailsChildren({ isViewLocationPage }) { - const { t } = useTranslation(); - const { - control, - watch, - register, - formState: { errors }, - } = useFormContext(); - const gardenTypeSelection = watch(gardenEnum.organic_status); - const [transitionalDate, setTransitionalDate] = useState(watch(gardenEnum.transition_date)); - return ( -
-
- - -
- -
- {gardenTypeSelection === 'Transitional' && ( - setTransitionalDate(e.target.value)} - value={transitionalDate} - /> - )} -
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Greenhouse/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Greenhouse/index.jsx deleted file mode 100644 index af36b0029c..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Greenhouse/index.jsx +++ /dev/null @@ -1,198 +0,0 @@ -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import Leaf from '../../../../assets/images/farmMapFilter/Leaf.svg'; -import Input from '../../../Form/Input'; -import { greenhouseEnum } from '../../../../containers/constants'; -import { Label } from '../../../Typography'; -import { getDateInputFormat } from '../../../../util/moment'; -import RadioGroup from '../../../Form/RadioGroup'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; -import InputBaseLabel from '../../../Form/InputBase/InputBaseLabel'; - -export default function PureGreenhouseWrapper(props) { - return ( - - - - ); -} - -export function PureGreenhouse({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const getDefaultValues = () => { - return { - [greenhouseEnum.organic_status]: 'Non-Organic', - ...persistedFormData, - [greenhouseEnum.transition_date]: getDateInputFormat( - persistedFormData[greenhouseEnum.transition_date] || new Date(), - ), - }; - }; - const onSubmit = (data) => { - const supplementalLighting = data[greenhouseEnum.supplemental_lighting]; - const co2Enrichment = data[greenhouseEnum.co2_enrichment]; - const greenhouseHeated = data[greenhouseEnum.greenhouse_heated]; - data[greenhouseEnum.total_area_unit] = data[greenhouseEnum.total_area_unit]?.value; - data[greenhouseEnum.perimeter_unit] = data[greenhouseEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'greenhouse', - supplemental_lighting: supplementalLighting, - co2_enrichment: co2Enrichment, - greenhouse_heated: greenhouseHeated, - }); - submitForm({ formData }); - }; - - return ( - } - showPerimeter={false} - /> - ); -} - -export function GreenhouseDetailsChildren({ isViewLocationPage }) { - const { t } = useTranslation(); - const { register, watch, control } = useFormContext(); - const greenhouseTypeSelection = watch(greenhouseEnum.organic_status); - const [transitionalDate, setTransitionalDate] = useState(watch(greenhouseEnum.transition_date)); - return ( -
- - - -
- {greenhouseTypeSelection === 'Transitional' && ( - setTransitionalDate(e.target.value)} - value={transitionalDate} - /> - )} -
-
- {greenhouseTypeSelection === 'Organic' && ( -
- -
- -
- - -
- -
- -
- -
-
- )} -
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/NaturalArea/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/NaturalArea/index.jsx deleted file mode 100644 index 865f7c75e2..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/NaturalArea/index.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { naturalAreaEnum } from '../../../../containers/constants'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureNaturalAreaWrapper(props) { - return ( - - - - ); -} - -export function PureNaturalArea({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const { t } = useTranslation(); - const onSubmit = (data) => { - data[naturalAreaEnum.total_area_unit] = data[naturalAreaEnum.total_area_unit]?.value; - data[naturalAreaEnum.perimeter_unit] = data[naturalAreaEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'natural_area', - }); - submitForm({ formData }); - }; - - return ( - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Residence/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Residence/index.jsx deleted file mode 100644 index d883eab2fb..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/Residence/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import { residenceEnum } from '../../../../containers/constants'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureResidenceWrapper(props) { - return ( - - - - ); -} - -export function PureResidence({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - data[residenceEnum.total_area_unit] = data[residenceEnum.total_area_unit]?.value; - - data[residenceEnum.perimeter_unit] = data[residenceEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - - type: 'residence', - }); - submitForm({ formData }); - }; - - return ( - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/SurfaceWater/index.jsx b/packages/webapp/src/components/LocationDetailLayout/AreaDetails/SurfaceWater/index.jsx deleted file mode 100644 index 38cd8c1714..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/AreaDetails/SurfaceWater/index.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import locationDetailStyles from '../../styles.module.scss'; -import { surfaceWaterEnum } from '../../../../containers/constants'; -import { Label } from '../../../Typography'; -import RadioGroup from '../../../Form/RadioGroup'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureSurfaceWaterWrapper(props) { - return ( - - - - ); -} - -export function PureSurfaceWater({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const usedForIrrigation = data[surfaceWaterEnum.used_for_irrigation]; - data[surfaceWaterEnum.total_area_unit] = data[surfaceWaterEnum.total_area_unit]?.value; - data[surfaceWaterEnum.perimeter_unit] = data[surfaceWaterEnum.perimeter_unit]?.value; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - type: 'surface_water', - used_for_irrigation: usedForIrrigation, - }); - submitForm({ formData }); - }; - - return ( - } - showPerimeter={true} - /> - ); -} - -export function SurfaceWaterDetailsChildren({ isViewLocationPage }) { - const { t } = useTranslation(); - const { control } = useFormContext(); - return ( -
-
- - -
-
- -
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/ExtraFormFieldsMap.jsx b/packages/webapp/src/components/LocationDetailLayout/ExtraFormFieldsMap.jsx new file mode 100644 index 0000000000..752fae3793 --- /dev/null +++ b/packages/webapp/src/components/LocationDetailLayout/ExtraFormFieldsMap.jsx @@ -0,0 +1,678 @@ +/* + * Copyright 2026 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +import { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { Label } from '../Typography'; +import RadioGroup from '../Form/RadioGroup'; +import InputBaseLabel from '../Form/InputBase/InputBaseLabel'; +import Input, { getInputErrors } from '../Form/Input'; +import Unit from '../Form/Unit'; +import { + barnEnum, + bufferZoneEnum, + fenceEnum, + fieldEnum, + gardenEnum, + greenhouseEnum, + surfaceWaterEnum, + watercourseEnum, + waterValveEnum, +} from '../../containers/constants'; +import styles from './styles.module.scss'; +import Leaf from '../../assets/images/farmMapFilter/Leaf.svg'; +import { + area_total_area, + line_length, + line_width, + water_valve_flow_rate, + watercourse_width, +} from '../../util/convert-units/unit'; +import { buffer } from 'd3'; + +// Areas Children + +function BarnDetailsChildren({ isViewLocationPage }) { + const { t } = useTranslation(); + const { control } = useFormContext(); + return ( + <> +
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+ + ); +} + +function FieldDetailsChildren({ isViewLocationPage }) { + const { t } = useTranslation(); + const { control, watch, register } = useFormContext(); + const fieldTypeSelection = watch(fieldEnum.organic_status); + const [transitionalDate, setTransitionalDate] = useState(watch(fieldEnum.transition_date)); + return ( +
+
+ + +
+ + + +
+ {fieldTypeSelection === 'Transitional' && ( + setTransitionalDate(e.target.value)} + value={transitionalDate} + /> + )} +
+
+ ); +} + +function GardenDetailsChildren({ isViewLocationPage }) { + const { t } = useTranslation(); + const { + control, + watch, + register, + formState: { errors }, + } = useFormContext(); + const gardenTypeSelection = watch(gardenEnum.organic_status); + const [transitionalDate, setTransitionalDate] = useState(watch(gardenEnum.transition_date)); + return ( +
+
+ + +
+ +
+ {gardenTypeSelection === 'Transitional' && ( + setTransitionalDate(e.target.value)} + value={transitionalDate} + /> + )} +
+
+ ); +} + +function GreenhouseDetailsChildren({ isViewLocationPage }) { + const { t } = useTranslation(); + const { register, watch, control } = useFormContext(); + const greenhouseTypeSelection = watch(greenhouseEnum.organic_status); + const [transitionalDate, setTransitionalDate] = useState(watch(greenhouseEnum.transition_date)); + return ( +
+ + + +
+ {greenhouseTypeSelection === 'Transitional' && ( + setTransitionalDate(e.target.value)} + value={transitionalDate} + /> + )} +
+
+ {greenhouseTypeSelection === 'Organic' && ( +
+ +
+ +
+ + +
+ +
+ +
+ +
+
+ )} +
+
+ ); +} + +function SurfaceWaterDetailsChildren({ isViewLocationPage }) { + const { t } = useTranslation(); + const { control } = useFormContext(); + return ( +
+
+ + +
+
+ +
+
+ ); +} + +// Lines Children +function BufferZoneDetailsChildren({ system, isViewLocationPage, isEditLocationPage }) { + const { t } = useTranslation(); + const { + register, + setValue, + getValues, + watch, + control, + formState: { errors }, + } = useFormContext(); + return ( +
+
+ +
+
+ +
+
+ ); +} + +function FenceDetailsChildren({ system, isViewLocationPage }) { + const { t } = useTranslation(); + const { + register, + setValue, + getValues, + watch, + control, + formState: { errors }, + } = useFormContext(); + return ( +
+
+ +
+
+ +
+ +
+
+
+ ); +} + +function WatercourseDetailsChildren({ system, isViewLocationPage, isEditLocationPage }) { + const { t } = useTranslation(); + const { + register, + setValue, + getValues, + watch, + control, + formState: { errors }, + } = useFormContext(); + return ( +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ +
+
+
+ ); +} + +// Points Children +function WaterValveDetailsChildren({ system, isViewLocationPage }) { + const { t } = useTranslation(); + const { + control, + register, + setValue, + getValues, + watch, + formState: { errors }, + } = useFormContext(); + return ( +
+
+ + +
+ + + +
+ ); +} + +function SoilSampleLocationDetailsChildren({ persistedFormData }) { + const { t } = useTranslation(); + + return ( +
+ + +
+ ); +} + +const ExtraLocationFormFieldsMap = { + //areas + barn: BarnDetailsChildren, + ceremonial_area: null, + farm_site_boundary: null, + field: FieldDetailsChildren, + garden: GardenDetailsChildren, + greenhouse: GreenhouseDetailsChildren, + natural_area: null, + residence: null, + surface_water: SurfaceWaterDetailsChildren, + // lines + buffer_zone: BufferZoneDetailsChildren, + fence: FenceDetailsChildren, + watercourse: WatercourseDetailsChildren, + // points + gate: null, + soil_sample_location: SoilSampleLocationDetailsChildren, + water_valve: WaterValveDetailsChildren, +}; + +export default ExtraLocationFormFieldsMap; diff --git a/packages/webapp/src/components/LocationDetailLayout/LineDetails.jsx b/packages/webapp/src/components/LocationDetailLayout/LineDetails.jsx new file mode 100644 index 0000000000..905dbb6cea --- /dev/null +++ b/packages/webapp/src/components/LocationDetailLayout/LineDetails.jsx @@ -0,0 +1,45 @@ +import { useTranslation } from 'react-i18next'; +import Input from '../Form/Input'; +import { fenceEnum as lineEnum } from '../../containers/constants'; +import InputAutoSize from '../Form/InputAutoSize'; +import { useFormContext } from 'react-hook-form'; + +export default function LineDetails({ + name, + children, + isCreateLocationPage, + isViewLocationPage, + isEditLocationPage, +}) { + const { t } = useTranslation(); + const { + register, + formState: { errors }, + } = useFormContext(); + + return ( + <> + + {children} + + + ); +} diff --git a/packages/webapp/src/components/LocationDetailLayout/LineDetails/BufferZone/index.jsx b/packages/webapp/src/components/LocationDetailLayout/LineDetails/BufferZone/index.jsx deleted file mode 100644 index 497ca0c317..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/LineDetails/BufferZone/index.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import { bufferZoneEnum } from '../../../../containers/constants'; -import Unit from '../../../Form/Unit'; -import { area_total_area, line_width } from '../../../../util/convert-units/unit'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureBufferZoneWrapper(props) { - return ( - - - - ); -} - -export function PureBufferZone({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - type: 'buffer_zone', - }); - formData[bufferZoneEnum.width_unit] = formData[bufferZoneEnum.width_unit]?.value; - formData[bufferZoneEnum.length_unit] = formData[bufferZoneEnum.length_unit]?.value; - formData[bufferZoneEnum.total_area_unit] = formData[bufferZoneEnum.total_area_unit]?.value; - submitForm({ formData }); - }; - - return ( - - } - /> - ); -} - -export function BufferZoneDetailsChildren({ isViewLocationPage, isEditLocationPage, system }) { - const { t } = useTranslation(); - const { - register, - setValue, - getValues, - watch, - control, - formState: { errors }, - } = useFormContext(); - return ( -
-
- -
-
- -
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/LineDetails/Fence/index.jsx b/packages/webapp/src/components/LocationDetailLayout/LineDetails/Fence/index.jsx deleted file mode 100644 index d2db03ae63..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/LineDetails/Fence/index.jsx +++ /dev/null @@ -1,125 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import Leaf from '../../../../assets/images/farmMapFilter/Leaf.svg'; -import { bufferZoneEnum, fenceEnum } from '../../../../containers/constants'; -import { Label } from '../../../Typography'; -import { line_length } from '../../../../util/convert-units/unit'; -import Unit from '../../../Form/Unit'; -import RadioGroup from '../../../Form/RadioGroup'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; -import InputBaseLabel from '../../../Form/InputBase/InputBaseLabel'; - -export default function PureFenceWrapper(props) { - return ( - - - - ); -} - -export function PureFence({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const isPressureTreated = data[fenceEnum.pressure_treated]; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - pressure_treated: isPressureTreated, - type: 'fence', - }); - formData[fenceEnum.width] = 0; - formData[fenceEnum.width_unit] = formData[fenceEnum.width_unit]?.value; - formData[fenceEnum.length_unit] = formData[fenceEnum.length_unit]?.value; - delete formData[bufferZoneEnum.total_area_unit]; - submitForm({ formData }); - }; - - return ( - - } - /> - ); -} - -export function FenceDetailsChildren({ system, isViewLocationPage }) { - const { t } = useTranslation(); - const { - register, - setValue, - getValues, - watch, - control, - formState: { errors }, - } = useFormContext(); - return ( -
-
- -
-
- -
- -
-
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/LineDetails/LineDetails.jsx b/packages/webapp/src/components/LocationDetailLayout/LineDetails/LineDetails.jsx deleted file mode 100644 index d69c9105c8..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/LineDetails/LineDetails.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import Input from '../../Form/Input'; -import { fenceEnum as lineEnum } from '../../../containers/constants'; -import PureWarningBox from '../../WarningBox'; -import { Label } from '../../Typography'; -import InputAutoSize from '../../Form/InputAutoSize'; -import { useFormContext } from 'react-hook-form'; - -export default function LineDetails({ - name, - history, - children, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, -}) { - const { t } = useTranslation(); - const { - register, - setValue, - formState: { errors }, - } = useFormContext(); - const [errorMessage, setErrorMessage] = useState(); - - useEffect(() => { - const handleOffline = () => setErrorMessage(t('FARM_MAP.AREA_DETAILS.NETWORK')); - const handleOnline = () => setErrorMessage(null); - window.addEventListener('offline', handleOffline); - window.addEventListener('online', handleOnline); - return () => { - window.removeEventListener('offline', handleOffline); - window.removeEventListener('online', handleOnline); - }; - }, []); - - useEffect(() => { - if (history?.location?.state?.error && !history?.location?.state?.error?.retire) { - setErrorMessage(history?.location?.state?.error); - } - }, [history?.location?.state?.error]); - - return ( - <> - {errorMessage && !isViewLocationPage && ( - - - - )} - - {children} - - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/LineDetails/Watercourse/index.jsx b/packages/webapp/src/components/LocationDetailLayout/LineDetails/Watercourse/index.jsx deleted file mode 100644 index 3dade95045..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/LineDetails/Watercourse/index.jsx +++ /dev/null @@ -1,191 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import { Label } from '../../../Typography'; -import locationDetailStyles from '../../styles.module.scss'; -import { - area_total_area, - line_length, - line_width, - watercourse_width, -} from '../../../../util/convert-units/unit'; -import Unit from '../../../Form/Unit'; -import { watercourseEnum } from '../../../../containers/constants'; -import RadioGroup from '../../../Form/RadioGroup'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureWatercourseWrapper(props) { - return ( - - - - ); -} - -export function PureWatercourse({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const usedForIrrigation = data[watercourseEnum.used_for_irrigation]; - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - type: 'watercourse', - used_for_irrigation: usedForIrrigation, - }); - formData[watercourseEnum.length_unit] = formData[watercourseEnum.length_unit]?.value; - formData[watercourseEnum.width_unit] = formData[watercourseEnum.width_unit]?.value; - formData[watercourseEnum.buffer_width_unit] = - formData[watercourseEnum.buffer_width_unit]?.value; - formData[watercourseEnum.total_area_unit] = formData[watercourseEnum.total_area_unit]?.value; - submitForm({ formData }); - }; - - return ( - - } - /> - ); -} - -export function WatercourseDetailsChildren({ system, isViewLocationPage, isEditLocationPage }) { - const { t } = useTranslation(); - const { - register, - setValue, - getValues, - watch, - control, - formState: { errors }, - } = useFormContext(); - return ( -
-
- -
-
- -
-
- -
-
- -
-
-
- - -
-
- -
-
-
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/LocationPageHeader.jsx b/packages/webapp/src/components/LocationDetailLayout/LocationPageHeader.jsx index 53295ed855..f0b580282f 100644 --- a/packages/webapp/src/components/LocationDetailLayout/LocationPageHeader.jsx +++ b/packages/webapp/src/components/LocationDetailLayout/LocationPageHeader.jsx @@ -38,7 +38,7 @@ export default function LocationPageHeader({ return ( diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails.jsx b/packages/webapp/src/components/LocationDetailLayout/PointDetails.jsx new file mode 100644 index 0000000000..2dcb49fb7c --- /dev/null +++ b/packages/webapp/src/components/LocationDetailLayout/PointDetails.jsx @@ -0,0 +1,45 @@ +import { useTranslation } from 'react-i18next'; +import Input from '../Form/Input'; +import { gateEnum as pointEnum } from '../../containers/constants'; +import InputAutoSize from '../Form/InputAutoSize'; +import { useFormContext } from 'react-hook-form'; + +export default function PointDetails({ + name, + children, + isCreateLocationPage, + isViewLocationPage, + isEditLocationPage, +}) { + const { t } = useTranslation(); + const { + register, + formState: { errors }, + } = useFormContext(); + + return ( + <> + + + {children} + + + ); +} diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails/Gate/index.jsx b/packages/webapp/src/components/LocationDetailLayout/PointDetails/Gate/index.jsx deleted file mode 100644 index 604d908b92..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/PointDetails/Gate/index.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureGateWrapper(props) { - return ( - - - - ); -} - -export function PureGate({ - history, - match, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - submitForm, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - }); - submitForm({ formData }); - }; - return ( - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails/PointDetails.jsx b/packages/webapp/src/components/LocationDetailLayout/PointDetails/PointDetails.jsx deleted file mode 100644 index 1d8d4dd51b..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/PointDetails/PointDetails.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import Input from '../../Form/Input'; -import { gateEnum as pointEnum } from '../../../containers/constants'; -import PureWarningBox from '../../WarningBox'; -import { Label } from '../../Typography'; -import InputAutoSize from '../../Form/InputAutoSize'; -import { useFormContext } from 'react-hook-form'; - -export default function PointDetails({ - name, - children, - history, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, -}) { - const { t } = useTranslation(); - const { - register, - setValue, - formState: { errors }, - } = useFormContext(); - const [errorMessage, setErrorMessage] = useState(); - - useEffect(() => { - const handleOffline = () => setErrorMessage(t('FARM_MAP.AREA_DETAILS.NETWORK')); - const handleOnline = () => setErrorMessage(null); - window.addEventListener('offline', handleOffline); - window.addEventListener('online', handleOnline); - return () => { - window.removeEventListener('offline', handleOffline); - window.removeEventListener('online', handleOnline); - }; - }, []); - - useEffect(() => { - if (history?.location?.state?.error) { - setErrorMessage(history?.location?.state?.error); - } - }, [history?.location?.state?.error]); - - return ( - <> - {errorMessage && !isViewLocationPage && ( - - - - )} - - - {children} - - - ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/index.jsx b/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/index.jsx deleted file mode 100644 index 14aa508e3f..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/index.jsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -import { useTranslation } from 'react-i18next'; -import styles from './styles.module.scss'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import Input from '../../../Form/Input'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureSoilSampleLocationWrapper(props) { - return ( - - - - ); -} - -export function PureSoilSampleLocation({ - history, - match, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - submitForm, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - }); - submitForm({ formData }); - }; - - return ( - } - /> - ); -} - -export function SoilSampleLocationDetailsChildren({ persistedFormData }) { - const { t } = useTranslation(); - - return ( -
- - -
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/styles.module.scss b/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/styles.module.scss deleted file mode 100644 index 440fbb113e..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/PointDetails/SoilSampleLocation/styles.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -.latLngContainer { - display: flex; - flex-wrap: wrap; - gap: 16px; - margin-bottom: 40px; - - > div { - flex-grow: 1; - min-width: 240px; - } -} diff --git a/packages/webapp/src/components/LocationDetailLayout/PointDetails/WaterValve/index.jsx b/packages/webapp/src/components/LocationDetailLayout/PointDetails/WaterValve/index.jsx deleted file mode 100644 index 058b7870a2..0000000000 --- a/packages/webapp/src/components/LocationDetailLayout/PointDetails/WaterValve/index.jsx +++ /dev/null @@ -1,123 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useFormContext } from 'react-hook-form'; -import locationDetailStyles from '../../styles.module.scss'; -import Unit from '../../../Form/Unit'; -import { waterValveEnum } from '../../../../containers/constants'; -import { water_valve_flow_rate } from '../../../../util/convert-units/unit'; -import { Label } from '../../../Typography'; -import { PersistedFormWrapper } from '../../PersistedFormWrapper'; -import RadioGroup from '../../../Form/RadioGroup'; -import { getFormDataWithoutNulls } from '../../../../containers/hooks/useHookFormPersist/utils'; -import { PureLocationDetailLayout } from '../../PureLocationDetailLayout'; - -export default function PureWaterValveWrapper(props) { - return ( - - - - ); -} - -export function PureWaterValve({ - history, - match, - submitForm, - system, - isCreateLocationPage, - isViewLocationPage, - isEditLocationPage, - persistedFormData, - useHookFormPersist, - handleRetire, - isAdmin, -}) { - const onSubmit = (data) => { - const formData = getFormDataWithoutNulls({ - ...persistedFormData, - ...data, - }); - formData[waterValveEnum.flow_rate_unit] = formData[waterValveEnum.flow_rate_unit]?.value; - submitForm({ formData }); - }; - return ( - - } - /> - ); -} - -export function WaterValveDetailsChildren({ isViewLocationPage, system }) { - const { t } = useTranslation(); - const { - control, - register, - setValue, - getValues, - watch, - formState: { errors }, - } = useFormContext(); - return ( -
-
- - -
- - - -
- ); -} diff --git a/packages/webapp/src/components/LocationDetailLayout/PureLocationDetailLayout.jsx b/packages/webapp/src/components/LocationDetailLayout/PureLocationDetailLayout.jsx index 4693f6b3d2..effea356a5 100644 --- a/packages/webapp/src/components/LocationDetailLayout/PureLocationDetailLayout.jsx +++ b/packages/webapp/src/components/LocationDetailLayout/PureLocationDetailLayout.jsx @@ -4,9 +4,9 @@ import { useForm, FormProvider } from 'react-hook-form'; import LocationButtons from './LocationButtons'; import LocationPageHeader from './LocationPageHeader'; import Form from '../Form'; -import AreaDetails from './AreaDetails/AreaDetails'; -import LineDetails from './LineDetails/LineDetails'; -import PointDetails from './PointDetails/PointDetails'; +import AreaDetails from './AreaDetails'; +import LineDetails from './LineDetails'; +import PointDetails from './PointDetails'; import RouterTab from '../RouterTab'; import useLocationRouterTabs from '../../containers/LocationDetails/useLocationRouterTabs'; import { Variant } from '../RouterTab/Tab'; @@ -60,7 +60,6 @@ export function PureLocationDetailLayout({ return ( . + */ + +import { PersistedFormWrapper } from './PersistedFormWrapper'; +import { getFormDataWithoutNulls } from '../../containers/hooks/useHookFormPersist/utils'; +import { PureLocationDetailLayout } from './PureLocationDetailLayout'; +import ExtraLocationFormFieldsMap from './ExtraFormFieldsMap'; +import { getFigureType } from '../../containers/LocationDetails/utils'; +import { getDateInputFormat } from '../../util/moment'; + +const getOrganicStatusDefaultValues = (persistedFormData) => { + return { + organic_status: 'Non-Organic', + ...persistedFormData, + transition_date: getDateInputFormat(persistedFormData['transition_date'] || new Date()), + }; +}; + +const getAreaConfig = (persistedFormData) => ({ + barn: { + showPerimeter: false, + defaultValues: persistedFormData, + }, + ceremonial_area: { + showPerimeter: true, + defaultValues: persistedFormData, + }, + farm_site_boundary: { + showPerimeter: true, + defaultValues: persistedFormData, + }, + field: { + showPerimeter: true, + defaultValues: getOrganicStatusDefaultValues(persistedFormData), + }, + garden: { + showPerimeter: true, + defaultValues: getOrganicStatusDefaultValues(persistedFormData), + }, + greenhouse: { + showPerimeter: false, + defaultValues: getOrganicStatusDefaultValues(persistedFormData), + }, + natural_area: { + showPerimeter: true, + defaultValues: persistedFormData, + }, + residence: { + showPerimeter: false, + defaultValues: persistedFormData, + }, + surface_water: { + showPerimeter: true, + defaultValues: persistedFormData, + }, +}); + +export default function PureLocationFormWrapper({ + history, + match, + submitForm, + system, + isCreateLocationPage = false, + isViewLocationPage = false, + isEditLocationPage = false, + persistedFormData, + useHookFormPersist = () => {}, + handleRetire = () => {}, + isAdmin = false, + locationType, +}) { + const onSubmit = (data) => { + // area units lift value up to top level + if (getFigureType(locationType) === 'area') { + data[`total_area_unit`] = data[`total_area_unit`]?.value; + data[`perimeter_unit`] = data[`perimeter_unit`]?.value; + } + if (getFigureType(locationType) === 'line') { + data[`length_unit`] = data[`length_unit`]?.value; + data[`width_unit`] = data[`width_unit`]?.value; + data['total_area_unit'] = data['total_area_unit']?.value; + if (locationType === 'watercourse') { + data['buffer_width_unit'] = data['buffer_width_unit']?.value; + } + if (locationType === 'fence') { + data['width'] = 0; + delete data['total_area_unit']; + } + } + if (getFigureType(locationType) === 'point') { + if (locationType === 'water_valve') { + data['flow_rate_unit'] = data['flow_rate_unit']?.value; + } + } + + const formData = getFormDataWithoutNulls({ + ...persistedFormData, + ...data, + type: locationType, + }); + + submitForm({ formData }); + }; + + const DetailsChildren = ExtraLocationFormFieldsMap[locationType] || undefined; + const areaConfig = getAreaConfig(persistedFormData)[locationType] || {}; + const figureType = getFigureType(locationType); + + return ( + + + ) : undefined + } + showPerimeter={figureType === 'area' ? areaConfig.showPerimeter : undefined} + /> + + ); +} diff --git a/packages/webapp/src/components/LocationDetailLayout/styles.module.scss b/packages/webapp/src/components/LocationDetailLayout/styles.module.scss index 9a8b9a46ec..3ddcb227c9 100644 --- a/packages/webapp/src/components/LocationDetailLayout/styles.module.scss +++ b/packages/webapp/src/components/LocationDetailLayout/styles.module.scss @@ -24,3 +24,15 @@ font-size: 16px; } } + +.latLngContainer { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin-bottom: 40px; + + > div { + flex-grow: 1; + min-width: 240px; + } +} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/EditBarn.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/EditBarn.jsx deleted file mode 100644 index 9221daa77e..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/EditBarn.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureBarn from '../../../../components/LocationDetailLayout/AreaDetails/Barn'; -import { deleteBarnLocation, editBarnLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { barnSelector } from '../../../barnSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditBarnDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editBarnLocation({ ...data, ...match.params, figure_id: barn.figure_id })); - }; - const barn = useSelector(barnSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteBarnLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditBarnDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/PostBarn.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/PostBarn.jsx deleted file mode 100644 index 7a28d17671..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/PostBarn.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureBarn from '../../../../components/LocationDetailLayout/AreaDetails/Barn'; -import { postBarnLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostBarnDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postBarnLocation(data)); - }; - - return ( - - ); -} - -export default PostBarnDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/saga.js deleted file mode 100644 index 6c00f0abcc..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/BarnDetailForm/saga.js +++ /dev/null @@ -1,131 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteBarnSuccess, - editBarnSuccess, - getLocationObjectFromBarn, - postBarnSuccess, -} from '../../../barnSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postBarnLocation = createAction(`postBarnLocationSaga`); - -export function* postBarnLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromBarn(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postBarnSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BARN'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.BARN') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editBarnLocation = createAction(`editBarnLocationSaga`); - -export function* editBarnLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromBarn({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editBarnSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BARN'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.BARN') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteBarnLocation = createAction(`deleteBarnLocationSaga`); - -export function* deleteBarnLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteBarnSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BARN'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* barnLocationSaga() { - yield takeLeading(postBarnLocation.type, postBarnLocationSaga); - yield takeLeading(editBarnLocation.type, editBarnLocationSaga); - yield takeLeading(deleteBarnLocation.type, deleteBarnLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/EditCeremonialArea.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/EditCeremonialArea.jsx deleted file mode 100644 index 6d91bb3b11..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/EditCeremonialArea.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureCeremonial from '../../../../components/LocationDetailLayout/AreaDetails/CeremonialArea'; -import { deleteCeremonialLocation, editCeremonialLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { ceremonialSelector } from '../../../ceremonialSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditCeremonialDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editCeremonialLocation({ - ...data, - ...match.params, - figure_id: ceremonial.figure_id, - }), - ); - }; - const ceremonial = useSelector(ceremonialSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteCeremonialLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditCeremonialDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/PostCeremonialArea.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/PostCeremonialArea.jsx deleted file mode 100644 index 5ea8f3d012..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/PostCeremonialArea.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureCeremonial from '../../../../components/LocationDetailLayout/AreaDetails/CeremonialArea'; -import { postCeremonialLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostCeremonialDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postCeremonialLocation(data)); - }; - - return ( - - ); -} - -export default PostCeremonialDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/saga.js deleted file mode 100644 index 8812e21f8b..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/saga.js +++ /dev/null @@ -1,131 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteCeremonialSuccess, - editCeremonialSuccess, - getLocationObjectFromCeremonial, - postCeremonialSuccess, -} from '../../../ceremonialSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postCeremonialLocation = createAction(`postCeremonialLocationSaga`); - -export function* postCeremonialLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromCeremonial(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postCeremonialSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.CA'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.CA') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editCeremonialLocation = createAction(`editCeremonialLocationSaga`); - -export function* editCeremonialLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromCeremonial({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editCeremonialSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.CA'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.CA') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteCeremonialLocation = createAction(`deleteCeremonialLocationSaga`); - -export function* deleteCeremonialLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteCeremonialSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.CA'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* ceremonialLocationSaga() { - yield takeLeading(postCeremonialLocation.type, postCeremonialLocationSaga); - yield takeLeading(editCeremonialLocation.type, editCeremonialLocationSaga); - yield takeLeading(deleteCeremonialLocation.type, deleteCeremonialLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/EditFarmSiteBoundary.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/EditFarmSiteBoundary.jsx deleted file mode 100644 index d82dd29296..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/EditFarmSiteBoundary.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureFarmSiteBoundary from '../../../../components/LocationDetailLayout/AreaDetails/FarmSiteBoundary'; -import { deleteFarmSiteBoundaryLocation, editFarmSiteBoundaryLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { farmSiteBoundarySelector } from '../../../farmSiteBoundarySlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditFarmSiteBoundaryDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editFarmSiteBoundaryLocation({ - ...data, - ...match.params, - figure_id: farmSiteBoundary.figure_id, - }), - ); - }; - const farmSiteBoundary = useSelector(farmSiteBoundarySelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteFarmSiteBoundaryLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditFarmSiteBoundaryDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/PostFarmSiteBoundary.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/PostFarmSiteBoundary.jsx deleted file mode 100644 index cac2cf0496..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/PostFarmSiteBoundary.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureFarmSiteBoundary from '../../../../components/LocationDetailLayout/AreaDetails/FarmSiteBoundary'; -import { postFarmSiteBoundaryLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostFarmSiteBoundaryDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postFarmSiteBoundaryLocation(data)); - }; - - return ( - - ); -} - -export default PostFarmSiteBoundaryDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/saga.js deleted file mode 100644 index 27981c4e64..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/saga.js +++ /dev/null @@ -1,135 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteFarmSiteBoundarySuccess, - editFarmSiteBoundarySuccess, - getLocationObjectFromFarmSiteBoundary, - postFarmSiteBoundarySuccess, -} from '../../../farmSiteBoundarySlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; - -export const postFarmSiteBoundaryLocation = createAction(`postFarmSiteBoundaryLocationSaga`); - -export function* postFarmSiteBoundaryLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromFarmSiteBoundary(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postFarmSiteBoundarySuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FSB'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.FSB') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editFarmSiteBoundaryLocation = createAction(`editFarmSiteBoundaryLocationSaga`); - -export function* editFarmSiteBoundaryLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromFarmSiteBoundary({ - ...formData, - location_id, - figure_id, - }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editFarmSiteBoundarySuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FSB'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.FSB') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteFarmSiteBoundaryLocation = createAction(`deleteFarmSiteBoundaryLocationSaga`); - -export function* deleteFarmSiteBoundaryLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteFarmSiteBoundarySuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FSB'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* farmSiteBoundaryLocationSaga() { - yield takeLeading(postFarmSiteBoundaryLocation.type, postFarmSiteBoundaryLocationSaga); - yield takeLeading(editFarmSiteBoundaryLocation.type, editFarmSiteBoundaryLocationSaga); - yield takeLeading(deleteFarmSiteBoundaryLocation.type, deleteFarmSiteBoundaryLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/EditField.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/EditField.jsx deleted file mode 100644 index f46816f616..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/EditField.jsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation, useHistory, useRouteMatch } from 'react-router-dom'; -import PureField from '../../../../components/LocationDetailLayout/AreaDetails/Field'; -import { deleteFieldLocation, editFieldLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { fieldSelector } from '../../../fieldSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditFieldDetailForm() { - const history = useHistory(); - const location = useLocation(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editFieldLocation({ ...data, ...match.params, figure_id: field.figure_id })); - }; - const field = useSelector(fieldSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteFieldLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditFieldDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/PostField.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/PostField.jsx deleted file mode 100644 index 1193f1719b..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/PostField.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureField from '../../../../components/LocationDetailLayout/AreaDetails/Field'; -import { postFieldLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostFieldDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postFieldLocation(data)); - }; - - return ( - - ); -} - -export default PostFieldDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/saga.js deleted file mode 100644 index 09ae593e52..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/FieldDetailForm/saga.js +++ /dev/null @@ -1,134 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteFieldSuccess, - editFieldSuccess, - getLocationObjectFromField, - postFieldSuccess, -} from '../../../fieldSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postFieldLocation = createAction(`postFieldLocationSaga`); - -export function* postFieldLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromField(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postFieldSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FIELD'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.FIELD') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editFieldLocation = createAction(`editFieldLocationSaga`); - -export function* editFieldLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromField({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editFieldSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FIELD'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.FIELD') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteFieldLocation = createAction(`deleteFieldLocationSaga`); - -export function* deleteFieldLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteFieldSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.FIELD'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* fieldLocationSaga() { - yield takeLeading(postFieldLocation.type, postFieldLocationSaga); - yield takeLeading(editFieldLocation.type, editFieldLocationSaga); - yield takeLeading(deleteFieldLocation.type, deleteFieldLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/EditGarden.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/EditGarden.jsx deleted file mode 100644 index 792979437c..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/EditGarden.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureGarden from '../../../../components/LocationDetailLayout/AreaDetails/Garden'; -import { deleteGardenLocation, editGardenLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { gardenSelector } from '../../../gardenSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditGardenDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editGardenLocation({ ...data, ...match.params, figure_id: garden.figure_id })); - }; - const garden = useSelector(gardenSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteGardenLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditGardenDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/PostGarden.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/PostGarden.jsx deleted file mode 100644 index d864975213..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/PostGarden.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureGarden from '../../../../components/LocationDetailLayout/AreaDetails/Garden'; -import { postGardenLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostGardenDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postGardenLocation(data)); - }; - - return ( - - ); -} - -export default PostGardenDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/saga.js deleted file mode 100644 index 7b135d4546..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GardenDetailForm/saga.js +++ /dev/null @@ -1,136 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteGardenSuccess, - editGardenSuccess, - getLocationObjectFromGarden, - postGardenSuccess, -} from '../../../gardenSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import history from '../../../../history'; -import i18n from '../../../../locales/i18n'; - -export const postGardenLocation = createAction(`postGardenLocationSaga`); - -export function* postGardenLocationSaga({ payload: data }) { - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - data.formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGarden(data.formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postGardenSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.GARDEN'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.GARDEN') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editGardenLocation = createAction(`editGardenLocationSaga`); - -export function* editGardenLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGarden({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editGardenSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.GARDEN'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.GARDEN') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteGardenLocation = createAction(`deleteGardenLocationSaga`); - -export function* deleteGardenLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteGardenSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.GARDEN'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* gardenLocationSaga() { - yield takeLeading(postGardenLocation.type, postGardenLocationSaga); - yield takeLeading(editGardenLocation.type, editGardenLocationSaga); - yield takeLeading(deleteGardenLocation.type, deleteGardenLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/EditGreenhouse.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/EditGreenhouse.jsx deleted file mode 100644 index 74b601d9ff..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/EditGreenhouse.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureGreenhouse from '../../../../components/LocationDetailLayout/AreaDetails/Greenhouse'; -import { deleteGreenhouseLocation, editGreenhouseLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { greenhouseSelector } from '../../../greenhouseSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditGreenhouseDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editGreenhouseLocation({ - ...data, - ...match.params, - figure_id: greenhouse.figure_id, - }), - ); - }; - const greenhouse = useSelector(greenhouseSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteGreenhouseLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditGreenhouseDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/PostGreenhouse.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/PostGreenhouse.jsx deleted file mode 100644 index bcb5a76e2e..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/PostGreenhouse.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureGreenhouse from '../../../../components/LocationDetailLayout/AreaDetails/Greenhouse'; -import { postGreenhouseLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostGreenhouseDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postGreenhouseLocation(data)); - }; - - return ( - - ); -} - -export default PostGreenhouseDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/saga.js deleted file mode 100644 index 894090b0c4..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/GreenhouseDetailForm/saga.js +++ /dev/null @@ -1,140 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteGreenhouseSuccess, - editGreenhouseSuccess, - getLocationObjectFromGreenHouse, - postGreenhouseSuccess, -} from '../../../greenhouseSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postGreenhouseLocation = createAction(`postGreenhouseLocationSaga`); - -export function* postGreenhouseLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGreenHouse(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postGreenhouseSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.GREENHOUSE'), - i18n.t('message:MAP.SUCCESS_POST'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.GREENHOUSE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editGreenhouseLocation = createAction(`editGreenhouseLocationSaga`); - -export function* editGreenhouseLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGreenHouse({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editGreenhouseSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.GREENHOUSE'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.GREENHOUSE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteGreenhouseLocation = createAction(`deleteGreenhouseLocationSaga`); - -export function* deleteGreenhouseLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteGreenhouseSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.GREENHOUSE'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* greenhouseLocationSaga() { - yield takeLeading(postGreenhouseLocation.type, postGreenhouseLocationSaga); - yield takeLeading(editGreenhouseLocation.type, editGreenhouseLocationSaga); - yield takeLeading(deleteGreenhouseLocation.type, deleteGreenhouseLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/EditNaturalArea.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/EditNaturalArea.jsx deleted file mode 100644 index c92443c0f2..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/EditNaturalArea.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureNaturalArea from '../../../../components/LocationDetailLayout/AreaDetails/NaturalArea'; -import { deleteNaturalAreaLocation, editNaturalAreaLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { naturalAreaSelector } from '../../../naturalAreaSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditNaturalAreaDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editNaturalAreaLocation({ - ...data, - ...match.params, - figure_id: naturalArea.figure_id, - }), - ); - }; - const naturalArea = useSelector(naturalAreaSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteNaturalAreaLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditNaturalAreaDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/PostNaturalArea.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/PostNaturalArea.jsx deleted file mode 100644 index c5d3962236..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/PostNaturalArea.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureNaturalArea from '../../../../components/LocationDetailLayout/AreaDetails/NaturalArea'; -import { postNaturalAreaLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostNaturalAreaDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postNaturalAreaLocation(data)); - }; - - return ( - - ); -} - -export default PostNaturalAreaDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/saga.js deleted file mode 100644 index 63adeedc4a..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/saga.js +++ /dev/null @@ -1,131 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteNaturalAreaSuccess, - editNaturalAreaSuccess, - getLocationObjectFromNaturalArea, - postNaturalAreaSuccess, -} from '../../../naturalAreaSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postNaturalAreaLocation = createAction(`postNaturalAreaLocationSaga`); - -export function* postNaturalAreaLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromNaturalArea(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postNaturalAreaSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.NA'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.NA') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editNaturalAreaLocation = createAction(`editNaturalAreaLocationSaga`); - -export function* editNaturalAreaLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromNaturalArea({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editNaturalAreaSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.NA'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.NA') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteNaturalAreaLocation = createAction(`deleteNaturalAreaLocationSaga`); - -export function* deleteNaturalAreaLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteNaturalAreaSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.NA'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* naturalAreaLocationSaga() { - yield takeLeading(postNaturalAreaLocation.type, postNaturalAreaLocationSaga); - yield takeLeading(editNaturalAreaLocation.type, editNaturalAreaLocationSaga); - yield takeLeading(deleteNaturalAreaLocation.type, deleteNaturalAreaLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/EditResidence.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/EditResidence.jsx deleted file mode 100644 index 5334c8d38c..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/EditResidence.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureResidence from '../../../../components/LocationDetailLayout/AreaDetails/Residence'; -import { deleteResidenceLocation, editResidenceLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { residenceSelector } from '../../../residenceSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditResidenceDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editResidenceLocation({ ...data, ...match.params, figure_id: residence.figure_id })); - }; - const residence = useSelector(residenceSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteResidenceLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditResidenceDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/PostResidence.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/PostResidence.jsx deleted file mode 100644 index f636b4c664..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/PostResidence.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureResidence from '../../../../components/LocationDetailLayout/AreaDetails/Residence'; -import { postResidenceLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostResidenceDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postResidenceLocation(data)); - }; - - return ( - - ); -} - -export default PostResidenceDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/saga.js deleted file mode 100644 index 8092f634f9..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/ResidenceDetailForm/saga.js +++ /dev/null @@ -1,140 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteResidenceSuccess, - editResidenceSuccess, - getLocationObjectFromResidence, - postResidenceSuccess, -} from '../../../residenceSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postResidenceLocation = createAction(`postResidenceLocationSaga`); - -export function* postResidenceLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromResidence(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postResidenceSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.RESIDENCE'), - i18n.t('message:MAP.SUCCESS_POST'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.RESIDENCE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editResidenceLocation = createAction(`editResidenceLocationSaga`); - -export function* editResidenceLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromResidence({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editResidenceSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.RESIDENCE'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.RESIDENCE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteResidenceLocation = createAction(`deleteResidenceLocationSaga`); - -export function* deleteResidenceLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteResidenceSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.RESIDENCE'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* residenceLocationSaga() { - yield takeLeading(postResidenceLocation.type, postResidenceLocationSaga); - yield takeLeading(editResidenceLocation.type, editResidenceLocationSaga); - yield takeLeading(deleteResidenceLocation.type, deleteResidenceLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/EditSurfaceWater.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/EditSurfaceWater.jsx deleted file mode 100644 index 829de4c06b..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/EditSurfaceWater.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureSurfaceWater from '../../../../components/LocationDetailLayout/AreaDetails/SurfaceWater'; -import { deleteSurfaceWaterLocation, editSurfaceWaterLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { surfaceWaterSelector } from '../../../surfaceWaterSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditSurfaceWaterDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editSurfaceWaterLocation({ - ...data, - ...match.params, - figure_id: surfaceWater.figure_id, - }), - ); - }; - const surfaceWater = useSelector(surfaceWaterSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteSurfaceWaterLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditSurfaceWaterDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/PostSurfaceWater.jsx b/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/PostSurfaceWater.jsx deleted file mode 100644 index b557f70fea..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/PostSurfaceWater.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureSurfaceWater from '../../../../components/LocationDetailLayout/AreaDetails/SurfaceWater'; -import { postSurfaceWaterLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostSurfaceWaterDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postSurfaceWaterLocation(data)); - }; - - return ( - - ); -} - -export default PostSurfaceWaterDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/saga.js deleted file mode 100644 index eac9d6db98..0000000000 --- a/packages/webapp/src/containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/saga.js +++ /dev/null @@ -1,141 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteSurfaceWaterSuccess, - editSurfaceWaterSuccess, - getLocationObjectFromSurfaceWater, - postSurfaceWaterSuccess, -} from '../../../surfaceWaterSlice'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; -import history from '../../../../history'; - -export const postSurfaceWaterLocation = createAction(`postSurfaceWaterLocationSaga`); - -export function* postSurfaceWaterLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromSurfaceWater(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postSurfaceWaterSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SURFACE_WATER'), - i18n.t('message:MAP.SUCCESS_POST'), - ]), - ); - yield put(canShowSuccessHeader(true)); - - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.SURFACE_WATER') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editSurfaceWaterLocation = createAction(`editSurfaceWaterLocationSaga`); - -export function* editSurfaceWaterLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromSurfaceWater({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editSurfaceWaterSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SURFACE_WATER'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.SURFACE_WATER') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteSurfaceWaterLocation = createAction(`deleteSurfaceWaterLocationSaga`); - -export function* deleteSurfaceWaterLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteSurfaceWaterSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SURFACE_WATER'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* surfaceWaterLocationSaga() { - yield takeLeading(postSurfaceWaterLocation.type, postSurfaceWaterLocationSaga); - yield takeLeading(editSurfaceWaterLocation.type, editSurfaceWaterLocationSaga); - yield takeLeading(deleteSurfaceWaterLocation.type, deleteSurfaceWaterLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/EditLocationDetailForm.tsx b/packages/webapp/src/containers/LocationDetails/EditLocationDetailForm.tsx new file mode 100644 index 0000000000..8d7563a62d --- /dev/null +++ b/packages/webapp/src/containers/LocationDetails/EditLocationDetailForm.tsx @@ -0,0 +1,163 @@ +/* + * Copyright 2026 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +import { useEffect, useState } from 'react'; +import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import { isAdminSelector, loginSelector, measurementSelector } from '../userFarmSlice'; +import { + useCheckDeleteLocationMutation, + useDeleteLocationMutation, + useUpdateLocationByTypeMutation, +} from '../../store/api/locationApi'; +import useLocationsById from '../../hooks/location/useLocationsById'; +import UnableToRetireModal from '../../components/Modals/UnableToRetireModal'; +import RetireConfirmationModal from '../../components/Modals/RetireConfirmationModal'; +import { formatLocationTypeToLocationForDB, useLocationPageType } from './utils'; +import { FigureType, InternalMapLocation, InternalMapLocationType } from '../../store/api/types'; +import { enqueueErrorSnackbar, enqueueSuccessSnackbar } from '../Snackbar/snackbarSlice'; +import { useTranslation } from 'react-i18next'; +import { setMapCache } from '../Map/mapCacheSlice'; +import PureLocationFormWrapper from '../../components/LocationDetailLayout/PureLocationFormWrapper'; + +function EditLocationDetailForm({ locationType }: { locationType: InternalMapLocationType }) { + const dispatch = useDispatch(); + const { t } = useTranslation(); + const location = useLocation(); + const history = useHistory(); + const match = useRouteMatch(); + // @ts-expect-error match not typed + const { location_id } = match.params; + const isAdmin = useSelector(isAdminSelector); + const system = useSelector(measurementSelector); + const { farm_id } = useSelector(loginSelector); + + const { locations: locationData } = useLocationsById(location_id); + + const [updateLocationByType] = useUpdateLocationByTypeMutation(); + const [deleteLocation] = useDeleteLocationMutation(); + const [checkDeleteLocation] = useCheckDeleteLocationMutation(); + + const { isViewLocationPage, isEditLocationPage } = useLocationPageType(); + + const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); + const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); + + const submitForm = async (data: { formData: any }) => { + if (!isEditLocationPage) return; + const { formData } = data; + formData.farm_id = farm_id; + try { + await updateLocationByType({ + data: formatLocationTypeToLocationForDB( + { + ...formData, + ...match.params, + figure_id: locationData?.figure_id, + }, + locationType, + ) as InternalMapLocation, + type: locationType, + location_id: location_id, + }).unwrap(); + history.push({ pathname: '/map' }); + dispatch( + enqueueSuccessSnackbar( + `${t(`FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`)} ${t( + 'message:MAP.SUCCESS_PATCH', + ) + .toString() + ?.toLowerCase()}`, + ), + ); + } catch (error) { + console.error(error); + dispatch( + enqueueErrorSnackbar( + `${t('message:MAP.FAIL_PATCH')} ${t( + `FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`, + ).toLowerCase()}`, + ), + ); + } + }; + + const handleRetire = async () => { + try { + await checkDeleteLocation({ location_id }).unwrap(); + setShowConfirmRetireModal(true); + } catch (_err) { + setShowCannotRetireModal(true); + } + }; + + const confirmRetire = async () => { + if (!isViewLocationPage) return; + try { + await deleteLocation({ location_id }).unwrap(); + if (locationData?.figure_type === FigureType.POINT) { + dispatch(setMapCache({ maxZoom: undefined, farm_id })); + } + history.push({ pathname: '/map' }); + dispatch( + enqueueSuccessSnackbar( + `${t(`FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`)} ${t( + 'message:MAP.SUCCESS_DELETE', + ) + .toString() + ?.toLowerCase()}`, + ), + ); + } catch (error) { + console.error(error); + dispatch( + enqueueErrorSnackbar( + `${t('message:MAP.FAIL_DELETE')} ${t( + `FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`, + ).toLowerCase()}`, + ), + ); + } + setShowConfirmRetireModal(false); + }; + + return ( + <> + + {isViewLocationPage && showCannotRetireModal && ( + setShowCannotRetireModal(false)} /> + )} + {showConfirmRetireModal && ( + setShowConfirmRetireModal(false)} + handleRetire={confirmRetire} + /> + )} + + ); +} + +export default EditLocationDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/EditBufferZone.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/EditBufferZone.jsx deleted file mode 100644 index 9a231b79b8..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/EditBufferZone.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureBufferZone from '../../../../components/LocationDetailLayout/LineDetails/BufferZone'; -import { deleteBufferZoneLocation, editBufferZoneLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { bufferZoneSelector } from '../../../bufferZoneSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditBufferZoneDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const location = useLocation(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editBufferZoneLocation({ - ...data, - ...match.params, - figure_id: bufferZone.figure_id, - }), - ); - }; - const bufferZone = useSelector(bufferZoneSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteBufferZoneLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditBufferZoneDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/PostBufferZone.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/PostBufferZone.jsx deleted file mode 100644 index dbb68bb18e..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/PostBufferZone.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureBufferZone from '../../../../components/LocationDetailLayout/LineDetails/BufferZone'; -import { postBufferZoneLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostBufferZoneDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postBufferZoneLocation(data)); - }; - - return ( - - ); -} - -export default PostBufferZoneDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/saga.js deleted file mode 100644 index 07fbc0a582..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/BufferZoneDetailForm/saga.js +++ /dev/null @@ -1,131 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteBufferZoneSuccess, - editBufferZoneSuccess, - getLocationObjectFromBufferZone, - postBufferZoneSuccess, -} from '../../../bufferZoneSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; - -export const postBufferZoneLocation = createAction(`postBufferZoneLocationSaga`); - -export function* postBufferZoneLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromBufferZone(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postBufferZoneSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BZ'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.BZ') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editBufferZoneLocation = createAction(`editBufferZoneLocationSaga`); - -export function* editBufferZoneLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromBufferZone({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editBufferZoneSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BZ'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.BZ') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteBufferZoneLocation = createAction(`deleteBufferZoneLocationSaga`); - -export function* deleteBufferZoneLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteBufferZoneSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.BZ'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* bufferZoneLocationSaga() { - yield takeLeading(postBufferZoneLocation.type, postBufferZoneLocationSaga); - yield takeLeading(editBufferZoneLocation.type, editBufferZoneLocationSaga); - yield takeLeading(deleteBufferZoneLocation.type, deleteBufferZoneLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/EditFence.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/EditFence.jsx deleted file mode 100644 index 808207e999..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/EditFence.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureFence from '../../../../components/LocationDetailLayout/LineDetails/Fence'; -import { deleteFenceLocation, editFenceLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { fenceSelector } from '../../../fenceSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditFenceDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const location = useLocation(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editFenceLocation({ ...data, ...match.params, figure_id: fence.figure_id })); - }; - const fence = useSelector(fenceSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteFenceLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditFenceDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/PostFence.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/PostFence.jsx deleted file mode 100644 index b897b43fa2..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/PostFence.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureFence from '../../../../components/LocationDetailLayout/LineDetails/Fence'; -import { postFenceLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostFenceDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postFenceLocation(data)); - }; - - return ( - - ); -} - -export default PostFenceDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/saga.js deleted file mode 100644 index 24671d9cd9..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/FenceDetailForm/saga.js +++ /dev/null @@ -1,134 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteFenceSuccess, - editFenceSuccess, - getLocationObjectFromFence, - postFenceSuccess, -} from '../../../fenceSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; - -export const postFenceLocation = createAction(`postFenceLocationSaga`); - -export function* postFenceLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromFence(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postFenceSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FENCE'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.FENCE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editFenceLocation = createAction(`editFenceLocationSaga`); - -export function* editFenceLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromFence({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editFenceSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.FENCE'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.FENCE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteFenceLocation = createAction(`deleteFenceLocationSaga`); - -export function* deleteFenceLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteFenceSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.FENCE'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* fenceLocationSaga() { - yield takeLeading(postFenceLocation.type, postFenceLocationSaga); - yield takeLeading(editFenceLocation.type, editFenceLocationSaga); - yield takeLeading(deleteFenceLocation.type, deleteFenceLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/EditWatercourse.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/EditWatercourse.jsx deleted file mode 100644 index e5eb910233..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/EditWatercourse.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureWatercourse from '../../../../components/LocationDetailLayout/LineDetails/Watercourse'; -import { deleteWatercourseLocation, editWatercourseLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { watercourseSelector } from '../../../watercourseSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditWatercourseDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const location = useLocation(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editWatercourseLocation({ - ...data, - ...match.params, - figure_id: watercourse.figure_id, - }), - ); - }; - const watercourse = useSelector(watercourseSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteWatercourseLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditWatercourseDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/PostWatercourse.jsx b/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/PostWatercourse.jsx deleted file mode 100644 index 970a6df0a1..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/PostWatercourse.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureWatercourse from '../../../../components/LocationDetailLayout/LineDetails/Watercourse'; -import { postWatercourseLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostWatercourseDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postWatercourseLocation(data)); - }; - - return ( - - ); -} - -export default PostWatercourseDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/saga.js deleted file mode 100644 index b2fd66afb7..0000000000 --- a/packages/webapp/src/containers/LocationDetails/LineDetails/WatercourseDetailForm/saga.js +++ /dev/null @@ -1,140 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteWatercourseSuccess, - editWatercourseSuccess, - getLocationObjectFromWatercourse, - postWatercourseSuccess, -} from '../../../watercourseSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import i18n from '../../../../locales/i18n'; - -export const postWatercourseLocation = createAction(`postWatercourseLocationSaga`); - -export function* postWatercourseLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromWatercourse(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(postWatercourseSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.WATERCOURSE'), - i18n.t('message:MAP.SUCCESS_POST'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.WATERCOURSE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editWatercourseLocation = createAction(`editWatercourseLocationSaga`); - -export function* editWatercourseLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromWatercourse({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editWatercourseSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.WATERCOURSE'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.WATERCOURSE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteWatercourseLocation = createAction(`deleteWatercourseLocationSaga`); - -export function* deleteWatercourseLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(deleteWatercourseSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.WATERCOURSE'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* watercourseLocationSaga() { - yield takeLeading(postWatercourseLocation.type, postWatercourseLocationSaga); - yield takeLeading(editWatercourseLocation.type, editWatercourseLocationSaga); - yield takeLeading(deleteWatercourseLocation.type, deleteWatercourseLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/EditGate.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/EditGate.jsx deleted file mode 100644 index da347b88f8..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/EditGate.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureGate from '../../../../components/LocationDetailLayout/PointDetails/Gate'; -import { deleteGateLocation, editGateLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { gateSelector } from '../../../gateSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditGateDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch(editGateLocation({ ...data, ...match.params, figure_id: gate.figure_id })); - }; - const gate = useSelector(gateSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteGateLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditGateDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/PostGate.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/PostGate.jsx deleted file mode 100644 index bd18374733..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/PostGate.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureGate from '../../../../components/LocationDetailLayout/PointDetails/Gate'; -import { postGateLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostGateDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postGateLocation(data)); - }; - - return ( - - ); -} - -export default PostGateDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/saga.js deleted file mode 100644 index 0a173612f1..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/GateDetailForm/saga.js +++ /dev/null @@ -1,134 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteGateSuccess, - editGateSuccess, - getLocationObjectFromGate, - postGateSuccess, -} from '../../../gateSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import { setMapCache } from '../../../Map/mapCacheSlice'; -import i18n from '../../../../locales/i18n'; - -export const postGateLocation = createAction(`postGateLocationSaga`); - -export function* postGateLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGate(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(postGateSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.GATE'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.GATE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editGateLocation = createAction(`editGateLocationSaga`); - -export function* editGateLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromGate({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editGateSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.GATE'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.GATE') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteGateLocation = createAction(`deleteGateLocationSaga`); - -export function* deleteGateLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(deleteGateSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.GATE'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* gateLocationSaga() { - yield takeLeading(postGateLocation.type, postGateLocationSaga); - yield takeLeading(editGateLocation.type, editGateLocationSaga); - yield takeLeading(deleteGateLocation.type, deleteGateLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/EditSoilSampleLocation.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/EditSoilSampleLocation.jsx deleted file mode 100644 index 2b478ebf91..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/EditSoilSampleLocation.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -import { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureSoilSampleLocation from '../../../../components/LocationDetailLayout/PointDetails/SoilSampleLocation'; -import { deleteSoilSampleLocationLocation, editSoilSampleLocationLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { soilSampleLocationSelector } from '../../../soilSampleLocationSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; - -function EditSoilSampleLocationDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editSoilSampleLocationLocation({ - ...data, - ...match.params, - figure_id: soilSampleLocation.figure_id, - }), - ); - }; - const soilSampleLocation = useSelector(soilSampleLocationSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteSoilSampleLocationLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditSoilSampleLocationDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/PostSoilSampleLocation.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/PostSoilSampleLocation.jsx deleted file mode 100644 index d59f84512d..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/PostSoilSampleLocation.jsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureSoilSampleLocation from '../../../../components/LocationDetailLayout/PointDetails/SoilSampleLocation'; -import { postSoilSampleLocationLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostSoilSampleLocationDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postSoilSampleLocationLocation(data)); - }; - - return ( - - ); -} - -export default PostSoilSampleLocationDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/saga.js deleted file mode 100644 index a66449bc47..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/saga.js +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -import { createAction } from '@reduxjs/toolkit'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import i18n from '../../../../locales/i18n'; -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { axios, getHeader } from '../../../saga'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { - deleteSoilSampleLocationSuccess, - editSoilSampleLocationSuccess, - getLocationObjectFromSoilSampleLocation, - postSoilSampleLocationSuccess, -} from '../../../soilSampleLocationSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import { setMapCache } from '../../../Map/mapCacheSlice'; - -export const postSoilSampleLocationLocation = createAction(`postSoilSampleLocationLocationSaga`); - -export function* postSoilSampleLocationLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromSoilSampleLocation(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(postSoilSampleLocationSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SOIL_SAMPLE_LOCATION'), - i18n.t('message:MAP.SUCCESS_POST'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.SOIL_SAMPLE_LOCATION') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editSoilSampleLocationLocation = createAction(`editSoilSampleLocationLocationSaga`); - -export function* editSoilSampleLocationLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromSoilSampleLocation({ - ...formData, - location_id, - figure_id, - }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editSoilSampleLocationSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SOIL_SAMPLE_LOCATION'), - i18n.t('message:MAP.SUCCESS_PATCH'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.SOIL_SAMPLE_LOCATION') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteSoilSampleLocationLocation = createAction( - `deleteSoilSampleLocationLocationSaga`, -); - -export function* deleteSoilSampleLocationLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(deleteSoilSampleLocationSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([ - i18n.t('FARM_MAP.MAP_FILTER.SOIL_SAMPLE_LOCATION'), - i18n.t('message:MAP.SUCCESS_DELETE'), - ]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* soilSampleLocationLocationSaga() { - yield takeLeading(postSoilSampleLocationLocation.type, postSoilSampleLocationLocationSaga); - yield takeLeading(editSoilSampleLocationLocation.type, editSoilSampleLocationLocationSaga); - yield takeLeading(deleteSoilSampleLocationLocation.type, deleteSoilSampleLocationLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/EditWaterValve.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/EditWaterValve.jsx deleted file mode 100644 index 38d1230cb7..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/EditWaterValve.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'; -import PureWaterValve from '../../../../components/LocationDetailLayout/PointDetails/WaterValve'; -import { deleteWaterValveLocation, editWaterValveLocation } from './saga'; -import { useCheckDeleteLocationMutation } from '../../../../store/api/locationApi'; -import { useDispatch, useSelector } from 'react-redux'; -import { isAdminSelector, measurementSelector } from '../../../userFarmSlice'; -import { waterValveSelector } from '../../../waterValveSlice'; -import { useLocationPageType } from '../../utils'; -import UnableToRetireModal from '../../../../components/Modals/UnableToRetireModal'; -import RetireConfirmationModal from '../../../../components/Modals/RetireConfirmationModal'; -import { - currentManagementPlansByLocationIdSelector, - plannedManagementPlansByLocationIdSelector, -} from '../../../Task/TaskCrops/managementPlansWithLocationSelector'; - -function EditWaterValveDetailForm() { - const location = useLocation(); - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const isAdmin = useSelector(isAdminSelector); - const system = useSelector(measurementSelector); - const submitForm = (data) => { - isEditLocationPage && - dispatch( - editWaterValveLocation({ - ...data, - ...match.params, - figure_id: waterValve.figure_id, - }), - ); - }; - const waterValve = useSelector(waterValveSelector(match.params.location_id)); - - useEffect(() => { - if (location?.state?.error?.retire) { - setShowCannotRetireModal(true); - } - }, [location?.state?.error]); - - const { isCreateLocationPage, isViewLocationPage, isEditLocationPage } = useLocationPageType(); - - const [showCannotRetireModal, setShowCannotRetireModal] = useState(false); - const [showConfirmRetireModal, setShowConfirmRetireModal] = useState(false); - const { location_id } = match.params; - const activeCrops = useSelector(currentManagementPlansByLocationIdSelector(location_id)); - const plannedCrops = useSelector(plannedManagementPlansByLocationIdSelector(location_id)); - const [checkDeleteLocation] = useCheckDeleteLocationMutation(); - const handleRetire = async () => { - // approach 1: redux store check for dependencies - // if (activeCrops.length === 0 && plannedCrops.length === 0) { - // setShowConfirmRetireModal(true); - // } else { - // setShowCannotRetireModal(true); - // } - - // approach 2: call backend for dependency check - try { - await checkDeleteLocation({ location_id }).unwrap(); - setShowConfirmRetireModal(true); - } catch (_err) { - setShowCannotRetireModal(true); - } - }; - - const confirmRetire = () => { - isViewLocationPage && dispatch(deleteWaterValveLocation({ location_id })); - setShowConfirmRetireModal(false); - }; - - return ( - <> - - {isViewLocationPage && showCannotRetireModal && ( - setShowCannotRetireModal(false)} /> - )} - {showConfirmRetireModal && ( - setShowConfirmRetireModal(false)} - handleRetire={confirmRetire} - /> - )} - - ); -} - -export default EditWaterValveDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/PostWaterValve.jsx b/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/PostWaterValve.jsx deleted file mode 100644 index 39eb836879..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/PostWaterValve.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useHistory, useRouteMatch } from 'react-router-dom'; -import PureWaterValve from '../../../../components/LocationDetailLayout/PointDetails/WaterValve'; -import { postWaterValveLocation } from './saga'; -import { useDispatch, useSelector } from 'react-redux'; -import { measurementSelector } from '../../../userFarmSlice'; -import useHookFormPersist from '../../../hooks/useHookFormPersist'; -import { hookFormPersistSelector } from '../../../hooks/useHookFormPersist/hookFormPersistSlice'; - -function PostWaterValveDetailForm() { - const history = useHistory(); - const match = useRouteMatch(); - const dispatch = useDispatch(); - const system = useSelector(measurementSelector); - const persistedFormData = useSelector(hookFormPersistSelector); - - const submitForm = (data) => { - dispatch(postWaterValveLocation(data)); - }; - - return ( - - ); -} - -export default PostWaterValveDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/saga.js b/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/saga.js deleted file mode 100644 index ad9ea01678..0000000000 --- a/packages/webapp/src/containers/LocationDetails/PointDetails/WaterValveDetailForm/saga.js +++ /dev/null @@ -1,134 +0,0 @@ -import { call, put, select, takeLeading } from 'redux-saga/effects'; -import { invalidateTags } from '../../../../store/api/apiSlice'; -import apiConfig from '../../../../apiConfig'; -import { loginSelector } from '../../../userFarmSlice'; -import { axios, getHeader } from '../../../saga'; -import { createAction } from '@reduxjs/toolkit'; -import { - deleteWaterValveSuccess, - editWaterValveSuccess, - getLocationObjectFromWaterValve, - postWaterValveSuccess, -} from '../../../waterValveSlice'; -import history from '../../../../history'; -import { canShowSuccessHeader, setSuccessMessage } from '../../../mapSlice'; -import { setMapCache } from '../../../Map/mapCacheSlice'; -import i18n from '../../../../locales/i18n'; - -export const postWaterValveLocation = createAction(`postWaterValveLocationSaga`); - -export function* postWaterValveLocationSaga({ payload: data }) { - const formData = data.formData; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromWaterValve(formData); - - try { - const result = yield call( - axios.post, - `${locationURL}/${locationObject.figure.type}`, - locationObject, - header, - ); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(postWaterValveSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.WV'), i18n.t('message:MAP.SUCCESS_POST')]), - ); - yield put(canShowSuccessHeader(true)); - history.push('/map'); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_POST')} ${i18n - .t('FARM_MAP.MAP_FILTER.WV') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const editWaterValveLocation = createAction(`editWaterValveLocationSaga`); - -export function* editWaterValveLocationSaga({ payload: data }) { - const { formData, location_id, figure_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - formData.farm_id = farm_id; - const header = getHeader(user_id, farm_id); - const locationObject = getLocationObjectFromWaterValve({ ...formData, location_id, figure_id }); - - try { - const result = yield call( - axios.put, - `${locationURL}/${locationObject.figure.type}/${location_id}`, - locationObject, - header, - ); - yield put(editWaterValveSuccess(result.data)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.WV'), i18n.t('message:MAP.SUCCESS_PATCH')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: `${i18n.t('message:MAP.FAIL_PATCH')} ${i18n - .t('FARM_MAP.MAP_FILTER.WV') - .toLowerCase()}`, - }, - ); - console.log(e); - } -} - -export const deleteWaterValveLocation = createAction(`deleteWaterValveLocationSaga`); - -export function* deleteWaterValveLocationSaga({ payload: data }) { - const { location_id } = data; - const { locationURL } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - - try { - const result = yield call(axios.delete, `${locationURL}/${location_id}`, header); - yield put(setMapCache({ maxZoom: undefined, farm_id })); - yield put(deleteWaterValveSuccess(location_id)); - yield put(invalidateTags(['Locations'])); - yield put( - setSuccessMessage([i18n.t('FARM_MAP.MAP_FILTER.WV'), i18n.t('message:MAP.SUCCESS_DELETE')]), - ); - yield put(canShowSuccessHeader(true)); - history.push({ pathname: '/map' }); - } catch (e) { - history.push( - { - pathname: history.location.pathname, - }, - { - error: { - retire: true, - }, - }, - ); - console.log(e); - } -} - -export default function* waterValveLocationSaga() { - yield takeLeading(postWaterValveLocation.type, postWaterValveLocationSaga); - yield takeLeading(editWaterValveLocation.type, editWaterValveLocationSaga); - yield takeLeading(deleteWaterValveLocation.type, deleteWaterValveLocationSaga); -} diff --git a/packages/webapp/src/containers/LocationDetails/PostLocationDetailForm.tsx b/packages/webapp/src/containers/LocationDetails/PostLocationDetailForm.tsx new file mode 100644 index 0000000000..9b6de5183d --- /dev/null +++ b/packages/webapp/src/containers/LocationDetails/PostLocationDetailForm.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2026 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +import { useHistory, useRouteMatch } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import { loginSelector, measurementSelector } from '../userFarmSlice'; +import { formatLocationTypeToLocationForDB } from './utils'; +import { InternalMapLocation, InternalMapLocationType } from '../../store/api/types'; +import { enqueueErrorSnackbar, enqueueSuccessSnackbar } from '../Snackbar/snackbarSlice'; +import useHookFormPersist from '../hooks/useHookFormPersist'; +import { hookFormPersistSelector } from '../hooks/useHookFormPersist/hookFormPersistSlice'; +import { setMapCache } from '../Map/mapCacheSlice'; +import { useAddLocationByTypeMutation } from '../../store/api/locationApi'; +import { useTranslation } from 'react-i18next'; +import PureLocationFormWrapper from '../../components/LocationDetailLayout/PureLocationFormWrapper'; + +function PostLocationDetailForm({ locationType }: { locationType: InternalMapLocationType }) { + const history = useHistory(); + const match = useRouteMatch(); + const dispatch = useDispatch(); + const { t } = useTranslation(); + const system = useSelector(measurementSelector); + const persistedFormData = useSelector(hookFormPersistSelector); + const { farm_id } = useSelector(loginSelector); + + const [addLocationByType] = useAddLocationByTypeMutation(); + + const submitForm = async (data: { formData: any }) => { + const { formData } = data; + formData.farm_id = farm_id; + const locationData = formatLocationTypeToLocationForDB( + formData, + locationType, + ) as InternalMapLocation; + try { + await addLocationByType({ + data: locationData, + type: locationType, + }).unwrap(); + if (locationData.figure.point) { + dispatch(setMapCache({ maxZoom: undefined, farm_id })); + } + history.push({ pathname: '/map' }); + dispatch( + enqueueSuccessSnackbar( + `${t(`FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`)} ${t('message:MAP.SUCCESS_POST') + .toString() + ?.toLowerCase()}`, + ), + ); + } catch (error) { + console.error(error); + dispatch( + enqueueErrorSnackbar( + `${t('message:MAP.FAIL_POST')} ${t( + `FARM_MAP.MAP_FILTER.${locationType.toUpperCase()}`, + ).toLowerCase()}`, + ), + ); + } + }; + + return ( + + ); +} + +export default PostLocationDetailForm; diff --git a/packages/webapp/src/containers/LocationDetails/utils.js b/packages/webapp/src/containers/LocationDetails/utils.js index aace93794f..708f139087 100644 --- a/packages/webapp/src/containers/LocationDetails/utils.js +++ b/packages/webapp/src/containers/LocationDetails/utils.js @@ -1,7 +1,17 @@ import { useMemo } from 'react'; import { useRouteMatch } from 'react-router-dom'; -import { barnEnum, fenceEnum, fieldEnum, greenhouseEnum, surfaceWaterEnum } from '../constants'; +import { + areaProperties, + fieldEnum, + figureProperties, + lineProperties, + locationProperties, + pointProperties, +} from '../constants'; import moment from 'moment'; +import { pick } from '../../util/pick'; +import { FigureType, InternalMapLocationType } from '../../store/api/types'; +import { getDateInputFormat } from '../../util/moment'; const isCreateLocationPage = (match) => match.path.includes('/create_location/'); const isViewLocationPage = (match) => /\w*\/:location_id\/details/.test(match.path); @@ -18,12 +28,6 @@ export const useLocationPageType = () => { ); }; -const boolToString = (bool) => { - if (bool === true) return 'true'; - else if (bool === false) return 'false'; - else return undefined; -}; - export const getFormData = (location) => { const result = { ...location }; result[fieldEnum.transition_date] && @@ -33,3 +37,96 @@ export const getFormData = (location) => { return result; }; + +const propertiesToPick = { + // areas + barn: ['wash_and_pack', 'cold_storage', 'used_for_animals'], + ceremonial_area: [], + farm_site_boundary: [], + field: ['station_id', 'organic_status', 'transition_date'], + garden: ['station_id', 'organic_status', 'transition_date'], + greenhouse: [ + 'organic_status', + 'transition_date', + 'supplemental_lighting', + 'co2_enrichment', + 'greenhouse_heated', + ], + natural_area: [], + residence: [], + surface_water: ['used_for_irrigation'], + // lines + buffer_zone: [], + fence: ['pressure_treated'], + watercourse: [ + 'used_for_irrigation', + 'includes_riparian_buffer', + 'buffer_width', + 'buffer_width_unit', + ], + // points + gate: [], + soil_sample_location: [], + water_valve: ['source', 'flow_rate', 'flow_rate_unit'], +}; + +export const getFigureType = (locationType) => { + switch (locationType) { + case InternalMapLocationType.BARN: + case InternalMapLocationType.CEREMONIAL_AREA: + case InternalMapLocationType.FARM_SITE_BOUNDARY: + case InternalMapLocationType.FIELD: + case InternalMapLocationType.GARDEN: + case InternalMapLocationType.GREENHOUSE: + case InternalMapLocationType.NATURAL_AREA: + case InternalMapLocationType.RESIDENCE: + case InternalMapLocationType.SURFACE_WATER: + return 'area'; + case InternalMapLocationType.BUFFER_ZONE: + case InternalMapLocationType.FENCE: + case InternalMapLocationType.WATERCOURSE: + return 'line'; + case InternalMapLocationType.GATE: + case InternalMapLocationType.SOIL_SAMPLE_LOCATION: + case InternalMapLocationType.WATER_VALVE: + return 'point'; + default: + throw new Error(`Unknown location type ${locationType}`); + } +}; + +const getFigureTypeProperties = (data, locationType) => { + const figureType = getFigureType(locationType); + const properties = { area: areaProperties, line: lineProperties, point: pointProperties }; + return { [figureType]: pick(data, properties[figureType]) }; +}; + +const getOrganicHistoryProperties = (data, locationType) => { + switch (locationType) { + case InternalMapLocationType.FIELD: + case InternalMapLocationType.GARDEN: + case InternalMapLocationType.GREENHOUSE: + return { + organic_history: { + effective_date: getDateInputFormat(), + organic_status: data.organic_status, + }, + }; + default: + return {}; + } +}; + +export const formatLocationTypeToLocationForDB = (data, locationType) => { + return { + figure: { + ...pick(data, figureProperties), + ...getFigureTypeProperties(data, locationType), + }, + [locationType]: { + ...pick(data, ['location_id', ...propertiesToPick[locationType]]), + ...getOrganicHistoryProperties(data, locationType), + }, + ...pick(data, locationProperties), + }; +}; diff --git a/packages/webapp/src/containers/Map/index.jsx b/packages/webapp/src/containers/Map/index.jsx index 836a8be3f4..d73e3b1059 100644 --- a/packages/webapp/src/containers/Map/index.jsx +++ b/packages/webapp/src/containers/Map/index.jsx @@ -34,7 +34,6 @@ import useDrawingManager from './useDrawingManager'; import { createShapeCapture } from './createShapeCapture'; import useMapAssetRenderer from './useMapAssetRenderer'; -import { getLocations } from '../saga'; import { mapFilterSettingSelector, setMapFilterHideAll, @@ -146,10 +145,6 @@ export default function Map({ isCompactSideMenu }) { if (drawingState.pointChanged) dispatch(setIsRedrawing(true)); }, [drawingState.pointChanged]); - useEffect(() => { - dispatch(getLocations()); - }, []); - useEffect(() => { if (showHeader) setShowSuccessHeader(true); }, [showHeader]); diff --git a/packages/webapp/src/containers/Map/useMapAssetRenderer.js b/packages/webapp/src/containers/Map/useMapAssetRenderer.js index 29eecf5da6..c5a0d133ee 100644 --- a/packages/webapp/src/containers/Map/useMapAssetRenderer.js +++ b/packages/webapp/src/containers/Map/useMapAssetRenderer.js @@ -149,8 +149,8 @@ const useMapAssetRenderer = ({ isClickable, showingConfirmButtons, drawingState ? drawNoFillArea : drawArea : isLine(assetType) - ? drawLine - : drawPoint; + ? drawLine + : drawPoint; }; const { maxZoomRef } = useMaxZoom(); diff --git a/packages/webapp/src/containers/Task/TaskLocations/index.jsx b/packages/webapp/src/containers/Task/TaskLocations/index.jsx index 8262613dbe..d789f57ada 100644 --- a/packages/webapp/src/containers/Task/TaskLocations/index.jsx +++ b/packages/webapp/src/containers/Task/TaskLocations/index.jsx @@ -19,11 +19,11 @@ import { useMaxZoom } from '../../Map/useMaxZoom'; import { managementPlanSelector } from '../../managementPlanSlice'; import { getProgress } from '../util'; import useAnimalsExist from '../../Animals/Inventory/useAnimalsExist'; -import { soilSampleLocationsSelector } from '../../soilSampleLocationSlice'; import useLocations from '../../../hooks/location/useLocations'; import useCropLocations from '../../../hooks/location/useCropLocations'; import useAnimalLocations from '../../../hooks/location/useAnimalLocations'; import useLocationsById from '../../../hooks/location/useLocationsById'; +import { InternalMapLocationType } from '../../../store/api/types'; export default function TaskLocationsSwitch() { const location = useLocation(); @@ -176,7 +176,9 @@ function TaskAnimalLocations({ history, location }) { function TaskSoilSampleLocations({ history, location }) { const { t } = useTranslation(); - const soilSampleLocations = useSelector(soilSampleLocationsSelector); + const { locations: soilSampleLocations } = useLocations({ + filterBy: InternalMapLocationType.SOIL_SAMPLE_LOCATION, + }); const onContinue = () => { history.push('/add_task/task_details', location.state); }; diff --git a/packages/webapp/src/containers/Task/TaskTypeSelection/index.jsx b/packages/webapp/src/containers/Task/TaskTypeSelection/index.jsx index b5c5c3a8b6..3a0ec3098a 100644 --- a/packages/webapp/src/containers/Task/TaskTypeSelection/index.jsx +++ b/packages/webapp/src/containers/Task/TaskTypeSelection/index.jsx @@ -10,12 +10,13 @@ import { showedSpotlightSelector } from '../../showedSpotlightSlice'; import { setSpotlightToShown } from '../../Map/saga'; import { currentAndPlannedManagementPlansSelector } from '../../managementPlanSlice'; import useAnimalsExist from '../../Animals/Inventory/useAnimalsExist'; -import { soilSampleLocationsSelector } from '../../soilSampleLocationSlice'; import { hasAvailableProductsSelector } from '../../productSlice'; import { TASK_TYPES } from '../constants'; import { useIsOffline } from '../../hooks/useOfflineDetector/useIsOffline'; import useCropLocations from '../../../hooks/location/useCropLocations'; import useAnimalLocations from '../../../hooks/location/useAnimalLocations'; +import { InternalMapLocationType } from '../../../store/api/types'; +import useLocations from '../../../hooks/location/useLocations'; function TaskTypeSelection() { const location = useLocation(); @@ -54,7 +55,10 @@ function TaskTypeSelection() { useSelector(currentAndPlannedManagementPlansSelector)?.length > 0; const { locations: animalLocations } = useAnimalLocations(); const hasAnimalMovementLocations = animalLocations?.length > 0; - const hasSoilSampleLocations = useSelector(soilSampleLocationsSelector)?.length > 0; + const { locations: soilSampleLocations } = useLocations({ + filterBy: InternalMapLocationType.SOIL_SAMPLE_LOCATION, + }); + const hasSoilSampleLocations = soilSampleLocations?.length > 0; const hasSoilAmendmentProducts = useSelector((state) => hasAvailableProductsSelector(state, TASK_TYPES.SOIL_AMENDMENT), ); diff --git a/packages/webapp/src/containers/barnSlice.js b/packages/webapp/src/containers/barnSlice.js deleted file mode 100644 index 62c497d07c..0000000000 --- a/packages/webapp/src/containers/barnSlice.js +++ /dev/null @@ -1,88 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const barnProperties = ['wash_and_pack', 'cold_storage', 'used_for_animals', 'location_id']; -export const getLocationObjectFromBarn = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - barn: pick(data, barnProperties), - ...pick(data, locationProperties), - }; -}; -const getBarnFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.barn, barnProperties), - }; -}; - -const upsertOneBarnWithLocation = (state, { payload: location }) => { - barnAdapter.upsertOne(state, getBarnFromLocationObject(location)); -}; -const upsertManyBarnWithLocation = (state, { payload: locations }) => { - barnAdapter.upsertMany( - state, - locations.map((location) => getBarnFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteBarn = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - barnAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const barnAdapter = createEntityAdapter({ - selectId: (barn) => barn.location_id, -}); - -const barnSlice = createSlice({ - name: 'barnReducer', - initialState: barnAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingBarnStart: onLoadingStart, - onLoadingBarnFail: onLoadingFail, - getBarnsSuccess: upsertManyBarnWithLocation, - postBarnSuccess: upsertOneBarnWithLocation, - editBarnSuccess: upsertOneBarnWithLocation, - deleteBarnSuccess: softDeleteBarn, - }, -}); -export const { - getBarnsSuccess, - postBarnSuccess, - editBarnSuccess, - onLoadingBarnStart, - onLoadingBarnFail, - deleteBarnSuccess, -} = barnSlice.actions; -export default barnSlice.reducer; - -export const barnReducerSelector = (state) => state.entitiesReducer[barnSlice.name]; - -const barnSelectors = barnAdapter.getSelectors((state) => state.entitiesReducer[barnSlice.name]); - -export const barnEntitiesSelector = barnSelectors.selectEntities; -export const barnsSelector = createSelector( - [barnSelectors.selectAll, loginSelector], - (barns, { farm_id }) => { - return barns.filter((barn) => barn.farm_id === farm_id && !barn.deleted); - }, -); - -export const barnSelector = (location_id) => - createSelector(barnEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/bufferZoneSlice.js b/packages/webapp/src/containers/bufferZoneSlice.js deleted file mode 100644 index 40c728b341..0000000000 --- a/packages/webapp/src/containers/bufferZoneSlice.js +++ /dev/null @@ -1,93 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, lineProperties, locationProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -const bufferZoneProperties = ['location_id']; -export const getLocationObjectFromBufferZone = (data) => { - return { - figure: { - ...pick(data, figureProperties), - line: pick(data, lineProperties), - }, - buffer_zone: pick(data, bufferZoneProperties), - ...pick(data, locationProperties), - }; -}; -const getBufferZoneFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - - ...pick(location.figure, figureProperties), - ...pick(location.figure.line, lineProperties), - ...pick(location.buffer_zone, bufferZoneProperties), - }; -}; - -const upsertOneBufferZoneWithLocation = (state, { payload: location }) => { - bufferZoneAdapter.upsertOne(state, getBufferZoneFromLocationObject(location)); -}; -const upsertManyBufferZoneWithLocation = (state, { payload: locations }) => { - bufferZoneAdapter.upsertMany( - state, - locations.map((location) => getBufferZoneFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteBufferZone = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - bufferZoneAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const bufferZoneAdapter = createEntityAdapter({ - selectId: (bufferZone) => bufferZone.location_id, -}); - -const bufferZoneSlice = createSlice({ - name: 'bufferZoneReducer', - initialState: bufferZoneAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingBufferZoneStart: onLoadingStart, - onLoadingBufferZoneFail: onLoadingFail, - getBufferZonesSuccess: upsertManyBufferZoneWithLocation, - postBufferZoneSuccess: upsertOneBufferZoneWithLocation, - editBufferZoneSuccess: upsertOneBufferZoneWithLocation, - deleteBufferZoneSuccess: softDeleteBufferZone, - }, -}); -export const { - getBufferZonesSuccess, - postBufferZoneSuccess, - editBufferZoneSuccess, - onLoadingBufferZoneStart, - onLoadingBufferZoneFail, - deleteBufferZoneSuccess, -} = bufferZoneSlice.actions; -export default bufferZoneSlice.reducer; - -export const bufferZoneReducerSelector = (state) => state.entitiesReducer[bufferZoneSlice.name]; - -const bufferZoneSelectors = bufferZoneAdapter.getSelectors( - (state) => state.entitiesReducer[bufferZoneSlice.name], -); - -export const bufferZoneEntitiesSelector = bufferZoneSelectors.selectEntities; -export const bufferZonesSelector = createSelector( - [bufferZoneSelectors.selectAll, loginSelector], - (bufferZones, { farm_id }) => { - return bufferZones.filter( - (bufferZone) => bufferZone.farm_id === farm_id && !bufferZone.deleted, - ); - }, -); - -export const bufferZoneSelector = (location_id) => - createSelector(bufferZoneEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/ceremonialSlice.js b/packages/webapp/src/containers/ceremonialSlice.js deleted file mode 100644 index 3d77b10bfe..0000000000 --- a/packages/webapp/src/containers/ceremonialSlice.js +++ /dev/null @@ -1,93 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const ceremonialProperties = ['location_id']; -export const getLocationObjectFromCeremonial = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - ceremonial_area: pick(data, ceremonialProperties), - ...pick(data, locationProperties), - }; -}; -const getCeremonialFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.ceremonial_area, ceremonialProperties), - }; -}; - -const upsertOneCeremonialWithLocation = (state, { payload: location }) => { - ceremonialAdapter.upsertOne(state, getCeremonialFromLocationObject(location)); -}; -const upsertManyCeremonialWithLocation = (state, { payload: locations }) => { - ceremonialAdapter.upsertMany( - state, - locations.map((location) => getCeremonialFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteCeremonial = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - ceremonialAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const ceremonialAdapter = createEntityAdapter({ - selectId: (ceremonial) => ceremonial.location_id, -}); - -const ceremonialSlice = createSlice({ - name: 'ceremonialReducer', - initialState: ceremonialAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingCeremonialStart: onLoadingStart, - onLoadingCeremonialFail: onLoadingFail, - getCeremonialsSuccess: upsertManyCeremonialWithLocation, - postCeremonialSuccess: upsertOneCeremonialWithLocation, - editCeremonialSuccess: upsertOneCeremonialWithLocation, - deleteCeremonialSuccess: softDeleteCeremonial, - }, -}); -export const { - getCeremonialsSuccess, - postCeremonialSuccess, - editCeremonialSuccess, - onLoadingCeremonialStart, - onLoadingCeremonialFail, - deleteCeremonialSuccess, -} = ceremonialSlice.actions; -export default ceremonialSlice.reducer; - -export const ceremonialReducerSelector = (state) => state.entitiesReducer[ceremonialSlice.name]; - -const ceremonialSelectors = ceremonialAdapter.getSelectors( - (state) => state.entitiesReducer[ceremonialSlice.name], -); - -export const ceremonialEntitiesSelector = ceremonialSelectors.selectEntities; -export const ceremonialsSelector = createSelector( - [ceremonialSelectors.selectAll, loginSelector], - (ceremonials, { farm_id }) => { - return ceremonials.filter( - (ceremonial) => ceremonial.farm_id === farm_id && !ceremonial.deleted, - ); - }, -); - -export const ceremonialSelector = (location_id) => - createSelector(ceremonialEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/farmSiteBoundarySlice.js b/packages/webapp/src/containers/farmSiteBoundarySlice.js deleted file mode 100644 index e4314e9bba..0000000000 --- a/packages/webapp/src/containers/farmSiteBoundarySlice.js +++ /dev/null @@ -1,94 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const farmSiteBoundaryProperties = ['location_id']; -export const getLocationObjectFromFarmSiteBoundary = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - farm_site_boundary: pick(data, farmSiteBoundaryProperties), - ...pick(data, locationProperties), - }; -}; -const getFarmSiteBoundaryFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.farm_site_boundary, farmSiteBoundaryProperties), - }; -}; - -const upsertOneFarmSiteBoundaryWithLocation = (state, { payload: location }) => { - farmSiteBoundaryAdapter.upsertOne(state, getFarmSiteBoundaryFromLocationObject(location)); -}; -const upsertManyFarmSiteBoundaryWithLocation = (state, { payload: locations }) => { - farmSiteBoundaryAdapter.upsertMany( - state, - locations.map((location) => getFarmSiteBoundaryFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteFarmSiteBoundary = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - farmSiteBoundaryAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const farmSiteBoundaryAdapter = createEntityAdapter({ - selectId: (farmSiteBoundary) => farmSiteBoundary.location_id, -}); - -const farmSiteBoundarySlice = createSlice({ - name: 'farmSiteBoundaryReducer', - initialState: farmSiteBoundaryAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingFarmSiteBoundaryStart: onLoadingStart, - onLoadingFarmSiteBoundaryFail: onLoadingFail, - getFarmSiteBoundarysSuccess: upsertManyFarmSiteBoundaryWithLocation, - postFarmSiteBoundarySuccess: upsertOneFarmSiteBoundaryWithLocation, - editFarmSiteBoundarySuccess: upsertOneFarmSiteBoundaryWithLocation, - deleteFarmSiteBoundarySuccess: softDeleteFarmSiteBoundary, - }, -}); -export const { - getFarmSiteBoundarysSuccess, - postFarmSiteBoundarySuccess, - editFarmSiteBoundarySuccess, - onLoadingFarmSiteBoundaryStart, - onLoadingFarmSiteBoundaryFail, - deleteFarmSiteBoundarySuccess, -} = farmSiteBoundarySlice.actions; -export default farmSiteBoundarySlice.reducer; - -export const farmSiteBoundaryReducerSelector = (state) => - state.entitiesReducer[farmSiteBoundarySlice.name]; - -const farmSiteBoundarySelectors = farmSiteBoundaryAdapter.getSelectors( - (state) => state.entitiesReducer[farmSiteBoundarySlice.name], -); - -export const farmSiteBoundaryEntitiesSelector = farmSiteBoundarySelectors.selectEntities; -export const farmSiteBoundarysSelector = createSelector( - [farmSiteBoundarySelectors.selectAll, loginSelector], - (farmSiteBoundarys, { farm_id }) => { - return farmSiteBoundarys.filter( - (farmSiteBoundary) => farmSiteBoundary.farm_id === farm_id && !farmSiteBoundary.deleted, - ); - }, -); - -export const farmSiteBoundarySelector = (location_id) => - createSelector(farmSiteBoundaryEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/fenceSlice.js b/packages/webapp/src/containers/fenceSlice.js deleted file mode 100644 index 9e07c2b1fd..0000000000 --- a/packages/webapp/src/containers/fenceSlice.js +++ /dev/null @@ -1,88 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, lineProperties, locationProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -const fenceProperties = ['pressure_treated', 'location_id']; -export const getLocationObjectFromFence = (data) => { - return { - figure: { - ...pick(data, figureProperties), - line: pick(data, lineProperties), - }, - fence: pick(data, fenceProperties), - ...pick(data, locationProperties), - }; -}; -const getFenceFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.line, lineProperties), - ...pick(location.fence, fenceProperties), - }; -}; - -const upsertOneFenceWithLocation = (state, { payload: location }) => { - fenceAdapter.upsertOne(state, getFenceFromLocationObject(location)); -}; -const upsertManyFenceWithLocation = (state, { payload: locations }) => { - fenceAdapter.upsertMany( - state, - locations.map((location) => getFenceFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteFence = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - fenceAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const fenceAdapter = createEntityAdapter({ - selectId: (fence) => fence.location_id, -}); - -const fenceSlice = createSlice({ - name: 'fenceReducer', - initialState: fenceAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingFenceStart: onLoadingStart, - onLoadingFenceFail: onLoadingFail, - getFencesSuccess: upsertManyFenceWithLocation, - postFenceSuccess: upsertOneFenceWithLocation, - editFenceSuccess: upsertOneFenceWithLocation, - deleteFenceSuccess: softDeleteFence, - }, -}); -export const { - getFencesSuccess, - postFenceSuccess, - editFenceSuccess, - onLoadingFenceStart, - onLoadingFenceFail, - deleteFenceSuccess, -} = fenceSlice.actions; -export default fenceSlice.reducer; - -export const fenceReducerSelector = (state) => state.entitiesReducer[fenceSlice.name]; - -const fenceSelectors = fenceAdapter.getSelectors((state) => state.entitiesReducer[fenceSlice.name]); - -export const fenceEntitiesSelector = fenceSelectors.selectEntities; -export const fencesSelector = createSelector( - [fenceSelectors.selectAll, loginSelector], - (fences, { farm_id }) => { - return fences.filter((fence) => fence.farm_id === farm_id && !fence.deleted); - }, -); - -export const fenceSelector = (location_id) => - createSelector(fenceEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/fieldSlice.js b/packages/webapp/src/containers/fieldSlice.js deleted file mode 100644 index a0f6fb587f..0000000000 --- a/packages/webapp/src/containers/fieldSlice.js +++ /dev/null @@ -1,95 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; -import { getDateInputFormat } from '../util/moment'; - -const fieldProperties = ['station_id', 'organic_status', 'transition_date', 'location_id']; -export const getLocationObjectFromField = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - field: { - ...pick(data, fieldProperties), - organic_history: { - effective_date: getDateInputFormat(), - organic_status: data.organic_status, - }, - }, - ...pick(data, locationProperties), - }; -}; -const getFieldFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.field, fieldProperties), - }; -}; - -const upsertOneFieldWithLocation = (state, { payload: location }) => { - fieldAdapter.upsertOne(state, getFieldFromLocationObject(location)); -}; -const upsertManyFieldWithLocation = (state, { payload: locations }) => { - fieldAdapter.upsertMany( - state, - locations.map((location) => getFieldFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteField = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - fieldAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const fieldAdapter = createEntityAdapter({ - selectId: (field) => field.location_id, -}); - -const fieldSlice = createSlice({ - name: 'fieldReducer', - initialState: fieldAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingFieldStart: onLoadingStart, - onLoadingFieldFail: onLoadingFail, - getFieldsSuccess: upsertManyFieldWithLocation, - postFieldSuccess: upsertOneFieldWithLocation, - editFieldSuccess: upsertOneFieldWithLocation, - deleteFieldSuccess: softDeleteField, - }, -}); -export const { - getFieldsSuccess, - postFieldSuccess, - editFieldSuccess, - onLoadingFieldStart, - onLoadingFieldFail, - deleteFieldSuccess, -} = fieldSlice.actions; -export default fieldSlice.reducer; - -export const fieldReducerSelector = (state) => state.entitiesReducer[fieldSlice.name]; - -const fieldSelectors = fieldAdapter.getSelectors((state) => state.entitiesReducer[fieldSlice.name]); - -export const fieldEntitiesSelector = fieldSelectors.selectEntities; -export const fieldsSelector = createSelector( - [fieldSelectors.selectAll, loginSelector], - (fields, { farm_id }) => { - return fields.filter((field) => field.farm_id === farm_id && !field.deleted); - }, -); - -export const fieldSelector = (location_id) => - createSelector(fieldEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/gardenSlice.js b/packages/webapp/src/containers/gardenSlice.js deleted file mode 100644 index 0088227f7c..0000000000 --- a/packages/webapp/src/containers/gardenSlice.js +++ /dev/null @@ -1,97 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; -import { getDateInputFormat } from '../util/moment'; - -const gardenProperties = ['station_id', 'organic_status', 'transition_date', 'location_id']; -export const getLocationObjectFromGarden = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - garden: { - ...pick(data, gardenProperties), - organic_history: { - effective_date: getDateInputFormat(), - organic_status: data.organic_status, - }, - }, - ...pick(data, locationProperties), - }; -}; -const getGardenFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.garden, gardenProperties), - }; -}; - -const upsertOneGardenWithLocation = (state, { payload: location }) => { - gardenAdapter.upsertOne(state, getGardenFromLocationObject(location)); -}; -const upsertManyGardenWithLocation = (state, { payload: locations }) => { - gardenAdapter.upsertMany( - state, - locations.map((location) => getGardenFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteGarden = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - gardenAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const gardenAdapter = createEntityAdapter({ - selectId: (garden) => garden.location_id, -}); - -const gardenSlice = createSlice({ - name: 'gardenReducer', - initialState: gardenAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingGardenStart: onLoadingStart, - onLoadingGardenFail: onLoadingFail, - getGardensSuccess: upsertManyGardenWithLocation, - postGardenSuccess: upsertOneGardenWithLocation, - editGardenSuccess: upsertOneGardenWithLocation, - deleteGardenSuccess: softDeleteGarden, - }, -}); -export const { - getGardensSuccess, - postGardenSuccess, - editGardenSuccess, - onLoadingGardenStart, - onLoadingGardenFail, - deleteGardenSuccess, -} = gardenSlice.actions; -export default gardenSlice.reducer; - -export const gardenReducerSelector = (state) => state.entitiesReducer[gardenSlice.name]; - -const gardenSelectors = gardenAdapter.getSelectors( - (state) => state.entitiesReducer[gardenSlice.name], -); - -export const gardenEntitiesSelector = gardenSelectors.selectEntities; -export const gardensSelector = createSelector( - [gardenSelectors.selectAll, loginSelector], - (gardens, { farm_id }) => { - return gardens.filter((garden) => garden.farm_id === farm_id && !garden.deleted); - }, -); - -export const gardenSelector = (location_id) => - createSelector(gardenEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/gateSlice.js b/packages/webapp/src/containers/gateSlice.js deleted file mode 100644 index 5543b1a10b..0000000000 --- a/packages/webapp/src/containers/gateSlice.js +++ /dev/null @@ -1,88 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, locationProperties, pointProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -const gateProperties = ['location_id']; -export const getLocationObjectFromGate = (data) => { - return { - figure: { - ...pick(data, figureProperties), - point: pick(data, pointProperties), - }, - gate: pick(data, gateProperties), - ...pick(data, locationProperties), - }; -}; -const getGateFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.point, pointProperties), - ...pick(location.gate, gateProperties), - }; -}; - -const upsertOneGateWithLocation = (state, { payload: location }) => { - gateAdapter.upsertOne(state, getGateFromLocationObject(location)); -}; -const upsertManyGateWithLocation = (state, { payload: locations }) => { - gateAdapter.upsertMany( - state, - locations.map((location) => getGateFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteGate = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - gateAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const gateAdapter = createEntityAdapter({ - selectId: (gate) => gate.location_id, -}); - -const gateSlice = createSlice({ - name: 'gateReducer', - initialState: gateAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingGateStart: onLoadingStart, - onLoadingGateFail: onLoadingFail, - getGatesSuccess: upsertManyGateWithLocation, - postGateSuccess: upsertOneGateWithLocation, - editGateSuccess: upsertOneGateWithLocation, - deleteGateSuccess: softDeleteGate, - }, -}); -export const { - getGatesSuccess, - postGateSuccess, - editGateSuccess, - onLoadingGateStart, - onLoadingGateFail, - deleteGateSuccess, -} = gateSlice.actions; -export default gateSlice.reducer; - -export const gateReducerSelector = (state) => state.entitiesReducer[gateSlice.name]; - -const gateSelectors = gateAdapter.getSelectors((state) => state.entitiesReducer[gateSlice.name]); - -export const gateEntitiesSelector = gateSelectors.selectEntities; -export const gatesSelector = createSelector( - [gateSelectors.selectAll, loginSelector], - (gates, { farm_id }) => { - return gates.filter((gate) => gate.farm_id === farm_id && !gate.deleted); - }, -); - -export const gateSelector = (location_id) => - createSelector(gateEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/greenhouseSlice.js b/packages/webapp/src/containers/greenhouseSlice.js deleted file mode 100644 index d1f1cb5248..0000000000 --- a/packages/webapp/src/containers/greenhouseSlice.js +++ /dev/null @@ -1,106 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; -import { getDateInputFormat } from '../util/moment'; - -const greenHouseProperties = [ - 'organic_status', - 'transition_date', - 'supplemental_lighting', - 'co2_enrichment', - 'greenhouse_heated', - 'location_id', -]; -export const getLocationObjectFromGreenHouse = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - greenhouse: { - ...pick(data, greenHouseProperties), - organic_history: { - effective_date: getDateInputFormat(), - organic_status: data.organic_status, - }, - }, - ...pick(data, locationProperties), - }; -}; -const getGreenhouseFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.greenhouse, greenHouseProperties), - }; -}; - -const upsertOneGreenhouseWithLocation = (state, { payload: location }) => { - greenhouseAdapter.upsertOne(state, getGreenhouseFromLocationObject(location)); -}; -const upsertManyGreenhouseWithLocation = (state, { payload: locations }) => { - greenhouseAdapter.upsertMany( - state, - locations.map((location) => getGreenhouseFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteGreenhouse = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - greenhouseAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const greenhouseAdapter = createEntityAdapter({ - selectId: (greenhouse) => greenhouse.location_id, -}); - -const greenhouseSlice = createSlice({ - name: 'greenhouseReducer', - initialState: greenhouseAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingGreenhouseStart: onLoadingStart, - onLoadingGreenhouseFail: onLoadingFail, - getGreenhousesSuccess: upsertManyGreenhouseWithLocation, - postGreenhouseSuccess: upsertOneGreenhouseWithLocation, - editGreenhouseSuccess: upsertOneGreenhouseWithLocation, - deleteGreenhouseSuccess: softDeleteGreenhouse, - }, -}); -export const { - getGreenhousesSuccess, - postGreenhouseSuccess, - editGreenhouseSuccess, - onLoadingGreenhouseStart, - onLoadingGreenhouseFail, - deleteGreenhouseSuccess, -} = greenhouseSlice.actions; -export default greenhouseSlice.reducer; - -export const greenhouseReducerSelector = (state) => state.entitiesReducer[greenhouseSlice.name]; - -const greenhouseSelectors = greenhouseAdapter.getSelectors( - (state) => state.entitiesReducer[greenhouseSlice.name], -); - -export const greenhouseEntitiesSelector = greenhouseSelectors.selectEntities; -export const greenhousesSelector = createSelector( - [greenhouseSelectors.selectAll, loginSelector], - (greenhouses, { farm_id }) => { - return greenhouses.filter( - (greenhouse) => greenhouse.farm_id === farm_id && !greenhouse.deleted, - ); - }, -); - -export const greenhouseSelector = (location_id) => - createSelector(greenhouseEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/naturalAreaSlice.js b/packages/webapp/src/containers/naturalAreaSlice.js deleted file mode 100644 index d77ea2a320..0000000000 --- a/packages/webapp/src/containers/naturalAreaSlice.js +++ /dev/null @@ -1,92 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const naturalAreaProperties = ['location_id']; -export const getLocationObjectFromNaturalArea = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - natural_area: pick(data, naturalAreaProperties), - ...pick(data, locationProperties), - }; -}; -const getNaturalAreaFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.natural_area, naturalAreaProperties), - }; -}; - -const upsertOneNaturalAreaWithLocation = (state, { payload: location }) => { - naturalAreaAdapter.upsertOne(state, getNaturalAreaFromLocationObject(location)); -}; -const upsertManyNaturalAreaWithLocation = (state, { payload: locations }) => { - naturalAreaAdapter.upsertMany( - state, - locations.map((location) => getNaturalAreaFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteNaturalArea = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - naturalAreaAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const naturalAreaAdapter = createEntityAdapter({ - selectId: (naturalArea) => naturalArea.location_id, -}); - -const naturalAreaSlice = createSlice({ - name: 'naturalAreaReducer', - initialState: naturalAreaAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingNaturalAreaStart: onLoadingStart, - onLoadingNaturalAreaFail: onLoadingFail, - getNaturalAreasSuccess: upsertManyNaturalAreaWithLocation, - postNaturalAreaSuccess: upsertOneNaturalAreaWithLocation, - editNaturalAreaSuccess: upsertOneNaturalAreaWithLocation, - deleteNaturalAreaSuccess: softDeleteNaturalArea, - }, -}); -export const { - getNaturalAreasSuccess, - postNaturalAreaSuccess, - editNaturalAreaSuccess, - onLoadingNaturalAreaStart, - onLoadingNaturalAreaFail, - deleteNaturalAreaSuccess, -} = naturalAreaSlice.actions; -export default naturalAreaSlice.reducer; - -export const naturalAreaReducerSelector = (state) => state.entitiesReducer[naturalAreaSlice.name]; - -const naturalAreaSelectors = naturalAreaAdapter.getSelectors( - (state) => state.entitiesReducer[naturalAreaSlice.name], -); - -export const naturalAreaEntitiesSelector = naturalAreaSelectors.selectEntities; -export const naturalAreasSelector = createSelector( - [naturalAreaSelectors.selectAll, loginSelector], - (naturalAreas, { farm_id }) => { - return naturalAreas.filter( - (naturalArea) => naturalArea.farm_id === farm_id && !naturalArea.deleted, - ); - }, -); - -export const naturalAreaSelector = (location_id) => - createSelector(naturalAreaEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/residenceSlice.js b/packages/webapp/src/containers/residenceSlice.js deleted file mode 100644 index a2b32bbaf1..0000000000 --- a/packages/webapp/src/containers/residenceSlice.js +++ /dev/null @@ -1,90 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const residenceProperties = ['location_id']; -export const getLocationObjectFromResidence = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - residence: pick(data, residenceProperties), - ...pick(data, locationProperties), - }; -}; -const getResidenceFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.residence, residenceProperties), - }; -}; - -const upsertOneResidenceWithLocation = (state, { payload: location }) => { - residenceAdapter.upsertOne(state, getResidenceFromLocationObject(location)); -}; -const upsertManyResidenceWithLocation = (state, { payload: locations }) => { - residenceAdapter.upsertMany( - state, - locations.map((location) => getResidenceFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteResidence = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - residenceAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const residenceAdapter = createEntityAdapter({ - selectId: (residence) => residence.location_id, -}); - -const residenceSlice = createSlice({ - name: 'residenceReducer', - initialState: residenceAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingResidenceStart: onLoadingStart, - onLoadingResidenceFail: onLoadingFail, - getResidencesSuccess: upsertManyResidenceWithLocation, - postResidenceSuccess: upsertOneResidenceWithLocation, - editResidenceSuccess: upsertOneResidenceWithLocation, - deleteResidenceSuccess: softDeleteResidence, - }, -}); -export const { - getResidencesSuccess, - postResidenceSuccess, - editResidenceSuccess, - onLoadingResidenceStart, - onLoadingResidenceFail, - deleteResidenceSuccess, -} = residenceSlice.actions; -export default residenceSlice.reducer; - -export const residenceReducerSelector = (state) => state.entitiesReducer[residenceSlice.name]; - -const residenceSelectors = residenceAdapter.getSelectors( - (state) => state.entitiesReducer[residenceSlice.name], -); - -export const residenceEntitiesSelector = residenceSelectors.selectEntities; -export const residencesSelector = createSelector( - [residenceSelectors.selectAll, loginSelector], - (residences, { farm_id }) => { - return residences.filter((residence) => residence.farm_id === farm_id && !residence.deleted); - }, -); - -export const residenceSelector = (location_id) => - createSelector(residenceEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/saga.js b/packages/webapp/src/containers/saga.js index fe0b8ebf56..a15f85c3b1 100644 --- a/packages/webapp/src/containers/saga.js +++ b/packages/webapp/src/containers/saga.js @@ -40,7 +40,6 @@ import { getTasksSaga, } from './Task/saga'; import { appVersionSelector, setAppVersion } from './appSettingSlice'; -import { getBarnsSuccess, onLoadingBarnFail, onLoadingBarnStart } from './barnSlice'; import { getBedMethodsSuccess, onLoadingBedMethodFail, @@ -51,16 +50,6 @@ import { onLoadingBroadcastMethodFail, onLoadingBroadcastMethodStart, } from './broadcastMethodSlice'; -import { - getBufferZonesSuccess, - onLoadingBufferZoneFail, - onLoadingBufferZoneStart, -} from './bufferZoneSlice'; -import { - getCeremonialsSuccess, - onLoadingCeremonialFail, - onLoadingCeremonialStart, -} from './ceremonialSlice'; import { getContainerMethodsSuccess, onLoadingContainerMethodFail, @@ -82,54 +71,25 @@ import { onLoadingDocumentFail, onLoadingDocumentStart, } from './documentSlice'; -import { - getFarmSiteBoundarysSuccess, - onLoadingFarmSiteBoundaryFail, - onLoadingFarmSiteBoundaryStart, -} from './farmSiteBoundarySlice'; -import { getFencesSuccess, onLoadingFenceFail, onLoadingFenceStart } from './fenceSlice'; -import { getFieldsSuccess, onLoadingFieldFail, onLoadingFieldStart } from './fieldSlice'; import { resetTasksFilter } from './filterSlice'; import { resetDateRange, setIsFetchingData } from './Finances/actions.js'; import { fetchAllData as fetchAllFinanceData } from './Finances/saga'; -import { getGardensSuccess, onLoadingGardenFail, onLoadingGardenStart } from './gardenSlice'; -import { getGatesSuccess, onLoadingGateFail, onLoadingGateStart } from './gateSlice'; -import { - getGreenhousesSuccess, - onLoadingGreenhouseFail, - onLoadingGreenhouseStart, -} from './greenhouseSlice'; import { getAllManagementPlansSuccess, getManagementPlansSuccess, onLoadingManagementPlanFail, onLoadingManagementPlanStart, } from './managementPlanSlice'; -import { - getNaturalAreasSuccess, - onLoadingNaturalAreaFail, - onLoadingNaturalAreaStart, -} from './naturalAreaSlice'; import { getPlantingManagementPlansSuccess, onLoadingPlantingManagementPlanFail, onLoadingPlantingManagementPlanStart, } from './plantingManagementPlanSlice'; -import { - getResidencesSuccess, - onLoadingResidenceFail, - onLoadingResidenceStart, -} from './residenceSlice'; import { getRowMethodsSuccess, onLoadingRowMethodFail, onLoadingRowMethodStart, } from './rowMethodSlice'; -import { - getSurfaceWatersSuccess, - onLoadingSurfaceWaterFail, - onLoadingSurfaceWaterStart, -} from './surfaceWaterSlice'; import { resetTasks } from './taskSlice'; import { isAdminSelector, @@ -141,23 +101,9 @@ import { userFarmsByFarmSelector, } from './userFarmSlice'; import { logUserInfoSuccess, userLogReducerSelector } from './userLogSlice'; -import { - getWaterValvesSuccess, - onLoadingWaterValveFail, - onLoadingWaterValveStart, -} from './waterValveSlice'; -import { - getWatercoursesSuccess, - onLoadingWatercourseFail, - onLoadingWatercourseStart, -} from './watercourseSlice'; import { api, invalidateTags } from '../store/api/apiSlice'; import { locationApi } from '../store/api/locationApi'; import { FarmLibraryTags, FarmTags } from '../store/api/apiTags'; -import { - getSoilSampleLocationsSuccess, - onLoadingSoilSampleLocationFail, -} from './soilSampleLocationSlice'; import { getFieldWorkTypes } from './Task/FieldWorkTask/saga'; import { getIrrigationTaskTypes } from './Task/IrrigationTaskTypes/saga'; @@ -281,7 +227,7 @@ export function* getFarmInfoSaga() { return; } localStorage.setItem('role_id', userFarm.role_id); - yield put(getLocations()); + yield put(locationApi.endpoints.getLocations.initiate()); yield put(getManagementPlans()); } catch (e) { console.log(e); @@ -325,81 +271,6 @@ export function* putFarmSaga({ payload: farm }) { } } -export const onLoadingLocationStart = createAction('onLoadingLocationStartSaga'); - -export function* onLoadingLocationStartSaga() { - yield put(onLoadingFieldStart()); - yield put(onLoadingGardenStart()); - yield put(onLoadingCeremonialStart()); - yield put(onLoadingBarnStart()); - yield put(onLoadingFarmSiteBoundaryStart()); - yield put(onLoadingGreenhouseStart()); - yield put(onLoadingSurfaceWaterStart()); - yield put(onLoadingNaturalAreaStart()); - yield put(onLoadingResidenceStart()); - yield put(onLoadingBufferZoneStart()); - yield put(onLoadingWatercourseStart()); - yield put(onLoadingFenceStart()); - yield put(onLoadingGateStart()); - yield put(onLoadingWaterValveStart()); -} - -export const getLocations = createAction('getLocationsSaga'); - -export function* getLocationsSaga() { - const { getLocationsByFarmIdUrl } = apiConfig; - let { user_id, farm_id } = yield select(loginSelector); - const header = getHeader(user_id, farm_id); - try { - yield put(onLoadingLocationStart()); - const result = yield call(axios.get, getLocationsByFarmIdUrl(farm_id), header); - yield put(getLocationsSuccess(result.data)); - } catch (e) { - console.log('failed to fetch fields from database'); - } -} - -export const getLocationsSuccess = createAction('getLocationsSuccessSaga'); - -export function* getLocationsSuccessSaga({ payload: locations }) { - const locations_by_figure_type = Object.keys(figureTypeActionMap).reduce( - (map, locationType) => Object.assign(map, { [locationType]: [] }), - {}, - ); - for (const location of locations) { - locations_by_figure_type[location.figure.type].push(location); - } - for (const figure_type in figureTypeActionMap) { - try { - yield put(figureTypeActionMap[figure_type].success(locations_by_figure_type[figure_type])); - } catch (e) { - yield put(figureTypeActionMap[figure_type].fail(e)); - console.log(e); - } - } -} - -const figureTypeActionMap = { - field: { success: getFieldsSuccess, fail: onLoadingFieldFail }, - garden: { success: getGardensSuccess, fail: onLoadingGardenFail }, - barn: { success: getBarnsSuccess, fail: onLoadingBarnFail }, - ceremonial_area: { success: getCeremonialsSuccess, fail: onLoadingCeremonialFail }, - farm_site_boundary: { success: getFarmSiteBoundarysSuccess, fail: onLoadingFarmSiteBoundaryFail }, - greenhouse: { success: getGreenhousesSuccess, fail: onLoadingGreenhouseFail }, - surface_water: { success: getSurfaceWatersSuccess, fail: onLoadingSurfaceWaterFail }, - natural_area: { success: getNaturalAreasSuccess, fail: onLoadingNaturalAreaFail }, - residence: { success: getResidencesSuccess, fail: onLoadingResidenceFail }, - buffer_zone: { success: getBufferZonesSuccess, fail: onLoadingBufferZoneFail }, - watercourse: { success: getWatercoursesSuccess, fail: onLoadingWatercourseFail }, - fence: { success: getFencesSuccess, fail: onLoadingFenceFail }, - gate: { success: getGatesSuccess, fail: onLoadingGateFail }, - water_valve: { success: getWaterValvesSuccess, fail: onLoadingWaterValveFail }, - soil_sample_location: { - success: getSoilSampleLocationsSuccess, - fail: onLoadingSoilSampleLocationFail, - }, -}; - export function* onLoadingManagementPlanAndPlantingMethodStartSaga() { yield put(onLoadingBroadcastMethodStart()); yield put(onLoadingBedMethodStart()); @@ -550,7 +421,7 @@ export const getCropsAndManagementPlans = createAction('getCropsAndManagementPla export function* getCropsAndManagementPlansSaga() { try { - yield all([call(getLocationsSaga), call(getCropsSaga)]); + yield all([put(locationApi.endpoints.getLocations.initiate()), call(getCropsSaga)]); yield call(getCropVarietiesSaga); yield call(getManagementPlansSaga); } catch (e) { @@ -731,15 +602,12 @@ export default function* getFarmIdSaga() { yield takeLeading(updateUser.type, updateUserSaga); yield takeLatest(getFarmInfo.type, getFarmInfoSaga); yield takeLeading(putFarm.type, putFarmSaga); - yield takeLatest(getLocations.type, getLocationsSaga); yield takeLatest(getManagementPlansByDate.type, getManagementPlansSaga); yield takeLatest(getManagementPlans.type, getManagementPlansSaga); yield takeLatest(getCrops.type, getCropsSaga); yield takeLatest(getCropVarieties.type, getCropVarietiesSaga); yield takeLatest(selectFarm.type, selectFarmSaga); yield takeLeading(selectFarmAndFetchAll.type, selectFarmAndFetchAllSaga); - yield takeLatest(onLoadingLocationStart.type, onLoadingLocationStartSaga); - yield takeLatest(getLocationsSuccess.type, getLocationsSuccessSaga); yield takeLatest(getDocuments.type, getDocumentsSaga); yield takeLatest( getManagementPlanAndPlantingMethodSuccess.type, diff --git a/packages/webapp/src/containers/soilSampleLocationSlice.js b/packages/webapp/src/containers/soilSampleLocationSlice.js deleted file mode 100644 index e82619088c..0000000000 --- a/packages/webapp/src/containers/soilSampleLocationSlice.js +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, locationProperties, pointProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -const soilSampleLocationProperties = ['location_id']; -export const getLocationObjectFromSoilSampleLocation = (data) => { - return { - figure: { - ...pick(data, figureProperties), - point: pick(data, pointProperties), - }, - soil_sample_location: pick(data, soilSampleLocationProperties), - ...pick(data, locationProperties), - }; -}; -const getSoilSampleLocationFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.point, pointProperties), - ...pick(location.soil_sample_location, soilSampleLocationProperties), - }; -}; - -const upsertOneSoilSampleLocationWithLocation = (state, { payload: location }) => { - soilSampleLocationAdapter.upsertOne(state, getSoilSampleLocationFromLocationObject(location)); -}; -const upsertManySoilSampleLocationWithLocation = (state, { payload: locations }) => { - soilSampleLocationAdapter.upsertMany( - state, - locations.map((location) => getSoilSampleLocationFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteSoilSampleLocation = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - soilSampleLocationAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const soilSampleLocationAdapter = createEntityAdapter({ - selectId: (soil_sample_location) => soil_sample_location.location_id, -}); - -const soilSampleLocationSlice = createSlice({ - name: 'soilSampleLocationReducer', - initialState: soilSampleLocationAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingSoilSampleLocationStart: onLoadingStart, - onLoadingSoilSampleLocationFail: onLoadingFail, - getSoilSampleLocationsSuccess: upsertManySoilSampleLocationWithLocation, - postSoilSampleLocationSuccess: upsertOneSoilSampleLocationWithLocation, - editSoilSampleLocationSuccess: upsertOneSoilSampleLocationWithLocation, - deleteSoilSampleLocationSuccess: softDeleteSoilSampleLocation, - }, -}); -export const { - getSoilSampleLocationsSuccess, - postSoilSampleLocationSuccess, - editSoilSampleLocationSuccess, - onLoadingSoilSampleLocationStart, - onLoadingSoilSampleLocationFail, - deleteSoilSampleLocationSuccess, -} = soilSampleLocationSlice.actions; -export default soilSampleLocationSlice.reducer; - -export const soilSampleLocationReducerSelector = (state) => - state.entitiesReducer[soilSampleLocationSlice.name]; - -const soilSampleLocationSelectors = soilSampleLocationAdapter.getSelectors( - (state) => state.entitiesReducer[soilSampleLocationSlice.name], -); - -export const soilSampleLocationEntitiesSelector = soilSampleLocationSelectors.selectEntities; -export const soilSampleLocationsSelector = createSelector( - [soilSampleLocationSelectors.selectAll, loginSelector], - (soilSampleLocations, { farm_id }) => { - return soilSampleLocations.filter( - (soil_sample_location) => - soil_sample_location.farm_id === farm_id && !soil_sample_location.deleted, - ); - }, -); - -export const soilSampleLocationSelector = (location_id) => - createSelector(soilSampleLocationEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/surfaceWaterSlice.js b/packages/webapp/src/containers/surfaceWaterSlice.js deleted file mode 100644 index d0840c16f3..0000000000 --- a/packages/webapp/src/containers/surfaceWaterSlice.js +++ /dev/null @@ -1,92 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { areaProperties, figureProperties, locationProperties } from './constants'; -import { pick } from '../util/pick'; - -const surfaceWaterProperties = ['used_for_irrigation', 'location_id']; -export const getLocationObjectFromSurfaceWater = (data) => { - return { - figure: { - ...pick(data, figureProperties), - area: pick(data, areaProperties), - }, - surface_water: pick(data, surfaceWaterProperties), - ...pick(data, locationProperties), - }; -}; -const getSurfaceWaterFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.area, areaProperties), - ...pick(location.surface_water, surfaceWaterProperties), - }; -}; - -const upsertOneSurfaceWaterWithLocation = (state, { payload: location }) => { - surfaceWaterAdapter.upsertOne(state, getSurfaceWaterFromLocationObject(location)); -}; -const upsertManySurfaceWaterWithLocation = (state, { payload: locations }) => { - surfaceWaterAdapter.upsertMany( - state, - locations.map((location) => getSurfaceWaterFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteSurfaceWater = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - surfaceWaterAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const surfaceWaterAdapter = createEntityAdapter({ - selectId: (surfaceWater) => surfaceWater.location_id, -}); - -const surfaceWaterSlice = createSlice({ - name: 'surfaceWaterReducer', - initialState: surfaceWaterAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingSurfaceWaterStart: onLoadingStart, - onLoadingSurfaceWaterFail: onLoadingFail, - getSurfaceWatersSuccess: upsertManySurfaceWaterWithLocation, - postSurfaceWaterSuccess: upsertOneSurfaceWaterWithLocation, - editSurfaceWaterSuccess: upsertOneSurfaceWaterWithLocation, - deleteSurfaceWaterSuccess: softDeleteSurfaceWater, - }, -}); -export const { - getSurfaceWatersSuccess, - postSurfaceWaterSuccess, - editSurfaceWaterSuccess, - onLoadingSurfaceWaterStart, - onLoadingSurfaceWaterFail, - deleteSurfaceWaterSuccess, -} = surfaceWaterSlice.actions; -export default surfaceWaterSlice.reducer; - -export const surfaceWaterReducerSelector = (state) => state.entitiesReducer[surfaceWaterSlice.name]; - -const surfaceWaterSelectors = surfaceWaterAdapter.getSelectors( - (state) => state.entitiesReducer[surfaceWaterSlice.name], -); - -export const surfaceWaterEntitiesSelector = surfaceWaterSelectors.selectEntities; -export const surfaceWatersSelector = createSelector( - [surfaceWaterSelectors.selectAll, loginSelector], - (surfaceWaters, { farm_id }) => { - return surfaceWaters.filter( - (surfaceWater) => surfaceWater.farm_id === farm_id && !surfaceWater.deleted, - ); - }, -); - -export const surfaceWaterSelector = (location_id) => - createSelector(surfaceWaterEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/waterValveSlice.js b/packages/webapp/src/containers/waterValveSlice.js deleted file mode 100644 index 44b057e966..0000000000 --- a/packages/webapp/src/containers/waterValveSlice.js +++ /dev/null @@ -1,92 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, locationProperties, pointProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -const waterValveProperties = ['source', 'flow_rate', 'flow_rate_unit', 'location_id']; -export const getLocationObjectFromWaterValve = (data) => { - return { - figure: { - ...pick(data, figureProperties), - point: pick(data, pointProperties), - }, - water_valve: pick(data, waterValveProperties), - ...pick(data, locationProperties), - }; -}; -const getWaterValveFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.point, pointProperties), - ...pick(location.water_valve, waterValveProperties), - }; -}; - -const upsertOneWaterValveWithLocation = (state, { payload: location }) => { - waterValveAdapter.upsertOne(state, getWaterValveFromLocationObject(location)); -}; -const upsertManyWaterValveWithLocation = (state, { payload: locations }) => { - waterValveAdapter.upsertMany( - state, - locations.map((location) => getWaterValveFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteWaterValve = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - waterValveAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const waterValveAdapter = createEntityAdapter({ - selectId: (waterValve) => waterValve.location_id, -}); - -const waterValveSlice = createSlice({ - name: 'waterValveReducer', - initialState: waterValveAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingWaterValveStart: onLoadingStart, - onLoadingWaterValveFail: onLoadingFail, - getWaterValvesSuccess: upsertManyWaterValveWithLocation, - postWaterValveSuccess: upsertOneWaterValveWithLocation, - editWaterValveSuccess: upsertOneWaterValveWithLocation, - deleteWaterValveSuccess: softDeleteWaterValve, - }, -}); -export const { - getWaterValvesSuccess, - postWaterValveSuccess, - editWaterValveSuccess, - onLoadingWaterValveStart, - onLoadingWaterValveFail, - deleteWaterValveSuccess, -} = waterValveSlice.actions; -export default waterValveSlice.reducer; - -export const waterValveReducerSelector = (state) => state.entitiesReducer[waterValveSlice.name]; - -const waterValveSelectors = waterValveAdapter.getSelectors( - (state) => state.entitiesReducer[waterValveSlice.name], -); - -export const waterValveEntitiesSelector = waterValveSelectors.selectEntities; -export const waterValvesSelector = createSelector( - [waterValveSelectors.selectAll, loginSelector], - (waterValves, { farm_id }) => { - return waterValves.filter( - (waterValve) => waterValve.farm_id === farm_id && !waterValve.deleted, - ); - }, -); - -export const waterValveSelector = (location_id) => - createSelector(waterValveEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/containers/watercourseSlice.js b/packages/webapp/src/containers/watercourseSlice.js deleted file mode 100644 index 73db123465..0000000000 --- a/packages/webapp/src/containers/watercourseSlice.js +++ /dev/null @@ -1,116 +0,0 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { figureProperties, lineProperties, locationProperties } from './constants'; -import { loginSelector, onLoadingFail, onLoadingStart, onLoadingSuccess } from './userFarmSlice'; -import { createSelector } from 'reselect'; -import { pick } from '../util/pick'; - -export const watercourseEnum = { - farm_id: 'farm_id', - name: 'name', - figure_id: 'figure_id', - type: 'type', - location_id: 'location_id', - notes: 'notes', - length: 'length', - width: 'width', - line_points: 'line_points', - length_unit: 'length_unit', - width_unit: 'width_unit', - includes_riparian_buffer: 'includes_riparian_buffer', - used_for_irrigation: 'used_for_irrigation', - buffer_width: 'buffer_width', - buffer_width_unit: 'buffer_width_unit', -}; - -const watercourseProperties = [ - 'used_for_irrigation', - 'includes_riparian_buffer', - 'buffer_width', - 'buffer_width_unit', - 'location_id', -]; -export const getLocationObjectFromWatercourse = (data) => { - return { - figure: { - ...pick(data, figureProperties), - line: pick(data, lineProperties), - }, - watercourse: pick(data, watercourseProperties), - ...pick(data, locationProperties), - }; -}; -const getWatercourseFromLocationObject = (location) => { - return { - ...pick(location, locationProperties), - ...pick(location.figure, figureProperties), - ...pick(location.figure.line, lineProperties), - ...pick(location.watercourse, watercourseProperties), - }; -}; - -const upsertOneWatercourseWithLocation = (state, { payload: location }) => { - watercourseAdapter.upsertOne(state, getWatercourseFromLocationObject(location)); -}; -const upsertManyWatercourseWithLocation = (state, { payload: locations }) => { - watercourseAdapter.upsertMany( - state, - locations.map((location) => getWatercourseFromLocationObject(location)), - ); - onLoadingSuccess(state); -}; -const softDeleteWatercourse = (state, { payload: location_id }) => { - state.loading = false; - state.error = null; - state.loaded = true; - watercourseAdapter.updateOne(state, { id: location_id, changes: { deleted: true } }); -}; - -const watercourseAdapter = createEntityAdapter({ - selectId: (watercourse) => watercourse.location_id, -}); - -const watercourseSlice = createSlice({ - name: 'watercourseReducer', - initialState: watercourseAdapter.getInitialState({ - loading: false, - error: undefined, - location_id: undefined, - loaded: false, - }), - reducers: { - onLoadingWatercourseStart: onLoadingStart, - onLoadingWatercourseFail: onLoadingFail, - getWatercoursesSuccess: upsertManyWatercourseWithLocation, - postWatercourseSuccess: upsertOneWatercourseWithLocation, - editWatercourseSuccess: upsertOneWatercourseWithLocation, - deleteWatercourseSuccess: softDeleteWatercourse, - }, -}); -export const { - getWatercoursesSuccess, - postWatercourseSuccess, - editWatercourseSuccess, - onLoadingWatercourseStart, - onLoadingWatercourseFail, - deleteWatercourseSuccess, -} = watercourseSlice.actions; -export default watercourseSlice.reducer; - -export const watercourseReducerSelector = (state) => state.entitiesReducer[watercourseSlice.name]; - -const watercourseSelectors = watercourseAdapter.getSelectors( - (state) => state.entitiesReducer[watercourseSlice.name], -); - -export const watercourseEntitiesSelector = watercourseSelectors.selectEntities; -export const watercoursesSelector = createSelector( - [watercourseSelectors.selectAll, loginSelector], - (watercourses, { farm_id }) => { - return watercourses.filter( - (watercourse) => watercourse.farm_id === farm_id && !watercourse.deleted, - ); - }, -); - -export const watercourseSelector = (location_id) => - createSelector(watercourseEntitiesSelector, (entities) => entities[location_id]); diff --git a/packages/webapp/src/main.jsx b/packages/webapp/src/main.jsx index c0564e7156..7ef6cacaea 100644 --- a/packages/webapp/src/main.jsx +++ b/packages/webapp/src/main.jsx @@ -26,23 +26,8 @@ import peopleSaga from './containers/Profile/People/saga'; import signUpSaga from './containers/CustomSignUp/saga'; import resetUserPasswordSaga from './containers/PasswordResetAccount/saga'; import outroSaga from './containers/Outro/saga'; -import fieldLocationSaga from './containers/LocationDetails/AreaDetails/FieldDetailForm/saga'; import documentSaga from './containers/Documents/saga'; import managementPlanSaga from './containers/Crop/saga'; -import gardenSaga from './containers/LocationDetails/AreaDetails/GardenDetailForm/saga'; -import gateSaga from './containers/LocationDetails/PointDetails/GateDetailForm/saga'; -import waterValveSaga from './containers/LocationDetails/PointDetails/WaterValveDetailForm/saga'; -import naturalAreaSaga from './containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/saga'; -import barnSaga from './containers/LocationDetails/AreaDetails/BarnDetailForm/saga'; -import surfaceWaterSaga from './containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/saga'; -import greenhouseSaga from './containers/LocationDetails/AreaDetails/GreenhouseDetailForm/saga'; -import ceremonialSaga from './containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/saga'; -import residenceSaga from './containers/LocationDetails/AreaDetails/ResidenceDetailForm/saga'; -import farmSiteBoundarySaga from './containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/saga'; -import fenceSaga from './containers/LocationDetails/LineDetails/FenceDetailForm/saga'; -import bufferZoneSaga from './containers/LocationDetails/LineDetails/BufferZoneDetailForm/saga'; -import watercourseSaga from './containers/LocationDetails/LineDetails/WatercourseDetailForm/saga'; -import soilSampleLocationLocationSaga from './containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/saga'; import financeSaga from './containers/Finances/saga'; import varietalSaga from './containers/AddCropVariety/saga'; import insightSaga from './containers/Insights/saga'; @@ -99,22 +84,7 @@ sagaMiddleware.run(peopleSaga); sagaMiddleware.run(signUpSaga); sagaMiddleware.run(resetUserPasswordSaga); sagaMiddleware.run(outroSaga); -sagaMiddleware.run(fieldLocationSaga); sagaMiddleware.run(managementPlanSaga); -sagaMiddleware.run(gardenSaga); -sagaMiddleware.run(gateSaga); -sagaMiddleware.run(barnSaga); -sagaMiddleware.run(surfaceWaterSaga); -sagaMiddleware.run(bufferZoneSaga); -sagaMiddleware.run(naturalAreaSaga); -sagaMiddleware.run(greenhouseSaga); -sagaMiddleware.run(residenceSaga); -sagaMiddleware.run(ceremonialSaga); -sagaMiddleware.run(waterValveSaga); -sagaMiddleware.run(farmSiteBoundarySaga); -sagaMiddleware.run(fenceSaga); -sagaMiddleware.run(watercourseSaga); -sagaMiddleware.run(soilSampleLocationLocationSaga); sagaMiddleware.run(financeSaga); sagaMiddleware.run(varietalSaga); sagaMiddleware.run(insightSaga); diff --git a/packages/webapp/src/routes/BarnDetailsRoutes.jsx b/packages/webapp/src/routes/BarnDetailsRoutes.jsx deleted file mode 100644 index 76504ff12f..0000000000 --- a/packages/webapp/src/routes/BarnDetailsRoutes.jsx +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditBarnDetailForm from '../containers/LocationDetails/AreaDetails/BarnDetailForm/EditBarn'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function BarnDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && } />} - } /> - - ); -} diff --git a/packages/webapp/src/routes/BufferZoneDetailsRoutes.jsx b/packages/webapp/src/routes/BufferZoneDetailsRoutes.jsx deleted file mode 100644 index 473865174c..0000000000 --- a/packages/webapp/src/routes/BufferZoneDetailsRoutes.jsx +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditBufferZoneDetailForm from '../containers/LocationDetails/LineDetails/BufferZoneDetailForm/EditBufferZone'; -import LocationManagementPlan from '../containers/LocationDetails/LocationManagementPlan'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; -import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; -import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; - -export default function BufferZoneDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - } /> - } - /> - } /> - - ); -} diff --git a/packages/webapp/src/routes/CeremonialAreaDetailsRoutes.jsx b/packages/webapp/src/routes/CeremonialAreaDetailsRoutes.jsx deleted file mode 100644 index 5c8fde635a..0000000000 --- a/packages/webapp/src/routes/CeremonialAreaDetailsRoutes.jsx +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditCeremonialAreaForm from '../containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/EditCeremonialArea'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function CeremonialAreaDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/FarmSiteBoundaryDetailsRoutes.jsx b/packages/webapp/src/routes/FarmSiteBoundaryDetailsRoutes.jsx deleted file mode 100644 index 29d2f74463..0000000000 --- a/packages/webapp/src/routes/FarmSiteBoundaryDetailsRoutes.jsx +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditFarmSiteBoundaryDetailForm from '../containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/EditFarmSiteBoundary'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; -import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; -import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; - -export default function FarmSiteBoundaryDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - } - /> - } - /> - - ); -} diff --git a/packages/webapp/src/routes/FenceDetailsRoutes.jsx b/packages/webapp/src/routes/FenceDetailsRoutes.jsx deleted file mode 100644 index bc8f5d8d99..0000000000 --- a/packages/webapp/src/routes/FenceDetailsRoutes.jsx +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditFenceDetailForm from '../containers/LocationDetails/LineDetails/FenceDetailForm/EditFence'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function FenceDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && ( - } /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/FieldDetailsRoutes.jsx b/packages/webapp/src/routes/FieldDetailsRoutes.jsx deleted file mode 100644 index 7c493417bb..0000000000 --- a/packages/webapp/src/routes/FieldDetailsRoutes.jsx +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditFieldDetailForm from '../containers/LocationDetails/AreaDetails/FieldDetailForm/EditField'; -import LocationManagementPlan from '../containers/LocationDetails/LocationManagementPlan'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; -import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; - -export default function FieldDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && ( - } /> - )} - } /> - } /> - } - /> - } /> - - ); -} diff --git a/packages/webapp/src/routes/GardenDetailsRoutes.jsx b/packages/webapp/src/routes/GardenDetailsRoutes.jsx deleted file mode 100644 index 810493aa7f..0000000000 --- a/packages/webapp/src/routes/GardenDetailsRoutes.jsx +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditGardenDetailForm from '../containers/LocationDetails/AreaDetails/GardenDetailForm/EditGarden'; -import LocationManagementPlan from '../containers/LocationDetails/LocationManagementPlan'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; -import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; -import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; - -export default function GardenDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && ( - } /> - )} - } /> - } /> - } - /> - } /> - - ); -} diff --git a/packages/webapp/src/routes/GateDetailsRoutes.jsx b/packages/webapp/src/routes/GateDetailsRoutes.jsx deleted file mode 100644 index 6e6ace1955..0000000000 --- a/packages/webapp/src/routes/GateDetailsRoutes.jsx +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditGateDetailForm from '../containers/LocationDetails/PointDetails/GateDetailForm/EditGate'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function GateDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && } />} - } /> - - ); -} diff --git a/packages/webapp/src/routes/GreenhouseDetailsRoutes.jsx b/packages/webapp/src/routes/GreenhouseDetailsRoutes.jsx deleted file mode 100644 index d30c16ecc6..0000000000 --- a/packages/webapp/src/routes/GreenhouseDetailsRoutes.jsx +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditGreenhouseDetailForm from '../containers/LocationDetails/AreaDetails/GreenhouseDetailForm/EditGreenhouse'; -import LocationManagementPlan from '../containers/LocationDetails/LocationManagementPlan'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; -import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; -import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; - -export default function GreenhouseDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } /> - )} - } /> - } /> - } - /> - } /> - - ); -} diff --git a/packages/webapp/src/routes/LocationDetailsRoutes.jsx b/packages/webapp/src/routes/LocationDetailsRoutes.jsx new file mode 100644 index 0000000000..f09d0a2711 --- /dev/null +++ b/packages/webapp/src/routes/LocationDetailsRoutes.jsx @@ -0,0 +1,119 @@ +/* + * Copyright 2026 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +/* eslint-disable react/no-children-prop */ +import { Route } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { isAdminSelector } from '../containers/userFarmSlice'; +import LocationTasks from '../containers/LocationDetails/LocationTasks'; +import LocationManagementPlan from '../containers/LocationDetails/LocationManagementPlan'; +import LocationFieldTechnology from '../containers/LocationDetails/LocationFieldTechnology'; +import LocationIrrigation from '../containers/LocationDetails/LocationIrrigation'; +import EditLocationDetailForm from '../containers/LocationDetails/EditLocationDetailForm'; + +// Location route configuration - maps location type to enabled tabs +export const locationRouteConfig = { + field: { + enabledTabs: ['details', 'crops', 'tasks', 'field_technology', 'irrigation'], + }, + garden: { + enabledTabs: ['details', 'crops', 'tasks', 'field_technology', 'irrigation'], + }, + greenhouse: { + enabledTabs: ['details', 'crops', 'tasks', 'field_technology', 'irrigation'], + }, + barn: { + enabledTabs: ['details', 'tasks'], + }, + gate: { + enabledTabs: ['details', 'tasks'], + }, + water_valve: { + enabledTabs: ['details', 'tasks'], + }, + soil_sample_location: { + enabledTabs: ['details', 'tasks'], + }, + buffer_zone: { + enabledTabs: ['details', 'crops', 'tasks', 'field_technology', 'irrigation'], + }, + watercourse: { + enabledTabs: ['details', 'tasks'], + }, + fence: { + enabledTabs: ['details', 'tasks'], + }, + natural_area: { + enabledTabs: ['details', 'tasks'], + }, + residence: { + enabledTabs: ['details', 'tasks'], + }, + surface_water: { + enabledTabs: ['details', 'tasks'], + }, + ceremonial_area: { + enabledTabs: ['details', 'tasks'], + }, + farm_site_boundary: { + enabledTabs: ['details', 'tasks', 'field_technology', 'irrigation'], + }, +}; + +export const allLocationTypes = Object.keys(locationRouteConfig); + +export default function LocationDetailsRoutes({ locationType }) { + const isAdmin = useSelector(isAdminSelector); + const config = locationRouteConfig[locationType]; + + if (!config) { + return null; + } + + const prefix = `/${locationType}/:location_id`; + + return ( + <> + } + /> + {isAdmin && ( + } + /> + )} + + {config.enabledTabs.includes('tasks') && ( + } /> + )} + + {config.enabledTabs.includes('crops') && ( + } /> + )} + + {config.enabledTabs.includes('field_technology') && ( + } /> + )} + + {config.enabledTabs.includes('irrigation') && ( + } /> + )} + + ); +} diff --git a/packages/webapp/src/routes/NaturalAreaDetailsRoutes.jsx b/packages/webapp/src/routes/NaturalAreaDetailsRoutes.jsx deleted file mode 100644 index 50a9152a22..0000000000 --- a/packages/webapp/src/routes/NaturalAreaDetailsRoutes.jsx +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditNaturalAreaDetailForm from '../containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/EditNaturalArea'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function NaturalAreaDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/ResidenceDetailsRoutes.jsx b/packages/webapp/src/routes/ResidenceDetailsRoutes.jsx deleted file mode 100644 index c339a08516..0000000000 --- a/packages/webapp/src/routes/ResidenceDetailsRoutes.jsx +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditResidenceDetailForm from '../containers/LocationDetails/AreaDetails/ResidenceDetailForm/EditResidence'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function ResidenceDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } /> - {isAdmin && ( - } /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/SoilSampleLocationDetailsRoutes.jsx b/packages/webapp/src/routes/SoilSampleLocationDetailsRoutes.jsx deleted file mode 100644 index b88fa8dd95..0000000000 --- a/packages/webapp/src/routes/SoilSampleLocationDetailsRoutes.jsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2025 LiteFarm.org - * This file is part of LiteFarm. - * - * LiteFarm is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LiteFarm is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details, see . - */ - -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditSoilSampleLocationDetailForm from '../containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/EditSoilSampleLocation'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function SoilSampleLocationDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/SurfaceWaterDetailsRoutes.jsx b/packages/webapp/src/routes/SurfaceWaterDetailsRoutes.jsx deleted file mode 100644 index 670b383d54..0000000000 --- a/packages/webapp/src/routes/SurfaceWaterDetailsRoutes.jsx +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditSurfaceWaterDetailForm from '../containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/EditSurfaceWater'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function SurfaceWaterDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/WaterValveDetailsRoutes.jsx b/packages/webapp/src/routes/WaterValveDetailsRoutes.jsx deleted file mode 100644 index 018ebf333b..0000000000 --- a/packages/webapp/src/routes/WaterValveDetailsRoutes.jsx +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditWaterValveDetailForm from '../containers/LocationDetails/PointDetails/WaterValveDetailForm/EditWaterValve'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function WaterValveDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/WatercourseDetailsRoutes.jsx b/packages/webapp/src/routes/WatercourseDetailsRoutes.jsx deleted file mode 100644 index 7387aea16f..0000000000 --- a/packages/webapp/src/routes/WatercourseDetailsRoutes.jsx +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable react/no-children-prop */ -import { Route } from 'react-router-dom'; -import EditWatercourseDetailForm from '../containers/LocationDetails/LineDetails/WatercourseDetailForm/EditWatercourse'; -import { useSelector } from 'react-redux'; -import { isAdminSelector } from '../containers/userFarmSlice'; -import LocationTasks from '../containers/LocationDetails/LocationTasks'; - -export default function WatercourseDetailsRoutes() { - const isAdmin = useSelector(isAdminSelector); - return ( - <> - } - /> - {isAdmin && ( - } - /> - )} - } /> - - ); -} diff --git a/packages/webapp/src/routes/index.jsx b/packages/webapp/src/routes/index.jsx index 803755d1a0..2e85091412 100644 --- a/packages/webapp/src/routes/index.jsx +++ b/packages/webapp/src/routes/index.jsx @@ -63,88 +63,20 @@ const Prices = React.lazy(() => import('../containers/Insights/Prices')); const TapeRoutes = React.lazy(() => import('./TapeRoutes')); const ExpiredTokenScreen = React.lazy(() => import('../containers/ExpiredTokenScreen')); const Map = React.lazy(() => import('../containers/Map')); -const PostFarmSiteBoundaryForm = React.lazy(() => - import( - '../containers/LocationDetails/AreaDetails/FarmSiteBoundaryDetailForm/PostFarmSiteBoundary' - ), -); -const FarmSiteBoundaryDetails = React.lazy(() => import('./FarmSiteBoundaryDetailsRoutes')); - -const PostFieldForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/FieldDetailForm/PostField'), -); -const FieldDetails = React.lazy(() => import('./FieldDetailsRoutes')); - -const PostGardenForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/GardenDetailForm/PostGarden'), -); -const GardenDetails = React.lazy(() => import('./GardenDetailsRoutes')); - -const PostGateForm = React.lazy(() => - import('../containers/LocationDetails/PointDetails/GateDetailForm/PostGate'), -); -const GateDetails = React.lazy(() => import('./GateDetailsRoutes')); - -const PostWaterValveForm = React.lazy(() => - import('../containers/LocationDetails/PointDetails/WaterValveDetailForm/PostWaterValve'), -); -const WaterValveDetails = React.lazy(() => import('./WaterValveDetailsRoutes')); - -const PostSoilSampleLocationForm = React.lazy(() => - import( - '../containers/LocationDetails/PointDetails/SoilSampleLocationDetailForm/PostSoilSampleLocation' - ), -); - -const SoilSampleLocationDetails = React.lazy(() => import('./SoilSampleLocationDetailsRoutes')); - -const PostBarnForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/BarnDetailForm/PostBarn'), -); -const BarnDetails = React.lazy(() => import('./BarnDetailsRoutes')); - -const PostNaturalAreaForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/NaturalAreaDetailForm/PostNaturalArea'), -); -const NaturalAreaDetails = React.lazy(() => import('./NaturalAreaDetailsRoutes')); -const PostSurfaceWaterForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/SurfaceWaterDetailForm/PostSurfaceWater'), -); -const SurfaceWaterDetails = React.lazy(() => import('./SurfaceWaterDetailsRoutes')); +const LocationDetailsRoutes = React.lazy(() => import('./LocationDetailsRoutes')); -const PostResidenceForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/ResidenceDetailForm/PostResidence'), -); -const ResidenceDetails = React.lazy(() => import('./ResidenceDetailsRoutes')); - -const PostCeremonialForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/CeremonialAreaDetailForm/PostCeremonialArea'), -); -const CeremonialAreaDetails = React.lazy(() => import('./CeremonialAreaDetailsRoutes')); +// Import config separately to access allLocationTypes at render time +import { allLocationTypes as allLocationTypesConfig } from './LocationDetailsRoutes.jsx'; +const allLocationTypes = allLocationTypesConfig; -const PostGreenhouseForm = React.lazy(() => - import('../containers/LocationDetails/AreaDetails/GreenhouseDetailForm/PostGreenhouse'), +const PostLocationDetailForm = React.lazy( + () => import('../containers/LocationDetails/PostLocationDetailForm'), ); -const GreenhouseDetails = React.lazy(() => import('./GreenhouseDetailsRoutes')); const CropManagement = React.lazy(() => import('../containers/Crop/CropManagement')); const CropDetail = React.lazy(() => import('../containers/Crop/CropDetail/index')); -const PostFenceForm = React.lazy(() => - import('../containers/LocationDetails/LineDetails/FenceDetailForm/PostFence'), -); -const FenceDetails = React.lazy(() => import('./FenceDetailsRoutes')); - -const PostBufferZoneForm = React.lazy(() => - import('../containers/LocationDetails/LineDetails/BufferZoneDetailForm/PostBufferZone'), -); -const BufferZoneDetails = React.lazy(() => import('./BufferZoneDetailsRoutes')); - -const PostWatercourseForm = React.lazy(() => - import('../containers/LocationDetails/LineDetails/WatercourseDetailForm/PostWatercourse'), -); -const WatercourseDetails = React.lazy(() => import('./WatercourseDetailsRoutes')); const AddSensorsForm = React.lazy(() => import('../containers/AddSensors')); const CropCatalogue = React.lazy(() => import('../containers/CropCatalogue')); @@ -153,34 +85,34 @@ const AddCrop = React.lazy(() => import('../containers/AddCropVariety/AddCropVar const EditCrop = React.lazy(() => import('../containers/EditCropVariety')); const ComplianceInfo = React.lazy(() => import('../containers/AddCropVariety/ComplianceInfo')); const AddNewCrop = React.lazy(() => import('../containers/AddNewCrop')); -const PlantingLocation = React.lazy(() => - import('../containers/Crop/AddManagementPlan/PlantingLocation'), +const PlantingLocation = React.lazy( + () => import('../containers/Crop/AddManagementPlan/PlantingLocation'), ); const Transplant = React.lazy(() => import('../containers/Crop/AddManagementPlan/Transplant')); const PlantingDate = React.lazy(() => import('../containers/Crop/AddManagementPlan/PlantingDate')); -const PlantingMethod = React.lazy(() => - import('../containers/Crop/AddManagementPlan/PlantingMethod'), +const PlantingMethod = React.lazy( + () => import('../containers/Crop/AddManagementPlan/PlantingMethod'), ); -const PlantInContainer = React.lazy(() => - import('../containers/Crop/AddManagementPlan/PlantInContainer'), +const PlantInContainer = React.lazy( + () => import('../containers/Crop/AddManagementPlan/PlantInContainer'), ); -const PlantBroadcast = React.lazy(() => - import('../containers/Crop/AddManagementPlan/BroadcastPlan'), +const PlantBroadcast = React.lazy( + () => import('../containers/Crop/AddManagementPlan/BroadcastPlan'), ); const BedPlan = React.lazy(() => import('../containers/Crop/AddManagementPlan/BedPlan/BedPlan')); -const BedPlanGuidance = React.lazy(() => - import('../containers/Crop/AddManagementPlan/BedPlan/BedPlanGuidance'), +const BedPlanGuidance = React.lazy( + () => import('../containers/Crop/AddManagementPlan/BedPlan/BedPlanGuidance'), ); -const ManagementPlanName = React.lazy(() => - import('../containers/Crop/AddManagementPlan/ManagementPlanName'), +const ManagementPlanName = React.lazy( + () => import('../containers/Crop/AddManagementPlan/ManagementPlanName'), ); const RowMethod = React.lazy(() => import('../containers/Crop/AddManagementPlan/RowMethod')); -const RowMethodGuidance = React.lazy(() => - import('../containers/Crop/AddManagementPlan/RowMethod/RowGuidance'), +const RowMethodGuidance = React.lazy( + () => import('../containers/Crop/AddManagementPlan/RowMethod/RowGuidance'), ); -const PlantedAlready = React.lazy(() => - import('../containers/Crop/AddManagementPlan/PlantedAlready'), +const PlantedAlready = React.lazy( + () => import('../containers/Crop/AddManagementPlan/PlantedAlready'), ); const Documents = React.lazy(() => import('../containers/Documents')); @@ -189,60 +121,63 @@ const EditDocument = React.lazy(() => import('../containers/Documents/Edit')); const AddDocument = React.lazy(() => import('../containers/Documents/Add')); const MainDocument = React.lazy(() => import('../containers/Documents/Main')); -const CertificationReportingPeriod = React.lazy(() => - import('../containers/Certifications/ReportingPeriod'), +const CertificationReportingPeriod = React.lazy( + () => import('../containers/Certifications/ReportingPeriod'), ); const CertificationSurvey = React.lazy(() => import('../containers/Certifications/Survey')); -const InterestedOrganic = React.lazy(() => - import('../containers/OrganicCertifierSurvey/InterestedOrganic/UpdateInterestedOrganic'), +const InterestedOrganic = React.lazy( + () => import('../containers/OrganicCertifierSurvey/InterestedOrganic/UpdateInterestedOrganic'), ); -const CertificationSelection = React.lazy(() => - import( - '../containers/OrganicCertifierSurvey/CertificationSelection/UpdateCertificationSelection' - ), +const CertificationSelection = React.lazy( + () => + import( + '../containers/OrganicCertifierSurvey/CertificationSelection/UpdateCertificationSelection' + ), ); -const CertifierSelectionMenu = React.lazy(() => - import( - '../containers/OrganicCertifierSurvey/CertifierSelectionMenu/UpdateCertifierSelectionMenu' - ), +const CertifierSelectionMenu = React.lazy( + () => + import( + '../containers/OrganicCertifierSurvey/CertifierSelectionMenu/UpdateCertifierSelectionMenu' + ), ); -const SetCertificationSummary = React.lazy(() => - import( - '../containers/OrganicCertifierSurvey/SetCertificationSummary/UpdateSetCertificationSummary' - ), +const SetCertificationSummary = React.lazy( + () => + import( + '../containers/OrganicCertifierSurvey/SetCertificationSummary/UpdateSetCertificationSummary' + ), ); -const RequestCertifier = React.lazy(() => - import('../containers/OrganicCertifierSurvey/RequestCertifier/UpdateRequestCertifier'), +const RequestCertifier = React.lazy( + () => import('../containers/OrganicCertifierSurvey/RequestCertifier/UpdateRequestCertifier'), ); -const ViewCertification = React.lazy(() => - import('../containers/OrganicCertifierSurvey/ViewCertification/ViewCertification'), +const ViewCertification = React.lazy( + () => import('../containers/OrganicCertifierSurvey/ViewCertification/ViewCertification'), ); const RenderSurvey = React.lazy(() => import('../containers/RenderSurvey/RenderSurvey')); const ExportDownload = React.lazy(() => import('../containers/ExportDownload')); -const ManagementTasks = React.lazy(() => - import('../containers/Crop/ManagementDetail/ManagementTasks'), +const ManagementTasks = React.lazy( + () => import('../containers/Crop/ManagementDetail/ManagementTasks'), ); -const ManagementDetails = React.lazy(() => - import('../containers/Crop/ManagementDetail/ManagementDetails'), +const ManagementDetails = React.lazy( + () => import('../containers/Crop/ManagementDetail/ManagementDetails'), ); -const EditManagementDetails = React.lazy(() => - import('../containers/Crop/ManagementDetail/EditManagementDetails'), +const EditManagementDetails = React.lazy( + () => import('../containers/Crop/ManagementDetail/EditManagementDetails'), ); -const CompleteManagementPlan = React.lazy(() => - import('../containers/Crop/CompleteManagementPlan/CompleteManagementPlan'), +const CompleteManagementPlan = React.lazy( + () => import('../containers/Crop/CompleteManagementPlan/CompleteManagementPlan'), ); -const AbandonManagementPlan = React.lazy(() => - import('../containers/Crop/CompleteManagementPlan/AbandonManagementPlan'), +const AbandonManagementPlan = React.lazy( + () => import('../containers/Crop/CompleteManagementPlan/AbandonManagementPlan'), ); const RepeatCropPlan = React.lazy(() => import('../containers/Crop/RepeatCropPlan')); -const RepeatCropPlanConfirmation = React.lazy(() => - import('../containers/Crop/RepeatCropPlan/Confirmation'), +const RepeatCropPlanConfirmation = React.lazy( + () => import('../containers/Crop/RepeatCropPlan/Confirmation'), ); const TaskAssignment = React.lazy(() => import('../containers/Task/TaskAssignment')); @@ -256,44 +191,44 @@ const Tasks = React.lazy(() => import('../containers/Task')); const ManageCustomTasks = React.lazy(() => import('../containers/Task/ManageCustomTasks')); const AddCustomTask = React.lazy(() => import('../containers/Task/AddCustomTask')); const TaskComplete = React.lazy(() => import('../containers/Task/TaskComplete')); -const HarvestCompleteQuantity = React.lazy(() => - import('../containers/Task/TaskComplete/HarvestComplete/Quantity'), +const HarvestCompleteQuantity = React.lazy( + () => import('../containers/Task/TaskComplete/HarvestComplete/Quantity'), ); -const HarvestUses = React.lazy(() => - import('../containers/Task/TaskComplete/HarvestComplete/HarvestUses'), +const HarvestUses = React.lazy( + () => import('../containers/Task/TaskComplete/HarvestComplete/HarvestUses'), ); const TaskCompleteStepOne = React.lazy(() => import('../containers/Task/TaskComplete/StepOne')); const TaskReadOnly = React.lazy(() => import('../containers/Task/TaskReadOnly')); const EditCustomTask = React.lazy(() => import('../containers/Task/EditCustomTask')); const TaskAbandon = React.lazy(() => import('../containers/Task/TaskAbandon')); // const EditCustomTaskUpdate = React.lazy(() => import('../containers/Task/EditCustomTaskUpdate')); -const TaskTransplantMethod = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskTransplantMethod'), +const TaskTransplantMethod = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskTransplantMethod'), ); -const TaskBedMethod = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskBedMethod'), +const TaskBedMethod = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskBedMethod'), ); -const TaskBedGuidance = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskBedGuidance'), +const TaskBedGuidance = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskBedGuidance'), ); -const TaskRowMethod = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskRowMethod'), +const TaskRowMethod = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskRowMethod'), ); -const TaskRowGuidance = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskRowGuidance'), +const TaskRowGuidance = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskRowGuidance'), ); -const TaskContainerMethod = React.lazy(() => - import('../containers/Task/TaskTransplantMethod/TaskContainerMethod'), +const TaskContainerMethod = React.lazy( + () => import('../containers/Task/TaskTransplantMethod/TaskContainerMethod'), ); const SensorList = React.lazy(() => import('../containers/SensorList')); const SensorReadings = React.lazy(() => import('../containers/SensorReadings/v2')); const IrrigationPrescription = React.lazy(() => import('../containers/IrrigationPrescription')); const Notification = React.lazy(() => import('../containers/Notification')); -const NotificationReadOnly = React.lazy(() => - import('../containers/Notification/NotificationReadOnly'), +const NotificationReadOnly = React.lazy( + () => import('../containers/Notification/NotificationReadOnly'), ); -const UnknownRecord = React.lazy(() => - import('../containers/ErrorHandler/UnknownRecord/UnknownRecord'), +const UnknownRecord = React.lazy( + () => import('../containers/ErrorHandler/UnknownRecord/UnknownRecord'), ); const Routes = ({ isCompactSideMenu }) => { @@ -547,88 +482,23 @@ const Routes = ({ isCompactSideMenu }) => { - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } /> - } /> - } - /> - } - /> - } /> - } - /> - } - /> + {allLocationTypes.map((type) => ( + } + /> + ))} - } - /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> + {allLocationTypes.map((type) => ( + } + /> + ))} @@ -933,88 +803,23 @@ const Routes = ({ isCompactSideMenu }) => { - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } /> - } /> - } - /> - } - /> - } /> - } - /> - } - /> + {allLocationTypes.map((type) => ( + } + /> + ))} - } - /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> + {allLocationTypes.map((type) => ( + } + /> + ))} @@ -1191,30 +996,13 @@ const Routes = ({ isCompactSideMenu }) => { - } - /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - } /> + {allLocationTypes.map((type) => ( + } + /> + ))} diff --git a/packages/webapp/src/store/api/farmNoteApi.ts b/packages/webapp/src/store/api/farmNoteApi.ts index 97b489add9..bbdabd8c15 100644 --- a/packages/webapp/src/store/api/farmNoteApi.ts +++ b/packages/webapp/src/store/api/farmNoteApi.ts @@ -138,7 +138,7 @@ export const farmNoteApi = api.injectEndpoints({ ...draft[noteIndex], note: data.note, is_private: data.is_private, - image_url: file ? 'pending' : data.image_url ?? draft[noteIndex].image_url, + image_url: file ? 'pending' : (data.image_url ?? draft[noteIndex].image_url), updated_at: new Date().toISOString(), to_sync: true, }; diff --git a/packages/webapp/src/store/api/locationApi.ts b/packages/webapp/src/store/api/locationApi.ts index 08f716ba33..71e19f29eb 100644 --- a/packages/webapp/src/store/api/locationApi.ts +++ b/packages/webapp/src/store/api/locationApi.ts @@ -66,11 +66,13 @@ export const locationApi = api.injectEndpoints({ type: InternalMapLocationType; }> >({ - query: ({ data, type, location_id }) => ({ - url: `${locationURL}/${type}/${location_id}`, - method: 'PUT', - body: data, - }), + query: ({ data, type, location_id }) => { + return { + url: `${locationURL}/${type}/${location_id}`, + method: 'PUT', + body: data, + }; + }, invalidatesTags: ['Locations'], }), }), diff --git a/packages/webapp/src/store/api/types/index.ts b/packages/webapp/src/store/api/types/index.ts index 09f7c61833..1d128a40ba 100644 --- a/packages/webapp/src/store/api/types/index.ts +++ b/packages/webapp/src/store/api/types/index.ts @@ -810,7 +810,7 @@ export interface SurfaceWater extends LocationWithFigure { [InternalMapLocationType.WATER_VALVE]: null; } -enum BufferWidthUnit { +export enum BufferWidthUnit { CM = 'cm', M = 'm', KM = 'km', @@ -842,14 +842,14 @@ export interface Watercourse extends LocationWithFigure { [InternalMapLocationType.WATER_VALVE]: null; } -enum WaterSource { +export enum WaterSource { MUNICIPAL_WATER = 'Municipal water', SURFACE_WATER = 'Surface water', GROUNDWATER = 'Groundwater', RAIN_WATER = 'Rain water', } -enum FlowRateUnit { +export enum FlowRateUnit { L_PER_MIN = 'l/min', L_PER_H = 'l/h', GAL_PER_MIN = 'gal/min', diff --git a/packages/webapp/src/store/reducer.js b/packages/webapp/src/store/reducer.js index 9b34c8ac7b..8bc694e479 100644 --- a/packages/webapp/src/store/reducer.js +++ b/packages/webapp/src/store/reducer.js @@ -29,22 +29,6 @@ import notificationReducer from '../containers/notificationSlice'; import chooseFarmFlowReducer from '../containers/ChooseFarm/chooseFarmFlowSlice'; import offlineReadinessReducer from '../hooks/useOfflineReadiness/offlineReadinessSlice'; -import barnReducer from '../containers/barnSlice'; -import ceremonialReducer from '../containers/ceremonialSlice'; -import farmSiteBoundaryReducer from '../containers/farmSiteBoundarySlice'; -import fieldReducer from '../containers/fieldSlice'; -import gardenReducer from '../containers/gardenSlice'; -import greenhouseReducer from '../containers/greenhouseSlice'; -import surfaceWaterReducer from '../containers/surfaceWaterSlice'; -import naturalAreaReducer from '../containers/naturalAreaSlice'; -import residenceReducer from '../containers/residenceSlice'; -import bufferZoneReducer from '../containers/bufferZoneSlice'; -import watercourseReducer from '../containers/watercourseSlice'; -import fenceReducer from '../containers/fenceSlice'; -import gateReducer from '../containers/gateSlice'; -import waterValveReducer from '../containers/waterValveSlice'; -import soilSampleLocationReducer from '../containers/soilSampleLocationSlice'; - import cropReducer from '../containers/cropSlice'; import cropVarietyReducer from '../containers/cropVarietySlice'; import taskReducer from '../containers/taskSlice'; @@ -165,21 +149,6 @@ const entitiesReducer = combineReducers({ cropReducer, cropVarietyReducer, alertReducer, - barnReducer, - ceremonialReducer, - farmSiteBoundaryReducer, - fieldReducer, - gardenReducer, - greenhouseReducer, - surfaceWaterReducer, - naturalAreaReducer, - residenceReducer, - bufferZoneReducer, - watercourseReducer, - fenceReducer, - gateReducer, - waterValveReducer, - soilSampleLocationReducer, showedSpotlightReducer, managementPlanReducer, cropManagementPlanReducer, diff --git a/packages/webapp/src/stories/MuiFullPagePopup/MuiFullPagePopup.stories.jsx b/packages/webapp/src/stories/MuiFullPagePopup/MuiFullPagePopup.stories.jsx index 0d251ecab2..a8c993eb0e 100644 --- a/packages/webapp/src/stories/MuiFullPagePopup/MuiFullPagePopup.stories.jsx +++ b/packages/webapp/src/stories/MuiFullPagePopup/MuiFullPagePopup.stories.jsx @@ -1,7 +1,6 @@ -import React from 'react'; import MuiFullPagePopup from '../../components/MuiFullPagePopup/v2'; import decorator from '../Pages/config/Decorators'; -import { Post } from '../Pages/LocationDetails/AreaDetails/Barn/Barn.stories'; +import { Post } from '../Pages/LocationDetails/AreaDetails.stories'; import { chromaticSmallScreen } from '../Pages/config/chromatic'; export default { @@ -12,11 +11,11 @@ export default { const Template = (args) => ; -export const Barn = Template.bind({}); -Barn.args = { +export const Area = Template.bind({}); +Area.args = { open: true, children: , }; -Barn.parameters = { +Area.parameters = { ...chromaticSmallScreen, }; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/AreaDetails.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails.stories.jsx similarity index 82% rename from packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/AreaDetails.stories.jsx rename to packages/webapp/src/stories/Pages/LocationDetails/AreaDetails.stories.jsx index 43551fb1c6..e5f6530d9f 100644 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/AreaDetails.stories.jsx +++ b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails.stories.jsx @@ -1,8 +1,7 @@ -import React from 'react'; -import AreaDetails from '../../../../components/LocationDetailLayout/AreaDetails/AreaDetails'; -import decorator from '../../config/Decorators'; +import AreaDetails from '../../../components/LocationDetailLayout/AreaDetails'; +import decorator from '../config/Decorators'; import { FormProvider, useForm } from 'react-hook-form'; -import { chromaticSmallScreen } from '../../config/chromatic'; +import { chromaticSmallScreen } from '../config/chromatic'; export default { title: 'Form/Location/Area/AreaDetails', @@ -56,7 +55,6 @@ Post.args = { getValues: (data) => {}, setError: (data) => {}, control: (data) => {}, - history: (data) => {}, match: { params: { location_id: 1 } }, children: (data) => {}, errors: (data) => {}, diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Barn/Barn.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Barn/Barn.stories.jsx deleted file mode 100644 index 761dae4ab2..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Barn/Barn.stories.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import Barn from '../../../../../components/LocationDetailLayout/AreaDetails/Barn'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/Barn', - decorators: decorator, - component: Barn, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/barn/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const WorkerView = Template.bind({}); -WorkerView.args = { - isViewLocationPage: true, - history: { location: { pathname: '/barn/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: false, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -WorkerView.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/CeremonialArea/CeremonialArea.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/CeremonialArea/CeremonialArea.stories.jsx deleted file mode 100644 index db785db26b..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/CeremonialArea/CeremonialArea.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import CeremonialArea from '../../../../../components/LocationDetailLayout/AreaDetails/CeremonialArea'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/CeremonialArea', - decorators: decorator, - component: CeremonialArea, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/ceremonial_area/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/FarmSiteBoundary/FarmSiteBoundary.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/FarmSiteBoundary/FarmSiteBoundary.stories.jsx deleted file mode 100644 index 2e121616ea..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/FarmSiteBoundary/FarmSiteBoundary.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import FarmSiteBoundary from '../../../../../components/LocationDetailLayout/AreaDetails/FarmSiteBoundary'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/FarmSiteBoundary', - decorators: decorator, - component: FarmSiteBoundary, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/farm_site_boundary/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Field/Field.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Field/Field.stories.jsx deleted file mode 100644 index 718df90ae6..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Field/Field.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import Field from '../../../../../components/LocationDetailLayout/AreaDetails/Field'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/Field', - decorators: decorator, - component: Field, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/field/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Garden/Garden.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Garden/Garden.stories.jsx deleted file mode 100644 index b13c22977c..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Garden/Garden.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import Garden from '../../../../../components/LocationDetailLayout/AreaDetails/Garden'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/Garden', - decorators: decorator, - component: Garden, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/garden/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Greenhouse/Greenhouse.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Greenhouse/Greenhouse.stories.jsx deleted file mode 100644 index a01a0544de..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Greenhouse/Greenhouse.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import Greenhouse from '../../../../../components/LocationDetailLayout/AreaDetails/Greenhouse'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/Greenhouse', - decorators: decorator, - component: Greenhouse, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/greenhouse/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/NaturalArea/NaturalArea.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/NaturalArea/NaturalArea.stories.jsx deleted file mode 100644 index b47b02cb24..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/NaturalArea/NaturalArea.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import NaturalArea from '../../../../../components/LocationDetailLayout/AreaDetails/NaturalArea'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/NaturalArea', - decorators: decorator, - component: NaturalArea, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/natural_area/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Residence/Residence.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Residence/Residence.stories.jsx deleted file mode 100644 index 4a63851626..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/Residence/Residence.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import Residence from '../../../../../components/LocationDetailLayout/AreaDetails/Residence'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/Residence', - decorators: decorator, - component: Residence, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/residence/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/SurfaceWater/SurfaceWater.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/SurfaceWater/SurfaceWater.stories.jsx deleted file mode 100644 index 778b0bc5ee..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/AreaDetails/SurfaceWater/SurfaceWater.stories.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import SurfaceWater from '../../../../../components/LocationDetailLayout/AreaDetails/SurfaceWater'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Area/SurfaceWater', - decorators: decorator, - component: SurfaceWater, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - areaType: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; - -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/surface_water/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/LineDetails.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/LineDetails.stories.jsx similarity index 73% rename from packages/webapp/src/stories/Pages/LocationDetails/LineDetails/LineDetails.stories.jsx rename to packages/webapp/src/stories/Pages/LocationDetails/LineDetails.stories.jsx index 6dd174f152..2c5a17ddcc 100644 --- a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/LineDetails.stories.jsx +++ b/packages/webapp/src/stories/Pages/LocationDetails/LineDetails.stories.jsx @@ -1,7 +1,6 @@ -import React from 'react'; -import LineDetails from '../../../../components/LocationDetailLayout/LineDetails/LineDetails'; -import decorator from '../../config/Decorators'; -import { chromaticSmallScreen } from '../../config/chromatic'; +import LineDetails from '../../../components/LocationDetailLayout/LineDetails'; +import decorator from '../config/Decorators'; +import { chromaticSmallScreen } from '../config/chromatic'; import { FormProvider, useForm } from 'react-hook-form'; export default { @@ -25,7 +24,6 @@ Post.args = { children: (data) => {}, setValue: (data) => {}, handleSubmit: (data) => {}, - history: (data) => {}, match: { params: { location_id: 1 } }, onError: (data) => {}, register: (data) => {}, diff --git a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/BufferZone/BufferZone.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/BufferZone/BufferZone.stories.jsx deleted file mode 100644 index 9a3712fb7b..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/BufferZone/BufferZone.stories.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import BufferZone from '../../../../../components/LocationDetailLayout/LineDetails/BufferZone'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Line/BufferZone', - decorators: decorator, - component: BufferZone, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, width: 1, length: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/buffer_zone/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Fence/Fence.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Fence/Fence.stories.jsx deleted file mode 100644 index 6eff380eaf..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Fence/Fence.stories.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import Fence from '../../../../../components/LocationDetailLayout/LineDetails/Fence'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Line/Fence', - decorators: decorator, - component: Fence, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, width: 1, length: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/fence/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Watercourse/Watercourse.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Watercourse/Watercourse.stories.jsx deleted file mode 100644 index c4a49a57d8..0000000000 --- a/packages/webapp/src/stories/Pages/LocationDetails/LineDetails/Watercourse/Watercourse.stories.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import Watercourse from '../../../../../components/LocationDetailLayout/LineDetails/Watercourse'; -import decorator from '../../../config/Decorators'; -import { chromaticSmallScreen } from '../../../config/chromatic'; - -export default { - title: 'Form/Location/Line/Watercourse', - decorators: decorator, - component: Watercourse, -}; - -const Template = (args) => ; - -export const Post = Template.bind({}); -Post.args = { - isCreateLocationPage: true, - history: (data) => {}, - match: { params: { location_id: 1 } }, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, width: 1, length: 2 }, -}; -Post.parameters = { - ...chromaticSmallScreen, -}; -export const View = Template.bind({}); -View.args = { - isViewLocationPage: true, - history: { location: { pathname: '/watercourse/location_id/details' } }, - match: { params: { location_id: 'location_id' } }, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -View.parameters = { - ...chromaticSmallScreen, -}; - -export const Edit = Template.bind({}); -Edit.args = { - match: { params: {} }, - isEditLocationPage: true, - submitForm: (data) => {}, - system: 'metric', - isAdmin: true, - - persistedFormData: { name: 'location', grid_points: {}, total_area: 1, perimeter: 2 }, -}; -Edit.parameters = { - ...chromaticSmallScreen, -}; diff --git a/packages/webapp/src/stories/Pages/LocationDetails/LocationFormWrapper.stories.jsx b/packages/webapp/src/stories/Pages/LocationDetails/LocationFormWrapper.stories.jsx new file mode 100644 index 0000000000..8d254ed362 --- /dev/null +++ b/packages/webapp/src/stories/Pages/LocationDetails/LocationFormWrapper.stories.jsx @@ -0,0 +1,344 @@ +/* + * Copyright 2026 LiteFarm.org + * This file is part of LiteFarm. + * + * LiteFarm is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LiteFarm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details, see . + */ + +import decorator from '../config/Decorators'; +import { chromaticSmallScreen } from '../config/chromatic'; +import PureLocationFormWrapper from '../../../components/LocationDetailLayout/PureLocationFormWrapper'; +import { InternalMapLocationType } from '../../../store/api/types'; +import { Provider } from 'react-redux'; +import { fakeDBLocation } from './util'; +import { pick } from '../../../util/pick'; +import { areaProperties, lineProperties, pointProperties } from '../../../containers/constants'; +import state from '../../../../.storybook/state'; + +const config = { + // areas + [InternalMapLocationType.BARN]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.BARN), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.BARN), + }, + [InternalMapLocationType.CEREMONIAL_AREA]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.CEREMONIAL_AREA), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.CEREMONIAL_AREA), + }, + [InternalMapLocationType.FARM_SITE_BOUNDARY]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.FARM_SITE_BOUNDARY), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.FARM_SITE_BOUNDARY), + }, + [InternalMapLocationType.FIELD]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.FIELD), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.FIELD), + }, + [InternalMapLocationType.GARDEN]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.GARDEN), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.GARDEN), + }, + [InternalMapLocationType.GREENHOUSE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.GREENHOUSE), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.GREENHOUSE), + }, + [InternalMapLocationType.NATURAL_AREA]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.NATURAL_AREA), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.NATURAL_AREA), + }, + [InternalMapLocationType.RESIDENCE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.RESIDENCE), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.RESIDENCE), + }, + [InternalMapLocationType.SURFACE_WATER]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.SURFACE_WATER), [ + 'name', + ...areaProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.SURFACE_WATER), + }, + // lines + [InternalMapLocationType.BUFFER_ZONE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.BUFFER_ZONE), [ + 'name', + ...lineProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.BUFFER_ZONE), + }, + [InternalMapLocationType.FENCE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.FENCE), [ + 'name', + ...lineProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.FENCE), + }, + [InternalMapLocationType.WATERCOURSE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.WATERCOURSE), [ + 'name', + ...lineProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.WATERCOURSE), + }, + // points + [InternalMapLocationType.GATE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.GATE), [ + 'name', + ...pointProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.GATE), + }, + [InternalMapLocationType.SOIL_SAMPLE_LOCATION]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.SOIL_SAMPLE_LOCATION), [ + 'name', + ...pointProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.SOIL_SAMPLE_LOCATION), + }, + [InternalMapLocationType.WATER_VALVE]: { + persistedFormData: pick(fakeDBLocation(InternalMapLocationType.WATER_VALVE), [ + 'name', + ...pointProperties, + ]), + locationAPIData: fakeDBLocation(InternalMapLocationType.WATER_VALVE), + }, +}; + +const getStore = (locationType) => ({ + getState: () => { + return { + api: { + queries: { + // Key format: 'endpointName(serializedArgs)' — check your actual hook call + // e.g. if called as useGetLocationsQuery() it's 'getLocations(undefined)' + // e.g. if called as useGetLocationsQuery({farm_id:'1'}) it's 'getLocations({"farm_id":"1"})' + 'getLocations(undefined)': { + status: 'fulfilled', + endpointName: 'getLocations', + requestId: 'PDUb54BP0uiv3UrAsO-kD', + startedTimeStamp: 1774388379804, + data: [{ ...config[locationType].locationAPIData }], + fulfilledTimeStamp: 1774388380476, + isUninitialized: false, + isLoading: false, + isSuccess: true, + isError: false, + originalArgs: undefined, + requestStatus: 'fulfilled', + }, + }, + mutations: {}, + provided: {}, + subscriptions: {}, + config: { + online: true, + focused: true, + middlewareRegistered: true, + refetchOnFocus: false, + refetchOnReconnect: false, + refetchOnMountOrArgChange: false, + keepUnusedDataFor: 60, + reducerPath: 'api', + }, + }, + ...state, + }; + }, + subscribe: () => () => {}, + dispatch: () => {}, +}); + +export default { + title: 'Form/Location/LocationFormWrappers', + decorators: decorator, + component: PureLocationFormWrapper, +}; + +const Template = (args) => ; + +const createStoryArgs = (locationType, mode) => { + const base = { + system: 'metric', + isAdmin: true, + history: () => {}, + submitForm: (data) => {}, + locationType, + persistedFormData: config[locationType].persistedFormData, + }; + + const location_id = config[locationType].locationAPIData.location_id; + + if (mode === 'post') { + return { + ...base, + isCreateLocationPage: true, + areaType: locationType, + match: { params: {} }, + }; + } + if (mode === 'view') { + return { + ...base, + isViewLocationPage: true, + match: { params: { location_id } }, + history: { location: { pathname: `/${locationType.toLowerCase()}/${location_id}/details` } }, + }; + } + if (mode === 'workerView') { + return { + ...base, + isViewLocationPage: true, + match: { params: { location_id } }, + history: { location: { pathname: `/${locationType.toLowerCase()}/${location_id}/details` } }, + isAdmin: false, + }; + } + if (mode === 'edit') { + return { + ...base, + isEditLocationPage: true, + match: { params: { location_id } }, + }; + } +}; + +const createStory = (locationType, mode) => ({ + render: () => ( + +