Skip to content

Commit

Permalink
Amycommits/appeals 37508 (#20503)
Browse files Browse the repository at this point in the history
* APPEALS-37508 validation works

* APPEALS-37508 added a check for lever errors to the save button.

* APPEALS-37508 cleaned up some of the validation functions
  • Loading branch information
amybids authored Jan 13, 2024
1 parent 5d45389 commit cc71f28
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 27 deletions.
37 changes: 28 additions & 9 deletions client/app/caseDistribution/components/BatchSize.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { css } from 'glamor';
import styles from 'app/styles/caseDistribution/InteractableLevers.module.scss';
import NumberField from 'app/components/NumberField';
import COPY from '../../../COPY';
import { getLeversByGroup, getUserIsAcdAdmin } from '../reducers/levers/leversSelector';
import { updateNumberLever } from '../reducers/levers/leversActions';
import { getLeversByGroup, getLeverErrors, getUserIsAcdAdmin } from '../reducers/levers/leversSelector';
import { updateNumberLever, addLeverErrors, removeLeverErrors } from '../reducers/levers/leversActions';
import { Constant } from '../constants';
import ACD_LEVERS from '../../../constants/ACD_LEVERS';
import { validateLeverInput } from '../utils';

const BatchSize = () => {
const theState = useSelector((state) => state);
Expand All @@ -20,21 +21,39 @@ const BatchSize = () => {
'& .usa-input-error label': { bottom: '15px', left: '89px' }
});

const errorMessages = {};

const dispatch = useDispatch();
const batchLevers = getLeversByGroup(theState, Constant.LEVERS, ACD_LEVERS.lever_groups.batch);
const [errorMessagesList] = useState(errorMessages);
const [batchSizeLevers, setBatchSizeLevers] = useState(batchLevers);

function leverErrors(leverItem) {
return getLeverErrors(theState, leverItem)
}

useEffect(() => {
setBatchSizeLevers(batchLevers);
}, [batchLevers]);

const updateNumberFieldLever = (leverType, leverItem) => (event) => {
dispatch(updateNumberLever(leverType, leverItem, event));
const handleValidation = (lever, leverItem, value) => {
const validationErrors = validateLeverInput(lever, value)
const errorExists = leverErrors(leverItem).length > 0
if(validationErrors.length > 0 && !errorExists) {
dispatch(addLeverErrors(validationErrors))
}

if (validationErrors.length === 0 && errorExists) {
dispatch(removeLeverErrors(leverItem))
}

}

const updateNumberFieldLever = (lever) => (event) => {
const { lever_group, item } = lever
handleValidation(lever, item, event)
dispatch(updateNumberLever(lever_group, item, event));
};



batchLevers?.sort((leverA, leverB) => leverA.lever_group_order - leverB.lever_group_order);

return (
Expand Down Expand Up @@ -62,8 +81,8 @@ const BatchSize = () => {
isInteger
readOnly={lever.is_disabled_in_ui}
value={lever.value}
errorMessage={errorMessagesList[lever.item]}
onChange={updateNumberFieldLever(ACD_LEVERS.lever_groups.batch, lever.item)}
errorMessage={leverErrors(lever.item)}
onChange={updateNumberFieldLever(lever)}
tabIndex={lever.is_disabled_in_ui ? -1 : null}
/> :
<label className={lever.is_disabled_in_ui ? styles.leverDisabled : styles.leverActive}>
Expand Down
42 changes: 28 additions & 14 deletions client/app/caseDistribution/components/DocketTimeGoals.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { useDispatch, useSelector } from 'react-redux';
import { css } from 'glamor';
import cx from 'classnames';
import styles from 'app/styles/caseDistribution/InteractableLevers.module.scss';
import { updateNumberLever } from '../reducers/levers/leversActions';
import { updateNumberLever, addLeverErrors, removeLeverErrors } from '../reducers/levers/leversActions';
import ToggleSwitch from 'app/components/ToggleSwitch/ToggleSwitch';
import NumberField from 'app/components/NumberField';
import COPY from '../../../COPY';
import { Constant, sectionTitles, docketTimeGoalPriorMappings } from '../constants';
import { getLeversByGroup, getUserIsAcdAdmin } from '../reducers/levers/leversSelector';
import { getLeversByGroup, getLeverErrors, getUserIsAcdAdmin } from '../reducers/levers/leversSelector';
import ACD_LEVERS from '../../../constants/ACD_LEVERS';
import { validateLeverInput } from '../utils';

const DocketTimeGoals = () => {

Expand All @@ -20,21 +21,22 @@ const DocketTimeGoals = () => {
'& .usa-input-error label': { bottom: '15px', left: '89px' }
});

const errorMessages = {};

const dispatch = useDispatch();
const theState = useSelector((state) => state);

// pull docket time goal and distribution levers from the store
const currentTimeLevers = getLeversByGroup(theState, Constant.LEVERS, ACD_LEVERS.lever_groups.docket_time_goal);
const isUserAcdAdmin = getUserIsAcdAdmin(theState);

function leverErrors(leverItem) {
return getLeverErrors(theState, leverItem)
}

const currentDistributionPriorLevers =
getLeversByGroup(theState, Constant.LEVERS, ACD_LEVERS.lever_groups.docket_distribution_prior);

const [docketDistributionLevers, setDistributionLever] = useState(currentDistributionPriorLevers);
const [docketTimeGoalLevers, setTimeGoalLever] = useState(currentTimeLevers);
const [errorMessagesList] = useState(errorMessages);

useEffect(() => {
setDistributionLever(currentDistributionPriorLevers);
Expand All @@ -44,8 +46,23 @@ const DocketTimeGoals = () => {
setTimeGoalLever(currentTimeLevers);
}, [currentTimeLevers]);

const updateNumberFieldLever = (leverType, leverItem) => (event) => {
dispatch(updateNumberLever(leverType, leverItem, event));
const handleValidation = (lever, leverItem, value) => {
const validationErrors = validateLeverInput(lever, value)
const errorExists = leverErrors(leverItem).length > 0
if(validationErrors.length > 0 && !errorExists) {
dispatch(addLeverErrors(validationErrors))
}

if (validationErrors.length === 0 && errorExists) {
dispatch(removeLeverErrors(leverItem))
}

}

const updateNumberFieldLever = (lever) => (event) => {
const { lever_group, item } = lever
handleValidation(lever, item, event)
dispatch(updateNumberLever(lever_group, item, event));
};

const toggleLever = (index) => () => {
Expand Down Expand Up @@ -89,8 +106,8 @@ const DocketTimeGoals = () => {
readOnly={docketTimeGoalLever.is_disabled_in_ui}
value={docketTimeGoalLever.value}
label={docketTimeGoalLever.unit}
errorMessage={errorMessagesList[docketTimeGoalLever.item]}
onChange={updateNumberFieldLever(ACD_LEVERS.lever_groups.docket_time_goal, docketTimeGoalLever.item)}
errorMessage={leverErrors(docketTimeGoalLever.item)}
onChange={updateNumberFieldLever(docketTimeGoalLever)}
/>
</div>
<div className={`${styles.leverRight} ${styles.docketLeverRight} ${leverNumberDiv}`}>
Expand All @@ -110,11 +127,8 @@ const DocketTimeGoals = () => {
readOnly={distributionPriorLever.is_disabled_in_ui}
value={distributionPriorLever.value}
label={distributionPriorLever.unit}
errorMessage={errorMessagesList[distributionPriorLever.item]}
onChange={
updateNumberFieldLever(ACD_LEVERS.lever_groups.docket_distribution_prior, true,
distributionPriorLever.item)
}
errorMessage={leverErrors(distributionPriorLever.item)}
onChange={updateNumberFieldLever(distributionPriorLever)}
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions client/app/caseDistribution/components/LeverSaveButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Button from 'app/components/Button';
import { useDispatch, useSelector } from 'react-redux';
import SaveModal from './SaveModal';
import { saveLevers } from '../reducers/levers/leversActions';
import { changedLevers, hasChangedLevers } from '../reducers/levers/leversSelector';
import { changedLevers, hasChangedLevers, hasNoLeverErrors } from '../reducers/levers/leversSelector';

export const LeverSaveButton = () => {
const dispatch = useDispatch();
Expand All @@ -12,7 +12,7 @@ export const LeverSaveButton = () => {
const [enableSave, setEnableSave] = useState(false);

useEffect(() => {
const enable = hasChangedLevers(theState);
const enable = hasChangedLevers(theState) && hasNoLeverErrors(theState);

setEnableSave(enable);
setShowModal(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
export const ACTIONS = {
ADD_LEVER_VALIDATION_ERRORS: 'ADD_LEVER_VALIDATION_ERRORS',
UPDATE_RADIO_LEVER: 'UPDATE_RADIO_LEVER',
UPDATE_COMBINATION_LEVER: 'UPDATE_COMBINATION_LEVER',
UPDATE_BOOLEAN_LEVER: 'UPDATE_BOOLEAN_LEVER',
UPDATE_TEXT_LEVER: 'UPDATE_TEXT_LEVER',
UPDATE_NUMBER_LEVER: 'UPDATE_NUMBER_LEVER',
SAVE_LEVERS: 'SAVE_LEVERS',
REMOVE_LEVER_VALIDATION_ERRORS: 'REMOVE_LEVER_VALIDATION_ERRORS',
REVERT_LEVERS: 'REVERT_LEVERS',
HIDE_BANNER: 'HIDE_BANNER',
LOAD_LEVERS: 'LOAD_LEVERS',
SET_USER_IS_ACD_ADMIN: 'SET_USER_IS_ACD_ADMIN'
SET_USER_IS_ACD_ADMIN: 'SET_USER_IS_ACD_ADMIN',
VALIDATE_INPUT: 'VALIDATE_INPUT'
};
20 changes: 20 additions & 0 deletions client/app/caseDistribution/reducers/levers/leversActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,23 @@ export const hideSuccessBanner = () =>
type: ACTIONS.HIDE_BANNER
});
};

export const addLeverErrors = (errors) =>
(dispatch) => {
dispatch({
type: ACTIONS.ADD_LEVER_VALIDATION_ERRORS,
payload: {
errors
}
});
};

export const removeLeverErrors = (leverItem) =>
(dispatch) => {
dispatch({
type: ACTIONS.REMOVE_LEVER_VALIDATION_ERRORS,
payload: {
leverItem
}
});
};
17 changes: 16 additions & 1 deletion client/app/caseDistribution/reducers/levers/leversReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
} from './leversSelector';
import {
createUpdatedLeversWithValues,
formatLeverHistory
formatLeverHistory,
leverErrorMessageExists
} from '../../utils';

// formattedHistory should be deleted.
Expand All @@ -19,6 +20,7 @@ export const initialState = {
historyList: [],
changesOccurred: false,
displayBanner: false,
leversErrors: [],
errors: [],
isUserAcdAdmin: false
};
Expand Down Expand Up @@ -100,6 +102,19 @@ const leversReducer = (state = initialState, action = {}) => {
errors: []
};

case ACTIONS.ADD_LEVER_VALIDATION_ERRORS:
return {
...state,
leversErrors: leverErrorMessageExists(state.leversErrors, action.payload.errors) ? state.leversErrors : [...state.leversErrors, ...action.payload.errors]
}
case ACTIONS.REMOVE_LEVER_VALIDATION_ERRORS:
const errorList = [...new Set(state.leversErrors.filter(error => error.leverItem !== action.payload.leverItem))]

return {
...state,
leversErrors: errorList
}

default:
return state;
}
Expand Down
23 changes: 23 additions & 0 deletions client/app/caseDistribution/reducers/levers/leversSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ const getAdminStatus = (state) => {
return state.caseDistributionLevers.isUserAcdAdmin;
};

const leverErrorList = (state, leverItem) => {
return state.caseDistributionLevers.leversErrors?.filter(error => error.leverItem === leverItem).map(error => error.message).join('')
}

const leverErrorCount = (state, leverItem) => {
return state.caseDistributionLevers.leversErrors.length
}


/**
* WILL NEED UPDATING WHEN RADIO AND COMBINATION LEVERS ARE EDITABLE
*/
Expand Down Expand Up @@ -89,6 +98,20 @@ export const createUpdatedRadioLever = (state, action) => {
return updateLeverGroup(state, leverGroup, leverItem, updateLeverValue);
};

export const getLeverErrors = createSelector(
[leverErrorList],
(errors) => {
return errors
}
)

export const hasNoLeverErrors = createSelector(
[leverErrorCount],
(count) => {
return count === 0
}
)

/**
* Do not trust this code. It is untested
* WILL NEED UPDATING WHEN RADIO AND COMBINATION LEVERS ARE EDITABLE
Expand Down
24 changes: 24 additions & 0 deletions client/app/caseDistribution/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,27 @@ export const formatLeverHistory = (leverHistoryList) => {

return formattedLeverHistory;
};

export const validateLeverInput = (lever, value) => {
const errors = []
const { item, min_value, max_value, data_type } = lever;
if (value === null || value === '') errors.push({leverItem: lever.item, message: ACD_LEVERS.validation_error_message.minimum_not_met })
if (parseFloat(value)) {
if (value < min_value) {
errors.push({leverItem: lever.item, message: ACD_LEVERS.validation_error_message.minimum_not_met })
}
if (max_value && value > max_value) {
errors.push({leverItem: lever.item, message: ACD_LEVERS.validation_error_message.out_of_bounds})
}
}

return errors
}

export const leverErrorMessageExists = (existingErrors, newErrors) => {
return existingErrors.some(existingError =>
newErrors.every(newError =>
JSON.stringify(existingError) === JSON.stringify(newError)
)
);
};
4 changes: 4 additions & 0 deletions client/constants/ACD_LEVERS.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@
"affinity": "affinity",
"docket_distribution_prior": "docket_distribution_prior",
"docket_time_goal": "docket_time_goal"
},
"validation_error_message": {
"minimum_not_met": "Please enter a value greater than or equal to 0",
"out_of_bounds": "Please enter a value from 0 to 999"
}
}

0 comments on commit cc71f28

Please sign in to comment.