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,