impress-2020/pages/donate.tsx

106 lines
3.4 KiB
TypeScript
Raw Normal View History

import DonationsPage from "../src/app/DonationsPage";
// @ts-ignore: doesn't understand module.exports
import connectToDb from "../src/server/db";
type Props = { campaigns: Campaign[] };
type Campaign = {
id: string;
name: string;
donationFeatures: DonationFeature[];
};
type DonationFeature = {
id: string;
donorName: string | null;
outfit: { id: string; name: string; updatedAt: string } | null;
};
export default function DonationsPageWrapper({ campaigns }: Props) {
return <DonationsPage campaigns={campaigns} />;
}
/**
* getStaticProps loads the donation info and organizes it into a convenient
* structure for the page props.
*
* This happens at build time! This data basically hasn't changed since 2017,
* so we'd rather have the performance benefits and reliability of just
* building it up-front than making this an SSR thing. (It also makes it harder
* for someone to add a surprise prank message years later when we're not
* paying attention, by editing the outfit titleit would still probably make
* it onto the site eventually, but not until the next build, which should be
* discouraging. But nobody's ever tried to prank this page before, so, shrug!)
*
* I also just went with a direct DB query, to avoid putting any of this in our
* GraphQL schema when it's really super-duper *just* for this page and *just*
* going to be requested in super-duper *one* way.
*/
export async function getStaticProps() {
const db = await connectToDb();
const [rows]: [QueryRow[]] = await db.query({
sql: `
SELECT
donation_features.id,
donations.donor_name,
campaigns.id, campaigns.name,
outfits.id, outfits.name, outfits.updated_at
FROM donation_features
INNER JOIN donations ON donations.id = donation_features.donation_id
INNER JOIN campaigns ON campaigns.id = donations.campaign_id
LEFT JOIN outfits ON outfits.id = donation_features.outfit_id
ORDER BY campaigns.created_at DESC, donations.created_at ASC;
`,
nestTables: true,
});
// Reorganize the query rows into campaign objects with lists of donation
// features.
const campaigns: Campaign[] = [];
for (const row of rows) {
// Find the campaign for this feature in our campaigns list, or add it if
// it's not present yet.
let campaign = campaigns.find((c) => c.id === String(row.campaigns.id));
if (campaign == null) {
campaign = {
id: String(row.campaigns.id),
name: row.campaigns.name,
donationFeatures: [],
};
campaigns.push(campaign);
}
// Reformat the outfit and donation feature into safer and more
// serializable forms.
const outfit =
row.outfits.id != null
? {
id: String(row.outfits.id),
name: row.outfits.name,
updatedAt: row.outfits.updated_at.toISOString(),
}
: null;
const donationFeature: DonationFeature = {
id: String(row.donation_features.id),
donorName: row.donations.donor_name,
outfit,
};
// Add this donation feature to the campaign.
campaign.donationFeatures.push(donationFeature);
}
return {
props: { campaigns },
};
}
type QueryRow = {
donation_features: { id: number };
donations: { donor_name: string | null };
campaigns: { id: string; name: string };
outfits:
| { id: number; name: string; updated_at: Date }
| { id: null; name: null; updated_at: null };
};