diff --git a/.github/workflows/codeforafrica-deploy-review-app.yml b/.github/workflows/codeforafrica-deploy-review-app.yml index 7eec5f81e..0d2f232e6 100644 --- a/.github/workflows/codeforafrica-deploy-review-app.yml +++ b/.github/workflows/codeforafrica-deploy-review-app.yml @@ -57,8 +57,6 @@ jobs: MONGODB_URL=${{ secrets.CODEFORAFRICA_MONGO_URL }}/${{ env.APP_NAME }} NEXT_PUBLIC_APP_URL=${{ env.NEXT_PUBLIC_APP_URL }} PAYLOAD_SECRET=${{ secrets.CODEFORAFRICA_PAYLOAD_SECRET_KEY }} - GHOST_URL=${{ secrets.GHOST_URL }} - GHOST_API_KEY=${{ secrets.GHOST_API_KEY }} NEXT_PUBLIC_APP_LOGO_URL=${{ secrets.NEXT_PUBLIC_APP_CFA_LOGO_URL }} NEXT_PUBLIC_APP_NAME=${{ secrets.NEXT_PUBLIC_APP_CFA_NAME }} cache-from: type=local,src=/tmp/.buildx-cache diff --git a/Dockerfile.codeforafrica b/Dockerfile.codeforafrica index 0cb590d89..9ae89ad8f 100644 --- a/Dockerfile.codeforafrica +++ b/Dockerfile.codeforafrica @@ -33,9 +33,7 @@ RUN pnpm install --recursive --offline --frozen-lockfile ARG PORT=3000 \ MONGODB_URL \ PAYLOAD_SECRET \ - NEXT_PUBLIC_APP_URL=http://localhost:3000 \ - GHOST_URL \ - GHOST_API_KEY + NEXT_PUBLIC_APP_URL=http://localhost:3000 RUN pnpm build-next --filter=codeforafrica @@ -62,8 +60,6 @@ ENV NODE_ENV=production \ PAYLOAD_CONFIG_PATH=${PAYLOAD_CONFIG_PATH} \ PAYLOAD_SECRET=${PAYLOAD_SECRET} \ MONGODB_URL=${MONGODB_URL} \ - GHOST_URL=${GHOST_URL} \ - GHOST_API_KEY=${GHOST_API_KEY} \ NEXT_PUBLIC_APP_LOGO_URL=${NEXT_PUBLIC_APP_LOGO_URL} \ NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME} \ NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} diff --git a/apps/codeforafrica/payload.config.ts b/apps/codeforafrica/payload.config.ts index 478e32f7d..9bbd93284 100644 --- a/apps/codeforafrica/payload.config.ts +++ b/apps/codeforafrica/payload.config.ts @@ -1,6 +1,7 @@ import path from "path"; import { buildConfig } from "payload/config"; +import GuidingPrinciples from "./src/payload/collections/GuidingPrinciples"; import Impact from "./src/payload/collections/Impact"; import Media from "./src/payload/collections/Media"; import Pages from "./src/payload/collections/Pages"; @@ -31,7 +32,13 @@ const adapter = s3Adapter({ export default buildConfig({ serverURL: appURL, - collections: [Impact, Pages, Media, Partners] as CollectionConfig[], + collections: [ + GuidingPrinciples, + Impact, + Pages, + Media, + Partners, + ] as CollectionConfig[], globals: [Settings] as GlobalConfig[], admin: { css: path.resolve(__dirname, "./src/payload/admin/scss/custom.scss"), diff --git a/apps/codeforafrica/public/images/cms/blocks/contact_form.jpg b/apps/codeforafrica/public/images/cms/blocks/contact_form.jpg new file mode 100644 index 000000000..06aeb1bc6 Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/contact_form.jpg differ diff --git a/apps/codeforafrica/public/images/cms/blocks/get_in_touch.jpg b/apps/codeforafrica/public/images/cms/blocks/get_in_touch.jpg new file mode 100644 index 000000000..ca1fb2b1e Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/get_in_touch.jpg differ diff --git a/apps/codeforafrica/public/images/cms/blocks/get_involved.jpg b/apps/codeforafrica/public/images/cms/blocks/get_involved.jpg new file mode 100644 index 000000000..e0759fe80 Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/get_involved.jpg differ diff --git a/apps/codeforafrica/public/images/cms/blocks/guiding_principles.png b/apps/codeforafrica/public/images/cms/blocks/guiding_principles.png new file mode 100644 index 000000000..1b5de838a Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/guiding_principles.png differ diff --git a/apps/codeforafrica/public/images/cms/blocks/join_our_slack.jpg b/apps/codeforafrica/public/images/cms/blocks/join_our_slack.jpg new file mode 100644 index 000000000..1d245879a Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/join_our_slack.jpg differ diff --git a/apps/codeforafrica/public/images/cms/blocks/meet_our_team.jpg b/apps/codeforafrica/public/images/cms/blocks/meet_our_team.jpg new file mode 100644 index 000000000..7cac36961 Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/meet_our_team.jpg differ diff --git a/apps/codeforafrica/public/images/cms/blocks/partners.png b/apps/codeforafrica/public/images/cms/blocks/partners.png new file mode 100644 index 000000000..7f87ee37e Binary files /dev/null and b/apps/codeforafrica/public/images/cms/blocks/partners.png differ diff --git a/apps/codeforafrica/src/components/ConnectBar/ConnectBar.js b/apps/codeforafrica/src/components/ConnectBar/ConnectBar.js index 988437a4c..8eee2dded 100644 --- a/apps/codeforafrica/src/components/ConnectBar/ConnectBar.js +++ b/apps/codeforafrica/src/components/ConnectBar/ConnectBar.js @@ -6,18 +6,14 @@ import SocialMediaButton from "@/codeforafrica/components/SocialMediaButton"; const ConnectBar = React.forwardRef(function ConnectBar(props, ref) { const { sx, title, links } = props; - if (!links || !Object.entries(links)?.length) { + if (!links?.length) { return null; } - const socialConnections = [ - "twitter", - "slack", - "linkedin", - "facebook", - "instagram", - "github", - ].flatMap((name) => (links[name] ? [{ name, url: links[name] }] : [])); + const socialConnections = links.map(({ platform, url }) => ({ + name: platform?.toLowerCase(), + url, + })); return ( <SocialMediaBar diff --git a/apps/codeforafrica/src/components/ConnectBar/ConnectBar.snap.js b/apps/codeforafrica/src/components/ConnectBar/ConnectBar.snap.js index 6f31c7ef2..46da7d44d 100644 --- a/apps/codeforafrica/src/components/ConnectBar/ConnectBar.snap.js +++ b/apps/codeforafrica/src/components/ConnectBar/ConnectBar.snap.js @@ -1,29 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`<ConnectBar /> renders unchanged 1`] = ` -<div> - <div - class="MuiStack-root css-150zc89-MuiStack-root" - > - <div - class="MuiStack-root css-1mbd5au-MuiStack-root" - > - <a - aria-label="facebook" - class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways MuiBox-root active css-tmo8yp-MuiTypography-root-MuiLink-root" - data-mui-internal-clone-element="true" - href="https://www.facebook.com/" - rel="noreferrer noopener" - target="_blank" - > - <div - aria-hidden="true" - class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1dz6c5u-MuiSvgIcon-root" - focusable="false" - viewbox="0 0 24 24" - /> - </a> - </div> - </div> -</div> -`; +exports[`<ConnectBar /> renders unchanged 1`] = `<div />`; diff --git a/apps/codeforafrica/src/components/GetInTouch/GetInTouch.js b/apps/codeforafrica/src/components/GetInTouch/GetInTouch.js index 71c01065b..e79914832 100644 --- a/apps/codeforafrica/src/components/GetInTouch/GetInTouch.js +++ b/apps/codeforafrica/src/components/GetInTouch/GetInTouch.js @@ -8,7 +8,7 @@ import TwoToneBackground from "@/codeforafrica/components/TwoToneBackground"; const GetInTouch = React.forwardRef(function GetInTouch(props, ref) { const { - action: { href, content }, + action: { href, label }, subtitle, title, sx, @@ -59,7 +59,7 @@ const GetInTouch = React.forwardRef(function GetInTouch(props, ref) { }, }} > - {content} + {label} </Button> </Stack> </Section> diff --git a/apps/codeforafrica/src/components/GetInTouch/GetInTouch.snap.js b/apps/codeforafrica/src/components/GetInTouch/GetInTouch.snap.js index c1d6a05e7..5d282a7c4 100644 --- a/apps/codeforafrica/src/components/GetInTouch/GetInTouch.snap.js +++ b/apps/codeforafrica/src/components/GetInTouch/GetInTouch.snap.js @@ -36,6 +36,7 @@ exports[`<GetInTouch /> renders unchanged 1`] = ` viewbox="0 0 24 24" /> </span> + Get in touch </a> </div> </div> diff --git a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.js b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.js index 1d4796ed2..92ae75ead 100644 --- a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.js +++ b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.js @@ -7,7 +7,7 @@ import React from "react"; import ImpactCardList from "@/codeforafrica/components/ImpactCardList"; const GetInvolved = React.forwardRef(function GetInvolved(props, ref) { - const { list, action, sx } = props; + const { impacts, action, sx } = props; if (!action?.href?.length) { return null; @@ -26,7 +26,7 @@ const GetInvolved = React.forwardRef(function GetInvolved(props, ref) { py: { xs: 5, sm: 10, md: 4, lg: 12.5 }, }} > - <ImpactCardList list={list} /> + <ImpactCardList list={impacts} /> <Button variant="contained" component={Link} @@ -39,7 +39,7 @@ const GetInvolved = React.forwardRef(function GetInvolved(props, ref) { width: { xs: "100%", sm: "fit-content" }, }} > - {action.content || action.href} + {action.label} </Button> </Section> </Box> @@ -47,12 +47,12 @@ const GetInvolved = React.forwardRef(function GetInvolved(props, ref) { }); GetInvolved.propTypes = { - list: PropTypes.arrayOf(PropTypes.shape({})), + impacts: PropTypes.arrayOf(PropTypes.shape({})), action: PropTypes.shape({}), }; GetInvolved.defaultProps = { - list: undefined, + impacts: undefined, action: undefined, }; diff --git a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.snap.js b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.snap.js index 072057c34..f824ab1a4 100644 --- a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.snap.js +++ b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.snap.js @@ -47,6 +47,15 @@ exports[`<GetInvolved /> renders unchanged 1`] = ` > 15000 </div> + <div + class="MuiBox-root css-2ligbh" + > + <span + class="MuiTypography-root MuiTypography-body3 css-s9m4rs-MuiTypography-root" + > + Our team makes an impact in more than 20 countries where members are present. + </span> + </div> </div> </div> </div> diff --git a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.test.js b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.test.js index d0fbd6634..60263bf9b 100644 --- a/apps/codeforafrica/src/components/GetInvolved/GetInvolved.test.js +++ b/apps/codeforafrica/src/components/GetInvolved/GetInvolved.test.js @@ -9,19 +9,27 @@ import theme from "@/codeforafrica/theme"; const render = createRender({ theme }); const defaultProps = { - list: [ + impacts: [ { title: "Beneficiaries trained", value: 15000, image: { src: "/images/Type=layout,%20Size=32,%20Color=1020E1.svg", }, - content: - "In 10 years, 15 000 trainees have learned new skills and knowledge within the civic tech and media space.", + description: [ + { + children: [ + { + text: "Our team makes an impact in more than 20 countries where members are present.", + children: null, + }, + ], + }, + ], }, ], action: { - content: "Get Involved", + label: "Get Involved", href: "/contact", }, }; diff --git a/apps/codeforafrica/src/components/GuidingPrinciplesCard/GuidingPrinciplesCard.js b/apps/codeforafrica/src/components/GuidingPrinciplesCard/GuidingPrinciplesCard.js index f5a428752..de49a3549 100644 --- a/apps/codeforafrica/src/components/GuidingPrinciplesCard/GuidingPrinciplesCard.js +++ b/apps/codeforafrica/src/components/GuidingPrinciplesCard/GuidingPrinciplesCard.js @@ -2,9 +2,11 @@ import { RichTypography } from "@commons-ui/next"; import { Card, CardContent, CardMedia } from "@mui/material"; import React from "react"; +import RichText from "@/codeforafrica/components/RichText"; + const GuidingPrinciplesCard = React.forwardRef( function GuidingPrinciplesCard(props, ref) { - const { title, content, icon } = props; + const { title, description, icon } = props; return ( <Card @@ -35,7 +37,7 @@ const GuidingPrinciplesCard = React.forwardRef( <RichTypography variant="h3" sx={{ my: 2.5 }}> {title} </RichTypography> - <RichTypography>{content}</RichTypography> + <RichText elements={description} /> </CardContent> </Card> ); diff --git a/apps/codeforafrica/src/components/GuidingPrinciplesCardList/GuidingPrinciplesCardList.js b/apps/codeforafrica/src/components/GuidingPrinciplesCardList/GuidingPrinciplesCardList.js index b2e7041f0..99ed308f2 100644 --- a/apps/codeforafrica/src/components/GuidingPrinciplesCardList/GuidingPrinciplesCardList.js +++ b/apps/codeforafrica/src/components/GuidingPrinciplesCardList/GuidingPrinciplesCardList.js @@ -7,13 +7,20 @@ import GuidingPrinciplesCard from "../GuidingPrinciplesCard"; const GuidingPrinciplesCardList = React.forwardRef( function GuidingPrinciplesCardList(props, ref) { - const { list, title, ...other } = props; + const { list, title, sx } = props; if (!list?.length) { return null; } return ( - <Section {...other} ref={ref}> + <Section + sx={{ + px: { xs: 2.5, sm: 0 }, + py: { xs: 2.5, md: 10 }, + ...sx, + }} + ref={ref} + > <RichTypography variant="h4">{title}</RichTypography> <Grid container diff --git a/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.snap.js b/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.snap.js index ad09c8d89..2eb50f825 100644 --- a/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.snap.js +++ b/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.snap.js @@ -41,6 +41,15 @@ exports[`<ImpactCardList /> renders unchanged 1`] = ` > 15000 </div> + <div + class="MuiBox-root css-2ligbh" + > + <span + class="MuiTypography-root MuiTypography-body3 css-s9m4rs-MuiTypography-root" + > + Our team makes an impact in more than 20 countries where members are present. + </span> + </div> </div> </div> </div> diff --git a/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.test.js b/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.test.js index e67879744..b81e90e6c 100644 --- a/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.test.js +++ b/apps/codeforafrica/src/components/ImpactCardList/ImpactCardList.test.js @@ -16,8 +16,16 @@ const defaultProps = { image: { src: "/images/Type=layout,%20Size=32,%20Color=1020E1.svg", }, - content: - "In 10 years, 15 000 trainees have learned new skills and knowledge within the civic tech and media space.", + description: [ + { + children: [ + { + text: "Our team makes an impact in more than 20 countries where members are present.", + children: null, + }, + ], + }, + ], }, ], }; diff --git a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.js b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.js index daf1dbf8e..d967053eb 100644 --- a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.js +++ b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.js @@ -4,8 +4,15 @@ import { Box, Button, Grid } from "@mui/material"; import PropTypes from "prop-types"; import React from "react"; +import RichText from "@/codeforafrica/components/RichText"; + const MeetOurTeam = React.forwardRef(function MeetOurTeam(props, ref) { - const { title, description, href, logo } = props; + const { + title, + description, + action: { href, label }, + image, + } = props; if (!title || !description) { return null; @@ -26,16 +33,14 @@ const MeetOurTeam = React.forwardRef(function MeetOurTeam(props, ref) { > {title} </RichTypography> - <RichTypography variant="body3" sx={{ pt: 5 }}> - {description} - </RichTypography> + <RichText sx={{ pt: 5 }} variant="body3" elements={description} /> <Button component={href ? Link : undefined} href={href} sx={{ width: { xs: "100%", sm: "auto" }, margin: "2.5rem 0" }} variant="contained-reverse" > - Meet our Team + {label} </Button> </Grid> @@ -48,8 +53,7 @@ const MeetOurTeam = React.forwardRef(function MeetOurTeam(props, ref) { > <Figure ImageProps={{ - src: logo, - alt: "Our offices across africa", + ...image, }} sx={{ height: { xs: "21.93rem", sm: "26rem", lg: "32.37rem" }, @@ -65,14 +69,17 @@ const MeetOurTeam = React.forwardRef(function MeetOurTeam(props, ref) { MeetOurTeam.propTypes = { title: PropTypes.string, - description: PropTypes.string, - logo: PropTypes.string, + description: PropTypes.arrayOf(PropTypes.shape({})), + image: PropTypes.shape({ + src: PropTypes.string, + alt: PropTypes.string, + }), }; MeetOurTeam.defaultProps = { title: undefined, description: undefined, - logo: undefined, + image: undefined, }; export default MeetOurTeam; diff --git a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.snap.js b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.snap.js index 66e53c6e4..936a75d06 100644 --- a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.snap.js +++ b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.snap.js @@ -19,17 +19,12 @@ exports[`<MeetOurTeam /> renders unchanged 1`] = ` > title </div> - <div - class="MuiTypography-root MuiTypography-body3 css-scexb3-MuiTypography-root" - > - description - </div> <a class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways MuiButtonBase-root MuiButton-root MuiButton-contained-reverse MuiButton-contained-reversePrimary MuiButton-sizeMedium MuiButton-contained-reverseSizeMedium MuiButton-root MuiButton-contained-reverse MuiButton-contained-reversePrimary MuiButton-sizeMedium MuiButton-contained-reverseSizeMedium css-16j572f-MuiTypography-root-MuiLink-root-MuiButtonBase-root-MuiButton-root" href="/about#our-team" tabindex="0" > - Meet our Team + Meet our team </a> </div> <div diff --git a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.test.js b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.test.js index 8bb38cca8..3591ec412 100644 --- a/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.test.js +++ b/apps/codeforafrica/src/components/MeetOurTeam/MeetOurTeam.test.js @@ -11,9 +11,27 @@ const render = createRender({ theme }); const defaultProps = { slug: "our-team", title: "title", - description: "description", - logo: "/images/Africa@2400x 1", - href: "/about#our-team", + description: { + children: [ + { + text: "Africa's", + bold: true, + children: null, + }, + { + text: "largest network of civic tech and open data labs", + children: null, + }, + ], + }, + image: { + alt: "Our offices across africa", + src: "/images/Africa@2400x 1", + }, + action: { + href: "/about#our-team", + label: "Meet our team", + }, }; describe("<MeetOurTeam />", () => { diff --git a/apps/codeforafrica/src/components/Partner/Partner.js b/apps/codeforafrica/src/components/Partner/Partner.js new file mode 100644 index 000000000..8e479d56d --- /dev/null +++ b/apps/codeforafrica/src/components/Partner/Partner.js @@ -0,0 +1,89 @@ +import { Section } from "@commons-ui/core"; +import { Box } from "@mui/material"; +import React from "react"; + +import AboutChildPageHeader from "@/codeforafrica/components/AboutChildPageHeader"; +import ConnectBar from "@/codeforafrica/components/ConnectBar"; +import RelatedProjects from "@/codeforafrica/components/RelatedProjects"; +import RichText from "@/codeforafrica/components/RichText"; +import SectionDivider from "@/codeforafrica/components/SectionDivider"; + +const Partner = React.forwardRef(function Partner( + { description, connect, relatedProjects, relatedProjectsTitle, name, logo }, + ref, +) { + return ( + <Box ref={ref} sx={{ pb: { xs: 10, md: 7 } }}> + <AboutChildPageHeader + name={name} + image={logo} + FigureProps={{ + sx: { + backgroundPositionY: "center", + backgroundRepeat: "no-repeat", + backgroundSize: "contain", + bgcolor: "background.default", + borderRadius: 0, + filter: "drop-shadow(0px 8.7px 17.4px rgba(0, 0, 0, 0.1))", + height: { xs: 116 }, + width: { xs: 247 }, + }, + }} + /> + <Section + sx={{ + px: { xs: 2.5, sm: 0 }, + pt: { xs: 2.5, md: 7 }, + maxWidth: { + sm: "648px", + md: "912px", + }, + }} + > + <RichText + variant="subheading" + component="p" + sx={{ + mb: 5, + typography: "subheading", + "& > p": { + mb: 5, + }, + }} + elements={description} + /> + + <ConnectBar title="Connect" links={connect} /> + </Section> + {relatedProjects?.length ? ( + <> + <SectionDivider + sx={{ + maxWidth: { + sm: "648px", + md: "912px", + }, + px: { xs: 2.5, sm: 0 }, + py: { xs: "30px", md: 5 }, + }} + /> + <RelatedProjects + sx={{ + maxWidth: { + sm: "648px", + md: "912px", + }, + pt: 0, + }} + tileListProps={{ fixed: true }} + titleProps={{ sx: { mb: { xs: "30px", md: 5 } } }} + title={relatedProjectsTitle} + projects={relatedProjects} + /> + </> + ) : null} + </Box> + ); +}); + +export default Partner; diff --git a/apps/codeforafrica/src/components/Partner/Partner.snap.js b/apps/codeforafrica/src/components/Partner/Partner.snap.js new file mode 100644 index 000000000..86026b06a --- /dev/null +++ b/apps/codeforafrica/src/components/Partner/Partner.snap.js @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Partners /> renders unchanged 1`] = ` +<div> + <div + class="MuiBox-root css-1gghq2r" + > + <div + class="MuiContainer-root MuiContainer-maxWidthLg MuiContainer-fixed MuiContainer-disableGutters css-1ilq66o-MuiContainer-root" + > + <div + class="MuiBox-root css-15sqwg8" + > + Lorem ipsum dolor sit amet consectetur adipiscing, elit ac primis praesent + tempor luctus libero, curae condimentum ultricies proin leo. Arcu ornare dis fermentum nisi consequat imperdiet porta viverra placerat nullam, dapibus molestie faucibus id mi lacinia orci magnis. Ridiculus aptent phasellus mus nisi porta rutrum tellus, ut venenatis feugiat massa volutpat. + Duis maecenas per erat odio quisque accumsan, donec tempus class euismod vulputate fermentum imperdiet, suspendisse blandit lacinia semper cursus. Neque tristique posuere a feugiat convallis tempor cras nunc, leo faucibus cum aptent placerat aenean lobortis, nibh iaculis ac nascetur praesent mus quisque. Nullam leo rutrum augue urna cubilia morbi enim, arcu risus mus mauris elementum pulvinar, laoreet bibendum convallis senectus ullamcorper malesuada. + </div> + </div> + </div> +</div> +`; diff --git a/apps/codeforafrica/src/components/Partner/Partner.test.js b/apps/codeforafrica/src/components/Partner/Partner.test.js new file mode 100644 index 000000000..fec4ef031 --- /dev/null +++ b/apps/codeforafrica/src/components/Partner/Partner.test.js @@ -0,0 +1,30 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import Partner from "./Partner"; + +import theme from "@/codeforafrica/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + description: [ + { + text: "Lorem ipsum dolor sit amet consectetur adipiscing, elit ac primis praesent", + }, + { + text: "tempor luctus libero, curae condimentum ultricies proin leo. Arcu ornare dis fermentum nisi consequat imperdiet porta viverra placerat nullam, dapibus molestie faucibus id mi lacinia orci magnis. Ridiculus aptent phasellus mus nisi porta rutrum tellus, ut venenatis feugiat massa volutpat. ", + }, + { + text: "Duis maecenas per erat odio quisque accumsan, donec tempus class euismod vulputate fermentum imperdiet, suspendisse blandit lacinia semper cursus. Neque tristique posuere a feugiat convallis tempor cras nunc, leo faucibus cum aptent placerat aenean lobortis, nibh iaculis ac nascetur praesent mus quisque. Nullam leo rutrum augue urna cubilia morbi enim, arcu risus mus mauris elementum pulvinar, laoreet bibendum convallis senectus ullamcorper malesuada.", + }, + ], +}; + +describe("<Partners />", () => { + it("renders unchanged", () => { + const { container } = render(<Partner {...defaultProps} />); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/codeforafrica/src/components/Partner/index.js b/apps/codeforafrica/src/components/Partner/index.js new file mode 100644 index 000000000..e10539fb2 --- /dev/null +++ b/apps/codeforafrica/src/components/Partner/index.js @@ -0,0 +1,3 @@ +import Partner from "./Partner"; + +export default Partner; diff --git a/apps/codeforafrica/src/components/TeamMembers/TeamMembers.snap.js b/apps/codeforafrica/src/components/TeamMembers/TeamMembers.snap.js index d89212ffe..be1521b9b 100644 --- a/apps/codeforafrica/src/components/TeamMembers/TeamMembers.snap.js +++ b/apps/codeforafrica/src/components/TeamMembers/TeamMembers.snap.js @@ -1,128 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`<TeamMembers /> renders unchanged 1`] = ` -<div> - <div - class="MuiContainer-root MuiContainer-maxWidthLg MuiContainer-fixed MuiContainer-disableGutters css-eoqn3z-MuiContainer-root" - > - <div - class="MuiTypography-root MuiTypography-h5Small css-1qwjj5w-MuiTypography-root" - > - Team - </div> - <div - class="MuiBox-root css-n6p80u" - > - <div - class="MuiGrid-root MuiGrid-container css-1p3emjt-MuiGrid-root" - > - <div - class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root" - > - <div - class="MuiPaper-root MuiPaper-elevation MuiPaper-elevation0 MuiCard-root css-wixki0-MuiPaper-root-MuiCard-root" - > - <a - class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways MuiButtonBase-root MuiCardActionArea-root css-mfu8qg-MuiTypography-root-MuiLink-root-MuiButtonBase-root-MuiCardActionArea-root" - href="/about/members/allan-cheboi" - tabindex="0" - > - <img - class="MuiCardMedia-root MuiCardMedia-media MuiCardMedia-img css-ys68yq-MuiCardMedia-root" - src="/images/image16.png" - /> - <div - class="MuiCardContent-root css-1bqtlyx-MuiCardContent-root" - > - <div - class="MuiTypography-root MuiTypography-body1SemiBold css-1nw8xqn-MuiTypography-root" - > - Allan Cheboi - </div> - <div - class="MuiTypography-root MuiTypography-body1 css-17e24dh-MuiTypography-root" - > - Senior investigations manager - </div> - </div> - <span - class="MuiCardActionArea-focusHighlight css-1v2exvi-MuiCardActionArea-focusHighlight" - /> - </a> - </div> - </div> - <div - class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root" - > - <div - class="MuiPaper-root MuiPaper-elevation MuiPaper-elevation0 MuiCard-root css-wixki0-MuiPaper-root-MuiCard-root" - > - <a - class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways MuiButtonBase-root MuiCardActionArea-root css-mfu8qg-MuiTypography-root-MuiLink-root-MuiButtonBase-root-MuiCardActionArea-root" - href="/about/members/amanda-strydom" - tabindex="0" - > - <img - class="MuiCardMedia-root MuiCardMedia-media MuiCardMedia-img css-ys68yq-MuiCardMedia-root" - src="/images/amanda.jpg" - /> - <div - class="MuiCardContent-root css-1bqtlyx-MuiCardContent-root" - > - <div - class="MuiTypography-root MuiTypography-body1SemiBold css-1nw8xqn-MuiTypography-root" - > - Amanda Strydom - </div> - <div - class="MuiTypography-root MuiTypography-body1 css-17e24dh-MuiTypography-root" - > - Senior programme manager - </div> - </div> - <span - class="MuiCardActionArea-focusHighlight css-1v2exvi-MuiCardActionArea-focusHighlight" - /> - </a> - </div> - </div> - <div - class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root" - > - <div - class="MuiPaper-root MuiPaper-elevation MuiPaper-elevation0 MuiCard-root css-wixki0-MuiPaper-root-MuiCard-root" - > - <a - class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways MuiButtonBase-root MuiCardActionArea-root css-mfu8qg-MuiTypography-root-MuiLink-root-MuiButtonBase-root-MuiCardActionArea-root" - href="/about/members/anita-igbine" - tabindex="0" - > - <img - class="MuiCardMedia-root MuiCardMedia-media MuiCardMedia-img css-ys68yq-MuiCardMedia-root" - src="/images/image1.jpg" - /> - <div - class="MuiCardContent-root css-1bqtlyx-MuiCardContent-root" - > - <div - class="MuiTypography-root MuiTypography-body1SemiBold css-1nw8xqn-MuiTypography-root" - > - Anita Igbine - </div> - <div - class="MuiTypography-root MuiTypography-body1 css-17e24dh-MuiTypography-root" - > - Investigative data analyst - </div> - </div> - <span - class="MuiCardActionArea-focusHighlight css-1v2exvi-MuiCardActionArea-focusHighlight" - /> - </a> - </div> - </div> - </div> - </div> - </div> -</div> -`; +exports[`<TeamMembers /> renders unchanged 1`] = `<div />`; diff --git a/apps/codeforafrica/src/components/TeamMembers/TeamMembers.test.js b/apps/codeforafrica/src/components/TeamMembers/TeamMembers.test.js index 018bcc6cf..fcff42135 100644 --- a/apps/codeforafrica/src/components/TeamMembers/TeamMembers.test.js +++ b/apps/codeforafrica/src/components/TeamMembers/TeamMembers.test.js @@ -3,7 +3,6 @@ import React from "react"; import TeamMembers from "./TeamMembers"; -import { team } from "@/codeforafrica/lib"; import theme from "@/codeforafrica/theme"; // eslint-disable-next-line testing-library/render-result-naming-convention @@ -11,7 +10,6 @@ const render = createRender({ theme }); const defaultProps = { title: "Team", - team: team.slice(0, 3), }; describe("<TeamMembers />", () => { diff --git a/apps/codeforafrica/src/lib/api.ghost/ghost.js b/apps/codeforafrica/src/lib/api.ghost/ghost.js deleted file mode 100644 index df8241647..000000000 --- a/apps/codeforafrica/src/lib/api.ghost/ghost.js +++ /dev/null @@ -1,9 +0,0 @@ -import GhostContentAPI from "@tryghost/content-api"; - -export default function initializeContentAPI() { - return new GhostContentAPI({ - url: process.env.GHOST_URL, - key: process.env.GHOST_API_KEY, - version: process.env.GHOST_API_VERSION || "v5.0", - }); -} diff --git a/apps/codeforafrica/src/lib/api.ghost/index.js b/apps/codeforafrica/src/lib/api.ghost/index.js deleted file mode 100644 index a5dc694a8..000000000 --- a/apps/codeforafrica/src/lib/api.ghost/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import { getAllPosts, getPost } from "@/codeforafrica/lib/api.ghost/posts"; -import equalsIgnoreCase from "@/codeforafrica/utils/equalsIgnoreCase"; - -export async function getAllOpportunities() { - const allPosts = await getAllPosts(); - return allPosts.filter((post) => - equalsIgnoreCase(post.primaryTag.name, "opportunities"), - ); -} - -export async function getAllOpportunitiesTags() { - const opportunities = await getAllOpportunities(); - const tags = opportunities.flatMap((post) => post.tags); - return ["All", ...new Set(tags)]; -} - -export async function getOpportnity(slug) { - return getPost(slug); -} - -export async function getAllStories() { - const allPosts = await getAllPosts(); - return allPosts.filter((post) => - equalsIgnoreCase(post.primaryTag.name, "stories"), - ); -} - -export async function getAllStoriesTags() { - const stories = await getAllStories(); - const tags = stories.flatMap((post) => post.tags); - return ["All", ...new Set(tags)]; -} - -export async function getStory(slug) { - return getPost(slug); -} - -export async function getRelatedStoriesByTags(tags, story = {}) { - const stories = await getAllStories(); - return stories.filter( - (s) => - s.id !== story.id && - s.tags.some((t) => tags.find((st) => equalsIgnoreCase(t, st))), - ); -} diff --git a/apps/codeforafrica/src/lib/api.ghost/posts.js b/apps/codeforafrica/src/lib/api.ghost/posts.js deleted file mode 100644 index 6f43cde8c..000000000 --- a/apps/codeforafrica/src/lib/api.ghost/posts.js +++ /dev/null @@ -1,147 +0,0 @@ -import { promises as fs } from "fs"; -import { join } from "path"; - -import camelcaseKeys from "camelcase-keys"; - -import initializeContentAPI from "@/codeforafrica/lib/api.ghost/ghost"; -import equalsIgnoreCase from "@/codeforafrica/utils/equalsIgnoreCase"; - -const cacheDir = join(process.cwd(), "public/data"); - -function transformPost(post) { - const { - customExcerpt, - excerpt: originalExcerpt, - featureImage, - featureImageAlt, - featureImageCaption, - metaDescription, - metaTitle, - primaryAuthor, - primaryTag, - publishedAt: publishedAtRaw, - slug, - tags: originalTags, - title, - twitterDescription, - twitterImage, - twitterTitle, - ogDescription, - ogImage, - ogTitle, - updatedAt, - ...other - } = camelcaseKeys(post, { deep: true }); - - const excerpt = customExcerpt || originalExcerpt || null; - const href = `/${primaryTag.slug}/${slug}`; - const publishedAt = new Date(publishedAtRaw).toLocaleDateString("en", { - year: "numeric", - month: "short", - day: "numeric", - }); - const tags = originalTags - .filter((t) => !equalsIgnoreCase(t.name, primaryTag.name)) - .map((tag) => tag.name); - - const seo = { - title: metaTitle || title, - description: metaDescription || excerpt, - openGraph: { - type: "article", - article: { - publishedTime: publishedAtRaw, - modifiedTime: updatedAt, - tags, - }, - }, - twitter: { - handle: primaryAuthor.twitter, - }, - }; - // seo will be merged with individual story page seo. This means "empty" - // values should be excluded. - const computedOgDescription = ogDescription || twitterDescription; - if (computedOgDescription) { - seo.openGraph.description = computedOgDescription; - } - const computedOgTitle = ogTitle || twitterTitle; - if (computedOgTitle) { - seo.openGraph.title = computedOgTitle; - } - const computedOgImage = ogImage || twitterImage || featureImage; - if (computedOgImage) { - seo.openGraph.images = [ - { - url: computedOgImage, - alt: - featureImageAlt || - featureImageCaption || - computedOgTitle || - seo.title, - }, - ]; - } - - return { - excerpt, - featureImage, - href, - primaryTag, - publishedAt, - slug, - tags, - seo, - title, - primaryAuthor, - ...other, - }; -} - -async function cachePosts(posts) { - const cacheFile = join(cacheDir, "posts.json"); - const data = { - date: new Date().toISOString(), - posts, - }; - await fs.writeFile(cacheFile, JSON.stringify(data)); -} - -async function getCachedPosts() { - try { - const cacheFile = join(cacheDir, "posts.json"); - const data = await fs.readFile(cacheFile, "utf8"); - return JSON.parse(data); - } catch (error) { - return null; - } -} - -export async function getAllPosts() { - // Check if we have a cached version of the posts - const cachedPosts = await getCachedPosts(); - if (cachedPosts) { - // check if the cache is older than 5 mins - // is 5 mins a good cache age? - // TODO: make this configurable - const cacheAge = new Date() - new Date(cachedPosts.date); - if (cacheAge < 300000) { - return cachedPosts.posts; - } - } - // If not, fetch from Ghost - const api = initializeContentAPI(); - const posts = await api.posts.browse({ - include: "authors,tags", - limit: "all", - }); - const allPosts = posts.map(transformPost); - // Cache the posts - await cachePosts(allPosts); - return allPosts; -} - -export async function getPost(slug) { - const allPost = await getAllPosts(); - return allPost.find((p) => p.slug === slug); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getBadges.js b/apps/codeforafrica/src/lib/api.netlify-cms/getBadges.js deleted file mode 100644 index 38db75923..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getBadges.js +++ /dev/null @@ -1,10 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const badgesDir = join(process.cwd(), "content/badges"); - -export default function getBadges(fields) { - const badges = getCollectionData(badgesDir, fields); - return badges; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getBody.js b/apps/codeforafrica/src/lib/api.netlify-cms/getBody.js deleted file mode 100644 index 711d77fb2..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getBody.js +++ /dev/null @@ -1,11 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -function getBody(page) { - const { content } = getCollectionBySlug("content/pages", page, [ - "content", - ]).items; - - return { content }; -} - -export default getBody; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getCollectionData.js b/apps/codeforafrica/src/lib/api.netlify-cms/getCollectionData.js deleted file mode 100644 index 554e6301a..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getCollectionData.js +++ /dev/null @@ -1,10 +0,0 @@ -import { getCollectionSlugs, getCollectionBySlug } from "./utils"; - -export default function getCollectionData(collectionDir, fields) { - const slugs = getCollectionSlugs(collectionDir); - const collections = slugs.map((slug) => { - const collection = getCollectionBySlug(collectionDir, slug, fields); - return fields?.length ? collection.items : collection.data; - }); - return collections; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getContactForm.js b/apps/codeforafrica/src/lib/api.netlify-cms/getContactForm.js deleted file mode 100644 index f30853e50..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getContactForm.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "contact-form"; - -function getContactForm() { - const contactForm = getCollectionBySlug("content/pages", "contact", [ - FIELD_NAME, - "slug", - ]).items[FIELD_NAME]; - const { "embed-code": embedCode, slug } = contactForm || {}; - - return { embedCode, slug }; -} - -export default getContactForm; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getDonors.js b/apps/codeforafrica/src/lib/api.netlify-cms/getDonors.js deleted file mode 100644 index e9573225c..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getDonors.js +++ /dev/null @@ -1,10 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const donorsDir = join(process.cwd(), "content/donors"); - -export default function getDonors(fields) { - const donors = getCollectionData(donorsDir, fields); - return donors; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getGetInTouch.js b/apps/codeforafrica/src/lib/api.netlify-cms/getGetInTouch.js deleted file mode 100644 index c9fb5a508..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getGetInTouch.js +++ /dev/null @@ -1,8 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "get-in-touch"; - -export default function getGetInTouch() { - return getCollectionBySlug("content/pages", "about", [FIELD_NAME, "slug"]) - .items[FIELD_NAME]; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getGuidingPrinciples.js b/apps/codeforafrica/src/lib/api.netlify-cms/getGuidingPrinciples.js deleted file mode 100644 index 3814964ed..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getGuidingPrinciples.js +++ /dev/null @@ -1,9 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const dir = join(process.cwd(), "content/guiding-principles"); - -export default function getGuidingPrinciples(fields) { - return getCollectionData(dir, fields); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getImpactList.js b/apps/codeforafrica/src/lib/api.netlify-cms/getImpactList.js deleted file mode 100644 index 71b547ebe..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getImpactList.js +++ /dev/null @@ -1,18 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const impactDir = join(process.cwd(), "content/impact"); - -function getImpactList() { - return getCollectionData(impactDir, [ - "id", - "slug", - "title", - "value", - "image", - "content", - ]); -} - -export default getImpactList; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getJoinUs.js b/apps/codeforafrica/src/lib/api.netlify-cms/getJoinUs.js deleted file mode 100644 index fcd77e8a1..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getJoinUs.js +++ /dev/null @@ -1,10 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "join-our-slack"; - -function getJoinUs() { - return getCollectionBySlug("content/pages", "contact", [FIELD_NAME, "slug"]) - .items[FIELD_NAME]; -} - -export default getJoinUs; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getNewsAndStories.js b/apps/codeforafrica/src/lib/api.netlify-cms/getNewsAndStories.js deleted file mode 100644 index 147f6f33a..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getNewsAndStories.js +++ /dev/null @@ -1,17 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "news-stories"; - -function getNewsAndStories(page) { - const { - "articles-count": count, - title, - slug, - } = getCollectionBySlug("content/pages", page, [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; - - return { count, title, slug }; -} - -export default getNewsAndStories; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOffices.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOffices.js deleted file mode 100644 index 3fabd374c..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOffices.js +++ /dev/null @@ -1,27 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const officesDir = join(process.cwd(), "content/offices"); - -export default function getOffices(fields) { - const offices = getCollectionData(officesDir, fields); - return offices.map((office) => { - const lat = office.location.latitude; - const lng = office.location.longitude; - return { - map: { - center: { - lat, - lng, - }, - position: { - lat, - lng, - }, - }, - content: office.content, - title: office.name, - }; - }); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurGuidingPrinciples.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurGuidingPrinciples.js deleted file mode 100644 index c5c6ad594..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurGuidingPrinciples.js +++ /dev/null @@ -1,30 +0,0 @@ -import { join } from "path"; - -import getGuidingPrinciples from "./getGuidingPrinciples"; -import { getCollectionBySlug } from "./utils"; - -const pagesDir = join(process.cwd(), "content/pages"); - -const FIELD_NAME = "guiding-principles"; - -function getOurGuidingPrinciples() { - const { - title, - "guiding-principle-list": principleIds, - slug, - } = getCollectionBySlug(pagesDir, "about", [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; - const allPrinciples = getGuidingPrinciples([ - "id", - "title", - "icon", - "content", - ]); - - const list = - principleIds?.map((id) => allPrinciples.find((p) => p.id === id)) ?? null; - return { title, list, slug }; -} - -export default getOurGuidingPrinciples; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurImpact.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurImpact.js deleted file mode 100644 index ca7c8aca4..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurImpact.js +++ /dev/null @@ -1,23 +0,0 @@ -import { join } from "path"; - -import getImpactList from "./getImpactList"; -import { getCollectionBySlug } from "./utils"; - -const pageDir = join(process.cwd(), "content/pages"); -const FIELD_NAME = "our-impact"; - -export default function geOurImpact(page = "index") { - const { - "our-impact": { - action = null, - "impact-list": impactIds, - title = null, - slug = null, - }, - } = getCollectionBySlug(pageDir, page, [FIELD_NAME, "slug"]).items; - const impact = getImpactList(); - // Need to maintain order of how impact were selected in ourImpact - const list = impactIds?.map((id) => impact.find((i) => i.id === id)) ?? null; - - return { action, list, title, slug }; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurMission.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurMission.js deleted file mode 100644 index 18507a1a8..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurMission.js +++ /dev/null @@ -1,18 +0,0 @@ -import { join } from "path"; - -import { getCollectionBySlug } from "./utils"; - -import marked from "@/codeforafrica/lib/marked"; - -const pagesDir = join(process.cwd(), "content/pages"); -const FIELD_NAME = "our-mission"; - -export default function getOurMission() { - const { "our-mission": ourMission } = getCollectionBySlug(pagesDir, "about", [ - FIELD_NAME, - "slug", - ]).items; - ourMission.description = marked(ourMission.description); - - return ourMission; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurOffices.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurOffices.js deleted file mode 100644 index e9572e71c..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurOffices.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "our-offices"; - -export default function getOurOffices(page = "contact") { - return getCollectionBySlug("content/pages", page, [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurOpportunities.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurOpportunities.js deleted file mode 100644 index 756ff319d..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurOpportunities.js +++ /dev/null @@ -1,12 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "opportunities"; - -export default function getOurOpportunities(page = "opportunities") { - const { slug } = getCollectionBySlug("content/pages", page, [ - FIELD_NAME, - "slug", - ]).items[FIELD_NAME]; - - return { slug }; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurPartners.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurPartners.js deleted file mode 100644 index 1fc920666..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurPartners.js +++ /dev/null @@ -1,37 +0,0 @@ -import { join } from "path"; - -import getPartners from "./getPartners"; -import { getCollectionBySlug } from "./utils"; - -import marked from "@/codeforafrica/lib/marked"; - -const pageDir = join(process.cwd(), "content/pages"); -const FIELD_NAME = "our-partners"; - -export default function geOurPartners(page = "index") { - const { - "our-partners": { - slug, - title: originalTitle, - "partners-list": partnersIds, - action = null, - }, - } = getCollectionBySlug(pageDir, page, [FIELD_NAME, "slug"]).items; - const title = marked.parseInline(originalTitle); - const allPartners = getPartners([ - "id", - "slug", - "name", - "content", - "href", - "logo", - "links", - ]); - // Need to maintain order of how partners were selected in ourPartners - const list = - page === "index" - ? partnersIds?.map((id) => allPartners.find((p) => p.id === id)) ?? null - : allPartners; - - return { slug, partners: { title, list, action } }; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurProjects.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurProjects.js deleted file mode 100644 index ccfc31d56..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurProjects.js +++ /dev/null @@ -1,12 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "projects"; - -export default function geOurProjects(page = "index") { - const { slug } = getCollectionBySlug("content/pages", page, [ - FIELD_NAME, - "slug", - ]).items[FIELD_NAME]; - - return { slug }; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurStories.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurStories.js deleted file mode 100644 index 7a14ae72f..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurStories.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "stories"; - -export default function getOurStories(page = "stories") { - return getCollectionBySlug("content/pages", page, [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getOurTeam.js b/apps/codeforafrica/src/lib/api.netlify-cms/getOurTeam.js deleted file mode 100644 index 950473562..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getOurTeam.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -export default function geOurPartners() { - const { - "our-team": { title, slug }, - } = getCollectionBySlug("content/pages", "about", ["our-team", "slug"]).items; - - return { title, slug }; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getPartners.js b/apps/codeforafrica/src/lib/api.netlify-cms/getPartners.js deleted file mode 100644 index fcebeb226..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getPartners.js +++ /dev/null @@ -1,13 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const partnersDir = join(process.cwd(), "content/partners"); - -export default function getPartners(fields) { - const partners = getCollectionData(partnersDir, fields); - return partners.map(({ slug = null, ...other }) => { - const href = slug ? `/about/partners/${slug}` : null; - return { ...other, slug, href }; - }); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getProjectTeam.js b/apps/codeforafrica/src/lib/api.netlify-cms/getProjectTeam.js deleted file mode 100644 index 7aba99fef..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getProjectTeam.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "team"; - -export default function getProjectTeam(page = "our-work-individual") { - return getCollectionBySlug("content/pages", page, [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getProjects.js b/apps/codeforafrica/src/lib/api.netlify-cms/getProjects.js deleted file mode 100644 index e6d72b3ad..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getProjects.js +++ /dev/null @@ -1,62 +0,0 @@ -import { join } from "path"; - -import getBadges from "./getBadges"; -import getDonors from "./getDonors"; -import getPartners from "./getPartners"; -import getTeam from "./getTeam"; -import { getCollectionSlugs, getCollectionBySlug } from "./utils"; - -import marked from "@/codeforafrica/lib/marked"; - -const projectsDir = join(process.cwd(), "content/projects"); - -export default function getProjects(fields) { - const slugs = getCollectionSlugs(projectsDir); - return slugs.map((_slug) => { - const collection = getCollectionBySlug(projectsDir, _slug, fields); - const project = collection.items; - if (project.badges?.length) { - const badges = getBadges(["id", "name", "content", "date"]); - project.badges = project.badges.map((id) => - badges.find((badge) => badge.id === id), - ); - } - if (project.partners?.length) { - const partners = getPartners([ - "id", - "slug", - "name", - "content", - "href", - "logo", - ]); - project.partners = { - title: "Partners", - list: project.partners.map((id) => - partners.find((partner) => partner.id === id), - ), - }; - } - if (project.donors?.length) { - const donors = getDonors(); - project.donors = { - title: "Donors", - list: project.donors.map((id) => - donors.find((donor) => donor.id === id), - ), - }; - } - - if (project.team?.length) { - const team = getTeam(); - project.team = { - title: "Team", - list: project.team.map((id) => team.find((m) => m.id === id)), - }; - } - - project.subtitle = marked(project.subtitle); - project.href = `/projects/${project.slug}`; - return project; - }); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getRelatedProjects.js b/apps/codeforafrica/src/lib/api.netlify-cms/getRelatedProjects.js deleted file mode 100644 index 9a6cffb57..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getRelatedProjects.js +++ /dev/null @@ -1,11 +0,0 @@ -import { getCollectionBySlug } from "./utils"; - -const FIELD_NAME = "related-projects"; - -function getRelatedProjects(page) { - return getCollectionBySlug("content/pages", page, [FIELD_NAME, "slug"]).items[ - FIELD_NAME - ]; -} - -export default getRelatedProjects; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/getTeam.js b/apps/codeforafrica/src/lib/api.netlify-cms/getTeam.js deleted file mode 100644 index 2f9f8672e..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/getTeam.js +++ /dev/null @@ -1,15 +0,0 @@ -import { join } from "path"; - -import getCollectionData from "./getCollectionData"; - -const teamDir = join(process.cwd(), "content/team"); - -export default function getTeam(fields) { - const teams = getCollectionData(teamDir, fields); - return teams - .filter((member) => !member.deactivated) - .map(({ slug = null, ...other }) => { - const href = slug ? `/about/members/${slug}` : null; - return { ...other, slug, href }; - }); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/index.js b/apps/codeforafrica/src/lib/api.netlify-cms/index.js deleted file mode 100644 index 0a8fc7a61..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import getBody from "./getBody"; -import getContactForm from "./getContactForm"; -import getGetInTouch from "./getGetInTouch"; -import getJoinUs from "./getJoinUs"; -import getNewsAndStories from "./getNewsAndStories"; -import getOffices from "./getOffices"; -import getOurGuidingPrinciples from "./getOurGuidingPrinciples"; -import getOurImpact from "./getOurImpact"; -import getOurMission from "./getOurMission"; -import getOurOffices from "./getOurOffices"; -import getOurOpportunities from "./getOurOpportunities"; -import getOurPartners from "./getOurPartners"; -import getOurProjects from "./getOurProjects"; -import getOurStories from "./getOurStories"; -import getOurTeam from "./getOurTeam"; -import getPartners from "./getPartners"; -import getCmsProjects from "./getProjects"; -import getProjectTeam from "./getProjectTeam"; -import getRelatedProjects from "./getRelatedProjects"; -import getTeam from "./getTeam"; -import getMeetOurTeam from "./sections/getMeetOurTeam"; -import getSeo from "./seo"; - -export { - getBody, - getContactForm, - getCmsProjects, - getGetInTouch, - getJoinUs, - getMeetOurTeam, - getNewsAndStories, - getOffices, - getOurGuidingPrinciples, - getOurImpact, - getOurMission, - getOurOffices, - getOurOpportunities, - getOurPartners, - getOurProjects, - getOurStories, - getSeo, - getRelatedProjects, - getOurTeam, - getPartners, - getProjectTeam, - getTeam, -}; diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/sections/getMeetOurTeam.js b/apps/codeforafrica/src/lib/api.netlify-cms/sections/getMeetOurTeam.js deleted file mode 100644 index 63ea47092..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/sections/getMeetOurTeam.js +++ /dev/null @@ -1,19 +0,0 @@ -import { join } from "path"; - -import { getCollectionBySlug } from "../utils"; - -import marked from "@/codeforafrica/lib/marked"; - -const indexPageDir = join(process.cwd(), "content/pages"); - -export default function getMeetOurTeam( - page = "index", - fields = ["meet-our-team", "slug"], -) { - const meetOurTeam = getCollectionBySlug(indexPageDir, page, fields).items[ - "meet-our-team" - ]; - meetOurTeam.logo = meetOurTeam.image?.src; - meetOurTeam.description = marked(meetOurTeam.description); - return meetOurTeam; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/sections/getSettings.js b/apps/codeforafrica/src/lib/api.netlify-cms/sections/getSettings.js deleted file mode 100644 index dc11d88b5..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/sections/getSettings.js +++ /dev/null @@ -1,9 +0,0 @@ -import { join } from "path"; - -import { getCollectionBySlug } from "../utils"; - -const settingsPageDir = join(process.cwd(), "content/settings"); - -export default function getSettings(section) { - return getCollectionBySlug(settingsPageDir, section).data; -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/seo.js b/apps/codeforafrica/src/lib/api.netlify-cms/seo.js deleted file mode 100644 index f5d91c099..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/seo.js +++ /dev/null @@ -1,29 +0,0 @@ -import { deepmerge } from "@mui/utils"; -import camelcaseKeys from "camelcase-keys"; - -import getSettings from "./sections/getSettings"; -import { getCollectionBySlug } from "./utils"; - -// 1. Since getCollectionBySlug creates a new object on every call, -// we don't need to clone when deep-merging. -// -// 2. If we have meta (at site or page seo level), it's title and description -// should override that level's title and description. - -function createSeo(pageSeo) { - const { - seo: { meta, ...siteSeo }, - site: { title, description }, - } = getSettings("general"); - - const seo = deepmerge(siteSeo, { title, description, ...meta, ...pageSeo }); - return camelcaseKeys(seo); -} - -export default function getSeo(page, pageSeo) { - const { - seo: { meta, ...seo }, - } = getCollectionBySlug("content/pages", page, ["seo"]).items; - - return createSeo(deepmerge({ ...seo, ...meta }, pageSeo)); -} diff --git a/apps/codeforafrica/src/lib/api.netlify-cms/utils.js b/apps/codeforafrica/src/lib/api.netlify-cms/utils.js deleted file mode 100644 index 490ba3cda..000000000 --- a/apps/codeforafrica/src/lib/api.netlify-cms/utils.js +++ /dev/null @@ -1,44 +0,0 @@ -import fs from "fs"; -import { join } from "path"; - -import matter from "gray-matter"; - -import marked from "@/codeforafrica/lib/marked"; - -export function getCollectionSlugs(collectionDir) { - return fs.readdirSync(collectionDir); -} - -export function getCollectionBySlug(collectionDir, slug, fields) { - const realSlug = slug.replace(/\.md$/, ""); - const fullPath = join(collectionDir, `${realSlug}.md`); - const fileContents = fs.readFileSync(fullPath, "utf8"); - const { data, content } = matter(fileContents); - - const markedContent = marked(content); - - data.slug = realSlug; - data.content = markedContent; - - const items = fields?.reduce((acc, curr) => { - if (curr === "content") { - acc.content = markedContent; - } else if (curr === "slug") { - acc.slug = realSlug; - } else { - // The slug field above works for folder-based collections e.g. impact - // but not for file-based collections e.g. get-in-touch in about. - // Since field names are guaranteed to be unique (in page) and are set - // in config, we can set the slug to be field name in file-based - // collections - let currData = data[curr] || null; - if (currData?.constructor === Object && fields.includes("slug")) { - currData = { slug: curr, ...currData }; - } - acc[curr] = currData; - } - return acc; - }, {}); - - return { items, data }; // return data which can be used as default incase of no fields -} diff --git a/apps/codeforafrica/src/lib/data/blockify/get-involved.js b/apps/codeforafrica/src/lib/data/blockify/get-involved.js new file mode 100644 index 000000000..46ea5c310 --- /dev/null +++ b/apps/codeforafrica/src/lib/data/blockify/get-involved.js @@ -0,0 +1,14 @@ +import formatImpacts from "@/codeforafrica/lib/data/utils/impacts"; + +function getInvolved(block) { + const { impacts, ...other } = block; + const ourImpacts = formatImpacts(impacts); + + return { + ...other, + impacts: ourImpacts, + slug: "get-involved", + }; +} + +export default getInvolved; diff --git a/apps/codeforafrica/src/lib/data/blockify/index.js b/apps/codeforafrica/src/lib/data/blockify/index.js index 9297d0f7f..696f0540e 100644 --- a/apps/codeforafrica/src/lib/data/blockify/index.js +++ b/apps/codeforafrica/src/lib/data/blockify/index.js @@ -1,9 +1,13 @@ +import getInvolved from "./get-involved"; import hero from "./hero"; +import meetOurTeam from "./meetOurTeam"; import ourImpact from "./our-impact"; const propsifyBlockBySlug = { hero, + "meet-our-team": meetOurTeam, "our-impact": ourImpact, + "get-involved": getInvolved, }; async function blockify(blocks) { diff --git a/apps/codeforafrica/src/lib/data/blockify/meetOurTeam.js b/apps/codeforafrica/src/lib/data/blockify/meetOurTeam.js new file mode 100644 index 000000000..515138ff5 --- /dev/null +++ b/apps/codeforafrica/src/lib/data/blockify/meetOurTeam.js @@ -0,0 +1,15 @@ +import { imageFromMedia } from "@/codeforafrica/lib/data/utils"; + +function meetOurTeam(block) { + const { image: media, actionButton, title, ...other } = block; + const image = imageFromMedia({ alt: title, ...media }); + + return { + ...other, + image, + slug: "meet-our-team", + title, + }; +} + +export default meetOurTeam; diff --git a/apps/codeforafrica/src/lib/data/blockify/our-impact.js b/apps/codeforafrica/src/lib/data/blockify/our-impact.js index c5e53b4e9..98a2f5133 100644 --- a/apps/codeforafrica/src/lib/data/blockify/our-impact.js +++ b/apps/codeforafrica/src/lib/data/blockify/our-impact.js @@ -1,16 +1,8 @@ -import { imageFromMedia } from "@/codeforafrica/lib/data/utils"; +import formatImpacts from "@/codeforafrica/lib/data/utils/impacts"; function ourImpact(block) { const { impacts, ...other } = block; - const ourImpacts = impacts.map((impact) => { - const { icon: media, title, ...rest } = impact; - const image = imageFromMedia({ alt: title, ...media }); - return { - ...rest, - image, - title, - }; - }); + const ourImpacts = formatImpacts(impacts); return { ...other, diff --git a/apps/codeforafrica/src/lib/data/common/index.js b/apps/codeforafrica/src/lib/data/common/index.js index 6925987d6..56331a583 100644 --- a/apps/codeforafrica/src/lib/data/common/index.js +++ b/apps/codeforafrica/src/lib/data/common/index.js @@ -1,4 +1,5 @@ import blockify from "@/codeforafrica/lib/data/blockify"; +import pagify from "@/codeforafrica/lib/data/pagify"; import getPageSeoFromMeta from "@/codeforafrica/lib/data/seo"; import { imageFromMedia } from "@/codeforafrica/lib/data/utils"; @@ -142,8 +143,9 @@ function getDefaultErrorPageProps(slug = "404") { } export async function getPageProps(api, context) { + const { params } = context; const slug = getPageSlug(context); - const { + let { docs: [page], } = await api.findPage(slug); if (!page) { @@ -152,11 +154,13 @@ export async function getPageProps(api, context) { } return null; } - + if (params?.slugs?.length > 2) { + page = await pagify(page, api, context); + } + const blocks = await blockify(page?.blocks); const settings = await api.findGlobal("settings"); const navbar = getNavBar(settings); const footer = getFooter(settings); - const blocks = await blockify(page.blocks); const seo = getPageSeoFromMeta(page, settings); return { diff --git a/apps/codeforafrica/src/lib/data/pagify/index.js b/apps/codeforafrica/src/lib/data/pagify/index.js new file mode 100644 index 000000000..62f8f69dd --- /dev/null +++ b/apps/codeforafrica/src/lib/data/pagify/index.js @@ -0,0 +1,13 @@ +import partners from "./partners"; + +const COLLECTION_BY_SLUG = { + partners, +}; + +async function pagify(parentPage, api, context) { + const { slug: collection } = parentPage; + const pageProps = COLLECTION_BY_SLUG[collection]; + return pageProps?.(api, context) ?? null; +} + +export default pagify; diff --git a/apps/codeforafrica/src/lib/data/pagify/partners.js b/apps/codeforafrica/src/lib/data/pagify/partners.js new file mode 100644 index 000000000..9e6797b75 --- /dev/null +++ b/apps/codeforafrica/src/lib/data/pagify/partners.js @@ -0,0 +1,30 @@ +import { imageFromMedia } from "@/codeforafrica/lib/data/utils"; + +async function partners(api, context) { + const { params, locale } = context; + const slug = params.slugs[2]; + const { docs } = await api.getCollection("partners", { + locale, + where: { + slug: { + equals: slug, + }, + }, + }); + if (!docs?.length) { + return null; + } + const [partner] = docs; + return { + blocks: [ + { + relatedProjects: [], // TODO(koechkevin) Related projects go here once projects implemented + ...partner, + logo: imageFromMedia(partner.logo), + blockType: "partner", + }, + ], + }; +} + +export default partners; diff --git a/apps/codeforafrica/src/lib/data/utils/impacts.js b/apps/codeforafrica/src/lib/data/utils/impacts.js new file mode 100644 index 000000000..08357c4e1 --- /dev/null +++ b/apps/codeforafrica/src/lib/data/utils/impacts.js @@ -0,0 +1,13 @@ +import { imageFromMedia } from "@/codeforafrica/lib/data/utils"; + +export default function formatImpacts(impacts) { + return impacts.map((impact) => { + const { icon: media, title, ...rest } = impact; + const image = imageFromMedia({ alt: title, ...media }); + return { + ...rest, + image, + title, + }; + }); +} diff --git a/apps/codeforafrica/src/lib/index.js b/apps/codeforafrica/src/lib/index.js deleted file mode 100644 index e61fdec30..000000000 --- a/apps/codeforafrica/src/lib/index.js +++ /dev/null @@ -1,757 +0,0 @@ -import fuse from "./api.fuse"; -import { - getBody, - getContactForm, - getCmsProjects, - getGetInTouch, - getJoinUs, - getMeetOurTeam, - getNewsAndStories, - getOffices, - getOurGuidingPrinciples, - getOurImpact, - getOurMission, - getOurOffices, - getOurOpportunities, - getOurPartners, - getOurProjects, - getOurStories, - getOurTeam, - getPartners, - getProjectTeam, - getRelatedProjects, - getTeam, - getSeo, -} from "./api.netlify-cms"; - -import { - getAllOpportunities, - getAllOpportunitiesTags, - getAllStories, - getAllStoriesTags, - getStory, - getRelatedStoriesByTags, -} from "@/codeforafrica/lib/api.ghost"; -import equalsIgnoreCase from "@/codeforafrica/utils/equalsIgnoreCase"; - -export const partners = getPartners([ - "id", - "slug", - "name", - "content", - "href", - "logo", - "links", -]); - -export const projects = getCmsProjects([ - "tag", - "name", - "slug", - "tagLine", - "icon", - "title", - "subtitle", - "content", - "thumbnail", - "href", - "externalHref", - "badges", - "partners", - "donors", - "team", - "links", -]); - -function getRandomInt(max) { - return Math.floor(Math.random() * max); -} - -function getRandomStartIndex(length, size) { - const max = length >= size ? length - size : length; - return getRandomInt(max); -} - -const meetOurTeam = getMeetOurTeam(); - -export const team = getTeam(); - -const DEFAULT_REVALIDATE = 3 * 60; // 3 minutes - -const ALL_TAG = "All"; - -function getProjectTags(options = { includeAll: true }) { - const tags = Array.from( - new Set(projects?.flatMap((a) => a.tag || [])), - ).sort(); - if (options?.includeAll) { - return [ALL_TAG, ...tags]; - } - return tags; -} - -function paginateResults(items, page, pageSize) { - // We need to initialize to null for serialization. - let count = null; - let results = []; - let pageNumber = null; - let pageSizeNumber = null; - if (items?.length) { - // Need to ensure page, pageSize are numbers and not strings - pageNumber = Number.parseInt(page, 10) || 1; - pageSizeNumber = Number.parseInt(pageSize, 10) || 6; - count = Math.ceil(items.length / pageSizeNumber); - results = items.slice( - (pageNumber - 1) * pageSizeNumber, - pageNumber * pageSizeNumber, - ); - } - return { - pagination: { - count, - page: pageNumber, - pageSize: pageSizeNumber, - }, - results, - }; -} - -function prioritiseFeaturedStory(stories) { - const index = stories.findIndex((s) => s.featured); - // If we have a featured story and it's not the first story, - if (index > 0) { - // we need to "push" the featured story to the top of list. - const featuredStory = stories[index]; - return [featuredStory, ...stories.filter((_, i) => i !== index)]; - } - return stories; -} - -export async function getStories(options) { - const { - tag: originalTag, - page = 1, - "page-size": pageSize = 10, - q, - } = options || {}; - const tag = originalTag || ALL_TAG; - - let stories = await getAllStories(); - if (equalsIgnoreCase(tag, ALL_TAG) && page === 1 && !q) { - stories = prioritiseFeaturedStory(stories); - } else { - if (!equalsIgnoreCase(tag, ALL_TAG)) { - stories = stories.filter((s) => - s.tags.some((t) => equalsIgnoreCase(t, tag)), - ); - } - if (q && stories.length) { - stories = fuse - .stories(stories) - .search(q) - .map((p) => p.item); - } - } - - return paginateResults(stories, page, pageSize); -} - -async function getProcessedNewsAndStories() { - const { title, count = 4, slug } = getNewsAndStories("index"); - const allStories = await getAllStories(); - const articles = prioritiseFeaturedStory(allStories).slice(0, count); - - return { title, articles, slug }; -} - -async function getHomePageStaticProps() { - const seo = getSeo("index"); - return { - props: { - seo, - sections: [ - { - ...getOurProjects(), - projects, - tags: getProjectTags({ includeAll: false }), - }, - { ...meetOurTeam }, - { - ...(await getProcessedNewsAndStories()), - }, - { - ...getOurPartners(), - }, - { - ...getOurImpact(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -export function getProjects(options) { - const { tag: originalTag, page, "page-size": pageSize, q } = options || {}; - const tag = originalTag || ALL_TAG; - - let found = projects.filter( - (p) => equalsIgnoreCase(tag, ALL_TAG) || equalsIgnoreCase(tag, p.tag), - ); - if (found.length && q) { - found = fuse - .projects(found) - .search(q) - .map((p) => p.item); - } - - return paginateResults(found, page, pageSize); -} - -export async function getOpportunities(options) { - const { tag: originalTag, page, "page-size": pageSize, q } = options || {}; - const tag = originalTag || ALL_TAG; - - let opportunities = await getAllOpportunities(); - if (!equalsIgnoreCase(tag, ALL_TAG)) { - opportunities = opportunities.filter((opportunity) => { - return opportunity.tags.some((t) => equalsIgnoreCase(t, tag)); - }); - } - if (opportunities.length && q) { - opportunities = fuse - .opportunities(opportunities) - .search(q) - .map((p) => p.item); - } - - return paginateResults(opportunities, page, pageSize); -} - -async function getOpportunitiesPageStaticProps() { - const allOpportunities = await getAllOpportunities(); - const tags = await getAllOpportunitiesTags(); - const seo = getSeo("opportunities"); - - return { - props: { - seo, - sections: [ - { - ...getOurOpportunities(), - opportunities: paginateResults(allOpportunities), - tags, - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function getOpportunityPageStaticProps(params) { - const actualSlug = params.slug.split("/")[2]; - const foundOpportunity = await getStory(actualSlug); - - if (foundOpportunity) { - const { seo: pageSeo, ...opportunity } = foundOpportunity; - const seo = getSeo("opportunities-individual", pageSeo); - - return { - props: { - seo, - opportunity, - }, - revalidate: DEFAULT_REVALIDATE, - }; - } - return { notFound: true }; -} - -function getImprintPageStaticProps() { - const seo = getSeo("imprint"); - - return { - props: { - seo, - ...getBody("imprint"), - sections: [], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -function getPrivacyPageStaticProps() { - const seo = getSeo("privacy-policy"); - - return { - props: { - seo, - ...getBody("privacy-policy"), - sections: [], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function getProjectPageStaticProps(params) { - const project = projects.find(({ href }) => - equalsIgnoreCase(href, params?.slug), - ); - - if (project) { - const { title, count = 3, slug } = getNewsAndStories("our-work-individual"); - const relatedStories = await getRelatedStoriesByTags([project.name]); - const relatedProjects = getRelatedProjects("our-work-individual"); - const seo = getSeo("our-work-individual", { - title: project.name, - description: - // subtitle could contain html content - project.subtitle.replace(/<[^>]*>/g, "").trim() || project.title, - }); - const startIndex = getRandomStartIndex(projects.length, 3); - - return { - props: { - seo, - project, - sections: [ - { - ...getProjectTeam(), - team: project?.team?.list || null, - }, - { - slug, - title, - articles: relatedStories.slice(0, count), - }, - { - ...relatedProjects, - projects: projects - .filter((p) => p.slug !== project.slug) - .slice(startIndex, startIndex + 3), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; - } - return { notFound: true }; -} - -function getProjectsPageStaticProps() { - const seo = getSeo("our-work"); - - return { - props: { - seo, - sections: [ - { - ...getOurProjects("our-work"), - tags: getProjectTags(), - projects: getProjects(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function getStoriesPageStaticProps() { - const articles = await getAllStories(); - const tags = await getAllStoriesTags(); - const seo = getSeo("stories"); - - return { - props: { - seo, - sections: [ - { - ...getOurStories(), - articles: paginateResults(articles), - tags, - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function getStoryPageStaticProps(slug) { - const actualSlug = slug.slug.split("/")[2]; - const story = await getStory(actualSlug); - const relatedArticles = await getRelatedStoriesByTags(story.tags, story); - - if (story) { - const { - title, - count = 3, - slug: articlesSlug, - } = getNewsAndStories("stories-individual"); - const { seo: pageSeo, ...article } = story; - const seo = getSeo("stories-individual", pageSeo); - - return { - props: { - seo, - article, - sections: [ - { - slug: articlesSlug, - title, - articles: relatedArticles?.slice(0, count) ?? null, - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; - } - return { notFound: true }; -} - -function getMembersFieldTags(options = { includeAll: true }) { - let countries = Array.from( - new Set(team?.flatMap((m) => m.country || [])), - ).sort(); - let teams = Array.from(new Set(team?.flatMap((m) => m.team || []))).sort(); - if (options?.includeAll) { - countries = [ALL_TAG, ...countries]; - teams = [ALL_TAG, ...teams]; - } - return [ - { field: "Country", tags: countries }, - { field: "Team", tags: teams }, - ]; -} - -export function getMembers(options) { - const { - field, - page, - "page-size": pageSize = 18, - q, - tag: originalTag, - } = options || {}; - const tag = originalTag || ALL_TAG; - - let found = team.filter( - (m) => - equalsIgnoreCase(tag, ALL_TAG) || - (field && equalsIgnoreCase(tag, m[field])), - ); - if (found.length && q) { - found = fuse - .members(found) - .search(q) - .map((p) => p.item); - } - - return paginateResults(found, page, pageSize); -} - -function getAboutImpactPageStaticProps() { - const seo = getSeo("about-impact"); - - return { - props: { - seo, - unit: "impact", - crumbs: [{ href: "/about", label: "About us" }, { label: "Impact" }], - sections: [ - { - ...getOurImpact("about"), - }, - { - ...getGetInTouch(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -function getAboutMemberPageStaticProps(params) { - const member = team.find(({ href }) => equalsIgnoreCase(href, params?.slug)); - - if (member) { - const relatedProjects = getRelatedProjects("about-members-individual"); - const seo = getSeo("about-members-individual", { - title: member.name, - description: member.title, - }); - - return { - props: { - seo, - member, - sections: [ - { - ...relatedProjects, - projects: projects.filter((p) => - p.team?.list?.find((m) => m.id === member.id), - ), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; - } - return { notFound: true }; -} - -function getAboutMembersPageStaticProps() { - const seo = getSeo("about-members"); - - return { - props: { - seo, - unit: "members", - crumbs: [{ href: "/about", label: "About us" }, { label: "Members" }], - sections: [ - { - ...getOurTeam(), - pathname: "/about/members", - tags: getMembersFieldTags(), - team: getMembers(), - }, - { - ...getGetInTouch(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -function getAboutPageStaticProps() { - const seo = getSeo("about"); - - return { - props: { - seo, - sections: [ - { - ...getOurMission(), - }, - { - ...getOurGuidingPrinciples(), - }, - - { - ...getOurTeam(), - tags: getMembersFieldTags(), - team: getMembers(), - }, - { - ...getOurPartners("about"), - }, - { - ...getOurImpact("about"), - }, - { - ...getGetInTouch(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -function getAboutPartnerPageStaticProps(params) { - const partner = partners.find(({ slug }) => - equalsIgnoreCase(`/about/partners/${slug}`, params?.slug), - ); - - if (partner) { - const relatedProjects = getRelatedProjects("about-partners-individual"); - const seo = getSeo("about-partners-individual", { - title: partner.name, - // TODO(kilemens): Add short description to each partner - }); - - return { - props: { - seo, - partner: { ...partner, image: partner.logo, title: "Partner" }, - sections: [ - { - ...relatedProjects, - projects: projects.filter((p) => - p.partners?.list?.find((l) => l.id === partner.id), - ), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; - } - return { notFound: true }; -} - -function getAboutPartnersPageStaticProps() { - const seo = getSeo("about-partners"); - - return { - props: { - seo, - unit: "partners", - crumbs: [{ href: "/about", label: "About us" }, { label: "Partners" }], - sections: [ - { - ...getOurPartners("about"), - }, - { - ...getGetInTouch(), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -function getContactPageStaticProps() { - const seo = getSeo("contact"); - - return { - props: { - seo, - sections: [ - { - ...getContactForm(), - }, - { - ...getJoinUs(), - }, - { - ...getOurOffices(), - addresses: getOffices(), - map: { - apiKey: process.env.GOOGLE_API_KEY ?? null, - zoom: 20, - zoomControl: false, - mapTypeControl: false, - scaleControl: false, - streetViewControl: false, - rotateControl: false, - fullscreenControl: false, - }, - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function getProcessedRecentStories(page) { - const allStories = await getAllStories(); - const { title, count = 3, slug } = getNewsAndStories(page); - const articles = allStories.slice(0, count); - return { title, articles, slug }; -} - -async function getErrorPageStaticProps() { - const seo = getSeo("error"); - - return { - props: { - seo, - sections: [ - { - ...(await getProcessedRecentStories("error")), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -async function get404PageStaticProps() { - const seo = getSeo("404"); - - return { - props: { - seo, - sections: [ - { - ...(await getProcessedRecentStories("404")), - }, - ], - }, - revalidate: DEFAULT_REVALIDATE, - }; -} - -export async function getPageStaticProps(params) { - switch (params?.slug) { - case "/": { - return getHomePageStaticProps(params); - } - case "/about": { - return getAboutPageStaticProps(params); - } - case "/about/members": { - return getAboutMembersPageStaticProps(params); - } - case "/about/partners": { - return getAboutPartnersPageStaticProps(params); - } - case "/about/impact": { - return getAboutImpactPageStaticProps(); - } - case "/contact": { - return getContactPageStaticProps(params); - } - case "/imprint": { - return getImprintPageStaticProps(params); - } - case "/opportunities": { - return getOpportunitiesPageStaticProps(params); - } - case "/privacy": { - return getPrivacyPageStaticProps(params); - } - case "/projects": { - return getProjectsPageStaticProps(params); - } - case "/stories": { - return getStoriesPageStaticProps(params); - } - case "/404": { - return get404PageStaticProps(); - } - case "/_error": { - return getErrorPageStaticProps(); - } - default: - if (params?.slug?.startsWith("/about/members/")) { - return getAboutMemberPageStaticProps(params); - } - if (params?.slug?.startsWith("/about/partners/")) { - return getAboutPartnerPageStaticProps(params); - } - if (params?.slug?.startsWith("/opportunities/")) { - return getOpportunityPageStaticProps(params); - } - if (params?.slug?.startsWith("/projects/")) { - return getProjectPageStaticProps(params); - } - if (params?.slug?.startsWith("/stories/")) { - return getStoryPageStaticProps(params); - } - return { notFound: true }; - } -} - -export async function getPageStaticPaths(primaryTag) { - const posts = - primaryTag === "stories" - ? await getAllStories() - : await getAllOpportunities(); - - // filter out items with slug to remove pagination - const actualPosts = posts.filter((post) => post.slug); - const path = actualPosts.map((post) => ({ - params: { slug: post.slug }, - })); - - return path; -} diff --git a/apps/codeforafrica/src/pages/[...slugs].page.js b/apps/codeforafrica/src/pages/[...slugs].page.js index 4c0ed596f..b47325524 100644 --- a/apps/codeforafrica/src/pages/[...slugs].page.js +++ b/apps/codeforafrica/src/pages/[...slugs].page.js @@ -1,27 +1,37 @@ import React from "react"; import { SWRConfig } from "swr"; +import ContactForm from "@/codeforafrica/components/ContactForm"; import CustomPageHeader from "@/codeforafrica/components/CustomPageHeader"; import FeaturedProjects from "@/codeforafrica/components/FeaturedProjects"; +import GetInTouch from "@/codeforafrica/components/GetInTouch"; import GetInvolved from "@/codeforafrica/components/GetInvolved"; +import GuidingPrinciplesCardList from "@/codeforafrica/components/GuidingPrinciplesCardList"; import Hero from "@/codeforafrica/components/Hero"; +import JoinOurSlack from "@/codeforafrica/components/JoinOurSlack"; import MeetOurTeam from "@/codeforafrica/components/MeetOurTeam"; import NewsAndStories from "@/codeforafrica/components/NewsAndStories"; import OurImpact from "@/codeforafrica/components/OurImpact"; import OurPartners from "@/codeforafrica/components/OurPartners"; import PageHeader from "@/codeforafrica/components/PageHeader"; +import Partner from "@/codeforafrica/components/Partner"; import { getPageServerSideProps } from "@/codeforafrica/lib/data"; const componentsBySlugs = { - hero: Hero, - "page-header": PageHeader, + "contact-form": ContactForm, "custom-page-header": CustomPageHeader, + "get-involved": GetInvolved, + "get-in-touch": GetInTouch, + hero: Hero, + "join-our-slack": JoinOurSlack, "meet-our-team": MeetOurTeam, "news-stories": NewsAndStories, - "get-involved": GetInvolved, + "our-guiding-principles": GuidingPrinciplesCardList, "our-impact": OurImpact, "our-partners": OurPartners, + "page-header": PageHeader, projects: FeaturedProjects, + partner: Partner, }; function Index({ blocks, fallback }) { diff --git a/apps/codeforafrica/src/pages/api/admin/config.js b/apps/codeforafrica/src/pages/api/admin/config.js deleted file mode 100644 index c6bcbd32e..000000000 --- a/apps/codeforafrica/src/pages/api/admin/config.js +++ /dev/null @@ -1,2029 +0,0 @@ -const APP_DIRECTORY = process.env.NEXT_PUBLIC_APP_DIRECTORY; - -const seoFields = { - label: "SEO", - name: "seo", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - required: false, - }, - { - label: "Description", - name: "description", - widget: "string", - required: false, - pattern: ["^.{1,150}$", "Must be up to 156 characters"], - }, - { - label: "Title Template", - name: "title-template", - widget: "string", - hint: "Uses title template from Settings | General if not provided", - required: false, - }, - { - label: "Meta data", - name: "meta", - widget: "object", - hint: "Search engines support", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - required: false, - hint: "Uses page title if not provided", - pattern: ["^.{1,70}$", "Must be up to 70 characters"], - }, - { - label: "Description", - name: "description", - widget: "text", - required: false, - hint: "Uses page description if not provided", - pattern: ["^.{1,150}$", "Must be up to 156 characters"], - }, - ], - }, - { - label: "Open Graph", - name: "open-graph", - hint: "Facebook, Slack, and other social media platforms", - widget: "object", - fields: [ - { - label: "Title", - hint: "Uses page title if not provided", - name: "title", - widget: "string", - required: false, - }, - { - label: "Description", - name: "description", - widget: "string", - hint: "Uses page description if not provided", - required: false, - }, - { - label: "Images", - name: "images", - widget: "list", - min: 1, - max: 1, - required: false, - fields: [ - { - name: "url", - label: "URL", - widget: "string", - }, - { - name: "width", - label: "Width", - widget: "string", - }, - { - name: "height", - label: "Height", - widget: "string", - }, - { - name: "alt", - label: "Alt Text", - widget: "string", - }, - ], - }, - ], - }, - { - label: "Twitter", - name: "twitter", - widget: "object", - fields: [ - { - label: "Handle", - name: "handle", - hint: "@username of content creator", - widget: "string", - required: false, - default: "@Code4Africa", - }, - ], - }, - ], -}; - -module.exports = { - backend: { - name: "github", - branch: "main", - repo: "CodeForAfrica/ui", - proxy_url: "http://localhost:8081/api/v1", // Set proxy to work on local repo - }, - media_folder: `${APP_DIRECTORY}public/images`, - public_folder: "/images", - local_backend: true, - collections: [ - { - name: "pages", - label: "Pages", - files: [ - { - label: "Index", - name: "index", - file: `${APP_DIRECTORY}content/pages/index.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "markdown", - }, - { - label: "Messages", - name: "messages", - widget: "list", - min: 3, - max: 3, - }, - { - label: "Subtitle", - name: "subtitle", - widget: "text", - }, - { - label: "Image", - name: "image", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Projects", - name: "projects", - widget: "object", - collapsed: true, - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - label: "Meet Our Team", - name: "meet-our-team", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Description", - name: "description", - widget: "markdown", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - { - label: "Image", - name: "image", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Our partners", - name: "our-partners", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "markdown", - }, - { - label: "Action", - name: "action", - widget: "object", - fields: [ - { - name: "content", - widget: "string", - }, - { - name: "href", - widget: "string", - }, - ], - }, - { - label: "Partners", - name: "partners-list", - widget: "relation", - collection: "partners", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - ], - }, - { - label: "News and stories", - name: "news-stories", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Number of stories", - name: "articles-count", - hint: "Including the featured story", - widget: "number", - value_type: "int", - min: 3, - }, - ], - }, - { - label: "Our Impact", - name: "our-impact", - widget: "object", - fields: [ - { - label: "Impact", - name: "impact-list", - widget: "relation", - collection: "impact", - search_fields: ["title"], - value_field: "id", - display_fields: ["title"], - multiple: true, - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "About", - name: "about", - file: `${APP_DIRECTORY}content/pages/about.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "text", - }, - { - label: "Background Image", - name: "image", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Our Mission", - name: "our-mission", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - name: "description", - widget: "markdown", - }, - ], - }, - { - label: "Guiding Principles", - name: "guiding-principles", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Guiding Principles", - name: "guiding-principle-list", - label_singular: "Guiding Principle", - widget: "relation", - collection: "guiding-principles", - search_fields: ["title"], - value_field: "id", - display_fields: ["title"], - multiple: true, - }, - ], - }, - { - label: "Our team", - name: "our-team", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - label: "Our partners", - name: "our-partners", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Partners", - name: "partners-list", - widget: "relation", - collection: "partners", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - ], - }, - { - label: "Our Impact", - name: "our-impact", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Impact", - name: "impact-list", - widget: "relation", - collection: "impact", - search_fields: ["title"], - value_field: "id", - display_fields: ["title"], - multiple: true, - }, - ], - }, - { - label: "Get in touch", - name: "get-in-touch", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - { - label: "Action", - name: "action", - widget: "object", - fields: [ - { - name: "content", - widget: "string", - }, - { - name: "href", - widget: "string", - }, - ], - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "About | Impact", - name: "about-impact", - file: `${APP_DIRECTORY}content/pages/about-impact.md`, - fields: [ - { - ...seoFields, - }, - ], - }, - { - label: "About | Members", - name: "about-members", - file: `${APP_DIRECTORY}content/pages/about-members.md`, - fields: [ - { - ...seoFields, - }, - ], - }, - { - label: "About | Members | Individual member", - name: "about-members-individual", - file: `${APP_DIRECTORY}content/pages/about-members-individual.md`, - fields: [ - { - label: "Related Projects", - name: "related-projects", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "About | Partners", - name: "about-partners", - file: `${APP_DIRECTORY}content/pages/about-partners.md`, - fields: [ - { - ...seoFields, - }, - ], - }, - { - label: "About | Partners | Individual partner", - name: "about-partners-individual", - file: `${APP_DIRECTORY}content/pages/about-partners-individual.md`, - fields: [ - { - label: "Related Projects", - name: "related-projects", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Contact Us", - name: "contact", - file: `${APP_DIRECTORY}content/pages/contact.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - ], - }, - { - label: "Contact form", - name: "contact-form", - widget: "object", - fields: [ - { - label: "Mailchimp code", - name: "embed-code", - widget: "code", - allow_language_selection: false, - default_language: "html", - output_code_only: true, - }, - ], - }, - { - label: "Join our Slack", - name: "join-our-slack", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - { - label: "Action", - name: "action", - widget: "object", - fields: [ - { - label: "Label", - name: "label", - widget: "string", - }, - { - label: "Link", - name: "href", - widget: "string", - }, - ], - }, - ], - }, - { - label: "Our offices", - name: "our-offices", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Our Work", - name: "our-work", - file: `${APP_DIRECTORY}content/pages/our-work.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - ], - }, - { - label: "Projects", - name: "projects", - widget: "object", - collapsed: true, - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Our Work | Individual work", - name: "our-work-individual", - file: `${APP_DIRECTORY}content/pages/our-work-individual.md`, - fields: [ - { - label: "Team", - name: "team", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - label: "Related Stories", - name: "news-stories", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Number of stories", - name: "articles-count", - widget: "number", - value_type: "int", - min: 3, - }, - ], - }, - { - label: "Related Projects", - name: "related-projects", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Opportunities", - name: "opportunities", - file: `${APP_DIRECTORY}content/pages/opportunities.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - ], - }, - { - label: "Opportunities", - name: "opportunities", - widget: "object", - collapsed: true, - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Opportunities | Individual opportunity", - name: "opportunities-individual", - file: `${APP_DIRECTORY}content/pages/opportunities-individual.md`, - fields: [ - { - ...seoFields, - }, - ], - }, - { - label: "Stories", - name: "stories", - file: `${APP_DIRECTORY}content/pages/stories.md`, - fields: [ - { - label: "Stories", - name: "stories", - widget: "object", - collapsed: true, - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Stories | Individual story", - name: "stories-individual", - file: `${APP_DIRECTORY}content/pages/stories-individual.md`, - fields: [ - { - label: "Related Stories", - name: "news-stories", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Number of stories", - name: "articles-count", - widget: "number", - value_type: "int", - min: 3, - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Imprint", - name: "imprint", - file: `${APP_DIRECTORY}content/pages/imprint.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - ], - }, - { - label: "Body", - name: "body", - widget: "markdown", - }, - { - ...seoFields, - }, - ], - }, - { - label: "Privacy Policy", - name: "privacy-policy", - file: `${APP_DIRECTORY}content/pages/privacy-policy.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "string", - }, - ], - }, - { - label: "Body", - name: "body", - widget: "markdown", - }, - { - ...seoFields, - }, - ], - }, - { - label: "404", - name: "404", - file: `${APP_DIRECTORY}content/pages/404.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "markdown", - }, - ], - }, - { - label: "News and stories", - name: "news-stories", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Number of stories", - name: "articles-count", - widget: "number", - value_type: "int", - min: 3, - }, - ], - }, - { - ...seoFields, - }, - ], - }, - { - label: "Error", - name: "error", - file: `${APP_DIRECTORY}content/pages/error.md`, - fields: [ - { - label: "Hero", - name: "hero", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "markdown", - }, - ], - }, - { - label: "News and stories", - name: "news-stories", - widget: "object", - collapsed: true, - summary: "{{fields.title}}", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Number of stories", - name: "articles-count", - widget: "number", - value_type: "int", - min: 3, - }, - ], - }, - { - ...seoFields, - }, - ], - }, - ], - }, - { - label: "Data | Badges", - name: "badges", - label_singular: "Badge", - folder: `${APP_DIRECTORY}content/badges`, - create: true, - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Date", - name: "date", - widget: "datetime", - format: "MMMM Do YYYY", - }, - ], - }, - { - name: "donors", - label: "Data | Donors", - label_singular: "Donor", - folder: `${APP_DIRECTORY}content/donors`, - create: true, - identifier_field: "name", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Logo", - name: "logo", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - required: false, - }, - ], - }, - ], - }, - { - label: "Data | Guiding Principles", - name: "guiding-principles", - label_singular: "Guiding Principle", - folder: `${APP_DIRECTORY}content/guiding-principles`, - create: true, - identifier_field: "title", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Icon", - name: "icon", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Data | Impact", - name: "impact", - label_singular: "Impact", - folder: `${APP_DIRECTORY}content/impact`, - create: true, - identifier_field: "title", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Value", - name: "value", - widget: "string", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Image", - name: "image", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Data | Offices", - name: "offices-addresses", - label_singular: "Office", - folder: `${APP_DIRECTORY}content/offices`, - create: true, - identifier_field: "name", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Address", - name: "body", - widget: "markdown", - }, - { - label: "Location", - name: "location", - widget: "object", - fields: [ - { - label: "Latitude", - name: "latitude", - widget: "number", - value_type: "float", - }, - { - label: "Longitude", - name: "longitude", - widget: "number", - value_type: "float", - }, - ], - }, - ], - }, - { - name: "partners", - label: "Data | Partners", - folder: `${APP_DIRECTORY}content/partners`, - create: true, - identifier_field: "name", - label_singular: "Partner", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Href", - name: "href", - widget: "string", - required: false, - }, - { - label: "Logo", - name: "logo", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - ], - }, - - { - label: "Links", - name: "links", - widget: "object", - fields: [ - { - label: "Facebook", - name: "facebook", - widget: "string", - required: false, - }, - { - label: "Twitter", - name: "twitter", - widget: "string", - required: false, - }, - { - label: "LinkedIn", - name: "linkedIn", - widget: "string", - required: false, - }, - { - label: "Instagram", - name: "instagram", - widget: "string", - required: false, - }, - { - label: "Github", - name: "github", - widget: "string", - required: false, - }, - { - label: "Slack", - name: "slack", - widget: "string", - required: false, - }, - ], - }, - ], - }, - { - label: "Data | Projects", - name: "projects", - folder: `${APP_DIRECTORY}content/projects`, - create: true, - label_singular: "Project", - identifier_field: "name", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Tag Line", - name: "tagLine", - widget: "string", - }, - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Subtitle", - name: "subtitle", - widget: "markdown", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Tag", - name: "tag", - widget: "string", - }, - { - label: "Icon", - name: "icon", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - ], - }, - { - label: "Thumbnail", - name: "thumbnail", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - ], - }, - { - label: "External URL", - name: "externalHref", - widget: "string", - }, - { - label: "Badges", - name: "badges", - widget: "relation", - label_singular: "Badge", - collection: "badges", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - { - name: "partners", - label: "Partners", - widget: "relation", - label_singular: "Partner", - collection: "partners", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - { - name: "donors", - label: "Donors", - widget: "relation", - label_singular: "Donor", - collection: "donors", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - { - name: "team", - label: "Team", - widget: "relation", - label_singular: "Team Member", - collection: "team", - search_fields: ["name"], - value_field: "id", - display_fields: ["name"], - multiple: true, - }, - { - name: "links", - label: "Links", - label_singular: "Link", - widget: "list", - summary: "{{content}} - {{href}}", - fields: [ - { - label: "slug", - name: "slug", - widget: "string", - }, - { - label: "Content", - name: "content", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - { - label: "Icon", - name: "icon", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - ], - }, - { - label: "Data | Team", - name: "team", - label_singular: "Team Member", - folder: `${APP_DIRECTORY}content/team`, - create: true, - identifier_field: "name", - fields: [ - { - label: "Id", - name: "id", - widget: "uuid", - }, - { - label: "Name", - name: "name", - widget: "string", - }, - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Description", - name: "body", - widget: "markdown", - }, - { - label: "Thumbnail", - name: "thumbnail", - widget: "object", - fields: [ - { - label: "Source", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - { - name: "links", - label: "Links", - widget: "object", - label_singular: "Link", - collapsed: true, - fields: [ - { - label: "Twitter", - name: "twitter", - widget: "string", - required: false, - }, - { - label: "Github", - name: "github", - widget: "string", - required: false, - }, - { - label: "LinkedIn", - name: "linkedin", - widget: "string", - required: false, - }, - { - label: "Facebook", - name: "facebook", - widget: "string", - required: false, - }, - { - label: "Instagram", - name: "instagram", - widget: "string", - required: false, - }, - { - label: "Slack", - name: "slack", - widget: "string", - required: false, - }, - ], - }, - { - label: "Country", - name: "country", - widget: "string", - }, - { - label: "Team", - name: "team", - widget: "string", - }, - { - label: "Deactivated", - name: "deactivated", - widget: "boolean", - default: false, - }, - ], - }, - { - name: "settings", - label: "Settings", - files: [ - { - name: "general", - label: "General", - file: `${APP_DIRECTORY}content/settings/general.md`, - fields: [ - { - label: "Site", - name: "site", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - hint: "The name of the site", - }, - { - label: "Description", - name: "description", - widget: "text", - required: false, - hint: "Helps with search results and when shared in social media platforms", - }, - ], - }, - { - label: "SEO", - name: "seo", - widget: "object", - fields: [ - { - label: "Title Template", - name: "title-template", - widget: "string", - hint: '"pre/suffix that should be included with every page. It replaces %s with your title string e.g. "%s | CfA" template will render "About | CfA" in about page with title "About"', - }, - { - label: "Meta data", - name: "meta", - widget: "object", - hint: "Search engines support", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - required: false, - hint: "Uses page title if not provided", - pattern: ["^.{1,70}$", "Must be up to 70 characters"], - }, - { - label: "Description", - name: "description", - widget: "text", - required: false, - hint: "Uses page description if not provided", - pattern: ["^.{1,150}$", "Must be up to 156 characters"], - }, - ], - }, - { - label: "Open Graph", - name: "open-graph", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - required: false, - hint: "Uses page title if not provided", - }, - { - label: "Description", - name: "description", - widget: "string", - required: false, - hint: "Uses page description if not provided", - }, - { - label: "Images", - name: "images", - widget: "list", - max: 1, - min: 1, - required: false, - fields: [ - { - name: "url", - label: "URL", - widget: "string", - }, - { - name: "width", - label: "Width", - widget: "string", - }, - { - name: "height", - label: "Height", - widget: "string", - }, - { - name: "alt", - label: "Alt Text", - widget: "string", - }, - ], - }, - ], - }, - { - label: "Twitter", - name: "twitter", - widget: "object", - fields: [ - { - label: "@Site", - name: "site", - widget: "string", - required: false, - default: "@Code4Africa", - }, - { - label: "Card Type", - name: "cardType", - widget: "hidden", - default: "summary_large_image", - }, - ], - }, - ], - }, - ], - }, - { - label: "Header", - name: "header", - file: `${APP_DIRECTORY}content/settings/header.md`, - fields: [ - { - label: "Logo", - name: "logo", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - { - label: "Main Navigation", - name: "main-navigation", - label_singular: "Navigation item", - widget: "list", - fields: [ - { - label: "Label", - name: "content", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - ], - }, - { - label: "Social Media Link", - name: "social-media-link", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - { - label: "Mobile Icon", - name: "mobile-icon", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - { - label: "Desktop Icon", - name: "desktop-icon", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - ], - }, - { - label: "Footer", - name: "footer", - file: `${APP_DIRECTORY}content/settings/footer.md`, - fields: [ - { - label: "Logo", - name: "logo", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - { - label: "Description", - name: "description", - widget: "markdown", - }, - { - label: "Stay in touch", - name: "stay-in-touch", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Links", - name: "links", - label_singular: "Link", - widget: "list", - fields: [ - { - label: "Label", - name: "label", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - { - label: "Icon", - name: "icon", - widget: "object", - fields: [ - { - label: "Src", - name: "src", - widget: "image", - }, - { - label: "Height", - name: "height", - widget: "string", - required: false, - }, - { - label: "Width", - name: "width", - widget: "string", - required: false, - }, - ], - }, - ], - }, - ], - }, - { - label: "Main Navigation", - name: "main-navigation", - label_singular: "Navigation item", - widget: "list", - fields: [ - { - label: "Label", - name: "content", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - ], - }, - { - label: "Secondary Navigation", - name: "secondary-navigation", - label_singular: "Navigation item", - widget: "list", - fields: [ - { - label: "Label", - name: "content", - widget: "string", - }, - { - label: "Href", - name: "href", - widget: "string", - }, - ], - }, - { - label: "Newsletter subscription", - name: "newsletter-subscription", - widget: "object", - fields: [ - { - label: "Title", - name: "title", - widget: "string", - }, - { - label: "Mailchimp code", - name: "embed-code", - widget: "code", - allow_language_selection: false, - default_language: "html", - output_code_only: true, - }, - ], - }, - ], - }, - ], - }, - ], -}; diff --git a/apps/codeforafrica/src/pages/api/admin/config.yml.page.js b/apps/codeforafrica/src/pages/api/admin/config.yml.page.js deleted file mode 100644 index 8ddba0207..000000000 --- a/apps/codeforafrica/src/pages/api/admin/config.yml.page.js +++ /dev/null @@ -1,29 +0,0 @@ -import yaml from "js-yaml"; - -import config from "./config"; - -import site from "@/codeforafrica/utils/site"; - -export default function handler(req, res) { - if (req.method === "GET") { - if (process.env.NODE_ENV === "production") { - // Set production configurations - config.backend.name = "github"; - config.backend.repo = process.env.GITHUB_BACKEND_REPO; - config.backend.base_url = site.url.replace(/\/+$/, ""); - config.backend.auth_endpoint = process.env.GITHUB_AUTH_ENDPOINT; - config.publish_mode = "editorial_workflow"; - // Remove dev configurations - config.local_backend = undefined; - config.backend.proxy_url = undefined; - } - config.logo_url = site.logoUrl; - const configFile = yaml.dump(config); - - res.setHeader("Content-Type", "text/yaml"); - res.setHeader("Content-Disposition", "attachment; filename=config.yml"); - res.send(configFile); - } - - return res.status(405).end(); -} diff --git a/apps/codeforafrica/src/pages/api/members/index.page.js b/apps/codeforafrica/src/pages/api/members/index.page.js deleted file mode 100644 index b24614719..000000000 --- a/apps/codeforafrica/src/pages/api/members/index.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getMembers } from "@/codeforafrica/lib"; - -const QUERY_PARAM_NAMES = ["field", "page", "page-size", "q", "tag"]; - -export default function handler(req, res) { - if (req.method === "GET") { - const { query: originalQuery } = req; - const query = Object.keys(originalQuery).reduce((acc, key) => { - const paramName = key.toLocaleLowerCase(); - if (QUERY_PARAM_NAMES.includes(key)) { - acc[paramName] = originalQuery[key]; - } - return acc; - }, {}); - const projects = getMembers(query); - return res.status(200).json(projects); - } - - return res.status(405).end(); -} diff --git a/apps/codeforafrica/src/pages/api/oauth/auth.js b/apps/codeforafrica/src/pages/api/oauth/auth.js deleted file mode 100644 index 84d325f5c..000000000 --- a/apps/codeforafrica/src/pages/api/oauth/auth.js +++ /dev/null @@ -1,37 +0,0 @@ -import { nanoid } from "nanoid"; -import { AuthorizationCode } from "simple-oauth2"; - -import site from "@/codeforafrica/utils/site"; - -const config = { - cms: { - oauth: { - redirectUrl: `${site.environmentUrl.replace( - /\/+$/, - "", - )}/api/oauth/callback`, - scope: "user, repo", - }, - }, -}; - -const client = new AuthorizationCode({ - client: { - id: process.env.OAUTH_CLIENT_ID, - secret: process.env.OAUTH_CLIENT_SECRET, - }, - auth: { - tokenHost: "https://github.com", - authorizePath: "/login/oauth/authorize", - }, -}); - -// Authorization uri definition -const authorizationUri = client.authorizeURL({ - redirect_uri: config.cms.oauth.redirectUrl, - scope: config.cms.oauth.scope, - state: nanoid(), -}); - -// Initial page redirecting to Github -export default authorizationUri; diff --git a/apps/codeforafrica/src/pages/api/oauth/auth.page.js b/apps/codeforafrica/src/pages/api/oauth/auth.page.js deleted file mode 100644 index 103b8b60c..000000000 --- a/apps/codeforafrica/src/pages/api/oauth/auth.page.js +++ /dev/null @@ -1,7 +0,0 @@ -import authorizationUri from "./auth"; - -// Initial page redirecting to Github -export default async (req, res) => { - res.writeHead(302, { Location: authorizationUri }); - res.end(); -}; diff --git a/apps/codeforafrica/src/pages/api/oauth/callback.js b/apps/codeforafrica/src/pages/api/oauth/callback.js deleted file mode 100644 index ddd71ba55..000000000 --- a/apps/codeforafrica/src/pages/api/oauth/callback.js +++ /dev/null @@ -1,14 +0,0 @@ -import { AuthorizationCode } from "simple-oauth2"; - -const client = new AuthorizationCode({ - client: { - id: process.env.OAUTH_CLIENT_ID, - secret: process.env.OAUTH_CLIENT_SECRET, - }, - auth: { - tokenHost: "https://github.com", - tokenPath: "/login/oauth/access_token", - }, -}); - -export default client; diff --git a/apps/codeforafrica/src/pages/api/oauth/callback.page.js b/apps/codeforafrica/src/pages/api/oauth/callback.page.js deleted file mode 100644 index 18aecebcd..000000000 --- a/apps/codeforafrica/src/pages/api/oauth/callback.page.js +++ /dev/null @@ -1,41 +0,0 @@ -import client from "./callback"; - -const oauthProvider = "github"; - -export default async (req, res) => { - const { code, state } = req.query; - const tokenParams = { - code, - state, - }; - - let status = "error"; - let content; - try { - const accessToken = await client.getToken(tokenParams); - status = "success"; - content = { - token: accessToken.token.access_token, - provider: oauthProvider, - }; - } catch (error) { - content = JSON.stringify(error); - } - - const script = ` - <script> - (function() { - function recieveMessage(e) { - window.opener.postMessage( - 'authorization:${oauthProvider}:${status}:${JSON.stringify( - content, - )}', - e.origin - ); - } - window.addEventListener("message", recieveMessage, false) - window.opener.postMessage("authorizing:${oauthProvider}", "*") - })() - </script>`; - return res.end(script); -}; diff --git a/apps/codeforafrica/src/pages/api/opportunities/index.page.js b/apps/codeforafrica/src/pages/api/opportunities/index.page.js deleted file mode 100644 index 2f29f9448..000000000 --- a/apps/codeforafrica/src/pages/api/opportunities/index.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getOpportunities } from "@/codeforafrica/lib"; - -const QUERY_PARAM_NAMES = ["tag", "page", "q"]; - -export default async function handler(req, res) { - if (req.method === "GET") { - const { query: originalQuery } = req; - const query = Object.keys(originalQuery).reduce((acc, key) => { - const paramName = key.toLocaleLowerCase(); - if (QUERY_PARAM_NAMES.includes(key)) { - acc[paramName] = originalQuery[key]; - } - return acc; - }, {}); - const projects = await getOpportunities(query); - return res.status(200).json(projects); - } - - return res.status(405).end(); -} diff --git a/apps/codeforafrica/src/pages/api/projects/index.page.js b/apps/codeforafrica/src/pages/api/projects/index.page.js deleted file mode 100644 index 952bdc3ba..000000000 --- a/apps/codeforafrica/src/pages/api/projects/index.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getProjects } from "@/codeforafrica/lib"; - -const QUERY_PARAM_NAMES = ["tag", "page", "q"]; - -export default function handler(req, res) { - if (req.method === "GET") { - const { query: originalQuery } = req; - const query = Object.keys(originalQuery).reduce((acc, key) => { - const paramName = key.toLocaleLowerCase(); - if (QUERY_PARAM_NAMES.includes(key)) { - acc[paramName] = originalQuery[key]; - } - return acc; - }, {}); - const projects = getProjects(query); - return res.status(200).json(projects); - } - - return res.status(405).end(); -} diff --git a/apps/codeforafrica/src/pages/api/stories/index.page.js b/apps/codeforafrica/src/pages/api/stories/index.page.js deleted file mode 100644 index 99bd73cc9..000000000 --- a/apps/codeforafrica/src/pages/api/stories/index.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getStories } from "@/codeforafrica/lib"; - -const QUERY_PARAM_NAMES = ["field", "page", "page-size", "q", "tag"]; - -export default async function handler(req, res) { - if (req.method === "GET") { - const { query: originalQuery } = req; - const query = Object.keys(originalQuery).reduce((acc, key) => { - const paramName = key.toLocaleLowerCase(); - if (QUERY_PARAM_NAMES.includes(key)) { - acc[paramName] = originalQuery[key]; - } - return acc; - }, {}); - const stories = await getStories(query); - return res.status(200).json(stories); - } - - return res.status(405).end(); -} diff --git a/apps/codeforafrica/src/payload/blocks/ContactForm.js b/apps/codeforafrica/src/payload/blocks/ContactForm.js new file mode 100644 index 000000000..cd1e1bd4c --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/ContactForm.js @@ -0,0 +1,18 @@ +const ContactForm = { + slug: "contact-form", + imageURL: "/images/cms/blocks/contact_form.jpg", + imageAltText: "Contact Form", + fields: [ + { + name: "embedCode", + type: "code", + label: "Embed Code", + required: true, + admin: { + language: "html", + }, + }, + ], +}; + +export default ContactForm; diff --git a/apps/codeforafrica/src/payload/blocks/GetInTouch.js b/apps/codeforafrica/src/payload/blocks/GetInTouch.js new file mode 100644 index 000000000..b50433007 --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/GetInTouch.js @@ -0,0 +1,24 @@ +import linkGroup from "../fields/links/linkGroup"; + +const GetInTouch = { + slug: "get-in-touch", + imageURL: "/images/cms/blocks/get_in_touch.jpg", + imageAltText: "Display Get In Touch Call to Action", + fields: [ + { + name: "title", + label: "Title", + required: true, + type: "text", + }, + { + name: "subtitle", + label: "Subtitle", + required: true, + type: "text", + }, + linkGroup({ overrides: { name: "action", label: "Action" } }), + ], +}; + +export default GetInTouch; diff --git a/apps/codeforafrica/src/payload/blocks/GetInvolved.js b/apps/codeforafrica/src/payload/blocks/GetInvolved.js new file mode 100644 index 000000000..28fb24395 --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/GetInvolved.js @@ -0,0 +1,17 @@ +import impacts from "../fields/impacts"; +import linkGroup from "../fields/links/linkGroup"; + +const GetInvolved = { + slug: "get-involved", + imageURL: "/images/cms/blocks/get_involved.jpg", + imageAltText: "Get Involved", + fields: [ + impacts({ + minRows: 3, + maxRows: 3, + }), + linkGroup({ overrides: { name: "action", label: "Action" } }), + ], +}; + +export default GetInvolved; diff --git a/apps/codeforafrica/src/payload/blocks/GuidingPrinciples.js b/apps/codeforafrica/src/payload/blocks/GuidingPrinciples.js new file mode 100644 index 000000000..1ef993ab1 --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/GuidingPrinciples.js @@ -0,0 +1,30 @@ +const GuidingPrinciples = { + slug: "our-guiding-principles", + imageURL: "/images/cms/blocks/guiding_principles.png", + imageAltText: "Guiding Principles", + fields: [ + { + name: "title", + label: { + en: "Title", + }, + type: "text", + localized: true, + required: true, + }, + { + name: "list", + label: { + en: "Guiding Principles", + }, + type: "relationship", + relationTo: "guiding-principles", + hasMany: true, + admin: { + isSortable: true, + }, + }, + ], +}; + +export default GuidingPrinciples; diff --git a/apps/codeforafrica/src/payload/blocks/JoinOurSlack.js b/apps/codeforafrica/src/payload/blocks/JoinOurSlack.js new file mode 100644 index 000000000..7bddbad80 --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/JoinOurSlack.js @@ -0,0 +1,24 @@ +import linkGroup from "../fields/links/linkGroup"; + +const JoinOurSlack = { + slug: "join-our-slack", + imageURL: "/images/cms/blocks/join_our_slack.jpg", + imageAltText: "Display link to join our Slack community", + fields: [ + { + name: "title", + label: "Title", + required: true, + type: "text", + }, + { + name: "subtitle", + label: "Subtitle", + required: true, + type: "text", + }, + linkGroup({ overrides: { name: "action", label: "Action" } }), + ], +}; + +export default JoinOurSlack; diff --git a/apps/codeforafrica/src/payload/blocks/MeetOurTeam.js b/apps/codeforafrica/src/payload/blocks/MeetOurTeam.js new file mode 100644 index 000000000..40af98c82 --- /dev/null +++ b/apps/codeforafrica/src/payload/blocks/MeetOurTeam.js @@ -0,0 +1,30 @@ +import image from "../fields/image"; +import linkGroup from "../fields/links/linkGroup"; +import richText from "../fields/richText"; + +const MeetOurTeam = { + slug: "meet-our-team", + imageURL: "/images/cms/blocks/meet_our_team.jpg", + imageAltText: "Display Team Call to Action", + fields: [ + { + name: "title", + label: "Title", + required: true, + type: "text", + }, + richText({ + name: "description", + label: "Description", + required: true, + }), + linkGroup({ overrides: { name: "action", label: "Action" } }), + image({ + overrides: { + required: true, + }, + }), + ], +}; + +export default MeetOurTeam; diff --git a/apps/codeforafrica/src/payload/blocks/OurPartners.js b/apps/codeforafrica/src/payload/blocks/OurPartners.js index 890dcc41f..8554afd10 100644 --- a/apps/codeforafrica/src/payload/blocks/OurPartners.js +++ b/apps/codeforafrica/src/payload/blocks/OurPartners.js @@ -1,5 +1,7 @@ const Partners = { slug: "our-partners", + imageURL: "/images/cms/blocks/partners.png", + imageAltText: "Our Partners", labels: { singular: { en: "Partners", diff --git a/apps/codeforafrica/src/payload/collections/GuidingPrinciples.js b/apps/codeforafrica/src/payload/collections/GuidingPrinciples.js new file mode 100644 index 000000000..a40661051 --- /dev/null +++ b/apps/codeforafrica/src/payload/collections/GuidingPrinciples.js @@ -0,0 +1,34 @@ +import image from "../fields/image"; +import richText from "../fields/richText"; + +const GuidingPrinciples = { + slug: "guiding-principles", + admin: { + useAsTitle: "title", + }, + fields: [ + { + name: "title", + label: { + en: "Title", + }, + type: "text", + required: true, + }, + image({ + overrides: { + name: "icon", + required: true, + }, + }), + richText({ + name: "description", + label: { + en: "Description", + }, + required: true, + }), + ], +}; + +export default GuidingPrinciples; diff --git a/apps/codeforafrica/src/payload/collections/Media.js b/apps/codeforafrica/src/payload/collections/Media.js index 018f3002d..9b4cf0ae1 100644 --- a/apps/codeforafrica/src/payload/collections/Media.js +++ b/apps/codeforafrica/src/payload/collections/Media.js @@ -18,6 +18,9 @@ const Media = { required: true, }, ], + hooks: { + afterRead: [({ doc }) => ({ ...doc, src: doc.url })], + }, }; export default Media; diff --git a/apps/codeforafrica/src/payload/collections/Pages.js b/apps/codeforafrica/src/payload/collections/Pages.js index 19a8445e2..5fc050320 100644 --- a/apps/codeforafrica/src/payload/collections/Pages.js +++ b/apps/codeforafrica/src/payload/collections/Pages.js @@ -1,6 +1,12 @@ +import ContactForm from "../blocks/ContactForm"; import CustomPageHeader from "../blocks/CustomPageHeader"; import Error from "../blocks/Error"; +import GetInTouch from "../blocks/GetInTouch"; +import GetInvolved from "../blocks/GetInvolved"; +import GuidingPrinciples from "../blocks/GuidingPrinciples"; import Hero from "../blocks/Hero"; +import JoinOurSlack from "../blocks/JoinOurSlack"; +import MeetOurTeam from "../blocks/MeetOurTeam"; import OurImpact from "../blocks/OurImpact"; import OurPartners from "../blocks/OurPartners"; import PageHeader from "../blocks/PageHeader"; @@ -39,11 +45,17 @@ const Pages = { // it's functiaonally equivalent with PageHeader so we keep it next to // PageHeader blocks: [ + ContactForm, Error, + GetInTouch, + GetInvolved, + GuidingPrinciples, Hero, - OurImpact, + JoinOurSlack, + MeetOurTeam, PageHeader, CustomPageHeader, + OurImpact, OurPartners, ], admin: { diff --git a/apps/codeforafrica/src/payload/fields/links/linkGroup.js b/apps/codeforafrica/src/payload/fields/links/linkGroup.js new file mode 100644 index 000000000..3931937bf --- /dev/null +++ b/apps/codeforafrica/src/payload/fields/links/linkGroup.js @@ -0,0 +1,24 @@ +import { deepmerge } from "@mui/utils"; + +import link from "./link"; + +/** + * group field consisting of a link field. + */ +function linkGroup({ linkConfig, overrides = {} } = {}) { + const generatedLinkGroup = { + name: "link", + label: { + en: "Link", + fr: "Lien", + pt: "Link", + }, + type: "group", + required: true, + fields: [link(linkConfig)], + }; + + return deepmerge(generatedLinkGroup, overrides); +} + +export default linkGroup;