From 8408136ffb6230effcc33b3eeb9711845e6f420c Mon Sep 17 00:00:00 2001 From: Micheal Wells Date: Mon, 4 Dec 2023 17:01:35 -0800 Subject: [PATCH 1/3] wip - move state out of props and rewrite state grabs to reduce rerenders - still typing lag from somewhere --- appv2/src/UI/App.tsx | 5 +- .../src/UI/Overlay/Records/Activity/Form.tsx | 31 ++------ .../Records/Activity/form/FormContainer.tsx | 73 ++++++++++++------- .../rjsf/business-rules/customValidation.ts | 5 +- appv2/src/state/sagas/activity.ts | 2 +- 5 files changed, 60 insertions(+), 56 deletions(-) diff --git a/appv2/src/UI/App.tsx b/appv2/src/UI/App.tsx index 9eacd2850..7875bb109 100644 --- a/appv2/src/UI/App.tsx +++ b/appv2/src/UI/App.tsx @@ -50,7 +50,7 @@ const AppUrlListener: React.FC = () => { return null; }; -const OverlayContentMemo = React.memo((props: any) => { +const OverlayContentMemo = (props) => {//React.memo((props: any) => { const overlayMenuOpen = useSelector((state: any) => state.AppMode?.overlay_menu_toggle); const fullScreen = useSelector((state: any) => state.AppMode?.panelFullScreen); const theme = createTheme(getDesignTokens(false) as ThemeOptions); @@ -133,7 +133,8 @@ const OverlayContentMemo = React.memo((props: any) => { } /> ); -}); + } +//}); const HeaderMemo = React.memo((props: any) => { const ref = useRef(0); diff --git a/appv2/src/UI/Overlay/Records/Activity/Form.tsx b/appv2/src/UI/Overlay/Records/Activity/Form.tsx index 293a3c5ac..f04f9b4b5 100644 --- a/appv2/src/UI/Overlay/Records/Activity/Form.tsx +++ b/appv2/src/UI/Overlay/Records/Activity/Form.tsx @@ -1,34 +1,19 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React from 'react'; import FormContainer from './form/FormContainer'; -import { useDispatch, useSelector } from 'react-redux'; -import { selectActivity } from 'state/reducers/activity'; -import _ from 'lodash'; -import { ACTIVITY_ON_FORM_CHANGE_REQUEST } from 'state/actions'; -import { validatorForActivity } from 'rjsf/business-rules/customValidation'; export const ActivityForm = (props) => { - const activityState = useSelector(selectActivity); - const dispatch = useDispatch(); - - const debouncedFormChange = - _.debounce((event, ref, lastField, callbackFun) => { - dispatch({ - type: ACTIVITY_ON_FORM_CHANGE_REQUEST, - payload: { eventFormData: event.formData, lastField: lastField, unsavedDelay: null} - }); - }, 150) return ( ); }; \ No newline at end of file diff --git a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx index 26144f190..4cf42bea0 100644 --- a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx +++ b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx @@ -14,6 +14,7 @@ import { } from '@mui/material'; import { Form } from '@rjsf/mui'; import React, { useEffect, useRef, useState } from 'react'; +import { validatorForActivity } from 'rjsf/business-rules/customValidation'; import { SelectAutoCompleteContextProvider } from 'UI/Overlay/Records/Activity/form/SelectAutoCompleteContext'; import ArrayFieldTemplate from 'rjsf/templates/ArrayFieldTemplate'; import FieldTemplate from 'rjsf/templates/FieldTemplate'; @@ -29,15 +30,16 @@ import { selectAuth } from 'state/reducers/auth'; import { selectConfiguration } from 'state/reducers/configuration'; import { selectActivity } from 'state/reducers/activity'; import { useDispatch } from 'react-redux'; -import { ACTIVITY_CHEM_TREATMENT_DETAILS_FORM_ON_CHANGE_REQUEST } from 'state/actions'; +import { ACTIVITY_CHEM_TREATMENT_DETAILS_FORM_ON_CHANGE_REQUEST, ACTIVITY_ON_FORM_CHANGE_REQUEST } from 'state/actions'; import { selectUserSettings } from 'state/reducers/userSettings'; import validator from '@rjsf/validator-ajv6'; import 'UI/Overlay/Records/Activity/form/aditionalFormStyles.css' import { getCustomErrorTransformer } from 'rjsf/business-rules/customErrorTransformer'; +import _ from 'lodash'; export interface IFormContainerProps { classes?: any; - activity: any; + // activity: any; customValidation?: any; customErrorTransformer?: any; isDisabled?: boolean; @@ -64,13 +66,25 @@ export interface IFormContainerProps { onFormSubmitSuccess?: (event: ISubmitEvent, formRef: any) => any; onSave?: Function; onSubmitAsOfficial?: Function; - isAlreadySubmitted: () => boolean; - canBeSubmittedWithoutErrors: () => boolean; + //isAlreadySubmitted: () => boolean; +// canBeSubmittedWithoutErrors: () => boolean; OnNavBack?: Function; } const FormContainer: React.FC = (props) => { - const [formData, setformData] = useState(props.activity?.form_data); + const activityState = useSelector((state) => state.ActivityPage.activity); + const dispatch = useDispatch(); + console.log('rendering') + + const debouncedFormChange = + _.debounce((event, ref, lastField, callbackFun) => { + //(event, ref, lastField, callbackFun) => { + dispatch({ + type: ACTIVITY_ON_FORM_CHANGE_REQUEST, + payload: { eventFormData: event.formData, lastField: lastField, unsavedDelay: null} + }); + }, 1000) + const [formData, setformData] = useState(activityState?.form_data); const [schemas, setSchemas] = useState<{ schema: any; uiSchema: any }>({ schema: null, uiSchema: null }); const formRef = React.createRef(); const [focusedFieldArgs, setFocusedFieldArgs] = useState(null); @@ -82,11 +96,10 @@ const FormContainer: React.FC = (props) => { const { MOBILE } = useSelector(selectConfiguration); - const dispatch = useDispatch(); const { darkTheme } = useSelector(selectUserSettings); const userSettingsState = useSelector(selectUserSettings); - const activityStateInStore = useSelector(selectActivity); + const suggestedTreatmentIDS = useSelector((state) => state.ActivityPage.suggestedTreatmentIDs); const rjsfThemeDark = createTheme({ ...rjsfTheme, @@ -125,7 +138,7 @@ const FormContainer: React.FC = (props) => { //revalidate formData after the setState is run $this.validate($this.state.formData); //update formData of the activity via onFormChange - props.onFormChange({ formData: formRef.current.state.formData }, formRef, null, (updatedFormData) => { + debouncedFormChange({ formData: formRef.current.state.formData }, formRef, null, (updatedFormData) => { setformData(updatedFormData); }); }); @@ -135,10 +148,10 @@ const FormContainer: React.FC = (props) => { const isActivityChemTreatment = () => { if ( - props.activity.activity_subtype === 'Activity_Treatment_ChemicalPlantTerrestrial' || - props.activity.activity_subtype === 'Activity_Treatment_ChemicalPlantAquatic' || - props.activity.activitySubtype === 'Activity_Treatment_ChemicalPlantTerrestrial' || - props.activity.activitySubtype === 'Activity_Treatment_ChemicalPlantAquatic' + activityState.activity_subtype === 'Activity_Treatment_ChemicalPlantTerrestrial' || + activityState.activity_subtype === 'Activity_Treatment_ChemicalPlantAquatic' || + activityState.activitySubtype === 'Activity_Treatment_ChemicalPlantTerrestrial' || + activityState.activitySubtype === 'Activity_Treatment_ChemicalPlantAquatic' ) { return true; } @@ -160,10 +173,11 @@ const FormContainer: React.FC = (props) => { useEffect(() => { const getApiSpec = async () => { - const subtype = props.activity?.activity_subtype || props.activity?.activitySubtype; + console.log('begin api spec stuff') + const subtype = activityState?.activity_subtype || activityState?.activitySubtype; if (!subtype) throw new Error('Activity has no Subtype specified'); let components; - const notMine = authState?.username !== activityStateInStore?.activity?.created_by; + const notMine = authState?.username !== activityState?.created_by; const notAdmin = authState?.accessRoles?.filter((role) => { return [1,18].includes(role.role_id) @@ -181,9 +195,9 @@ const FormContainer: React.FC = (props) => { let modifiedSchema = subtypeSchema; // Handle activity_id linking fetches try { - const suggestedTreatmentIDs = activityStateInStore?.suggestedTreatmentIDs ?? []; + //const suggestedTreatmentIDs = activityStateInStore?.suggestedTreatmentIDs ?? []; - if (props.activity?.activity_type === 'Monitoring') { + if (activityState?.activity_type === 'Monitoring') { if (MOBILE) { uiSchema = { ...uiSchema, @@ -198,7 +212,7 @@ const FormContainer: React.FC = (props) => { } else { try { // move this to action or reducer - if (suggestedTreatmentIDs?.length) { + if (suggestedTreatmentIDS?.length) { modifiedSchema = { ...modifiedSchema, properties: { @@ -209,7 +223,7 @@ const FormContainer: React.FC = (props) => { ...modifiedSchema?.properties.activity_type_data.properties, linked_id: { ...modifiedSchema?.properties?.activity_type_data?.properties?.linked_id, - options: suggestedTreatmentIDs + options: suggestedTreatmentIDS } } } @@ -219,7 +233,7 @@ const FormContainer: React.FC = (props) => { ...components, schemas: { ...components.schemas, - [props.activity.activity_subtype]: modifiedSchema + [activityState.activity_subtype]: modifiedSchema } }; } @@ -239,11 +253,12 @@ const FormContainer: React.FC = (props) => { if (authenticated) { getApiSpec(); } - }, [props.activity.activity_subtype, authenticated, MOBILE, activityStateInStore.suggestedTreatmentIDs]); + console.log('end api spec stuff') + }, [activityState.activity_subtype, authenticated, MOBILE, suggestedTreatmentIDS]); const [isDisabled, setIsDisabled] = useState(false); useEffect(() => { - const notMine = authState?.username !== activityStateInStore?.activity?.created_by; + const notMine = authState?.username !== activityState.created_by; const notAdmin = authState?.accessRoles?.filter((role) => { return role.role_id === 18; @@ -282,21 +297,23 @@ const FormContainer: React.FC = (props) => { 'single-select-autocomplete': SingleSelectAutoComplete }} readonly={props.isDisabled} - key={props.activity?._id + keyInt.toString()} + key={activityState?._id + keyInt.toString()} disabled={isDisabled} - formData={activityStateInStore.activity.form_data || null} + formData={activityState.form_data || null} schema={schemas.schema} uiSchema={schemas.uiSchema} - liveValidate={true} - customValidate={props.customValidation} + liveValidate={false} + customValidate={validatorForActivity(activityState, null)} validator={validator} showErrorList={'top'} transformErrors={getCustomErrorTransformer()} autoComplete="off" onChange={(event) => { - props.onFormChange(event, formRef, focusedFieldArgs, (updatedFormData) => { + console.log('A CHANGE') + debouncedFormChange(event, formRef, focusedFieldArgs, (updatedFormData) => { //setformData(updatedFormData); }); + console.log('AFTER DEBOUNCE') }} onError={(error) => { if (!props.onFormSubmitError) { @@ -321,7 +338,7 @@ const FormContainer: React.FC = (props) => { {isActivityChemTreatment() && ( { //todo redux chem treatment form on change dispatch({ @@ -335,7 +352,7 @@ const FormContainer: React.FC = (props) => { callback(); } }} - form_data={activityStateInStore.activity.form_data} + form_data={activityState.form_data} schema={schemas.schema} /> )} diff --git a/appv2/src/rjsf/business-rules/customValidation.ts b/appv2/src/rjsf/business-rules/customValidation.ts index 39db3adfe..ed28e39f8 100644 --- a/appv2/src/rjsf/business-rules/customValidation.ts +++ b/appv2/src/rjsf/business-rules/customValidation.ts @@ -15,8 +15,9 @@ function combineValidators(validators: rjsfValidator[]): rjsfValidator { } export function validatorForActivity(activity, linkedActivity): rjsfValidator { + console.log('recombining validators') return combineValidators([ - getAreaValidator(activity.activity_subtype), + /* getAreaValidator(activity.activity_subtype), getDateAndTimeValidator(activity.activity_subtype), getDateAndTimeValidatorOther(activity.activity_subtype), getWindValidator(activity.activity_subtype), @@ -35,7 +36,7 @@ export function validatorForActivity(activity, linkedActivity): rjsfValidator { getJurisdictionPercentValidator(), getInvasivePlantsValidator(linkedActivity), getPlotIdentificationTreesValidator(activity.activity_subtype), - accessDescriptionMinChars() + accessDescriptionMinChars()*/ ]); } diff --git a/appv2/src/state/sagas/activity.ts b/appv2/src/state/sagas/activity.ts index 77b576b9b..0bbfe1864 100644 --- a/appv2/src/state/sagas/activity.ts +++ b/appv2/src/state/sagas/activity.ts @@ -218,7 +218,7 @@ function* activityPageSaga() { handle_ACTIVITY_GET_SUGGESTED_JURISDICTIONS_REQUEST_ONLINE ), takeLatest(ACTIVITY_GET_SUGGESTED_JURISDICTIONS_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), - takeLatest(ACTIVITY_ON_FORM_CHANGE_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), + takeEvery(ACTIVITY_ON_FORM_CHANGE_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), takeEvery(ACTIVITY_SAVE_SUCCESS, handle_ACTIVITY_SET_SAVED_HASH_REQUEST), takeEvery(ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST, handle_ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST), takeEvery(ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST_ONLINE, handle_ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST_ONLINE), From 842c419605ac0b1f6845e929887bedb85e38f083 Mon Sep 17 00:00:00 2001 From: Robert Johnstone Date: Tue, 5 Dec 2023 12:41:05 +0800 Subject: [PATCH 2/3] upgrade ajv and rjsf --- appv2/package-lock.json | 178 ++++++++++++++---- appv2/package.json | 8 +- .../Admin/email-setup/EmailSettings.tsx | 2 +- .../Admin/email-setup/EmailTemplates.tsx | 2 +- .../Records/Activity/form/FormContainer.tsx | 5 +- database/src/migrations/fix.sql | 0 6 files changed, 147 insertions(+), 48 deletions(-) create mode 100644 database/src/migrations/fix.sql diff --git a/appv2/package-lock.json b/appv2/package-lock.json index 95ebeceb4..a8b521863 100644 --- a/appv2/package-lock.json +++ b/appv2/package-lock.json @@ -34,10 +34,10 @@ "@mui/x-data-grid": "^5.17.18", "@react-leaflet/core": "^2.1.0", "@reduxjs/toolkit": "^1.9.5", - "@rjsf/core": "^5.0.0-beta.15", - "@rjsf/mui": "^5.0.0-beta.15", - "@rjsf/utils": "^5.0.0-beta.15", - "@rjsf/validator-ajv6": "^5.0.0-beta.15", + "@rjsf/core": "^5.15.0", + "@rjsf/mui": "^5.15.0", + "@rjsf/utils": "^5.15.0", + "@rjsf/validator-ajv8": "^5.15.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/user-event": "^8.1.3", "@tmcw/togeojson": "^4.4.1", @@ -3106,9 +3106,9 @@ } }, "node_modules/@rjsf/core": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.12.1.tgz", - "integrity": "sha512-1YFhZ90/uHRx1akQmDdIjBxGMjs/5gtuTLUFwl6GbOwTm2fhZRh3qXRFyTXz81Oy6TGcbrxBJEYvFg2iHjYKCA==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.15.0.tgz", + "integrity": "sha512-tD+S0Sx6TtiW1Wp2E8fLArlKcPDXaOS0MBnM6FNWZuum1bbQFoavjm/wf0PpKQ2AohsKGvWXjIAajC+HX4Anww==", "dependencies": { "lodash": "^4.17.21", "lodash-es": "^4.17.21", @@ -3120,14 +3120,14 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.8.x", + "@rjsf/utils": "^5.12.x", "react": "^16.14.0 || >=17" } }, "node_modules/@rjsf/mui": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.12.1.tgz", - "integrity": "sha512-d7cNFIdt6N24m5NPrNSqfCe2SUyUjX48Goo7z4J9vOHWxo5kdDfBEa3UwNA/DR9lo+9cYY7QTvKbgrTkxWU58A==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.15.0.tgz", + "integrity": "sha512-OQ2fivrW2O0mMQx7pJGe5zLyaAPyqvFyktatWpNQV2Gge/Ge0IDmtN0VMU6Im48JZECjO35l5W+LGEEABQ/UNA==", "engines": { "node": ">=14" }, @@ -3136,15 +3136,15 @@ "@emotion/styled": "^11.6.0", "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", - "@rjsf/core": "^5.8.x", - "@rjsf/utils": "^5.8.x", + "@rjsf/core": "^5.12.x", + "@rjsf/utils": "^5.12.x", "react": ">=17" } }, "node_modules/@rjsf/utils": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.12.1.tgz", - "integrity": "sha512-/k8+7WdLwhaYsOQvH5BQINipj2IJvjEW3QQv4jQQ7sXtkpdUjieZayRfaE8DHfRdm9HjgJURJFDy3EODkWPl6A==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.15.0.tgz", + "integrity": "sha512-+K+WA/tGD8jSDRB6QzCvyCY0/rVZ+q/u0f07AGfx/h/ICVcJjOy5fWtUANQ0bBltLNPqjXqpFb3e3SxDXzSVaA==", "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", @@ -3159,12 +3159,13 @@ "react": "^16.14.0 || >=17" } }, - "node_modules/@rjsf/validator-ajv6": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv6/-/validator-ajv6-5.12.1.tgz", - "integrity": "sha512-NzywRc+lX7POq3iSGgjXAiMMYuQnoLoGbC32DYgmQ2st+LOuws5Yc6ToG5dX05uEuL95MOjnOk+f+3vSPiLL1A==", + "node_modules/@rjsf/validator-ajv8": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.15.0.tgz", + "integrity": "sha512-+kSemuPRZzayt+CQHu2f4oN6CeCn7gSxQYC8CU3gDZXL48HKlvnq8VlgFWetbtawojQ7x8NjAyuBTJDIP/TqZg==", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21" }, @@ -3172,9 +3173,29 @@ "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.8.x" + "@rjsf/utils": "^5.12.x" } }, + "node_modules/@rjsf/validator-ajv8/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rjsf/validator-ajv8/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@rollup/plugin-commonjs": { "version": "24.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", @@ -5907,6 +5928,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -16398,7 +16455,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -21659,9 +21715,9 @@ } }, "@rjsf/core": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.12.1.tgz", - "integrity": "sha512-1YFhZ90/uHRx1akQmDdIjBxGMjs/5gtuTLUFwl6GbOwTm2fhZRh3qXRFyTXz81Oy6TGcbrxBJEYvFg2iHjYKCA==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.15.0.tgz", + "integrity": "sha512-tD+S0Sx6TtiW1Wp2E8fLArlKcPDXaOS0MBnM6FNWZuum1bbQFoavjm/wf0PpKQ2AohsKGvWXjIAajC+HX4Anww==", "requires": { "lodash": "^4.17.21", "lodash-es": "^4.17.21", @@ -21671,14 +21727,14 @@ } }, "@rjsf/mui": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.12.1.tgz", - "integrity": "sha512-d7cNFIdt6N24m5NPrNSqfCe2SUyUjX48Goo7z4J9vOHWxo5kdDfBEa3UwNA/DR9lo+9cYY7QTvKbgrTkxWU58A==" + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.15.0.tgz", + "integrity": "sha512-OQ2fivrW2O0mMQx7pJGe5zLyaAPyqvFyktatWpNQV2Gge/Ge0IDmtN0VMU6Im48JZECjO35l5W+LGEEABQ/UNA==" }, "@rjsf/utils": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.12.1.tgz", - "integrity": "sha512-/k8+7WdLwhaYsOQvH5BQINipj2IJvjEW3QQv4jQQ7sXtkpdUjieZayRfaE8DHfRdm9HjgJURJFDy3EODkWPl6A==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.15.0.tgz", + "integrity": "sha512-+K+WA/tGD8jSDRB6QzCvyCY0/rVZ+q/u0f07AGfx/h/ICVcJjOy5fWtUANQ0bBltLNPqjXqpFb3e3SxDXzSVaA==", "requires": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", @@ -21687,14 +21743,33 @@ "react-is": "^18.2.0" } }, - "@rjsf/validator-ajv6": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv6/-/validator-ajv6-5.12.1.tgz", - "integrity": "sha512-NzywRc+lX7POq3iSGgjXAiMMYuQnoLoGbC32DYgmQ2st+LOuws5Yc6ToG5dX05uEuL95MOjnOk+f+3vSPiLL1A==", + "@rjsf/validator-ajv8": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.15.0.tgz", + "integrity": "sha512-+kSemuPRZzayt+CQHu2f4oN6CeCn7gSxQYC8CU3gDZXL48HKlvnq8VlgFWetbtawojQ7x8NjAyuBTJDIP/TqZg==", "requires": { - "ajv": "^6.12.6", + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } } }, "@rollup/plugin-commonjs": { @@ -23874,6 +23949,32 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -31841,8 +31942,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "require-main-filename": { "version": "2.0.0", diff --git a/appv2/package.json b/appv2/package.json index 7dd0be70d..f8b4c1698 100644 --- a/appv2/package.json +++ b/appv2/package.json @@ -37,10 +37,10 @@ "@mui/x-data-grid": "^5.17.18", "@react-leaflet/core": "^2.1.0", "@reduxjs/toolkit": "^1.9.5", - "@rjsf/core": "^5.0.0-beta.15", - "@rjsf/mui": "^5.0.0-beta.15", - "@rjsf/utils": "^5.0.0-beta.15", - "@rjsf/validator-ajv6": "^5.0.0-beta.15", + "@rjsf/core": "^5.15.0", + "@rjsf/mui": "^5.15.0", + "@rjsf/utils": "^5.15.0", + "@rjsf/validator-ajv8": "^5.15.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/user-event": "^8.1.3", "@tmcw/togeojson": "^4.4.1", diff --git a/appv2/src/UI/Overlay/Admin/email-setup/EmailSettings.tsx b/appv2/src/UI/Overlay/Admin/email-setup/EmailSettings.tsx index a47347ace..a5136de40 100644 --- a/appv2/src/UI/Overlay/Admin/email-setup/EmailSettings.tsx +++ b/appv2/src/UI/Overlay/Admin/email-setup/EmailSettings.tsx @@ -1,5 +1,5 @@ import { RJSFSchema, UiSchema } from '@rjsf/utils'; -import validator from '@rjsf/validator-ajv6'; +import validator from '@rjsf/validator-ajv8'; import React, { useEffect } from 'react'; import { Form } from '@rjsf/mui'; import { diff --git a/appv2/src/UI/Overlay/Admin/email-setup/EmailTemplates.tsx b/appv2/src/UI/Overlay/Admin/email-setup/EmailTemplates.tsx index d8c75c5ad..9ccb60093 100644 --- a/appv2/src/UI/Overlay/Admin/email-setup/EmailTemplates.tsx +++ b/appv2/src/UI/Overlay/Admin/email-setup/EmailTemplates.tsx @@ -1,7 +1,7 @@ import { Card, CardContent, Grid } from '@mui/material'; import { Form } from '@rjsf/mui'; import { RJSFSchema, UiSchema } from '@rjsf/utils'; -import validator from '@rjsf/validator-ajv6'; +import validator from '@rjsf/validator-ajv8'; import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { EMAIL_TEMPLATES_RETRIEVE_REQUEST, EMAIL_TEMPLATES_SET_ACTIVE, EMAIL_TEMPLATES_UPDATE } from 'state/actions'; diff --git a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx index 4cf42bea0..31319f788 100644 --- a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx +++ b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx @@ -32,7 +32,7 @@ import { selectActivity } from 'state/reducers/activity'; import { useDispatch } from 'react-redux'; import { ACTIVITY_CHEM_TREATMENT_DETAILS_FORM_ON_CHANGE_REQUEST, ACTIVITY_ON_FORM_CHANGE_REQUEST } from 'state/actions'; import { selectUserSettings } from 'state/reducers/userSettings'; -import validator from '@rjsf/validator-ajv6'; +import validator from '@rjsf/validator-ajv8'; import 'UI/Overlay/Records/Activity/form/aditionalFormStyles.css' import { getCustomErrorTransformer } from 'rjsf/business-rules/customErrorTransformer'; import _ from 'lodash'; @@ -76,7 +76,7 @@ const FormContainer: React.FC = (props) => { const dispatch = useDispatch(); console.log('rendering') - const debouncedFormChange = + const debouncedFormChange = _.debounce((event, ref, lastField, callbackFun) => { //(event, ref, lastField, callbackFun) => { dispatch({ @@ -313,7 +313,6 @@ const FormContainer: React.FC = (props) => { debouncedFormChange(event, formRef, focusedFieldArgs, (updatedFormData) => { //setformData(updatedFormData); }); - console.log('AFTER DEBOUNCE') }} onError={(error) => { if (!props.onFormSubmitError) { diff --git a/database/src/migrations/fix.sql b/database/src/migrations/fix.sql new file mode 100644 index 000000000..e69de29bb From af5a3b34d07c880857706db9d022ceb2f5a1ec6b Mon Sep 17 00:00:00 2001 From: Micheal Wells Date: Tue, 5 Dec 2023 10:30:21 -0800 Subject: [PATCH 3/3] get v2 form state changes merge worthy --- .../Records/Activity/form/FormContainer.tsx | 38 ++++++++----------- appv2/src/main.tsx | 1 + .../rjsf/business-rules/customValidation.ts | 5 +-- appv2/src/state/sagas/activity.ts | 2 +- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx index 31319f788..7a1362b4a 100644 --- a/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx +++ b/appv2/src/UI/Overlay/Records/Activity/form/FormContainer.tsx @@ -24,11 +24,7 @@ import MultiSelectAutoComplete from 'rjsf/widgets/MultiSelectAutoComplete'; import SingleSelectAutoComplete from 'rjsf/widgets/SingleSelectAutoComplete'; import rjsfTheme from 'UI/Overlay/Records/Activity/form/rjsfTheme'; import ChemicalTreatmentDetailsForm from './ChemicalTreatmentDetailsForm/ChemicalTreatmentDetailsForm'; -import PasteButtonComponent from './PasteButtonComponent'; import { useSelector } from 'util/use_selector'; -import { selectAuth } from 'state/reducers/auth'; -import { selectConfiguration } from 'state/reducers/configuration'; -import { selectActivity } from 'state/reducers/activity'; import { useDispatch } from 'react-redux'; import { ACTIVITY_CHEM_TREATMENT_DETAILS_FORM_ON_CHANGE_REQUEST, ACTIVITY_ON_FORM_CHANGE_REQUEST } from 'state/actions'; import { selectUserSettings } from 'state/reducers/userSettings'; @@ -74,9 +70,8 @@ export interface IFormContainerProps { const FormContainer: React.FC = (props) => { const activityState = useSelector((state) => state.ActivityPage.activity); const dispatch = useDispatch(); - console.log('rendering') - const debouncedFormChange = + const debouncedFormChange = _.debounce((event, ref, lastField, callbackFun) => { //(event, ref, lastField, callbackFun) => { dispatch({ @@ -91,13 +86,16 @@ const FormContainer: React.FC = (props) => { const [open, setOpen] = React.useState(false); const [alertMsg, setAlertMsg] = React.useState(null); const [field, setField] = React.useState(''); - const { authenticated } = useSelector(selectAuth); - const authState = useSelector(selectAuth); - const { MOBILE } = useSelector(selectConfiguration); + const authenticated = useSelector((state) => state.Auth.authenticated); + const username = useSelector((state) => state.Auth.username); + const accessRoles = useSelector((state) => state.Auth.accessRoles); + + const MOBILE = useSelector((state) => state.Configuration.current.MOBILE); const { darkTheme } = useSelector(selectUserSettings); - const userSettingsState = useSelector(selectUserSettings); + const apiDocsWithViewOptions = useSelector((state) => state.UserSettings.apiDocsWithViewOptions); + const apiDocsWithSelectOptions = useSelector((state) => state.UserSettings.apiDocsWithSelectOptions); const suggestedTreatmentIDS = useSelector((state) => state.ActivityPage.suggestedTreatmentIDs); @@ -139,7 +137,6 @@ const FormContainer: React.FC = (props) => { $this.validate($this.state.formData); //update formData of the activity via onFormChange debouncedFormChange({ formData: formRef.current.state.formData }, formRef, null, (updatedFormData) => { - setformData(updatedFormData); }); }); }, 100); @@ -173,21 +170,20 @@ const FormContainer: React.FC = (props) => { useEffect(() => { const getApiSpec = async () => { - console.log('begin api spec stuff') const subtype = activityState?.activity_subtype || activityState?.activitySubtype; if (!subtype) throw new Error('Activity has no Subtype specified'); let components; - const notMine = authState?.username !== activityState?.created_by; + const notMine = username !== activityState?.created_by; const notAdmin = - authState?.accessRoles?.filter((role) => { + accessRoles?.filter((role) => { return [1,18].includes(role.role_id) }).length === 0; if (notAdmin && notMine) { - components = (userSettingsState.apiDocsWithViewOptions as any).components; + components = (apiDocsWithViewOptions as any).components; } else if (!notAdmin && notMine) { - components = (userSettingsState.apiDocsWithViewOptions as any).components; + components = (apiDocsWithViewOptions as any).components; } else { - components = (userSettingsState.apiDocsWithSelectOptions as any).components; + components = (apiDocsWithSelectOptions as any).components; } let uiSchema = RootUISchemas[subtype]; @@ -253,14 +249,13 @@ const FormContainer: React.FC = (props) => { if (authenticated) { getApiSpec(); } - console.log('end api spec stuff') }, [activityState.activity_subtype, authenticated, MOBILE, suggestedTreatmentIDS]); const [isDisabled, setIsDisabled] = useState(false); useEffect(() => { - const notMine = authState?.username !== activityState.created_by; + const notMine = username !== activityState.created_by; const notAdmin = - authState?.accessRoles?.filter((role) => { + accessRoles?.filter((role) => { return role.role_id === 18; }).length === 0; if (notAdmin && notMine) { @@ -268,7 +263,7 @@ const FormContainer: React.FC = (props) => { } else { setIsDisabled(false); } - }, [JSON.stringify(authState?.accessRoles), JSON.stringify(authState?.username)]); + }, [JSON.stringify(accessRoles), JSON.stringify(username)]); // hack to make fields rerender only on paste event const [keyInt, setKeyInt] = useState(0); @@ -309,7 +304,6 @@ const FormContainer: React.FC = (props) => { transformErrors={getCustomErrorTransformer()} autoComplete="off" onChange={(event) => { - console.log('A CHANGE') debouncedFormChange(event, formRef, focusedFieldArgs, (updatedFormData) => { //setformData(updatedFormData); }); diff --git a/appv2/src/main.tsx b/appv2/src/main.tsx index a6384c533..4313e3329 100644 --- a/appv2/src/main.tsx +++ b/appv2/src/main.tsx @@ -18,6 +18,7 @@ import(/* webpackChunkName: "app_config" */ './state/config').then(({ CONFIG }) console.log('about to render'); const root = createRoot(container); if (root) { + console.log('rendered') root.render( diff --git a/appv2/src/rjsf/business-rules/customValidation.ts b/appv2/src/rjsf/business-rules/customValidation.ts index ed28e39f8..39db3adfe 100644 --- a/appv2/src/rjsf/business-rules/customValidation.ts +++ b/appv2/src/rjsf/business-rules/customValidation.ts @@ -15,9 +15,8 @@ function combineValidators(validators: rjsfValidator[]): rjsfValidator { } export function validatorForActivity(activity, linkedActivity): rjsfValidator { - console.log('recombining validators') return combineValidators([ - /* getAreaValidator(activity.activity_subtype), + getAreaValidator(activity.activity_subtype), getDateAndTimeValidator(activity.activity_subtype), getDateAndTimeValidatorOther(activity.activity_subtype), getWindValidator(activity.activity_subtype), @@ -36,7 +35,7 @@ export function validatorForActivity(activity, linkedActivity): rjsfValidator { getJurisdictionPercentValidator(), getInvasivePlantsValidator(linkedActivity), getPlotIdentificationTreesValidator(activity.activity_subtype), - accessDescriptionMinChars()*/ + accessDescriptionMinChars() ]); } diff --git a/appv2/src/state/sagas/activity.ts b/appv2/src/state/sagas/activity.ts index 0bbfe1864..77b576b9b 100644 --- a/appv2/src/state/sagas/activity.ts +++ b/appv2/src/state/sagas/activity.ts @@ -218,7 +218,7 @@ function* activityPageSaga() { handle_ACTIVITY_GET_SUGGESTED_JURISDICTIONS_REQUEST_ONLINE ), takeLatest(ACTIVITY_GET_SUGGESTED_JURISDICTIONS_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), - takeEvery(ACTIVITY_ON_FORM_CHANGE_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), + takeLatest(ACTIVITY_ON_FORM_CHANGE_SUCCESS, handle_ACTIVITY_SET_CURRENT_HASH_REQUEST), takeEvery(ACTIVITY_SAVE_SUCCESS, handle_ACTIVITY_SET_SAVED_HASH_REQUEST), takeEvery(ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST, handle_ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST), takeEvery(ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST_ONLINE, handle_ACTIVITY_GET_SUGGESTED_PERSONS_REQUEST_ONLINE),