diff --git a/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.test.tsx b/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.test.tsx
index b0b8b9cb5b..7ca4978177 100644
--- a/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.test.tsx
@@ -1,86 +1,93 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
-import { LocalizationProvider } from '@mui/x-date-pickers';
-import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
import { render } from '@testing-library/react';
+import TestRouter from '__tests__/util/TestRouter';
+import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import theme from 'src/theme';
+import { HcmDataQuery } from '../../Shared/HcmData/HCMData.generated';
import {
- ContextType,
- HcmData,
- MinisterHousingAllowanceContext,
-} from '../Shared/Context/MinisterHousingAllowanceContext';
+ marriedBothIneligible,
+ marriedSpouseIneligible,
+ marriedUserIneligible,
+ singleIneligible,
+} from '../../Shared/HcmData/mockData';
+import { MinistryHousingAllowanceRequestsQuery } from '../MinisterHousingAllowance.generated';
+import { MinisterHousingAllowanceProvider } from '../Shared/Context/MinisterHousingAllowanceContext';
import { IneligibleDisplay } from './IneligibleDisplay';
interface TestComponentProps {
- contextValue: Partial;
+ hcmMock: HcmDataQuery['hcm'];
}
-const TestComponent: React.FC = ({ contextValue }) => {
+const TestComponent: React.FC = ({ hcmMock }) => {
return (
-
-
+
+ mocks={{
+ HcmData: {
+ hcm: hcmMock,
+ },
+ MinistryHousingAllowanceRequests: {
+ ministryHousingAllowanceRequests: {
+ nodes: [],
+ },
+ },
+ }}
>
-
-
-
+
+
+
+
+
);
};
describe('IneligibleDisplay', () => {
- it('should render page with single staff', () => {
- const { getByText, queryByText } = render(
- ,
+ it('should render page with single ineligible staff', async () => {
+ const { findByRole, findByText, findByTestId } = render(
+ ,
);
- expect(getByText('Your MHA')).toBeInTheDocument();
expect(
- getByText(
+ await findByRole('heading', { name: 'Your MHA' }),
+ ).toBeInTheDocument();
+ expect(
+ await findByText(
/our records indicate that you have not applied for minister's housing allowance/i,
),
).toBeInTheDocument();
- expect(
- queryByText(/Jane has not completed the required ibs courses/i),
- ).not.toBeInTheDocument();
+ expect(await findByTestId('user-ineligible-message')).toBeInTheDocument();
});
- it('should render page with married staff', () => {
- const { getByText } = render(
- ,
+ it('should render page with married staff and ineligible spouse', async () => {
+ const { findByTestId } = render(
+ ,
);
- expect(
- getByText(/Jane has not completed the required ibs courses/i),
- ).toBeInTheDocument();
+ expect(await findByTestId('spouse-ineligible-message')).toBeInTheDocument();
+ expect(await findByTestId('user-ineligible-message')).toBeInTheDocument();
+ });
+
+ it('should render page with married staff and ineligible user', async () => {
+ const { findByTestId, queryByTestId } = render(
+ ,
+ );
+
+ expect(await findByTestId('user-ineligible-message')).toBeInTheDocument();
+ expect(queryByTestId('spouse-ineligible-message')).not.toBeInTheDocument();
+ });
+
+ it('should render page with both married staff ineligible', async () => {
+ const { findByTestId, queryByTestId } = render(
+ ,
+ );
+
+ expect(await findByTestId('user-ineligible-message')).toBeInTheDocument();
+ expect(queryByTestId('spouse-ineligible-message')).not.toBeInTheDocument();
});
});
diff --git a/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.tsx b/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.tsx
index f33e8031f8..d0131ab221 100644
--- a/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/MainPages/IneligibleDisplay.tsx
@@ -4,12 +4,14 @@ import { useMinisterHousingAllowance } from '../Shared/Context/MinisterHousingAl
export const IneligibleDisplay: React.FC = () => {
const { t } = useTranslation();
- const { isMarried, preferredName, spousePreferredName } =
- useMinisterHousingAllowance();
-
- // TODO - Add spouse to API and check eligibility
- // We will get this from HCM data in the future
- const spouseEligibleForMHA = false;
+ const {
+ isMarried,
+ preferredName,
+ spousePreferredName,
+ userEligibleForMHA,
+ spouseEligibleForMHA,
+ hcmLoading,
+ } = useMinisterHousingAllowance();
return (
<>
@@ -27,21 +29,38 @@ export const IneligibleDisplay: React.FC = () => {
MHA@cru.org.
- {isMarried && spouseEligibleForMHA === false && (
-
-
-
- Completing a Minister's Housing Allowance will submit the
- request for {preferredName}. {spousePreferredName} has not
- completed the required IBS courses to meet eligibility criteria.
- When you calculate your salary, you will see the approved amount
- that can be applied to {preferredName}'s salary. If you
- believe this is incorrect, please contact Personnel Records at
- 407-826-2252 or MHA@cru.org.
-
-
-
- )}
+ {!hcmLoading &&
+ isMarried &&
+ userEligibleForMHA &&
+ !spouseEligibleForMHA && (
+
+
+
+ Completing a Minister's Housing Allowance calculation
+ form will submit the request for {preferredName}.{' '}
+ {spousePreferredName} has not completed the required IBS
+ courses to meet eligibility criteria.
+
+
+
+ )}
+ {!hcmLoading &&
+ ((isMarried && !spouseEligibleForMHA) || !userEligibleForMHA) && (
+
+
+
+ Once approved, when you calculate your salary, you will see
+ the approved amount that can be applied to {preferredName}
+ 's salary. If you believe this is incorrect, please
+ contact Personnel Records at 407-826-2236 or{' '}
+ MHA@cru.org.
+
+
+
+ )}
>
);
diff --git a/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.test.tsx b/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.test.tsx
index 6f7f06cbfa..ae1d353ef2 100644
--- a/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
-import { render } from '@testing-library/react';
+import { render, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
@@ -9,15 +10,21 @@ import theme from 'src/theme';
import { HcmDataQuery } from '../Shared/HcmData/HCMData.generated';
import {
marriedMhaAndNoException,
- marriedNoMhaNoException,
+ marriedSpouseIneligible,
+ singleIneligible,
singleMhaNoException,
singleNoMhaNoException,
} from '../Shared/HcmData/mockData';
import { MinisterHousingAllowanceReport } from './MinisterHousingAllowance';
-import { MinistryHousingAllowanceRequestsQuery } from './MinisterHousingAllowance.generated';
+import {
+ CreateHousingAllowanceRequestMutation,
+ MinistryHousingAllowanceRequestsQuery,
+} from './MinisterHousingAllowance.generated';
import { MinisterHousingAllowanceProvider } from './Shared/Context/MinisterHousingAllowanceContext';
import { mockMHARequest } from './mockData';
+const mutationSpy = jest.fn();
+
interface TestComponentProps {
hcmMock: HcmDataQuery['hcm'];
mhaRequestsMock: MinistryHousingAllowanceRequestsQuery['ministryHousingAllowanceRequests']['nodes'];
@@ -33,6 +40,7 @@ const TestComponent: React.FC = ({
mocks={{
HcmData: {
@@ -44,6 +52,7 @@ const TestComponent: React.FC = ({
},
},
}}
+ onCall={mutationSpy}
>
@@ -66,17 +75,15 @@ describe('MinisterHousingAllowanceReport', () => {
expect(await findByText('John Doe')).toBeInTheDocument();
});
- it('renders married, no pending, no approved correctly', async () => {
- const { findByText } = render(
- ,
+ it('renders married with ineligible spouse, no approved requests', async () => {
+ const { findByText, findByTestId } = render(
+ ,
);
expect(
await findByText(/our records indicate that you have not applied for/i),
).toBeInTheDocument();
- expect(
- await findByText(/will submit the request for john. jane has not/i),
- ).toBeInTheDocument();
+ expect(await findByTestId('spouse-ineligible-message')).toBeInTheDocument();
expect(await findByText('John Doe and Jane Doe')).toBeInTheDocument();
});
@@ -151,4 +158,48 @@ describe('MinisterHousingAllowanceReport', () => {
expect(await findByText('Current MHA Request')).toBeInTheDocument();
});
+
+ describe('Create MHA Request Eligibility', () => {
+ it('should allow create mutation when user is eligible', async () => {
+ const { findByText, getByRole } = render(
+ ,
+ );
+
+ // Wait for data to load
+ await findByText('John Doe');
+
+ const button = getByRole('button', { name: 'Request New MHA' });
+ expect(button).toBeEnabled();
+
+ userEvent.click(button);
+
+ await waitFor(() =>
+ expect(mutationSpy).toHaveGraphqlOperation(
+ 'CreateHousingAllowanceRequest',
+ ),
+ );
+ });
+
+ it('should block create mutation when user is not eligible', async () => {
+ const { getByRole, findByText } = render(
+ ,
+ );
+
+ await findByText('John Doe');
+
+ const button = getByRole('button', { name: 'Request New MHA' });
+ userEvent.click(button);
+
+ // Should show error message and not trigger mutation
+ expect(
+ await findByText('You are not eligible to create a new MHA request.'),
+ ).toBeInTheDocument();
+
+ await waitFor(() => {
+ expect(mutationSpy).not.toHaveGraphqlOperation(
+ 'CreateHousingAllowanceRequest',
+ );
+ });
+ });
+ });
});
diff --git a/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx b/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx
index 9e2507e5eb..85f7ba44cf 100644
--- a/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx
@@ -39,8 +39,12 @@ export const MinisterHousingAllowanceReport = () => {
spousePreferredName,
userHcmData,
spouseHcmData,
+ userEligibleForMHA,
+ spouseEligibleForMHA,
} = useMinisterHousingAllowance();
+ const canAccessMHA = userEligibleForMHA || spouseEligibleForMHA;
+
const personNumber = userHcmData?.staffInfo?.personNumber ?? '';
const spousePersonNumber = spouseHcmData?.staffInfo?.personNumber ?? '';
const lastName = userHcmData?.staffInfo?.lastName ?? '';
@@ -56,6 +60,13 @@ export const MinisterHousingAllowanceReport = () => {
const [createMHA] = useCreateHousingAllowanceRequestMutation();
const onCreateMHARequest = async () => {
+ if (!userEligibleForMHA) {
+ enqueueSnackbar(t('You are not eligible to create a new MHA request.'), {
+ variant: 'error',
+ });
+ return;
+ }
+
await createMHA({
variables: {
requestAttributes: {},
@@ -122,7 +133,7 @@ export const MinisterHousingAllowanceReport = () => {
) : (
<>
- {hasNoRequests ? (
+ {hasNoRequests || !canAccessMHA ? (
) : (
@@ -149,7 +160,7 @@ export const MinisterHousingAllowanceReport = () => {
>
)}
- {previousApprovedRequest && (
+ {canAccessMHA && previousApprovedRequest && (
diff --git a/src/components/Reports/MinisterHousingAllowance/RequestPage/RequestPage.test.tsx b/src/components/Reports/MinisterHousingAllowance/RequestPage/RequestPage.test.tsx
index 7aa61e8730..e39cfff7c6 100644
--- a/src/components/Reports/MinisterHousingAllowance/RequestPage/RequestPage.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/RequestPage/RequestPage.test.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { render, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { MhaRentOrOwnEnum } from 'src/graphql/types.generated';
@@ -69,15 +70,17 @@ const TestComponent: React.FC = ({
return (
-
-
- onCall={mutationSpy}
- >
- {content}
-
-
+
+
+
+ onCall={mutationSpy}
+ >
+ {content}
+
+
+
);
};
@@ -181,6 +184,7 @@ describe('RequestPage', () => {
hasCalcValues: true,
setHasCalcValues,
updateMutation,
+ userEligibleForMHA: true,
requestData: {
id: 'request-id',
requestAttributes: {
@@ -306,6 +310,7 @@ describe('RequestPage', () => {
setHasCalcValues,
updateMutation,
setIsPrint,
+ userEligibleForMHA: true,
requestData: {
id: 'request-id',
requestAttributes: {
diff --git a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/AutosaveCustomTextField.test.tsx b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/AutosaveCustomTextField.test.tsx
index 9b42543a55..f9e2a77caa 100644
--- a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/AutosaveCustomTextField.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/AutosaveCustomTextField.test.tsx
@@ -3,6 +3,7 @@ import { ThemeProvider } from '@mui/material/styles';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import * as yup from 'yup';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
@@ -22,32 +23,35 @@ const defaultSchema = yup.object({
});
const TestComponent: React.FC = () => (
-
-
- onCall={mutationSpy}
- >
-
+
+
+ onCall={mutationSpy}
>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
describe('AutosaveCustomTextField', () => {
@@ -76,7 +80,7 @@ describe('AutosaveCustomTextField', () => {
const { getByRole } = render();
const input = getByRole('textbox');
- await userEvent.type(input, '1500');
+ userEvent.type(input, '1500');
input.blur();
await waitFor(() =>
diff --git a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.test.tsx b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.test.tsx
index be6389c04e..9c00c9d042 100644
--- a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.test.tsx
@@ -1,9 +1,11 @@
import { ThemeProvider } from '@mui/material/styles';
import { renderHook, waitFor } from '@testing-library/react';
+import { SnackbarProvider } from 'notistack';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
import theme from 'src/theme';
import { UpdateMinistryHousingAllowanceRequestMutation } from '../../MinisterHousingAllowance.generated';
+import { mockMHARequest } from '../../mockData';
import {
ContextType,
MinisterHousingAllowanceContext,
@@ -14,43 +16,49 @@ const mutationSpy = jest.fn();
interface TestComponentProps {
children: React.ReactNode;
+ userEligibleForMHA?: boolean;
}
-const TestComponent: React.FC = ({ children }) => {
+const TestComponent: React.FC = ({
+ children,
+ userEligibleForMHA = true,
+}) => {
return (
-
- onCall={mutationSpy}
- >
-
+
+ onCall={mutationSpy}
>
- {children}
-
-
+
+ {children}
+
+
+
);
};
describe('useSaveField', () => {
- it('should update ministry housing allowance request', async () => {
+ it('should update ministry housing allowance request when user is eligible', async () => {
const { result } = renderHook(
() =>
useSaveField({
formValues: { rentalValue: 50 },
}),
{
- wrapper: TestComponent,
+ wrapper: ({ children }) => (
+ {children}
+ ),
},
);
@@ -61,7 +69,7 @@ describe('useSaveField', () => {
'UpdateMinistryHousingAllowanceRequest',
{
input: {
- requestId: 'request-id',
+ requestId: '1',
requestAttributes: {
rentalValue: 100,
},
@@ -70,4 +78,26 @@ describe('useSaveField', () => {
),
);
});
+
+ it('should block mutation when user is not eligible', async () => {
+ const { result } = renderHook(
+ () =>
+ useSaveField({
+ formValues: { rentalValue: 50 },
+ }),
+ {
+ wrapper: ({ children }) => (
+ {children}
+ ),
+ },
+ );
+
+ result.current({ rentalValue: 100 });
+
+ await waitFor(() => {
+ expect(mutationSpy).not.toHaveGraphqlOperation(
+ 'UpdateMinistryHousingAllowanceRequest',
+ );
+ });
+ });
});
diff --git a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.ts b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.ts
index 09c5377632..c9e9c2bf76 100644
--- a/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.ts
+++ b/src/components/Reports/MinisterHousingAllowance/Shared/AutoSave/useSaveField.ts
@@ -1,4 +1,6 @@
import { useCallback } from 'react';
+import { useSnackbar } from 'notistack';
+import { useTranslation } from 'react-i18next';
import { MinistryHousingAllowanceRequestAttributesInput } from 'pages/api/graphql-rest.page.generated';
import { calculateAnnualTotals } from 'src/hooks/useAnnualTotal';
import { useUpdateMinistryHousingAllowanceRequestMutation } from '../../MinisterHousingAllowance.generated';
@@ -10,12 +12,14 @@ interface UseSaveFieldOptions {
}
export const useSaveField = ({ formValues }: UseSaveFieldOptions) => {
- const { requestData } = useMinisterHousingAllowance();
+ const { requestData, userEligibleForMHA } = useMinisterHousingAllowance();
const [updateMinistryHousingAllowanceRequest] =
useUpdateMinistryHousingAllowanceRequestMutation({
refetchQueries: ['MinistryHousingAllowanceRequest'],
});
const values = requestData?.requestAttributes;
+ const { t } = useTranslation();
+ const { enqueueSnackbar } = useSnackbar();
const saveField = useCallback(
async (
@@ -24,6 +28,15 @@ export const useSaveField = ({ formValues }: UseSaveFieldOptions) => {
if (!requestData?.id) {
return;
}
+ if (!userEligibleForMHA) {
+ enqueueSnackbar(
+ t('You are not eligible to make changes to this request.'),
+ {
+ variant: 'error',
+ },
+ );
+ return;
+ }
const updatedValues = {
...formValues,
@@ -60,7 +73,14 @@ export const useSaveField = ({ formValues }: UseSaveFieldOptions) => {
});
} catch (error) {}
},
- [formValues, updateMinistryHousingAllowanceRequest, requestData],
+ [
+ formValues,
+ updateMinistryHousingAllowanceRequest,
+ requestData,
+ userEligibleForMHA,
+ enqueueSnackbar,
+ t,
+ ],
);
return saveField;
diff --git a/src/components/Reports/MinisterHousingAllowance/Shared/Context/MinisterHousingAllowanceContext.tsx b/src/components/Reports/MinisterHousingAllowance/Shared/Context/MinisterHousingAllowanceContext.tsx
index 8fd2142579..eff298fb09 100644
--- a/src/components/Reports/MinisterHousingAllowance/Shared/Context/MinisterHousingAllowanceContext.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Shared/Context/MinisterHousingAllowanceContext.tsx
@@ -50,6 +50,10 @@ export type ContextType = {
spouseHcmData?: HcmData | null;
preferredName: string;
spousePreferredName: string;
+ userEligibleForMHA: boolean;
+ spouseEligibleForMHA: boolean;
+ hcmLoading: boolean;
+ hcmError?: ApolloError;
requestData?:
| MinistryHousingAllowanceRequestQuery['ministryHousingAllowanceRequest']
@@ -125,7 +129,11 @@ export const MinisterHousingAllowanceProvider: React.FC = ({
}));
}, [initialSteps, isComplete]);
- const { data: hcmData } = useHcmDataQuery();
+ const {
+ data: hcmData,
+ loading: hcmLoading,
+ error: hcmError,
+ } = useHcmDataQuery();
const [userHcmData, setUserHcmData] = useState();
const [spouseHcmData, setSpouseHcmData] = useState(null);
@@ -153,6 +161,15 @@ export const MinisterHousingAllowanceProvider: React.FC = ({
[spouseHcmData],
);
+ const userEligibleForMHA = useMemo(
+ () => userHcmData?.mhaEit?.mhaEligibility ?? false,
+ [userHcmData],
+ );
+ const spouseEligibleForMHA = useMemo(
+ () => spouseHcmData?.mhaEit?.mhaEligibility ?? false,
+ [spouseHcmData],
+ );
+
const [isDrawerOpen, setIsDrawerOpen] = useState(true);
const toggleDrawer = useCallback(() => {
setIsDrawerOpen((prev) => !prev);
@@ -202,6 +219,10 @@ export const MinisterHousingAllowanceProvider: React.FC = ({
spouseHcmData,
preferredName,
spousePreferredName,
+ userEligibleForMHA,
+ spouseEligibleForMHA,
+ hcmLoading,
+ hcmError,
isPrint,
setIsPrint,
setIsComplete,
@@ -228,6 +249,10 @@ export const MinisterHousingAllowanceProvider: React.FC = ({
spouseHcmData,
preferredName,
spousePreferredName,
+ userEligibleForMHA,
+ spouseEligibleForMHA,
+ hcmLoading,
+ hcmError,
isPrint,
setIsPrint,
setIsComplete,
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.test.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.test.tsx
index 6274decc61..e64493a007 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/CostOfHome.test.tsx
@@ -5,6 +5,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { render, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import * as yup from 'yup';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
@@ -36,25 +37,27 @@ const TestComponent: React.FC = ({
rentOrOwn,
contextValue,
}) => (
-
-
-
- onCall={mutationSpy}
- >
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ onCall={mutationSpy}
+ >
+
+
+
+
+
+
+
+
+
+
+
);
describe('CostOfHome', () => {
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/EndingSection.test.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/EndingSection.test.tsx
index 70a95a6605..ab03f0a47c 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/EndingSection.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/EndingSection.test.tsx
@@ -4,6 +4,7 @@ import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { render } from '@testing-library/react';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import * as yup from 'yup';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
@@ -20,19 +21,21 @@ const mockSchema = {
} as unknown as yup.Schema;
const TestComponent: React.FC = () => (
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
describe('EndingSection', () => {
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.test.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.test.tsx
index a7266717b2..96aff8f724 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.test.tsx
@@ -5,6 +5,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { render, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import * as yup from 'yup';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
@@ -31,25 +32,27 @@ interface TestComponentProps {
}
const TestComponent: React.FC = ({ contextValue }) => (
-
-
-
- onCall={mutationSpy}
- >
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ onCall={mutationSpy}
+ >
+
+
+
+
+
+
+
+
+
+
+
);
describe('FairRentalValue', () => {
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.test.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.test.tsx
index 694b9fed7f..fbfcacdf06 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.test.tsx
@@ -5,6 +5,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { render, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
@@ -15,6 +16,7 @@ import {
ContextType,
MinisterHousingAllowanceContext,
} from '../../Shared/Context/MinisterHousingAllowanceContext';
+import { mockMHARequest } from '../../mockData';
import { Calculation } from './Calculation';
const submit = jest.fn();
@@ -37,27 +39,29 @@ const TestComponent: React.FC = ({
rentOrOwn = MhaRentOrOwnEnum.Own,
}) => (
-
-
-
- onCall={mutationSpy}
- >
-
-
-
-
-
-
-
-
+
+
+
+
+ onCall={mutationSpy}
+ >
+
+
+
+
+
+
+
+
+
);
@@ -215,6 +219,7 @@ describe('Calculation', () => {
setHasCalcValues,
setIsPrint,
updateMutation,
+ userEligibleForMHA: true,
requestData: {
id: 'request-id',
requestAttributes: {
@@ -318,6 +323,7 @@ describe('Calculation', () => {
setHasCalcValues,
setIsPrint,
updateMutation,
+ userEligibleForMHA: true,
requestData: {
id: 'request-id',
requestAttributes: {
@@ -377,5 +383,73 @@ describe('Calculation', () => {
expect(queryByRole('button', { name: /back/i })).not.toBeInTheDocument();
expect(queryByRole('checkbox')).not.toBeInTheDocument();
});
+
+ describe('Update Checkbox Eligibility', () => {
+ it('should allow update mutation when user is eligible', async () => {
+ const { getByRole } = render(
+ ,
+ );
+
+ const checkbox = getByRole('checkbox', {
+ name: /i understand that my approved/i,
+ });
+
+ userEvent.click(checkbox);
+
+ await waitFor(() =>
+ expect(updateMutation).toHaveBeenCalledWith({
+ variables: {
+ input: {
+ requestId: '1',
+ requestAttributes: {
+ iUnderstandMhaPolicy: true,
+ },
+ },
+ },
+ }),
+ );
+ });
+
+ it('should block checkbox update when user is not eligible', async () => {
+ const { findByRole, findByText } = render(
+ ,
+ );
+
+ const checkbox = await findByRole('checkbox', {
+ name: /i understand that my approved/i,
+ });
+
+ userEvent.click(checkbox);
+
+ // Should show error message and not trigger mutation
+ expect(
+ await findByText(
+ 'You are not eligible to make changes to this request.',
+ ),
+ ).toBeInTheDocument();
+
+ await waitFor(() => {
+ expect(updateMutation).not.toHaveBeenCalled();
+ });
+ });
+ });
});
});
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx
index 0c6f7edade..f52423e4dd 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx
@@ -15,6 +15,7 @@ import {
} from '@mui/material';
import { Formik } from 'formik';
import { DateTime } from 'luxon';
+import { useSnackbar } from 'notistack';
import { Trans, useTranslation } from 'react-i18next';
import * as yup from 'yup';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
@@ -102,6 +103,7 @@ export const Calculation: React.FC = ({
const locale = useLocale();
const { query } = useRouter();
const print = query.print === 'true';
+ const { enqueueSnackbar } = useSnackbar();
const {
handleNextStep,
@@ -113,9 +115,20 @@ export const Calculation: React.FC = ({
requestData,
updateMutation,
userHcmData,
+ userEligibleForMHA,
} = useMinisterHousingAllowance();
- const updateCheckbox = (value: boolean) =>
+ const updateCheckbox = (value: boolean) => {
+ if (!userEligibleForMHA) {
+ enqueueSnackbar(
+ t('You are not eligible to make changes to this request.'),
+ {
+ variant: 'error',
+ },
+ );
+ return;
+ }
+
updateMutation({
variables: {
input: {
@@ -126,6 +139,7 @@ export const Calculation: React.FC = ({
},
},
});
+ };
const request = requestData ? requestData.requestAttributes : null;
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.test.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.test.tsx
index d25f979a5f..c5fb3fc939 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.test.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.test.tsx
@@ -3,6 +3,7 @@ import { ThemeProvider } from '@mui/material/styles';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Formik } from 'formik';
+import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
@@ -12,6 +13,7 @@ import {
ContextType,
MinisterHousingAllowanceContext,
} from '../../Shared/Context/MinisterHousingAllowanceContext';
+import { mockMHARequest } from '../../mockData';
import { RentOwn } from './RentOwn';
const submit = jest.fn();
@@ -25,21 +27,23 @@ interface TestComponentProps {
const TestComponent: React.FC = ({ contextValue }) => (
-
-
- onCall={mutationSpy}
- >
-
-
-
-
-
-
-
+
+
+
+ onCall={mutationSpy}
+ >
+
+
+
+
+
+
+
+
);
@@ -52,7 +56,8 @@ describe('RentOwn', () => {
pageType: PageEnum.New,
updateMutation,
setHasCalcValues,
- requestData: { id: 'request-id' },
+ requestData: mockMHARequest,
+ userEligibleForMHA: true,
} as unknown as ContextType
}
/>,
@@ -64,7 +69,7 @@ describe('RentOwn', () => {
expect(getByText('Own')).toBeInTheDocument();
expect(await findAllByRole('radio', { checked: false })).toHaveLength(2);
- await userEvent.click(getByText('Rent'));
+ userEvent.click(getByText('Rent'));
await waitFor(() =>
expect(updateMutation).toHaveBeenCalledWith({
@@ -83,7 +88,7 @@ describe('RentOwn', () => {
overallAmount: null,
iUnderstandMhaPolicy: null,
},
- requestId: 'request-id',
+ requestId: '1',
},
},
}),
@@ -98,7 +103,7 @@ describe('RentOwn', () => {
contextValue={
{
pageType: PageEnum.Edit,
- requestData: { id: 'request-id' },
+ requestData: mockMHARequest,
} as unknown as ContextType
}
/>,
@@ -112,4 +117,66 @@ describe('RentOwn', () => {
expect(getByRole('radio', { name: 'Rent' })).not.toBeChecked();
expect(getByRole('radio', { name: 'Own' })).not.toBeChecked();
});
+
+ describe('Update Request Eligibility', () => {
+ it('should allow update mutation when user is eligible', async () => {
+ const { getByText } = render(
+ ,
+ );
+
+ await userEvent.click(getByText('Rent'));
+
+ await waitFor(() =>
+ expect(updateMutation).toHaveBeenCalledWith({
+ variables: {
+ input: {
+ requestAttributes: {
+ rentOrOwn: 'RENT',
+ rentalValue: null,
+ furnitureCostsOne: null,
+ avgUtilityOne: null,
+ mortgageOrRentPayment: null,
+ furnitureCostsTwo: null,
+ repairCosts: null,
+ avgUtilityTwo: null,
+ unexpectedExpenses: null,
+ overallAmount: null,
+ iUnderstandMhaPolicy: null,
+ },
+ requestId: '1',
+ },
+ },
+ }),
+ );
+ });
+
+ it('should block update mutation when user is not eligible', async () => {
+ const { getByText } = render(
+ ,
+ );
+
+ await userEvent.click(getByText('Rent'));
+
+ // Wait a moment to ensure the mutation would have been called if it was going to be
+ await waitFor(() => {
+ expect(updateMutation).not.toHaveBeenCalled();
+ });
+ });
+ });
});
diff --git a/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.tsx b/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.tsx
index 05384a2e6c..bf8d18a20a 100644
--- a/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.tsx
+++ b/src/components/Reports/MinisterHousingAllowance/Steps/StepTwo/RentOwn.tsx
@@ -9,6 +9,7 @@ import {
Typography,
} from '@mui/material';
import { useFormikContext } from 'formik';
+import { useSnackbar } from 'notistack';
import { useTranslation } from 'react-i18next';
import { PageEnum } from 'src/components/Reports/Shared/CalculationReports/Shared/sharedTypes';
import { MhaRentOrOwnEnum } from 'src/graphql/types.generated';
@@ -19,6 +20,7 @@ import { useMinisterHousingAllowance } from '../../Shared/Context/MinisterHousin
export const RentOwn: React.FC = () => {
const { t } = useTranslation();
+ const { enqueueSnackbar } = useSnackbar();
const {
values,
@@ -38,9 +40,20 @@ export const RentOwn: React.FC = () => {
handlePreviousStep,
requestData,
updateMutation,
+ userEligibleForMHA,
} = useMinisterHousingAllowance();
const updateRequest = (id: string, rentOrOwn: MhaRentOrOwnEnum) => {
+ if (!userEligibleForMHA) {
+ enqueueSnackbar(
+ t('You are not eligible to make changes to this request.'),
+ {
+ variant: 'error',
+ },
+ );
+ return;
+ }
+
updateMutation({
variables: {
input: {
diff --git a/src/components/Reports/Shared/HcmData/HCMData.graphql b/src/components/Reports/Shared/HcmData/HCMData.graphql
index a2ba094540..c068fb40fc 100644
--- a/src/components/Reports/Shared/HcmData/HCMData.graphql
+++ b/src/components/Reports/Shared/HcmData/HCMData.graphql
@@ -29,6 +29,9 @@ query HcmData {
lastUpdatedDate
currentApprovedAmountForStaff
}
+ mhaEit {
+ mhaEligibility
+ }
exceptionSalaryCap {
amount
effectiveDate
diff --git a/src/components/Reports/Shared/HcmData/mockData.ts b/src/components/Reports/Shared/HcmData/mockData.ts
index 393b130ee5..f4f581177b 100644
--- a/src/components/Reports/Shared/HcmData/mockData.ts
+++ b/src/components/Reports/Shared/HcmData/mockData.ts
@@ -46,6 +46,9 @@ const noMhaAndNoException: HcmDataQuery['hcm'][0] = {
lastUpdatedDate: null,
currentApprovedAmountForStaff: null,
},
+ mhaEit: {
+ mhaEligibility: true,
+ },
exceptionSalaryCap: {
amount: null,
effectiveDate: null,
@@ -106,3 +109,51 @@ export const marriedNoMhaNoException: HcmDataQuery['hcm'] = [
staffInfo: janeDoe,
},
];
+export const marriedSpouseIneligible: HcmDataQuery['hcm'] = [
+ noMhaAndNoException,
+ {
+ ...noMhaAndNoException,
+ staffInfo: janeDoe,
+ mhaEit: {
+ mhaEligibility: false,
+ },
+ },
+];
+
+export const singleIneligible: HcmDataQuery['hcm'] = [
+ {
+ ...noMhaAndNoException,
+ mhaEit: {
+ mhaEligibility: false,
+ },
+ },
+];
+
+export const marriedUserIneligible: HcmDataQuery['hcm'] = [
+ {
+ ...noMhaAndNoException,
+ mhaEit: {
+ mhaEligibility: false,
+ },
+ },
+ {
+ ...noMhaAndNoException,
+ staffInfo: janeDoe,
+ },
+];
+
+export const marriedBothIneligible: HcmDataQuery['hcm'] = [
+ {
+ ...noMhaAndNoException,
+ mhaEit: {
+ mhaEligibility: false,
+ },
+ },
+ {
+ ...noMhaAndNoException,
+ staffInfo: janeDoe,
+ mhaEit: {
+ mhaEligibility: false,
+ },
+ },
+];