diff --git a/app/components/Application/ApplicationDetailsCardItemCustomFields.tsx b/app/components/Application/ApplicationDetailsCardItemCustomFields.tsx
index d99f77321b..d30250237a 100644
--- a/app/components/Application/ApplicationDetailsCardItemCustomFields.tsx
+++ b/app/components/Application/ApplicationDetailsCardItemCustomFields.tsx
@@ -12,192 +12,181 @@ import FuelField from 'containers/Forms/FuelField';
import EmissionCategoryRowIdField from 'containers/Forms/EmissionCategoryRowIdField';
import NaicsField from 'containers/Forms/NaicsField';
-const customFields = (
- showDiff: boolean,
- diffPathArray: any[],
- diffArray: any[],
- handleEnums: (...args: any[]) => any,
- previousIsEmpty: boolean,
- setHasErrors: (...args: any[]) => any
-) => {
- const setErrorIcon: (...args: any[]) => object = (props) => {
- if (props?.errorSchema?.__errors || props.rawErrors) {
- setHasErrors(true);
- return ;
- }
-
- return undefined;
- };
-
- const CUSTOM_FIELDS: Record> = {
- TitleField: (props) => {props.title}
,
- StringField: (props) => {
- const errorIcon: object = setErrorIcon(props);
-
- const {idSchema, formData} = props;
- const id = idSchema?.$id;
- let prevValue;
- let hasDiff = false;
- if (showDiff) {
- hasDiff = diffPathArray.includes(id.replace(/^[^_]*_/g, ''));
- prevValue =
- diffArray[diffPathArray.indexOf(id.replace(/^[^_]*_/g, ''))];
+// Some formData values are numbers that map to enums, this function uses the number values to return the enum names stored in the schema
+const getDisplayValue = (schema, value) => {
+ if (schema.enum && schema.enumNames) {
+ // TODO: needs a fix on jsonschema types (missing enumNames)
+ const enumIndex = schema.enum.indexOf(value);
+ if (enumIndex === -1) return null;
+ return schema.enumNames[enumIndex];
+ }
+
+ return value;
+};
- if ((hasDiff || previousIsEmpty) && prevValue !== formData) {
- prevValue = handleEnums(props, false, prevValue);
- const currentValue = handleEnums(props, true, prevValue);
+const ErrorIcon: React.FunctionComponent = (props) => {
+ if (props?.errorSchema?.__errors || props.rawErrors) {
+ return ;
+ }
- return (
- <>
-
- {prevValue ?? [No Data Entered]}
-
- --->
-
- {currentValue ?? [No Data Entered]}
-
- >
- );
- }
- }
+ return null;
+};
- if (formData === null || formData === undefined || formData === '')
+const CUSTOM_FIELDS: Record> = {
+ TitleField: (props) => {props.title}
,
+ StringField: (props) => {
+ const {
+ idSchema,
+ schema,
+ formData,
+ formContext: {showDiff, idDiffMap}
+ } = props;
+ const displayValue = getDisplayValue(schema, formData);
+ const id = idSchema?.$id;
+ if (showDiff) {
+ const diff = idDiffMap?.[id];
+
+ if (diff && diff.lhs !== formData) {
+ const prevValue = getDisplayValue(schema, diff.lhs);
return (
<>
- [No Data Entered]
- {errorIcon}
+
+ {prevValue ?? [No Data Entered]}
+
+ --->
+
+ {displayValue ?? [No Data Entered]}
+
>
);
+ }
+ }
- const value = handleEnums(props, true, prevValue);
+ if (formData === null || formData === undefined || formData === '')
return (
<>
- {value}
- {errorIcon}
+ [No Data Entered]
+ {ErrorIcon}
>
);
- },
- BooleanField: (props) => {
- const errorIcon = setErrorIcon(props);
- const {idSchema, formData} = props;
- const id = idSchema?.$id;
- const hasDiff = diffPathArray.includes(
- idSchema.$id.replace(/^[^_]*_/g, '')
+ return (
+ <>
+ {displayValue}
+ {ErrorIcon}
+ >
+ );
+ },
+ BooleanField: (props) => {
+ const {
+ idSchema,
+ formData,
+ formContext: {showDiff, idDiffMap}
+ } = props;
+ const id = idSchema?.$id;
+ const diff = idDiffMap?.[id];
+
+ if (showDiff && diff) {
+ return (
+ <>
+
+ {diff.lhs ? 'Yes' : 'No'}
+
+ --->
+
+ {formData ? 'Yes' : 'No'}
+
+ >
);
+ }
- if (showDiff && hasDiff) {
- const prevValue =
- diffArray[
- diffPathArray.indexOf(idSchema.$id.replace(/^[^_]*_/g, ''))
- ];
- return (
- <>
-
- {prevValue ? 'Yes' : 'No'}
-
- --->
-
- {formData ? 'Yes' : 'No'}
-
- >
- );
- }
-
+ return (
+
+ {formData ? <>Yes {ErrorIcon}> : <>No {ErrorIcon}>}{' '}
+
+ );
+ },
+ naics: (props) => ,
+ emissionSource: EmissionSourceFields,
+ emissionGas: (props) => ,
+ product: (props) => (
+
+ ),
+ productRowId: (props) => (
+
+ ),
+ fuel: (props) => ,
+ fuelRowId: (props) => (
+
+ ),
+ emissionCategoryRowId: (props) => (
+
+ ),
+ NumberField: (props) => {
+ const {
+ idSchema,
+ formData,
+ schema,
+ formContext: {showDiff, idDiffMap}
+ } = props;
+ const id = idSchema?.$id;
+ if (formData === null || formData === undefined || formData === '')
return (
-
- {formData ? <>Yes {errorIcon}> : <>No {errorIcon}>}{' '}
-
+ <>
+ [No Data Entered]
+ {ErrorIcon}
+ >
);
- },
- naics: (props) => ,
- emissionSource: EmissionSourceFields,
- emissionGas: (props) => (
-
- ),
- product: (props) => (
-
- ),
- productRowId: (props) => (
-
- ),
- fuel: (props) => ,
- fuelRowId: (props) => (
-
- ),
- emissionCategoryRowId: (props) => (
-
- ),
- NumberField: (props) => {
- const errorIcon = setErrorIcon(props);
-
- const {idSchema, formData} = props;
- const id = idSchema?.$id;
- if (formData === null || formData === undefined || formData === '')
- return (
- <>
- [No Data Entered]
- {errorIcon}
- >
- );
+ const displayValue = getDisplayValue(schema, formData);
- let prevValue;
- let hasDiff = false;
- if (showDiff) {
- hasDiff = diffPathArray.includes(id.replace(/^[^_]*_/g, ''));
- prevValue =
- diffArray[diffPathArray.indexOf(id.replace(/^[^_]*_/g, ''))];
- if (hasDiff || previousIsEmpty) {
- prevValue = handleEnums(props, false, prevValue);
- const currentValue = handleEnums(props, true, prevValue);
+ const diff = idDiffMap?.[id];
- return (
- <>
-
- {prevValue ? (
-
- ) : (
- [No Data Entered]
- )}
-
- --->
-
- {currentValue ? (
-
- ) : (
- [No Data Entered]
- )}
-
- >
- );
- }
- }
+ if (showDiff && diff) {
+ const prevValue = getDisplayValue(schema, diff.lhs);
- const value = handleEnums(props, true, prevValue);
return (
<>
-
- {errorIcon}
+
+ {prevValue !== null && prevValue !== undefined ? (
+
+ ) : (
+ [No Data Entered]
+ )}
+
+ --->
+
+ {displayValue !== null && displayValue !== undefined ? (
+
+ ) : (
+ [No Data Entered]
+ )}
+
>
);
}
- };
- return CUSTOM_FIELDS;
+
+ return (
+ <>
+
+ {ErrorIcon}
+ >
+ );
+ }
};
-export default customFields;
+export default CUSTOM_FIELDS;
diff --git a/app/containers/Applications/ApplicationDetailsCardItem.tsx b/app/containers/Applications/ApplicationDetailsCardItem.tsx
index dfbe06455c..879a3b4a89 100644
--- a/app/containers/Applications/ApplicationDetailsCardItem.tsx
+++ b/app/containers/Applications/ApplicationDetailsCardItem.tsx
@@ -1,7 +1,7 @@
-import React, {useState, useMemo} from 'react';
+import React, {useState, useEffect} from 'react';
import {Button, Card, Collapse, Col, Row} from 'react-bootstrap';
import {createFragmentContainer, graphql} from 'react-relay';
-import JsonSchemaForm, {FieldProps, AjvError} from '@rjsf/core';
+import JsonSchemaForm, {AjvError, ErrorSchema} from '@rjsf/core';
import {FormJson} from 'next-env';
import {ApplicationDetailsCardItem_formResult} from '__generated__/ApplicationDetailsCardItem_formResult.graphql';
import {ApplicationDetailsCardItem_query} from '__generated__/ApplicationDetailsCardItem_query.graphql';
@@ -49,20 +49,27 @@ export const ApplicationDetailsCardItemComponent: React.FunctionComponent
const {formJson} = formJsonByFormId;
const {schema, uiSchema, customFormats} = formJson as FormJson;
+ const formIdPrefix = `${formJsonByFormId.name
+ .toLowerCase()
+ .replace(' ', '-')}`;
+
const transformErrors = (errors: AjvError[]) => {
return customTransformErrors(errors, formJson);
};
+ const handleChange = (_formData, errorSchema: ErrorSchema) => {
+ if (setHasErrors) {
+ if (errorSchema?.__errors) {
+ setHasErrors(true);
+ } else {
+ setHasErrors(false);
+ }
+ }
+ };
+
// Expands or collapses the form_result card
const [isOpen, setIsOpen] = useState(false);
- // The array of paths to each difference between diffFrom result & diffTo result (each path matches up with idSchema)
- const diffPathArray = [];
- // The array of differences
- const diffArray = [];
- // If the diffFrom result is empty, there is no path. This flag gives us control over what to show in the diff in that case.
- let previousIsEmpty = false;
-
let diffTo;
// Select the correct form result to diff to by matching formJson slugs
diffToResults.forEach((result) => {
@@ -70,7 +77,16 @@ export const ApplicationDetailsCardItemComponent: React.FunctionComponent
diffTo = result;
});
- useMemo(() => {
+ const [idDiffMap, setIdDiffMap] = useState<
+ Record
+ >({});
+ const [formData, setFormData] = useState(
+ review ? diffTo.node.formResult : formResult.formResult
+ );
+ // The array of paths to each difference between diffFrom result & diffTo result (each path matches up with idSchema)
+ useEffect(() => {
+ const newIdDiffMap: Record = {};
+ let newFormData = Array.isArray(formData) ? [...formData] : {...formData};
if (diffFromResults && showDiff) {
let diffFrom;
// Select the correct form result to diff from by matching formJson slugs
@@ -87,67 +103,67 @@ export const ApplicationDetailsCardItemComponent: React.FunctionComponent
const rhs = diffTo.node.formResult;
const differences = diff(lhs, rhs);
- // These are the default values for empty form results. If the form results for the diffFrom are empty, set the previousIsEmpty flag
- if (
- JSON.stringify(diffFrom) === '[]' ||
- JSON.stringify(diffFrom) === '{}' ||
- JSON.stringify(diffFrom) === '[{}]'
- ) {
- previousIsEmpty = true;
- } else if (differences) {
+ if (differences) {
// Populate the diffPathArray and diffArray
differences.forEach((difference) => {
- if (difference.path) {
- diffPathArray.push(difference.path.join('_'));
- diffArray.push(difference.lhs);
+ if (difference.kind === 'A') {
+ // Array differences
+ const arrayElementPath = [
+ formIdPrefix,
+ ...(difference.path ?? []),
+ difference.index
+ ].join('_');
+ if (difference.item.kind === 'N') {
+ // Added an element to the array
+ Object.keys(difference.item.rhs).forEach((key) => {
+ newIdDiffMap[`${arrayElementPath}_${key}`] = {
+ lhs: null,
+ rhs: difference.item.rhs[key]
+ };
+ });
+ } else if (difference.item.kind === 'D') {
+ console.log(difference);
+ const deletedItem = {};
+ // Deleted an element from the array
+ Object.keys(difference.item.lhs).forEach((key) => {
+ deletedItem[key] = null;
+ newIdDiffMap[`${arrayElementPath}_${key}`] = {
+ lhs: difference.item.lhs[key],
+ rhs: null
+ };
+ });
+ if (Array.isArray(newFormData) && !difference.path) {
+ newFormData = [
+ ...newFormData.slice(0, difference.index),
+ deletedItem,
+ ...newFormData.slice(difference.index)
+ ];
+ }
+ }
+ } else if (difference.path) {
+ const {lhs, rhs} = difference;
+ newIdDiffMap[[formIdPrefix, ...difference.path].join('_')] = {
+ lhs,
+ rhs
+ };
}
});
}
}
+ setFormData(newFormData);
+ setIdDiffMap(newIdDiffMap);
}, [
- diffArray,
- diffPathArray,
formResult.formJsonByFormId.slug,
formResult.formResult,
diffFromResults,
showDiff
]);
- // Some formData values are numbers that map to enums, this function uses the number values to return the enum names stored in the schema
- const handleEnums = (props, isCurrent, prevValue) => {
- if (props.schema.enum && props.schema.enumNames) {
- // TODO: needs a fix on jsonschema types (missing enumNames)
- const enumIndex = isCurrent
- ? props.schema.enum.indexOf(props.formData)
- : props.schema.enum.indexOf(prevValue);
- if (enumIndex === -1 && isCurrent) return props.formData;
- if (enumIndex === -1 && !isCurrent) return '[No Data Entered]';
- return props.schema.enumNames[enumIndex];
- }
-
- if (isCurrent) return props.formData;
-
- return prevValue;
- };
-
- const CUSTOM_FIELDS: Record<
- string,
- React.FunctionComponent
- > = customFields(
- showDiff,
- diffPathArray,
- diffArray,
- handleEnums,
- previousIsEmpty,
- setHasErrors
- );
const classTag = formJsonByFormId.slug;
// Override submit button for each form with an empty fragment
// eslint-disable-next-line react/jsx-no-useless-fragment
const buttonOverride = <>>;
- const formIdPrefix = `${formJsonByFormId.name
- .toLowerCase()
- .replace(' ', '-')}`;
+
return (
ArrayFieldTemplate={SummaryFormArrayFieldTemplate}
FieldTemplate={SummaryFormFieldTemplate}
showErrorList={false}
- fields={CUSTOM_FIELDS}
+ fields={customFields}
customFormats={customFormats}
transformErrors={transformErrors}
schema={schema}
idPrefix={formIdPrefix}
uiSchema={uiSchema}
+ onChange={handleChange}
ObjectFieldTemplate={FormObjectFieldTemplate}
- formData={review ? diffTo.node.formResult : formResult.formResult}
+ formData={formData}
formContext={{
query,
showDiff,
- diffPathArray,
- diffArray,
- previousIsEmpty
+ idDiffMap
}}
>
{buttonOverride}
@@ -229,9 +244,7 @@ export const ApplicationDetailsCardItemComponent: React.FunctionComponent
export default createFragmentContainer(ApplicationDetailsCardItemComponent, {
formResult: graphql`
fragment ApplicationDetailsCardItem_formResult on FormResult {
- id
formResult
- versionNumber
formJsonByFormId {
name
slug
diff --git a/schema/data/dev/form_result.sql b/schema/data/dev/form_result.sql
index d90c784e09..6f7c062863 100644
--- a/schema/data/dev/form_result.sql
+++ b/schema/data/dev/form_result.sql
@@ -61,10 +61,16 @@ begin;
"emissionCategoryRowId": 1
},
{
- "fuelRowId": 11,
+ "fuelRowId": 10,
"quantity": 40120,
"fuelUnits": "kL",
"emissionCategoryRowId": 3
+ },
+ {
+ "fuelRowId": 12,
+ "quantity": 40,
+ "fuelUnits": "kL",
+ "emissionCategoryRowId": 2
}
]'),
('[
@@ -76,15 +82,6 @@ begin;
"requiresEmissionAllocation": true,
"requiresProductAmount": true,
"isCiipProduct": true
- },
- {
- "productAmount": 12000,
- "productRowId": 26,
- "productUnits": "t",
- "productEmissions": 1300,
- "requiresEmissionAllocation": true,
- "requiresProductAmount": true,
- "isCiipProduct": true
}
]');