diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46358486..b8c0c80a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,10 +56,10 @@ Chayn team members usually respond within 3 business days. Chayn is open to all kinds of contributions, such as: - additional software tests / test coverage -- dependency updates *check Dependabot pull requests +- dependency updates \*check Dependabot pull requests - code (requested features, bug fixes, quality enhancements, maintenance help) - accessibility and language support. -- no-code (documentation, translations) *see spam policy below for accepted documentation changes. +- no-code (documentation, translations) \*see spam policy below for accepted documentation changes. # Chayn's Spam Contribution Policy 🚫 diff --git a/app/ThemeRegistry.tsx b/app/ThemeRegistry.tsx new file mode 100644 index 00000000..873d699e --- /dev/null +++ b/app/ThemeRegistry.tsx @@ -0,0 +1,17 @@ +import { AppRouterCacheProvider } from '@mui/material-nextjs/v13-appRouter'; +import CssBaseline from '@mui/material/CssBaseline'; +import { ThemeProvider } from '@mui/material/styles'; +import theme from '../styles/theme'; + +// This implementation is from mui integrations with nextjs app router +// see https://mui.com/material-ui/integrations/nextjs/#app-router +export default function ThemeRegistry({ children }: { children: React.ReactNode }) { + return ( + + + + {children} + + + ); +} diff --git a/pages/chat.tsx b/app/[locale]/chat/chat.tsx similarity index 58% rename from pages/chat.tsx rename to app/[locale]/chat/chat.tsx index 895d7771..02423617 100644 --- a/pages/chat.tsx +++ b/app/[locale]/chat/chat.tsx @@ -1,22 +1,18 @@ +'use client'; + import { Box } from '@mui/material'; import { ISbStoryData, useStoryblokState } from '@storyblok/react'; -import { GetStaticPropsContext, NextPage } from 'next'; import { useTranslations } from 'next-intl'; import Head from 'next/head'; -import { SignUpBanner } from '../components/banner/SignUpBanner'; -import NoDataAvailable from '../components/common/NoDataAvailable'; -import CrispButton from '../components/crisp/CrispButton'; -import Header, { HeaderProps } from '../components/layout/Header'; -import StoryblokPageSection from '../components/storyblok/StoryblokPageSection'; -import { useTypedSelector } from '../hooks/store'; -import { getStoryblokPageProps } from '../utils/getStoryblokPageProps'; -import { getEventUserData } from '../utils/logEvent'; - -interface Props { - story: ISbStoryData | null; -} +import { SignUpBanner } from '../../../components/banner/SignUpBanner'; +import NoDataAvailable from '../../../components/common/NoDataAvailable'; +import CrispButton from '../../../components/crisp/CrispButton'; +import Header, { HeaderProps } from '../../../components/layout/Header'; +import StoryblokPageSection from '../../../components/storyblok/StoryblokPageSection'; +import { useTypedSelector } from '../../../hooks/store'; +import { getEventUserData } from '../../../utils/logEvent'; -const Chat: NextPage = ({ story }) => { +const Chat = ({ story }: { story: ISbStoryData | null }) => { story = useStoryblokState(story); const t = useTranslations('Courses'); @@ -68,21 +64,4 @@ const Chat: NextPage = ({ story }) => { ); }; -export async function getStaticProps({ locale, preview = false }: GetStaticPropsContext) { - const storyblokProps = await getStoryblokPageProps('chat', locale, preview); - - return { - props: { - ...storyblokProps, - messages: { - ...require(`../messages/shared/${locale}.json`), - ...require(`../messages/navigation/${locale}.json`), - ...require(`../messages/courses/${locale}.json`), - ...require(`../messages/chat/${locale}.json`), - }, - }, - revalidate: 3600, // revalidate every hour - }; -} - export default Chat; diff --git a/app/[locale]/chat/page.tsx b/app/[locale]/chat/page.tsx new file mode 100644 index 00000000..a3e130a2 --- /dev/null +++ b/app/[locale]/chat/page.tsx @@ -0,0 +1,19 @@ +import { getLocale } from 'next-intl/server'; +import { locales } from '../../../i18n/config'; +import { getStoryblokPageProps } from '../../../utils/getStoryblokPageProps'; +import Chat from './chat'; + +export const revalidate = 3600; + +export default async function Page() { + const preview = false; + const locale = await getLocale(); + const storyblokProps = await getStoryblokPageProps('chat', locale, preview); + return ; +} + +export async function generateStaticParams() { + return locales.map((locale) => { + return { params: { locale } }; + }); +} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 00000000..450d822f --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,13 @@ +import { locales } from '../../i18n/config'; + +const Layout = ({ children }: { children: React.ReactNode }) => { + return <>{children}; +}; + +export async function generateStaticParams() { + return locales.map((locale) => { + return { params: { locale } }; + }); +} + +export default Layout; diff --git a/app/[locale]/meet-the-team/meet-the-team.tsx b/app/[locale]/meet-the-team/meet-the-team.tsx new file mode 100644 index 00000000..e6304ea1 --- /dev/null +++ b/app/[locale]/meet-the-team/meet-the-team.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { ISbStoryData, useStoryblokState } from '@storyblok/react'; +import NoDataAvailable from '../../../components/common/NoDataAvailable'; +import StoryblokMeetTheTeamPage, { + StoryblokMeetTheTeamPageProps, +} from '../../../components/storyblok/StoryblokMeetTheTeamPage'; + +interface MeetTheTeamProps { + story: ISbStoryData | null; +} + +const MeetTheTeam = ({ story }: MeetTheTeamProps) => { + const storyData = useStoryblokState(story); + + if (!storyData) { + return ; + } + + return ; +}; + +export default MeetTheTeam; diff --git a/app/[locale]/meet-the-team/page.tsx b/app/[locale]/meet-the-team/page.tsx new file mode 100644 index 00000000..a24daf43 --- /dev/null +++ b/app/[locale]/meet-the-team/page.tsx @@ -0,0 +1,11 @@ +import { getStoryblokPageProps } from '../../../utils/getStoryblokPageProps'; +import MeetTheTeam from './meet-the-team'; + +export const revalidate = 3600; + +export default async function Page({ params }: { params: { locale: string } }) { + const preview = false; + const locale = params.locale; + const storyblokProps = await getStoryblokPageProps('meet-the-team', locale, preview); + return ; +} diff --git a/app/[locale]/welcome/[partnerName]/page.tsx b/app/[locale]/welcome/[partnerName]/page.tsx new file mode 100644 index 00000000..903489a8 --- /dev/null +++ b/app/[locale]/welcome/[partnerName]/page.tsx @@ -0,0 +1,46 @@ +import { getStoryblokApi, ISbStoriesParams, ISbStoryData } from '@storyblok/react'; +import { locales } from '../../../../i18n/config'; +import { getStoryblokPageProps } from '../../../../utils/getStoryblokPageProps'; +import Welcome from './welcome'; + +export const revalidate = 3600; + +export default async function Page({ + params, +}: { + params: { partnerName: string; locale: string }; +}) { + const preview = false; + const locale = params.locale; + const partnerName = params?.partnerName; + const storyblokProps = await getStoryblokPageProps(`welcome/${partnerName}`, locale, preview); + return ; +} + +export async function generateStaticParams() { + let sbParams: ISbStoriesParams = { + published: true, + starts_with: 'partnership/', + }; + + const storyblokApi = getStoryblokApi(); + let data = await storyblokApi.getAll('cdn/links', sbParams); + + let paths: any = []; + + data.forEach((story: Partial) => { + if (!story.slug) return; + + // get array for slug because of catch all + let splittedSlug = story.slug.split('/'); + + if (locales) { + // create additional languages + for (const locale of locales) { + paths.push({ params: { partnerName: splittedSlug[1] } }); + } + } + }); + + return paths; +} diff --git a/app/[locale]/welcome/[partnerName]/welcome.tsx b/app/[locale]/welcome/[partnerName]/welcome.tsx new file mode 100644 index 00000000..9b5edd09 --- /dev/null +++ b/app/[locale]/welcome/[partnerName]/welcome.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { ISbStoryData, useStoryblokState } from '@storyblok/react'; +import NoDataAvailable from '../../../../components/common/NoDataAvailable'; +import StoryblokWelcomePage, { + StoryblokWelcomePageProps, +} from '../../../../components/storyblok/StoryblokWelcomePage'; + +interface WelcomeProps { + story: ISbStoryData | null; +} + +const Welcome = ({ story }: WelcomeProps) => { + story = useStoryblokState(story); + + if (!story) { + return ; + } + + return ( + + ); +}; + +export default Welcome; diff --git a/app/appLayout.tsx b/app/appLayout.tsx new file mode 100644 index 00000000..ad804588 --- /dev/null +++ b/app/appLayout.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { Analytics } from '@mui/icons-material'; +import { usePathname } from 'next/navigation'; +import { Hotjar } from 'nextjs-hotjar'; +import { useEffect } from 'react'; +import { AppBarSpacer } from '../components/layout/AppBarSpacer'; +import Consent from '../components/layout/Consent'; +import Footer from '../components/layout/Footer'; +import LanguageMenuAppRoute from '../components/layout/LanguageMenuAppRoute'; +import LeaveSiteButton from '../components/layout/LeaveSiteButton'; +import TopBar from '../components/layout/TopBar'; +import firebase from '../config/firebase'; +import { AuthGuard } from '../guards/AuthGuard'; + +interface AppLayoutProps { + children?: React.ReactNode; +} + +// Init firebase +firebase; + +export default function AppLayout({ children }: AppLayoutProps) { + const pathname = usePathname(); + + // Get top level directory of path e.g pathname /courses/course_name has pathHead courses + const pathHead = pathname?.split('/')[1]; // E.g. courses | therapy | partner-admin + + useEffect(() => { + // Check if entry path is from a partner referral and if so, store referring partner in local storage + // This enables us to redirect a user to the correct sign up page later (e.g. in SignUpBanner) + const path = pathname; + + if (path?.includes('/welcome/')) { + const referralPartner = path.split('/')[2]; // Gets "bumble" from /welcome/bumble + + if (referralPartner) { + window.localStorage.setItem('referralPartner', referralPartner); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + <> + + + + + {pathHead !== 'partner-admin' && } + {children as JSX.Element} +