From f5fd0c3acbb3135ec9167e153e2bb9ab2d692263 Mon Sep 17 00:00:00 2001 From: GDamaso <80939533+GDamaso@users.noreply.github.com> Date: Sat, 13 Jul 2024 22:33:08 -0700 Subject: [PATCH] fix: Bb 265 fix soilleaf test data validation (#266) * Remove EmptyFieldDetails * Update Interfaces Just testing it out * Make Cheked radio button optional * Cleanup and fix for validationSchema data validation is working at this point * Define field type to BB Field Interface * Remove null options from necessary tests values * Explain empty string hack in comment * Lint * Remove dead code * Change numbers into empty strings This way input fields are not initialized to a valid input * HasLeafTest instead of soil for leaf tests validation * Fix leafTests not being added to exported file * Removed $ from HasSoilTest and HasLeafTest --- .../FieldsAndSoil/FieldsAndSoil.tsx | 87 +++++++++++-------- .../Input/RadioButton/CustomRadioButton.tsx | 2 +- frontend/src/Constants/EmptyFieldDetails.ts | 27 ------ frontend/src/Constants/InitialFarmDetails.ts | 32 ++++--- .../src/Interface/FieldDetailsInterface.tsx | 6 +- frontend/src/Interface/NmpInterface.tsx | 2 +- frontend/src/Utils/convertToNMP.ts | 38 ++++---- 7 files changed, 97 insertions(+), 97 deletions(-) delete mode 100644 frontend/src/Constants/EmptyFieldDetails.ts diff --git a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx index bbb87443..bd84b7e4 100644 --- a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx +++ b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx @@ -14,7 +14,6 @@ import CustomRadioButton from '@Commons/Input/RadioButton/CustomRadioButton'; import CustomSelect from '@Commons/Input/Select/CustomSelect'; import initialFarmDetails from '@Constants/InitialFarmDetails'; import ComponentText from '@Constants/ComponentText'; -import emptyFieldDetails from '@Constants/EmptyFieldDetails'; import { FIELDS_AND_SOIL } from '@Constants/ModuleIDs'; import soilTestOptions from '@Constants/SoilTestOptions'; import InputModuleInterface from '@Interface/InputModuleinterface'; @@ -48,18 +47,16 @@ const FieldsAndSoilComponent: FC = ({ // Builds field info inside the field form module. const [, setFieldsInfo] = useState(farmDetails); const [fieldIndex, setFieldIndex] = useState(farmDetails.Fields.length); - const [initialFieldValues, setInitialFieldValues] = useState( - initialFarmDetails.Fields[fieldIndex], - ); // Only triggered once, it would show list and persists. const [isSubmitted, setSubmitted] = useState(farmDetails.Fields.length > 0); // Would trigger when new field button is clicked. const [isFieldAdded, setFieldAdd] = useState(false); - // For checked attribute - const [isSoilTestEnabled, setSoilTestEnabled] = useState(null); - const [isLeafTestEnabled, setLeafTestEnabled] = useState(null); - const hasSoilTestSchema = (hasSoilTest: string, message: string = 'Required') => Yup.number().when(hasSoilTest, (value, schema) => (value ? schema.notRequired() : schema.required(message))); + const radioOptions = [ + { id: 'true', label: 'Yes', value: true }, + { id: 'false', label: 'No', value: false }, + ]; + const initialValues: FieldDetailInterface = initialFarmDetails.Fields[0]; const validationSchema = Yup.object().shape({ FieldName: Yup.string().max(24).required('Required'), @@ -70,14 +67,36 @@ const FieldsAndSoilComponent: FC = ({ Comment: Yup.string().max(200, 'Comments should be lower than 200 chars'), HasSoilTest: Yup.boolean().nullable().required('A Soil Test must be either `Yes` or `No`'), HasLeafTest: Yup.boolean().nullable().required('A Leaf Test must be either `Yes` or `No`'), - TestingMethod: hasSoilTestSchema('HasSoilTest', 'Must enter Testing Method'), - sampleDate: hasSoilTestSchema('HasSoilTest', 'Must enter Sample Date'), - valNO3H: hasSoilTestSchema('HasSoilTest'), - ValP: hasSoilTestSchema('HasSoilTest'), - valK: hasSoilTestSchema('HasSoilTest'), - valPH: hasSoilTestSchema('HasSoilTest'), - leafTissueP: hasSoilTestSchema('HasLeafTest'), - leafTissueK: hasSoilTestSchema('HasLeafTest'), + SoilTest: Yup.object().when('HasSoilTest', { + is: true, + then: (object) => + object + .shape({ + TestingMethod: Yup.string().when('HasSoilTest', { + is: true, + then: (schema) => schema.required(), + otherwise: (schema) => schema.notRequired(), + }), + sampleDate: Yup.string().required(), + valNO3H: Yup.number().min(0).max(7).required(), + ValP: Yup.number().min(0).max(7).required(), + valK: Yup.number().min(0).max(7).required(), + valPH: Yup.number().min(0).max(7).required(), + }) + .required(), + otherwise: (schema) => schema.notRequired(), + }), + LeafTest: Yup.object().when('HasLeafTest', { + is: true, + then: (schema) => + schema + .shape({ + leafTissueP: Yup.number().min(0).max(7).required(), + leafTissueK: Yup.number().min(0).max(7).required(), + }) + .required(), + otherwise: (schema) => schema.notRequired(), + }), }); /** @@ -90,7 +109,8 @@ const FieldsAndSoilComponent: FC = ({ const addFieldData = (values: FieldDetailInterface): void => { setTimeout(() => { const farmInfo: FarmDetailsInterface = { ...farmDetails }; - farmInfo.Fields.push({ + + const newField: FieldDetailInterface = { Id: fieldIndex, FieldName: values.FieldName, Area: values.Area, @@ -117,17 +137,21 @@ const FieldsAndSoilComponent: FC = ({ plantAgeYears: '', numberOfPlantsPerAcre: 0, distanceBtwnPlantsRows: '', - willPlantsBePruned: undefined, + willPlantsBePruned: false, whereWillPruningsGo: '', - willSawdustBeApplied: undefined, + willSawdustBeApplied: false, }, ], - }); + }; + + farmInfo.Fields.push(newField); + + // splice or pop to remove Crops after getting pushed to array if (farmInfo.Fields[fieldIndex].Crops.length === 1) { - // splice or pop to remove Crops after getting pushed to array // Crops is not optional so this line is needed farmInfo.Fields[fieldIndex].Crops.splice(0, 1); } + setFieldsInfo(farmInfo); setFieldIndex((prevIndex) => prevIndex + 1); setSubmitted(true); @@ -137,17 +161,9 @@ const FieldsAndSoilComponent: FC = ({ const addNewField = () => { handleFormState(FIELDS_AND_SOIL, undefined, ACTIVE); - setInitialFieldValues(emptyFieldDetails); setFieldAdd(true); - setSoilTestEnabled(null); - setLeafTestEnabled(null); }; - const radioOptions = [ - { id: 'true', label: 'Yes', value: true }, - { id: 'false', label: 'No', value: false }, - ]; - return ( <> {isSubmitted && ( @@ -166,7 +182,7 @@ const FieldsAndSoilComponent: FC = ({ {(isFieldAdded || !isSubmitted) && ( { @@ -203,6 +219,7 @@ const FieldsAndSoilComponent: FC = ({ width="70%" /> + Add Soil Test @@ -213,10 +230,8 @@ const FieldsAndSoilComponent: FC = ({ id={`HasSoilTest${option.id}`} name="HasSoilTest" type="radio" - checked={isSoilTestEnabled === option.value} onChange={() => { setFieldValue('HasSoilTest', option.value); - setSoilTestEnabled(option.value); }} /> ))} @@ -234,6 +249,7 @@ const FieldsAndSoilComponent: FC = ({

)} + {values.HasSoilTest && ( <> @@ -262,8 +278,8 @@ const FieldsAndSoilComponent: FC = ({ = ({ )}
+ Add Leaf Test @@ -294,10 +311,8 @@ const FieldsAndSoilComponent: FC = ({ id={`HasLeafTest${option.id}`} name="HasLeafTest" type="radio" - checked={isLeafTestEnabled === option.value} onChange={() => { setFieldValue('HasLeafTest', option.value); - setLeafTestEnabled(option.value); }} /> ))} diff --git a/frontend/src/Commons/Input/RadioButton/CustomRadioButton.tsx b/frontend/src/Commons/Input/RadioButton/CustomRadioButton.tsx index 0d4d2f1c..e890f164 100644 --- a/frontend/src/Commons/Input/RadioButton/CustomRadioButton.tsx +++ b/frontend/src/Commons/Input/RadioButton/CustomRadioButton.tsx @@ -13,7 +13,7 @@ interface CustomRadioProps { id: string; type: string; width?: string; - checked: boolean; + checked?: boolean; onChange: (event: React.ChangeEvent) => void; } diff --git a/frontend/src/Constants/EmptyFieldDetails.ts b/frontend/src/Constants/EmptyFieldDetails.ts deleted file mode 100644 index 86fa530e..00000000 --- a/frontend/src/Constants/EmptyFieldDetails.ts +++ /dev/null @@ -1,27 +0,0 @@ -import FieldDetailInterface from 'src/Interface/FieldDetailsInterface'; - -const emptyFieldDetails: FieldDetailInterface = { - Id: 0, - FieldName: '', - Area: 0, - Comment: '', - HasSoilTest: null, - HasLeafTest: null, - SoilTest: { TestingMethod: '', sampleDate: '', valNO3H: 0, ValP: 0, valK: 0, valPH: 0 }, - LeafTest: { leafTissueP: 0, leafTissueK: 0 }, - Crops: [ - { - id: 0, - cropId: '', - yield: 0, - plantAgeYears: '', - numberOfPlantsPerAcre: 0, - distanceBtwnPlantsRows: '', - willPlantsBePruned: undefined, - whereWillPruningsGo: '', - willSawdustBeApplied: undefined, - }, - ], -}; - -export default emptyFieldDetails; diff --git a/frontend/src/Constants/InitialFarmDetails.ts b/frontend/src/Constants/InitialFarmDetails.ts index d262c20f..43eefc1d 100644 --- a/frontend/src/Constants/InitialFarmDetails.ts +++ b/frontend/src/Constants/InitialFarmDetails.ts @@ -1,13 +1,19 @@ -/** +/* * @desc This populates the initial values of Formik input fields, * which must be initialized. It does not like null or undefined * values which changes components from being controlled - * to uncontrolled. + * to uncontrolled. Number values are being initialized to empty strings + * because of this. It's a small hack to get the behaviour we want. * @author @GDamaso + * */ -import FarmDetailsInterface from '@Interface/FarmDetailsInterface'; -const initialFarmDetails: FarmDetailsInterface = { +/* + * This has an 'any' type because we need to initialize some numbers to an empty string + * to get the correct behaviour from formik/yup validation. It should be a FarmDetailInterface + * otherwise, which do not take strings instead of numbers... mostly. + */ +const initialFarmDetails: any = { Year: '', FarmName: '', FarmRegion: '', @@ -16,23 +22,23 @@ const initialFarmDetails: FarmDetailsInterface = { { Id: 0, FieldName: '', - Area: 0, + Area: '', Comment: '', - HasSoilTest: null, - HasLeafTest: null, - SoilTest: { TestingMethod: '', sampleDate: '', valNO3H: 0, ValP: 0, valK: 0, valPH: 0 }, - LeafTest: { leafTissueP: 0, leafTissueK: 0 }, + HasSoilTest: '', + HasLeafTest: '', + SoilTest: { TestingMethod: '', sampleDate: '', valNO3H: '', ValP: '', valK: '', valPH: '' }, + LeafTest: { leafTissueP: '', leafTissueK: '' }, Crops: [ { id: 0, cropId: '', - yield: 0, + yield: '', plantAgeYears: '', - numberOfPlantsPerAcre: 0, + numberOfPlantsPerAcre: '', distanceBtwnPlantsRows: '', - willPlantsBePruned: undefined, + willPlantsBePruned: false, whereWillPruningsGo: '', - willSawdustBeApplied: undefined, + willSawdustBeApplied: false, }, ], }, diff --git a/frontend/src/Interface/FieldDetailsInterface.tsx b/frontend/src/Interface/FieldDetailsInterface.tsx index 5332e84b..05c4a584 100644 --- a/frontend/src/Interface/FieldDetailsInterface.tsx +++ b/frontend/src/Interface/FieldDetailsInterface.tsx @@ -12,9 +12,9 @@ interface FieldDetailInterface { Id: number; FieldName: string; Area: number; - Comment?: string | null; - HasSoilTest: boolean | null; - HasLeafTest: boolean | null; + Comment?: string | undefined; + HasSoilTest?: boolean; + HasLeafTest?: boolean; SoilTest: SoilTestInterface; LeafTest: LeafTestInterface; Crops: CropsDetailsInterface[]; diff --git a/frontend/src/Interface/NmpInterface.tsx b/frontend/src/Interface/NmpInterface.tsx index 57b5edcb..01852906 100644 --- a/frontend/src/Interface/NmpInterface.tsx +++ b/frontend/src/Interface/NmpInterface.tsx @@ -72,7 +72,7 @@ interface NmpInterface { HasMixedLiveStock: boolean; HasHorticulturalCrops: boolean; HasBerries: boolean; - LeafTests: any; + LeafTests: boolean; LeafTestingMethod: string; UserJourney: number; }; diff --git a/frontend/src/Utils/convertToNMP.ts b/frontend/src/Utils/convertToNMP.ts index 3e32c3f5..2c81f2f5 100644 --- a/frontend/src/Utils/convertToNMP.ts +++ b/frontend/src/Utils/convertToNMP.ts @@ -2,6 +2,7 @@ import FarmDetailsInterface from '@Interface/FarmDetailsInterface'; import NmpInterface from '@Interface/NmpInterface'; import NmpFieldInterface from '@Interface/NmpFieldInterface'; import { templateFieldNMP } from '@Constants/templateNMP'; +import FieldDetailInterface from '@Interface/FieldDetailsInterface'; /** * @desc Convert the FarmDetailsInterface to the NmpInterface structure. @@ -13,22 +14,27 @@ const convertToNMP = ( newDetails: FarmDetailsInterface, prevDetails: NmpInterface, ): NmpInterface => { - const newFields: NmpFieldInterface[] = newDetails.Fields.map((field) => ({ - ...templateFieldNMP, - Id: field.Id, - Area: field.Area, - Comment: field.Comment || templateFieldNMP.Comment, - FieldName: field.FieldName, - HasSoilTest: field.HasSoilTest || templateFieldNMP.HasSoilTest, - SoilTest: { - ...templateFieldNMP.SoilTest, - valNO3H: field.SoilTest?.valNO3H || templateFieldNMP.SoilTest.valNO3H, - ValP: field.SoilTest?.ValP || templateFieldNMP.SoilTest.ValP, - valK: field.SoilTest?.valK || templateFieldNMP.SoilTest.valK, - valPH: field.SoilTest?.valPH || templateFieldNMP.SoilTest.valPH, - }, - LeafTest: field.LeafTest || templateFieldNMP.LeafTest, - })); + const newFields: NmpFieldInterface[] = newDetails.Fields.map((field: FieldDetailInterface) => { + return { + ...templateFieldNMP, + Id: field.Id, + Area: field.Area, + Comment: field.Comment || templateFieldNMP.Comment, + FieldName: field.FieldName, + HasSoilTest: field.HasSoilTest || templateFieldNMP.HasSoilTest, + SoilTest: { + ...templateFieldNMP.SoilTest, + valNO3H: field.SoilTest?.valNO3H || templateFieldNMP.SoilTest.valNO3H, + ValP: field.SoilTest?.ValP || templateFieldNMP.SoilTest.ValP, + valK: field.SoilTest?.valK || templateFieldNMP.SoilTest.valK, + valPH: field.SoilTest?.valPH || templateFieldNMP.SoilTest.valPH, + }, + LeafTest: { + leafTissueP: field.LeafTest?.leafTissueP || templateFieldNMP.LeafTest.leafTissueP, + leafTissueK: field.LeafTest?.leafTissueK || templateFieldNMP.LeafTest.leafTissueK, + }, + }; + }); return { ...prevDetails,