Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

@/climatemappedafrica Static Build #987

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions apps/climatemappedafrica/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# dependencies
node_modules
.pnp
.pnp.js
.pnpm-debug.log

# typescript
dist/

# testing
coverage

# next.js
.next/
out/

# payload
build/

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Vercel
.vercel
.now

# turbo
.turbo
test-results/
playwright-report/
3 changes: 1 addition & 2 deletions apps/climatemappedafrica/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.3",
"private": true,
"author": "Code for Africa <[email protected]>",
"description": "Climate Mapped Africa ",
"description": "ClimateMapped Africa",
"keywords": [
"climatemappedafrica",
"next",
Expand Down Expand Up @@ -65,7 +65,6 @@
"next": "catalog:",
"next-images": "catalog:",
"next-seo": "catalog:",
"nodemailer-sendgrid": "catalog:",
"papaparse": "catalog:",
"payload": "catalog:",
"plaiceholder": "catalog:",
Expand Down
47 changes: 32 additions & 15 deletions apps/climatemappedafrica/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@ import path from "path";
import { spawn } from "child_process";
import express from "express";
import next from "next";
import nodemailerSendgrid from "nodemailer-sendgrid";
import payload from "payload";
import { Payload } from "payload/dist/payload";
import { loadEnvConfig } from "@next/env";
import type { Payload } from "payload/dist/payload";

// TODO(kilemensi): Figure out why alias "@/climatemappedafrica" doesn't work here
import { getClient } from "./src/lib/payload/payload-client";

const projectDir = process.cwd();
loadEnvConfig(projectDir);

const dev = process.env.NODE_ENV !== "production";
const hostname = process.env.NEXT_HOSTNAME || "localhost";
const port = parseInt(process.env.PORT || "3000", 10);
const sendGridAPIKey = process.env.SENDGRID_API_KEY;
// TODO(kilemensi): Migrate to SMTP email setup instead of SendGrid specific
const smtpAuthPass = process.env.SMTP_PASS || process.env.SENDGRID_API_KEY;
const smtpFromName =
process.env.SMTP_FROM_NAME ||
process.env.SENDGRID_FROM_NAME ||
"ClimateMapped Africa CMS";
const smtpFromAddress =
process.env.SMTP_FROM_ADDRESS ||
process.env.SENDGRID_FROM_EMAIL ||
"[email protected]";
const smtpPort = Number(process.env.SMTP_PORT || 587);

if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
process.on("SIGTERM", () => process.exit(0));
Expand All @@ -25,24 +36,30 @@ const app = express();
const start = async (): Promise<void> => {
let localPayload: Payload;
try {
localPayload = await payload.init({
...(sendGridAPIKey
localPayload = await getClient({
...(smtpAuthPass
? {
email: {
transportOptions: nodemailerSendgrid({
apiKey: sendGridAPIKey,
}),
fromName:
process.env.SENDGRID_FROM_NAME || "ClimateMapped Africa CMS",
fromAddress:
process.env.SENDGRID_FROM_EMAIL || "[email protected]",
transportOptions: {
auth: {
user: process.env.SMTP_USER || "apikey",
apiKey: smtpAuthPass,
},
host: process.env.SMTP_HOST || "smtp.sendgrid.net",
port: smtpPort,
secure: smtpPort === 465, // true for port 465, false (the default) for 587 and others
},
fromName: smtpFromName,
fromAddress: smtpFromAddress,
},
}
: undefined),
secret: process.env.PAYLOAD_SECRET,
express: app,
local: false,
onInit: (initPayload) => {
initPayload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
initPayload.logger.info(
`Payload Admin URL: ${initPayload.getAdminURL()}`,
);
},
});
} catch (e: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Icon({ item, handleIconClick, currentItemIndex, index }) {
>
<Image
src={index === currentItemIndex ? secondaryIcon : primaryIcon}
layout="fill"
fill
/>
</Box>
<Typography
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RichText } from "@commons-ui/payload";
import { Grid, Typography, IconButton, Avatar } from "@mui/material";
import { useTour } from "@reactour/tour";
import Image from "next/image";
import Image from "next/legacy/image";
import PropTypes from "prop-types";
import React from "react";

Expand Down
37 changes: 35 additions & 2 deletions apps/climatemappedafrica/src/lib/data/common/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { blockify } from "@/climatemappedafrica/lib/data/blockify";
import { fetchProfile } from "@/climatemappedafrica/lib/hurumap";

// TODO(kilemensi): Use HURUmap APIs (or CMS) to pick geographies we'd like to
// build pages for at build time (It can't be all geographies
// as that will take forever)
const GEOGRAPHIES = ["af", "ke", "tz"];
kilemensi marked this conversation as resolved.
Show resolved Hide resolved

export function imageFromMedia(alt, url) {
return { alt, src: url };
}
Expand Down Expand Up @@ -57,9 +62,37 @@ async function getNavBar(siteSettings, variant, { slug }, hurumapProfile) {
};
}

export async function getPagePaths(api) {
const hurumapSettings = await api.findGlobal("settings-hurumap");
const { docs: pages } = await api.getCollection("pages");
const {
page: { value: explorePage },
} = hurumapSettings;
const paths = pages.flatMap(({ slug }) => {
// TODO(kilemensi): Handle parent > child page relation e.g. /insights/news
kilemensi marked this conversation as resolved.
Show resolved Hide resolved
if (slug !== explorePage?.slug) {
return {
params: {
slugs: [slug === "index" ? "" : slug],
},
};
}
// HURUmap profile page
return GEOGRAPHIES.map((code) => ({
params: {
slugs: [explorePage.slug, code],
},
}));
});
return {
paths,
fallback: true,
};
}

export async function getPageProps(api, context) {
// For now, ClimatemappedAfrica only supports single paths i.e. /, /about, etc.,
// so params.slug[0] is good enough
// For now, ClimateMappedAfrica only supports single paths i.e. /, /about, etc.,
// so params.slugs[0] is good enough
const slugs = context.params?.slugs || undefined;
const [slug] = slugs || ["index"];
const { draftMode = false } = context;
Expand Down
4 changes: 2 additions & 2 deletions apps/climatemappedafrica/src/lib/data/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/* eslint-disable import/prefer-default-export */
export { getPageStaticPaths, getPageStaticProps } from "./local";

export { getPageServerSideProps } from "./local";
export default undefined;
16 changes: 13 additions & 3 deletions apps/climatemappedafrica/src/lib/data/local/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { payload } from "@/climatemappedafrica/lib";
import { getPageProps } from "@/climatemappedafrica/lib/data/common";
import {
getPagePaths,
getPageProps,
} from "@/climatemappedafrica/lib/data/common";
import payload from "@/climatemappedafrica/lib/payload";

export const api = payload;

export async function getPageServerSideProps(context) {
export async function getPageStaticPaths() {
return getPagePaths(api);
}

export async function getPageStaticProps(context) {
const props = await getPageProps(api, context);

// TODO(kilemensi): We need to differentiate 404 from server errors (5xx)
// https://nextjs.org/docs/14/pages/building-your-application/data-fetching/incremental-static-regeneration#error-handling-and-revalidation
if (!props) {
return { notFound: true };
}
return {
props,
revalidate: 120, // in seconds
};
}
3 changes: 2 additions & 1 deletion apps/climatemappedafrica/src/lib/data/utils/get-members.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ function getQueryFromParams(params) {
};
}

export async function getMembers(api, params) {
// TODO(kilemensi): Confirm if params is undefined when using static build
export async function getMembers(api, params = {}) {
const { page: queryPage = 1 } = params;
const options = {
limit: 18,
Expand Down
8 changes: 7 additions & 1 deletion apps/climatemappedafrica/src/lib/payload/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import payload from "payload";
import { getClient } from "./payload-client";

async function findPage(slug, options) {
const payload = await getClient();
return payload.find({
...options,
collection: "pages",
Expand All @@ -14,6 +15,7 @@ async function findPage(slug, options) {
}

async function getCollection(collection, options) {
const payload = await getClient();
return payload.find({
limit: 0,
...options,
Expand All @@ -22,13 +24,15 @@ async function getCollection(collection, options) {
}

async function findGlobal(slug, options) {
const payload = await getClient();
return payload.findGlobal({
...options,
slug,
});
}

async function createCollection(collection, data, options) {
const payload = await getClient();
return payload.create({
collection,
data,
Expand All @@ -37,13 +41,15 @@ async function createCollection(collection, data, options) {
}

async function deleteCollection(collection, options) {
const payload = await getClient();
return payload.delete({
...options,
collection,
});
}

async function updateCollection(collection, id, data, options) {
const payload = await getClient();
const args = {
...options,
collection,
Expand Down
39 changes: 39 additions & 0 deletions apps/climatemappedafrica/src/lib/payload/payload-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// TODO(kilemensi): Need to update eslint to handle globalThis but we should
// do that when upgrading Next.js
/* eslint no-undef:0 */
import { loadEnvConfig } from "@next/env";
import payload from "payload";

const projectDir = process.cwd();
loadEnvConfig(projectDir);

let cached = globalThis.payload;

if (!cached) {
globalThis.payload = { client: null, promise: null };
cached = globalThis.payload;
}

export async function getClient(options) {
if (cached.client) {
return cached.client;
}

if (!cached.promise) {
cached.promise = payload.init({
// https://payloadcms.com/docs/local-api/overview#nextjs-conflict-with-local-api
local: !options?.express,
secret: process.env.PAYLOAD_SECRET,
...options,
});
}
try {
cached.client = await cached.promise;
} catch (e) {
cached.promise = null;
throw e;
}
return cached.client;
}

export default undefined;
17 changes: 12 additions & 5 deletions apps/climatemappedafrica/src/pages/[[...slugs]].js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import Tutorial from "@/climatemappedafrica/components/HURUmap/Tutorial";
import Navigation from "@/climatemappedafrica/components/Navigation";
import PageHero from "@/climatemappedafrica/components/PageHero";
import Summary from "@/climatemappedafrica/components/Summary";
import { getPageServerSideProps } from "@/climatemappedafrica/lib/data";
import {
getPageStaticPaths,
getPageStaticProps,
} from "@/climatemappedafrica/lib/data";

const componentsBySlugs = {
"data-indicators": DataIndicators,
Expand All @@ -27,7 +30,7 @@ const componentsBySlugs = {
team: AboutTeam,
};

function Index({ blocks, menus, footer: footerProps, seo = {}, fallback }) {
function Page({ blocks = [], menus, footer: footerProps, seo = {}, fallback }) {
const {
query: { showTutorial },
} = useRouter();
Expand Down Expand Up @@ -92,8 +95,12 @@ function Index({ blocks, menus, footer: footerProps, seo = {}, fallback }) {
);
}

export async function getServerSideProps(context) {
return getPageServerSideProps(context);
export async function getStaticPaths() {
return getPageStaticPaths();
}

export default Index;
export async function getStaticProps(context) {
return getPageStaticProps(context);
}

export default Page;
12 changes: 8 additions & 4 deletions apps/climatemappedafrica/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
"NEXT_PUBLIC_VERCEL_ENV",
"NEXT_PUBLIC_VERCEL_URL",
"NODE_TLS_REJECT_UNAUTHORIZED",
"PAYLOAD_SECRET",
"PROJECT_ROOT",
"S3_UPLOAD_KEY",
"S3_UPLOAD_SECRET",
"S3_UPLOAD_BUCKET",
"S3_UPLOAD_REGION"
"S3_ACCESS_KEY_ID",
"S3_SECRET_ACCESS_KEY",
"S3_BUCKET",
"S3_REGION",
"SENDGRID_API_KEY",
"SENDGRID_FROM_EMAIL",
"SENDGRID_FROM_NAME"
]
}
}
Expand Down
Loading
Loading