Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MPDX-8088] Create setup start page #1004

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ 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
- `PRIVACY_POLICY_URL` - URL of the privacy policy
- `TERMS_OF_USE_URL` - URL of the terms of use

#### Auth provider

Expand Down
2 changes: 2 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ const config = {
process.env.HS_SETTINGS_SERVICES_SUGGESTIONS,
HS_SETUP_FIND_ORGANIZATION: process.env.HS_SETUP_FIND_ORGANIZATION,
ALERT_MESSAGE: process.env.ALERT_MESSAGE,
PRIVACY_POLICY_URL: process.env.PRIVACY_POLICY_URL,
TERMS_OF_USE_URL: process.env.TERMS_OF_USE_URL,
DD_ENV: process.env.DD_ENV ?? 'development',
},
experimental: {
Expand Down
39 changes: 39 additions & 0 deletions pages/setup/start.page.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import StartPage from './start.page';

const push = jest.fn();
const router = {
push,
};

describe('Setup start page', () => {
it('autocomplete renders and button saves and advances to the next page', async () => {
Object.defineProperty(window, 'navigator', {
value: { ...window.navigator, language: 'fr-FR' },
});

const mutationSpy = jest.fn();
const { getByRole } = render(
<TestRouter router={router}>
<GqlMockedProvider onCall={mutationSpy}>
<StartPage />
</GqlMockedProvider>
</TestRouter>,
);

const autocomplete = getByRole('combobox');
expect(autocomplete).toHaveValue('French (français)');
userEvent.click(autocomplete);
userEvent.click(getByRole('option', { name: 'German (Deutsch)' }));
userEvent.click(getByRole('button', { name: "Let's Begin" }));
await waitFor(() =>
expect(mutationSpy).toHaveGraphqlOperation('UpdatePersonalPreferences', {
input: { attributes: { locale: 'de' } },
}),
);
expect(push).toHaveBeenCalledWith('/setup/connect');
});
});
99 changes: 99 additions & 0 deletions pages/setup/start.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
import React, { ReactElement, useState } from 'react';
import { Autocomplete, TextField } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { useUpdatePersonalPreferencesMutation } from 'src/components/Settings/preferences/accordions/UpdatePersonalPreferences.generated';
import { SetupPage } from 'src/components/Setup/SetupPage';
import { LargeButton } from 'src/components/Setup/styledComponents';
import {
PrivacyPolicyLink,
TermsOfUseLink,
} from 'src/components/Shared/Links/Links';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import { formatLanguage, languages } from 'src/lib/data/languages';
import { loadSession } from '../api/utils/pagePropsHelpers';

// This is the first page of the tour, and it lets users choose their language. It is always shown.
const StartPage = (): ReactElement => {
const { t } = useTranslation();
const { appName } = useGetAppSettings();
const { push } = useRouter();
const [savePreferences] = useUpdatePersonalPreferencesMutation();

const [locale, setLocale] = useState<string>(
(typeof window === 'undefined'
? null

Check warning on line 26 in pages/setup/start.page.tsx

View check run for this annotation

Codecov / codecov/patch

pages/setup/start.page.tsx#L26

Added line #L26 was not covered by tests
: window.navigator.language.toLowerCase()) || 'en-us',
);

const handleSave = async () => {
await savePreferences({
variables: {
input: {
attributes: {
locale,
},
},
},
});
push('/setup/connect');
};

return (
<>
<Head>
<title>
{appName} | {t('Setup - Start')}
</title>
</Head>
<SetupPage title={t("It's time to get started")}>
<p>
{t(
`Developing a healthy team of ministry partners sets your ministry up to thrive.
{{appName}} is designed to help you do the right things, with the right people at the right time to be fully funded.`,
{ appName },
)}
</p>
<p>
{t(
`To get started, we're going to walk with you through a few key steps to set you up for success in {{appName}}!`,
{ appName },
)}
</p>
<p>{t('It looks like you speak')}</p>
<Autocomplete
autoHighlight
disableClearable
value={locale}
onChange={(_, value) => {
setLocale(value);
}}
options={languages.map((language) => language.id) || []}
getOptionLabel={(locale) => formatLanguage(locale)}
fullWidth
renderInput={(params) => (
<TextField
{...params}
placeholder={t('Language')}
label={t('Language')}
/>
)}
/>
<p>
{t('By Clicking "Let\'s Begin!" you have read and agree to the ')}
<PrivacyPolicyLink />
{t(' and the ')}
<TermsOfUseLink />
</p>
<LargeButton variant="contained" fullWidth onClick={handleSave}>
{t("Let's Begin")}
</LargeButton>
</SetupPage>
</>
);
};

export const getServerSideProps = loadSession;

export default StartPage;
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import React, { useState } from 'react';
import { useApolloClient } from '@apollo/client';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ChevronRight from '@mui/icons-material/ChevronRight';
import { Box, Button, Drawer, List, Link as MuiLink } from '@mui/material';
import { Box, Button, Drawer, List } from '@mui/material';
import { styled } from '@mui/material/styles';
import { signOut } from 'next-auth/react';
import { useTranslation } from 'react-i18next';
import {
PrivacyPolicyLink,
TermsOfUseLink,
} from 'src/components/Shared/Links/Links';
import { NextLinkComposed } from 'src/components/common/Links/NextLinkComposed';
import { clearDataDogUser } from 'src/lib/dataDog';
import { useAccountListId } from '../../../../../../hooks/useAccountListId';
Expand Down Expand Up @@ -222,23 +226,9 @@ export const ProfileMenuPanel: React.FC = () => {
{t('Sign Out')}
</Button>
<Box display="flex" justifyContent="center" py={1}>
<MuiLink
href="https://get.mpdx.org/privacy-policy/"
target="_blank"
color="secondary"
variant="caption"
>
{t('Privacy Policy')}
</MuiLink>
<PrivacyPolicyLink color="secondary" variant="caption" />
&nbsp; • &nbsp;
<MuiLink
href="https://get.mpdx.org/terms-of-use/"
target="_blank"
color="secondary"
variant="caption"
>
{t('Terms of Use')}
</MuiLink>
<TermsOfUseLink color="secondary" variant="caption" />
</Box>
</Box>
</LeafListItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import {
ListItemText,
Menu,
MenuItem,
Link as MuiLink,
Typography,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { signOut } from 'next-auth/react';
import { useSnackbar } from 'notistack';
import { useTranslation } from 'react-i18next';
import {
PrivacyPolicyLink,
TermsOfUseLink,
} from 'src/components/Shared/Links/Links';
import { AccountList } from 'src/graphql/types.generated';
import { useRequiredSession } from 'src/hooks/useRequiredSession';
import { clearDataDogUser } from 'src/lib/dataDog';
Expand Down Expand Up @@ -374,21 +377,9 @@ const ProfileMenu = (): ReactElement => {
)}
</MenuItem>
<MenuItemFooter>
<MuiLink
href="https://get.mpdx.org/privacy-policy/"
target="_blank"
onClick={handleProfileMenuClose}
>
{t('Privacy Policy')}
</MuiLink>
<PrivacyPolicyLink />
&nbsp; • &nbsp;
<MuiLink
href="https://get.mpdx.org/terms-of-use/"
target="_blank"
onClick={handleProfileMenuClose}
>
{t('Terms of Use')}
</MuiLink>
<TermsOfUseLink />
</MenuItemFooter>
</MenuWrapper>
</>
Expand Down
69 changes: 69 additions & 0 deletions src/components/Setup/SetupPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { ReactNode } from 'react';
import { CampaignOutlined } from '@mui/icons-material';
import { Card, CardContent, Divider, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';

const PageBackground = styled('div')(({ theme }) => ({
minWidth: '100vw',
// Compensate for varying toolbar height
minHeight: 'calc(100vh - var(--toolbar-height))',
'--toolbar-height': '64px',
'@media (min-width: 0px) and (orientation: landscape)': {
'--toolbar-height': '48px',
},
'@media (min-width: 600px)': {
'--toolbar-height': '64px',
},
backgroundColor: theme.palette.primary.main,
paddingTop: theme.spacing(8),
}));

const PageCard = styled(Card)(({ theme }) => ({
marginInline: 'auto',
maxWidth: theme.spacing(75),
}));

const PageContent = styled(CardContent)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
alignItems: 'center',
textAlign: 'center',
}));

