From 42b9a6aee14418f706ed1a0cdad24cdc3032593b Mon Sep 17 00:00:00 2001 From: Katelyn Grimes Date: Fri, 22 May 2026 13:33:21 -0400 Subject: [PATCH 1/2] Fix wrong title for MHA --- .../hrTools/mhaCalculator/[requestId].page.tsx | 8 ++++---- .../[accountListId]/hrTools/mhaCalculator/index.page.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/accountLists/[accountListId]/hrTools/mhaCalculator/[requestId].page.tsx b/pages/accountLists/[accountListId]/hrTools/mhaCalculator/[requestId].page.tsx index 28431428bf..8a52b7f801 100644 --- a/pages/accountLists/[accountListId]/hrTools/mhaCalculator/[requestId].page.tsx +++ b/pages/accountLists/[accountListId]/hrTools/mhaCalculator/[requestId].page.tsx @@ -51,7 +51,7 @@ export const HousingAllowanceRequestPageContent: React.FC = () => { const { requestData, loading, isMutating, pageType } = useMinisterHousingAllowance(); - const title = t("{{mode}} Minister's Housing Allowance Request", { + const title = t("{{mode}} Minister's Housing Allowance Calculation Tool", { mode: pageType, }); @@ -76,7 +76,7 @@ export const HousingAllowanceRequestPageContent: React.FC = () => { isOpen={isNavListOpen} selectedId={'mhaCalculator' + pageType} onClose={handleNavListToggle} - navType={NavTypeEnum.Reports} + navType={NavTypeEnum.HrTools} /> } leftOpen={isNavListOpen} @@ -87,7 +87,7 @@ export const HousingAllowanceRequestPageContent: React.FC = () => { { lastSavedAt={requestData?.updatedAt ?? null} /> } - headerType={HeaderTypeEnum.Report} + headerType={HeaderTypeEnum.HrTools} /> diff --git a/pages/accountLists/[accountListId]/hrTools/mhaCalculator/index.page.tsx b/pages/accountLists/[accountListId]/hrTools/mhaCalculator/index.page.tsx index f7fd204006..72bc76b703 100644 --- a/pages/accountLists/[accountListId]/hrTools/mhaCalculator/index.page.tsx +++ b/pages/accountLists/[accountListId]/hrTools/mhaCalculator/index.page.tsx @@ -58,7 +58,7 @@ const MinisterHousingAllowancePage: React.FC = () => { From 2085de340f8aa84d59d2f90b82de03b79f1665ca Mon Sep 17 00:00:00 2001 From: Katelyn Grimes Date: Wed, 27 May 2026 11:22:40 -0400 Subject: [PATCH 2/2] Remove field validation from mha --- .../MinisterHousingAllowance.graphql | 1 + .../StepThree/CalcComponents/CostOfHome.tsx | 20 +---- .../CalcComponents/FairRentalValue.tsx | 12 +-- .../Steps/StepThree/Calculation.test.tsx | 90 ++++--------------- .../Steps/StepThree/Calculation.tsx | 70 ++++++++------- 5 files changed, 56 insertions(+), 137 deletions(-) diff --git a/src/components/HrTools/MinisterHousingAllowance/MinisterHousingAllowance.graphql b/src/components/HrTools/MinisterHousingAllowance/MinisterHousingAllowance.graphql index 56b413f924..a573d587a5 100644 --- a/src/components/HrTools/MinisterHousingAllowance/MinisterHousingAllowance.graphql +++ b/src/components/HrTools/MinisterHousingAllowance/MinisterHousingAllowance.graphql @@ -95,6 +95,7 @@ mutation SubmitMinistryHousingAllowanceRequest( ) { submitMinistryHousingAllowanceRequest(input: $input) { ministryHousingAllowanceRequest { + id requestAttributes { ...RequestAttributes } diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.tsx index 981b3722bb..b1b03ee7fd 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.tsx @@ -24,7 +24,7 @@ export const CostOfHome: React.FC = ({ const locale = useLocale(); const currency = 'USD'; - const { values, touched, errors } = useFormikContext(); + const { values } = useFormikContext(); const { totalCostOfHome, annualCostOfHome } = useAnnualTotal( values, @@ -49,10 +49,6 @@ export const CostOfHome: React.FC = ({ sx={{ width: '30%', color: 'text.secondary', - border: - touched.mortgageOrRentPayment && errors.mortgageOrRentPayment - ? '2px solid red' - : '', }} > = ({ sx={{ width: '30%', color: 'text.secondary', - border: - touched.furnitureCostsTwo && errors.furnitureCostsTwo - ? '2px solid red' - : '', }} > = ({ sx={{ width: '30%', color: 'text.secondary', - border: - touched.repairCosts && errors.repairCosts ? '2px solid red' : '', }} > = ({ sx={{ width: '30%', color: 'text.secondary', - border: - touched.avgUtilityTwo && errors.avgUtilityTwo - ? '2px solid red' - : '', }} > = ({ sx={{ width: '30%', color: 'text.secondary', - border: - touched.unexpectedExpenses && errors.unexpectedExpenses - ? '2px solid red' - : '', }} > = ({ schema }) => { const locale = useLocale(); const currency = 'USD'; - const { values, touched, errors } = useFormikContext(); + const { values } = useFormikContext(); const { totalFairRental, annualFairRental } = useAnnualTotal( values, @@ -50,8 +50,6 @@ export const FairRentalValue: React.FC = ({ schema }) => { sx={{ width: '30%', color: 'text.secondary', - border: - touched.rentalValue && errors.rentalValue ? '2px solid red' : '', }} > = ({ schema }) => { sx={{ width: '30%', color: 'text.secondary', - border: - touched.furnitureCostsOne && errors.furnitureCostsOne - ? '2px solid red' - : '', }} > = ({ schema }) => { sx={{ width: '30%', color: 'text.secondary', - border: - touched.avgUtilityOne && errors.avgUtilityOne - ? '2px solid red' - : '', }} > { expect(getByRole('button', { name: /submit/i })).toBeInTheDocument(); }); - it('should show validation error when inputs are invalid', async () => { - const { findByText, getByRole, findByRole, getByText } = render( - , - ); - - const row = await findByRole('row', { - name: /average monthly amount for unexpected/i, - }); - const input = within(row).getByPlaceholderText(/\$0/i); - - userEvent.type(input, '100'); - expect(input).toHaveValue('100'); - userEvent.clear(input); - expect(input).toHaveValue(''); - - input.focus(); - userEvent.tab(); - - expect(await findByText('Required field.')).toBeInTheDocument(); - - const submitButton = getByRole('button', { name: /submit/i }); - - userEvent.click(submitButton); - - expect(await findByRole('alert')).toBeInTheDocument(); - expect( - getByText('Please enter a value for all required fields.'), - ).toBeInTheDocument(); - }); - it('should show validation error when checkbox is not checked', async () => { const { findByText, findByRole, getByText } = render( , @@ -230,34 +192,6 @@ describe('Calculation', () => { expect(await findByText('Invalid email address.')).toBeInTheDocument(); }); - it('shows validation error when input is 0', async () => { - const { findByRole, findByText } = render( - , - ); - - const row = await findByRole('row', { - name: /average monthly amount for unexpected/i, - }); - const input = within(row).getByPlaceholderText(/\$0/i); - - userEvent.type(input, '0'); - - input.focus(); - userEvent.tab(); - - expect(input).toHaveValue('$0.00'); - - expect(await findByText('Must be greater than $0.')).toBeInTheDocument(); - }); - it('shows confirmation modal when submit is clicked', async () => { const { getByRole, getByText, findByRole } = render( { id: 'request-id', requestAttributes: { ...mockMHARequest.requestAttributes, + rentOrOwn: MhaRentOrOwnEnum.Rent, iUnderstandMhaPolicy: false, phoneNumber: '1234567890', emailAddress: 'john.doe@cru.org', @@ -296,16 +231,10 @@ describe('Calculation', () => { }); const input4 = within(row4).getByPlaceholderText(/\$0/i); - const row5 = getByRole('row', { - name: /average monthly amount for unexpected/i, - }); - const input5 = within(row5).getByPlaceholderText(/\$0/i); - userEvent.type(input1, '1000'); userEvent.type(input2, '200'); userEvent.type(input3, '300'); userEvent.type(input4, '400'); - userEvent.type(input5, '500'); const checkbox = getByRole('checkbox', { name: /i understand that my approved/i, }); @@ -337,7 +266,22 @@ describe('Calculation', () => { userEvent.click(confirmButton); await waitFor(() => { - expect(mutationSpy).toHaveBeenCalledTimes(6); + expect(mutationSpy).toHaveBeenCalledTimes(5); // 4 field updates + 1 submit + }); + + expect(updateMutation).toHaveBeenCalledWith({ + variables: { + input: { + requestId: 'request-id', + requestAttributes: { + mortgageOrRentPayment: 1000, + furnitureCostsTwo: 200, + repairCosts: 300, + avgUtilityTwo: 400, + unexpectedExpenses: 0, + }, + }, + }, }); expect(mutationSpy).toHaveGraphqlOperation( diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx index 3fa0d186f6..03ace236e6 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx @@ -63,26 +63,11 @@ export interface CalculationFormValues { const getValidationSchema = (rentOrOwn?: MhaRentOrOwnEnum) => { const baseSchema = { - mortgageOrRentPayment: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - furnitureCostsTwo: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - repairCosts: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - avgUtilityTwo: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - unexpectedExpenses: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), + mortgageOrRentPayment: yup.number().nullable(), + furnitureCostsTwo: yup.number().nullable(), + repairCosts: yup.number().nullable(), + avgUtilityTwo: yup.number().nullable(), + unexpectedExpenses: yup.number().nullable(), phoneNumber: phoneNumber(i18n.t).required( i18n.t('Phone Number is required.'), ), @@ -99,18 +84,9 @@ const getValidationSchema = (rentOrOwn?: MhaRentOrOwnEnum) => { if (rentOrOwn === MhaRentOrOwnEnum.Own) { return yup.object({ ...baseSchema, - rentalValue: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - furnitureCostsOne: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), - avgUtilityOne: yup - .number() - .moreThan(0, i18n.t('Must be greater than $0.')) - .required(i18n.t('Required field.')), + rentalValue: yup.number().nullable(), + furnitureCostsOne: yup.number().nullable(), + avgUtilityOne: yup.number().nullable(), }); } @@ -154,6 +130,30 @@ export const Calculation: React.FC = ({ }, }); + const transformNullValues = (values: CalculationFormValues) => { + const isOwn = rentOrOwn === MhaRentOrOwnEnum.Own; + + return updateMutation({ + variables: { + input: { + requestId: requestData?.id ?? '', + requestAttributes: { + mortgageOrRentPayment: values.mortgageOrRentPayment ?? 0, + furnitureCostsTwo: values.furnitureCostsTwo ?? 0, + repairCosts: values.repairCosts ?? 0, + avgUtilityTwo: values.avgUtilityTwo ?? 0, + unexpectedExpenses: values.unexpectedExpenses ?? 0, + ...(isOwn && { + rentalValue: values.rentalValue ?? 0, + furnitureCostsOne: values.furnitureCostsOne ?? 0, + avgUtilityOne: values.avgUtilityOne ?? 0, + }), + }, + }, + }, + }); + }; + const request = requestData ? requestData.requestAttributes : null; const actionRequired = @@ -207,9 +207,11 @@ export const Calculation: React.FC = ({ validationSchema={schema} validateOnChange validateOnBlur - onSubmit={() => { + onSubmit={async (values) => { try { - submitMutation({ + await transformNullValues(values); + + await submitMutation({ variables: { input: { requestId: requestData?.id ?? '' } }, }); enqueueSnackbar(t('MHA request submitted successfully.'), {