diff --git a/README.md b/README.md index c7dcebddae..afe5e506f7 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Note: there is a test account you can use. Get this from another developer if yo - `HS_HOME_SUGGESTIONS` - Comma-separated IDs of the HelpScout articles to suggest on the dashboard page - `HS_REPORTS_SUGGESTIONS` - Comma-separated IDs of the HelpScout articles to suggest on the reports pages - `HS_TASKS_SUGGESTIONS` - Comma-separated IDs of the HelpScout articles to suggest on the tasks page +- `SHOW_BANNER` - Display a hard-coded banner on the Dashboard. Set to `true` or `false` #### Auth provider diff --git a/next.config.js b/next.config.js index 21556856c3..e1c12d7156 100644 --- a/next.config.js +++ b/next.config.js @@ -95,6 +95,7 @@ module.exports = withPlugins([ HS_REPORTS_SUGGESTIONS: process.env.HS_REPORTS_SUGGESTIONS, HS_TASKS_SUGGESTIONS: process.env.HS_TASKS_SUGGESTIONS, ALERT_MESSAGE: process.env.ALERT_MESSAGE, + SHOW_BANNER: process.env.SHOW_BANNER, }, experimental: { modularizeImports: { diff --git a/pages/accountLists/[accountListId].page.tsx b/pages/accountLists/[accountListId].page.tsx index 0ac9535dd8..7d5a2c53c9 100644 --- a/pages/accountLists/[accountListId].page.tsx +++ b/pages/accountLists/[accountListId].page.tsx @@ -29,6 +29,7 @@ const AccountListIdPage = ({ const { openTaskModal } = useTaskModal(); const [selectedMenuItem, setSelectedMenuItem] = useState(-1); const [dialogOpen, setDialogOpen] = useState(false); + const shouldShowBanner = process.env.SHOW_BANNER === 'true'; useEffect(() => { suggestArticles('HS_HOME_SUGGESTIONS'); @@ -65,7 +66,11 @@ const AccountListIdPage = ({ {appName} | {data.accountList.name} - + {modal && renderDialog(selectedMenuItem, dialogOpen, setDialogOpen)} diff --git a/src/components/Dashboard/Dashboard.test.tsx b/src/components/Dashboard/Dashboard.test.tsx index ad2e7f804d..85a0651ecb 100644 --- a/src/components/Dashboard/Dashboard.test.tsx +++ b/src/components/Dashboard/Dashboard.test.tsx @@ -216,3 +216,49 @@ describe('Dashboard', () => { ).toEqual('Committed $700'); }); }); + +describe('Static Banner', () => { + beforeEach(() => { + beforeTestResizeObserver(); + }); + + afterAll(() => { + afterTestResizeObserver(); + }); + + it('should show the banner if the shouldShowBanner prop is true', () => { + const shouldShowBanner = true; + + const { getByTestId } = render( + + + + + , + ); + + expect(getByTestId('staticBanner')).toBeInTheDocument(); + }); + + it('should NOT show the banner if the shouldShowBanner prop is false', () => { + const shouldShowBanner = false; + + const { queryByTestId } = render( + + + + + , + ); + + expect(queryByTestId('staticBanner')).not.toBeInTheDocument(); + }); +}); diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index b33fa9d43e..47fe509944 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -7,10 +7,12 @@ import MonthlyGoal from './MonthlyGoal/MonthlyGoal'; import Balance from './Balance'; import DonationHistories from './DonationHistories'; import ThisWeek from './ThisWeek'; +import { StaticBanner } from '../Shared/staticBanner/StaticBanner'; interface Props { data: GetDashboardQuery; accountListId: string; + shouldShowBanner?: boolean; } const variants = { @@ -27,7 +29,11 @@ const variants = { }, }; -const Dashboard = ({ data, accountListId }: Props): ReactElement => { +const Dashboard = ({ + data, + accountListId, + shouldShowBanner = false, +}: Props): ReactElement => { return ( <> @@ -39,6 +45,7 @@ const Dashboard = ({ data, accountListId }: Props): ReactElement => { exit="exit" variants={variants} > + {shouldShowBanner && } { + const { queryByTestId } = render( + + mocks={{ + mockResponseNonCru, + }} + > + + , + ); + + await waitFor(() => + expect(queryByTestId('nonCruOrgReminder')).toBeInTheDocument(), + ); +}); + +test('static banner does not display for user in a Cru org', async () => { + const { queryByTestId } = render( + + mocks={{ + mockResponseCru, + }} + > + + , + ); + + await waitFor(() => + expect(queryByTestId('nonCruOrgReminder')).not.toBeInTheDocument(), + ); +}); diff --git a/src/components/Shared/staticBanner/StaticBanner.tsx b/src/components/Shared/staticBanner/StaticBanner.tsx new file mode 100644 index 0000000000..c5e21a4400 --- /dev/null +++ b/src/components/Shared/staticBanner/StaticBanner.tsx @@ -0,0 +1,53 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import Alert from '@mui/material/Alert'; +import { Link } from '@mui/material'; +import { useGetUsersOrganizationsQuery } from './getOrganizationType.generated'; + +interface StaticBannerProps { + severity?: 'error' | 'info' | 'success' | 'warning'; +} + +export const StaticBanner: React.FC = ({ + severity = 'warning', +}) => { + const { t } = useTranslation(); + + const { data, loading } = useGetUsersOrganizationsQuery(); + const nonCruUser = useMemo(() => { + const foundCruOrg = data?.userOrganizationAccounts.some( + (org) => + org.organization.organizationType === 'Cru-International' || + org.organization.organizationType === 'Cru', + ); + return !foundCruOrg; + }, [data]); + + return ( +
+ {!loading && nonCruUser ? ( + + {t( + `Due to data privacy regulations and costs, Cru will no longer be able to host MPDX data for non-Cru/non-CCCI ministries. `, + )} + + {t( + `Your data in MPDX will be deleted if you don’t export from MPDX by January 31, 2024,`, + )} + + {t( + ` or let us know why you might need an extension. For more information and to take action, read `, + )} + + {t('this communication.')} + + + ) : null} +
+ ); +}; diff --git a/src/components/Shared/staticBanner/getOrganizationType.graphql b/src/components/Shared/staticBanner/getOrganizationType.graphql new file mode 100644 index 0000000000..e72dea52f5 --- /dev/null +++ b/src/components/Shared/staticBanner/getOrganizationType.graphql @@ -0,0 +1,7 @@ +query GetUsersOrganizations { + userOrganizationAccounts { + organization { + organizationType + } + } +}