const StyledIcon = styled(CampaignOutlined)(({ theme }) => ({
width: 'auto',
height: theme.spacing(8),
color: theme.palette.primary.main,
}));

const HeaderWrapper = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
}));

const HeaderTypography = styled(Typography)({
fontSize: '2.75rem',
fontWeight: 'bold',
});

interface SetupPageProps {
title: string;
children: ReactNode;
}

export const SetupPage: React.FC<SetupPageProps> = ({ title, children }) => (
<PageBackground>
<PageCard variant="outlined">
<PageContent>
<HeaderWrapper>
<StyledIcon />
<HeaderTypography variant="h2">{title}</HeaderTypography>
</HeaderWrapper>
<Divider sx={{ width: '100%' }} />
{children}
</PageContent>
</PageCard>
</PageBackground>
);
6 changes: 6 additions & 0 deletions src/components/Setup/styledComponents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Button } from '@mui/material';
import { styled } from '@mui/material/styles';

export const LargeButton = styled(Button)(({ theme }) => ({
fontSize: theme.spacing(4),
}));
20 changes: 20 additions & 0 deletions src/components/Shared/Links/Links.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { render } from '@testing-library/react';
import { PrivacyPolicyLink, TermsOfUseLink } from './Links';

describe('PrivacyPolicyLink', () => {
it('uses the link from an environment variable', () => {
process.env.PRIVACY_POLICY_URL = 'privacy-policy.com';

const { getByRole } = render(<PrivacyPolicyLink />);
expect(getByRole('link')).toHaveAttribute('href', 'privacy-policy.com');
});
});

describe('TermsOfUseLink', () => {
it('uses the link from an environment variable', () => {
process.env.TERMS_OF_USE_URL = 'terms-of-use.com';

const { getByRole } = render(<TermsOfUseLink />);
expect(getByRole('link')).toHaveAttribute('href', 'terms-of-use.com');
});
});
22 changes: 22 additions & 0 deletions src/components/Shared/Links/Links.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Link, LinkProps } from '@mui/material';
import { useTranslation } from 'react-i18next';

export const PrivacyPolicyLink: React.FC<LinkProps> = (props) => {
const { t } = useTranslation();

return (
<Link href={process.env.PRIVACY_POLICY_URL} target="_blank" {...props}>
{t('Privacy Policy')}
</Link>
);
};

export const TermsOfUseLink: React.FC<LinkProps> = (props) => {
const { t } = useTranslation();

return (
<Link href={process.env.TERMS_OF_USE_URL} target="_blank" {...props}>
{t('Terms of Use')}
</Link>
);
};
1 change: 1 addition & 0 deletions src/components/User/GetUser.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ query GetUser {
email
}
preferences {
id
language: locale
locale: localeDisplay
}
Expand Down
Loading