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

Refactor partner welcome pages #1134

Merged
merged 5 commits into from
Sep 12, 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: 0 additions & 2 deletions .github/workflows/lighthouse-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ jobs:
https://bloom-frontend-git-develop-chaynhq.vercel.app/meet-the-team
https://bloom-frontend-git-develop-chaynhq.vercel.app/auth/register
https://bloom-frontend-git-develop-chaynhq.vercel.app/welcome/bumble
https://bloom-frontend-git-develop-chaynhq.vercel.app/partnership/bumble
budgetPath: ./lighthouse_desktop_budget.json # test performance budgets
configPath: '.github/configs/lighthouse-desktop-rc.yml' # set lighthouse config
- name: Upload artifact
Expand Down Expand Up @@ -70,7 +69,6 @@ jobs:
https://bloom-frontend-git-develop-chaynhq.vercel.app/meet-the-team
https://bloom-frontend-git-develop-chaynhq.vercel.app/auth/register
https://bloom-frontend-git-develop-chaynhq.vercel.app/welcome/bumble
https://bloom-frontend-git-develop-chaynhq.vercel.app/partnership/bumble
budgetPath: ./lighthouse_budget.json # test performance budgets
configPath: '.github/configs/lighthouse-rc.yml' # set lighthouse config
- name: Upload artifact
Expand Down
20 changes: 9 additions & 11 deletions components/forms/RegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const RegisterForm = (props: RegisterFormProps) => {
const userLoading = useTypedSelector((state) => state.user.loading);

const [loading, setLoading] = useState<boolean>(false);
const [codeInput, setCodeInput] = useState<string>('');
const [codeInput, setCodeInput] = useState<string>(codeParam ?? '');
const [nameInput, setNameInput] = useState<string>('');
const [emailInput, setEmailInput] = useState<string>('');
const [passwordInput, setPasswordInput] = useState<string>('');
Expand All @@ -76,11 +76,9 @@ const RegisterForm = (props: RegisterFormProps) => {
const tS = useTranslations('Shared');
const router = useRouter();

useEffect(() => {
if (codeParam) {
setCodeInput(codeParam);
}
}, [codeParam]);
// Include access code field if the partner requires access codes, or the user
// has provided an access code for additional features on signup (i.e. not required, but additional access)
const includeCodeField = partnerName && (accessCodeRequired || codeParam);

useEffect(() => {
// Redirects in 2 scenarios:
Expand Down Expand Up @@ -194,15 +192,15 @@ const RegisterForm = (props: RegisterFormProps) => {
setLoading(true);
setFormError('');

partnerName && accessCodeRequired && (await validateAccessCode());
includeCodeField && (await validateAccessCode());
await createUserRecord();
setLoading(false);
};

return (
<Box sx={containerStyle}>
<form autoComplete="off" onSubmit={submitHandler}>
{partnerName && accessCodeRequired && (
{includeCodeField && (
<TextField
id="partnerAccessCode"
onChange={(e) => setCodeInput(e.target.value)}
Expand Down Expand Up @@ -279,15 +277,15 @@ interface PartnerRegisterFormProps {

export const PartnerRegisterForm = ({ partnerName, codeParam }: PartnerRegisterFormProps) => {
const partners = useTypedSelector((state) => state.partners);
const [accessCodeRequired, setAccessCodeRequired] = useState<boolean>(true);
const [accessCodeRequired, setAccessCodeRequired] = useState<boolean>(false);
const [partnerId, setPartnerId] = useState<string | undefined>(undefined);

useGetAutomaticAccessCodeFeatureForPartnerQuery(partnerName);

useEffect(() => {
const partnerData = partners.find((p) => p.name.toLowerCase() === partnerName.toLowerCase());
if (partnerData) {
setAccessCodeRequired(!hasAutomaticAccessFeature(partnerData));
if (partnerData && hasAutomaticAccessFeature(partnerData) === false) {
setAccessCodeRequired(true);
setPartnerId(partnerData.id);
}
}, [partners, partnerName]);
Expand Down
75 changes: 0 additions & 75 deletions components/forms/WelcomeCodeForm.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion components/layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const Footer = () => {
addUniquePartner(partnersList, partner + '');
}

if (router.pathname.includes('/welcome') || router.pathname.includes('/partnership')) {
if (router.pathname.includes('/welcome')) {
const partnerName = router.asPath.split('/')[2].split('?')[0];
addUniquePartner(partnersList, partnerName);
}
Expand Down
190 changes: 79 additions & 111 deletions components/storyblok/StoryblokWelcomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Box, Button, Card, CardContent, Container, Typography } from '@mui/material';
import { Box, Button, Container } from '@mui/material';
import { ISbRichtext, storyblokEditable } from '@storyblok/react';
import { useTranslations } from 'next-intl';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { render } from 'storyblok-rich-text-react-renderer';
import Link from '../../components/common/Link';
import WelcomeCodeForm from '../../components/forms/WelcomeCodeForm';
import PartnerHeader from '../../components/layout/PartnerHeader';
import StoryblokPageSection, {
StoryblokPageSectionProps,
Expand All @@ -16,33 +14,29 @@ import {
generatePartnerPromoGoToCoursesEvent,
} from '../../constants/events';
import { PartnerContent, getPartnerContent } from '../../constants/partners';
import { useTypedSelector } from '../../hooks/store';
import { useAppDispatch, useTypedSelector } from '../../hooks/store';
import illustrationBloomHeadYellow from '../../public/illustration_bloom_head_yellow.svg';
import welcomeToBloom from '../../public/welcome_to_bloom.svg';
import { useGetAutomaticAccessCodeFeatureForPartnerQuery } from '../../store/api';
import { rowStyle } from '../../styles/common';
import hasAutomaticAccessFeature from '../../utils/hasAutomaticAccessCodeFeature';
import logEvent, { getEventUserData } from '../../utils/logEvent';
import { RichTextOptions } from '../../utils/richText';
import Link from '../common/Link';

const introContainerStyle = {
maxWidth: 600,
width: { xs: '100%', md: '45%' },
fontSize: '1.375rem',
fontFamily: 'Montserrat, sans-serif',
fontStyle: 'italic',
lineHeight: 1.75,
'p, a, span': {
fontSize: '1.375rem',
fontFamily: 'Montserrat, sans-serif',
fontStyle: 'italic',
lineHeight: 1.75,
backgroundColor: 'secondary.light',
textAlign: 'center',
'*': {
marginX: 'auto !important',
},
} as const;

const rowItem = {
width: { xs: '100%', sm: '60%', md: '45%' },
height: '100%',
const introTextStyle = {
width: { xs: '100%', md: '100%' },

'p, a, span': {
fontSize: '1.25rem',
fontFamily: 'Montserrat, sans-serif',
lineHeight: 1.4,
},
} as const;

export interface StoryblokWelcomePageProps {
Expand All @@ -67,6 +61,52 @@ const StoryblokWelcomePage = (props: StoryblokWelcomePageProps) => {
imageAlt: 'alt.bloomHead',
};

const [codeParam, setCodeParam] = useState<string>('');
const router = useRouter();
const dispatch: any = useAppDispatch();
const t = useTranslations('Welcome');

const userId = useTypedSelector((state) => state.user.id);
const userCreatedAt = useTypedSelector((state) => state.user.createdAt);
const partnerAccesses = useTypedSelector((state) => state.partnerAccesses);
const partnerAdmin = useTypedSelector((state) => state.partnerAdmin);
const entryPartnerReferral = useTypedSelector((state) => state.user.entryPartnerReferral);
const entryPartnerAccessCode = useTypedSelector((state) => state.user.entryPartnerAccessCode);
const eventUserData = getEventUserData(userCreatedAt, partnerAccesses, partnerAdmin);

// Ensure partner access codes are stored in state and url query, to handle app refreshes and redirects
useEffect(() => {
const { code } = router.query;

if (code) {
// code in url query
setCodeParam(code + '');
} else if (
entryPartnerReferral === partnerContent.name.toLowerCase() &&
entryPartnerAccessCode
) {
// Entry code in state, add to url query in case of refresh
router.replace(
{
query: { ...router.query, code: entryPartnerAccessCode },
},
undefined,
{
shallow: true,
},
);
setCodeParam(entryPartnerAccessCode);
}
}, [dispatch, router, entryPartnerAccessCode, entryPartnerReferral, partnerContent.name]);

const logPromoEvent = () => {
if (userId) {
logEvent(generatePartnerPromoGoToCoursesEvent(partnerContent.name), eventUserData);
} else {
logEvent(generatePartnerPromoGetStartedEvent(partnerContent.name), eventUserData);
}
};

return (
<Box
{...storyblokEditable({
Expand All @@ -87,9 +127,24 @@ const StoryblokWelcomePage = (props: StoryblokWelcomePageProps) => {
imageSrc={headerProps.imageSrc}
imageAlt={headerProps.imageAlt}
/>
<Container sx={{ ...rowStyle, backgroundColor: 'primary.light' }}>
<Box sx={introContainerStyle}>{render(introduction, RichTextOptions)}</Box>
<CallToActionCard partnerName={partnerContent.name} />
<Container sx={introContainerStyle}>
<Box sx={introTextStyle}>{render(introduction, RichTextOptions)}</Box>

<Button
sx={{ mt: 4, px: 6 }}
variant="contained"
component={Link}
color="secondary"
size="large"
onClick={logPromoEvent}
href={
userId
? '/courses'
: `/auth/register?partner=${partnerContent.name.toLocaleLowerCase()}${codeParam && '&code=' + codeParam}`
}
>
{t(userId ? 'goToCourses' : 'getStarted')}
</Button>
</Container>
{page_sections?.length > 0 &&
page_sections.map((section: any, index: number) => (
Expand All @@ -99,91 +154,4 @@ const StoryblokWelcomePage = (props: StoryblokWelcomePageProps) => {
);
};

const CallToActionCard = ({ partnerName }: { partnerName: string }) => {
const router = useRouter();

const userId = useTypedSelector((state) => state.user.id);
const userCreatedAt = useTypedSelector((state) => state.user.createdAt);
const partnerAccesses = useTypedSelector((state) => state.partnerAccesses);
const partnerAdmin = useTypedSelector((state) => state.partnerAdmin);
const partners = useTypedSelector((state) => state.partners);
const eventUserData = getEventUserData(userCreatedAt, partnerAccesses, partnerAdmin);

const [accessCodeRequired, setAccessCodeRequired] = useState<boolean>(true);
const [codeParam, setCodeParam] = useState<string>('');

const t = useTranslations('Welcome');

useGetAutomaticAccessCodeFeatureForPartnerQuery(partnerName);
useEffect(() => {
const partnerData = partners.find((p) => p.name.toLowerCase() === partnerName.toLowerCase());
if (partnerData) {
setAccessCodeRequired(!hasAutomaticAccessFeature(partnerData));
}
}, [partners, partnerName]);

useEffect(() => {
const { code } = router.query;
if (code) setCodeParam(code + '');
}, [setCodeParam, router.query]);

return (
<Card sx={rowItem}>
<CardContent>
{userId && (
<>
<Typography variant="h2" component="h2">
{t('continueCourses')}
</Typography>
<Typography>{t('continueCoursesDescription')}</Typography>
<Button
sx={{ mt: 3 }}
variant="contained"
fullWidth
component={Link}
color="secondary"
onClick={() => {
logEvent(generatePartnerPromoGoToCoursesEvent(partnerName), eventUserData);
}}
href="/courses"
>
{t('goToCourses')}
</Button>
</>
)}
{!userId && (accessCodeRequired || codeParam) && (
<>
<Typography variant="h2" component="h2">
{t('getStarted')}
</Typography>
<Typography>{t.rich('accessIntroduction', { partnerName })}</Typography>
<WelcomeCodeForm codeParam={codeParam} partnerParam={partnerName} />
</>
)}
{!userId && !accessCodeRequired && !codeParam && (
<>
<Typography variant="h2" component="h2">
{t('getStarted')}
</Typography>
<Typography>{t.rich('publicIntroduction', { partnerName })}</Typography>
<Button
sx={{ mt: 3 }}
variant="contained"
fullWidth
component={Link}
color="secondary"
onClick={() => {
logEvent(generatePartnerPromoGetStartedEvent(partnerName), eventUserData);
}}
href={`/auth/register?partner=${partnerName.toLocaleLowerCase()}`}
>
{t('getStarted')}
</Button>
</>
)}
</CardContent>
</Card>
);
};

export default StoryblokWelcomePage;
Loading
Loading