diff --git a/components/MiniNewsCard.module.css b/components/MiniNewsCard.module.css new file mode 100644 index 00000000..879bb08b --- /dev/null +++ b/components/MiniNewsCard.module.css @@ -0,0 +1,57 @@ +.card { + padding: calc(30rem / 16) calc(40rem / 16); + max-width: calc(524rem / 16); + background-color: var(--primary-background); + border-radius: calc(20rem / 16); + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + margin-bottom: 1rem; +} + +.fit.card { + max-width: unset; + padding: unset; + border-radius: unset; +} + +.date { + font-size: calc(18rem / 16); + margin: 0; +} + +.author { + color: var(--secondary-heading); + font-style: normal; +} + +.content { + overflow: hidden; +} + +.content > *:first-child { + margin-top: 0; +} + +@media only screen and (max-width: calc(768rem / 16)) { + .card { + padding: 0; + max-width: unset; + background-color: transparent; + border-radius: 0; + } + + .date { + font-weight: 600; + } + + .content > *:first-child { + margin-top: 1rem; + } + + .content ul, + .content ol { + padding: 0 1rem; + } +} diff --git a/components/MiniNewsCard.tsx b/components/MiniNewsCard.tsx new file mode 100644 index 00000000..9425dc75 --- /dev/null +++ b/components/MiniNewsCard.tsx @@ -0,0 +1,42 @@ +import React, { ReactNode } from "react"; + +import { Link } from "./Link"; + +import styles from "./MiniNewsCard.module.css"; + +interface MiniNewsCardProps { + date: Date; + author: string; + children: ReactNode; + permalink: string; + fit?: boolean; +} + +export const MiniNewsCard: React.FC = ({ + date, + author, + children, + permalink, + fit = false, +}) => { + const classes = fit ? [styles.card, styles.fit] : [styles.card]; + + return ( +
+

+ +

+
{author}
+
{children}
+ + Learn more + +
+ ); +}; diff --git a/components/NewsCard.module.css b/components/NewsCard.module.css index fd83dd67..735ab630 100644 --- a/components/NewsCard.module.css +++ b/components/NewsCard.module.css @@ -3,6 +3,7 @@ max-width: calc(524rem / 16); background-color: var(--primary-background); border-radius: calc(20rem / 16); + margin-bottom: 1rem; } .fit.card { @@ -30,6 +31,7 @@ padding: 0; max-width: unset; background-color: transparent; + border-radius: 0; } .date { diff --git a/lib/news.ts b/lib/news.ts index 6abd9547..40d98c9f 100644 --- a/lib/news.ts +++ b/lib/news.ts @@ -15,6 +15,7 @@ export const NEWS_PATH = path.join("content", "news"); export interface Metadata { author: string; date: string; + permalink: string; } export interface News { @@ -40,6 +41,15 @@ export async function getNewsTermsByYear(year: string): Promise { .sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b)); } +export async function getNewsDateByTerm( + year: string, + term: Term +): Promise { + return (await getNewsByTerm(year, term)).map( + (news) => news.split("-").slice(0, 3).join("-") // retrieves date from filename + ); +} + export async function getNewsByTerm( year: string, term: Term @@ -63,6 +73,7 @@ export async function getNewsBySlug( "utf-8" ); const { content, data: metadata } = matter(raw); + const slugDate = slug.split("-").slice(0, 3).join("-"); return { content: await serialize(content), @@ -71,6 +82,7 @@ export async function getNewsBySlug( date: getLocalDateFromEST( parse(metadata.date, DATE_FORMAT, new Date()) ).toString(), + permalink: `/news/${year}/${term}/${slugDate}`, } as Metadata, }; } diff --git a/pages/index.tsx b/pages/index.tsx index e6ced0e6..a683b5f5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,7 +8,7 @@ import { EmailSignup } from "@/components/EmailSignup"; import { EventDescriptionCard } from "@/components/EventDescriptionCard"; import { Image } from "@/components/Image"; import { Link } from "@/components/Link"; -import { NewsCard } from "@/components/NewsCard"; +import { MiniNewsCard } from "@/components/MiniNewsCard"; import { GetShapesConfig } from "@/components/ShapesBackground"; import { SocialLinks } from "@/components/SocialLinks"; import { Title } from "@/components/Title"; @@ -20,7 +20,7 @@ import styles from "./index.module.css"; interface Props { moreEvents: boolean; // true if there are more than 2 upcoming events events: Event[]; // array of 0 - 2 events - news: News; + news: News[]; // array of 3 news items } export default function Home(props: Props) { @@ -86,14 +86,17 @@ export default function Home(props: Props) { See past news here


- { - - - - } + {props.news.length > 0 + ? props.news.map((news) => ( + + + + )) + : null} @@ -116,7 +119,7 @@ export const getStaticProps: GetStaticProps = async () => { props: { moreEvents: upcomingEvents.length > 2, events: upcomingEvents.slice(0, 2), - news: recentNews[0], + news: recentNews.slice(0, 3), }, }; }; diff --git a/pages/news/[year]/[term]/[date].module.css b/pages/news/[year]/[term]/[date].module.css new file mode 100644 index 00000000..e752a424 --- /dev/null +++ b/pages/news/[year]/[term]/[date].module.css @@ -0,0 +1,8 @@ +.page { + padding-bottom: calc(30rem / 16); +} + +.page > h1 { + padding-bottom: calc(16rem / 16); + border-bottom: calc(1rem / 16) solid var(--primary-heading); +} diff --git a/pages/news/[year]/[term]/[date].tsx b/pages/news/[year]/[term]/[date].tsx new file mode 100644 index 00000000..443d2e0e --- /dev/null +++ b/pages/news/[year]/[term]/[date].tsx @@ -0,0 +1,111 @@ +import { ParsedUrlQuery } from "querystring"; + +import { GetStaticPaths, GetStaticProps } from "next"; +import { MDXRemote } from "next-mdx-remote"; +import React from "react"; + +import { NewsCard } from "@/components/NewsCard"; +import { + ShapesConfig, + defaultGetShapesConfig, + GetShapesConfig, +} from "@/components/ShapesBackground"; +import { Title } from "@/components/Title"; +import { + getNewsBySlug, + getNewsByTerm, + getNewsTermsByYear, + getNewsDateByTerm, + getNewsYears, + News, +} from "@/lib/news"; +import { Term } from "@/utils"; + +import styles from "./[date].module.css"; + +interface Props { + year: string; + term: Term; + news: News[]; +} + +export default function DateNews({ news }: Props) { + const date = new Date(news[0].metadata.date).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); + return ( +
+ {["News", `${date}`]} +

+ News: {date} +

+ {news.map(({ content, metadata }, idx) => ( + + + + ))} +
+ ); +} + +DateNews.getShapesConfig = ((width, height) => { + return window.innerWidth <= 768 + ? ({} as ShapesConfig) + : defaultGetShapesConfig(width, height); +}) as GetShapesConfig; + +export const getStaticProps: GetStaticProps = async ( + context +) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { year, term, date } = context.params!; + const slugs = (await getNewsByTerm(year, term)).filter((slug) => + slug.includes(date) + ); + + const news = await Promise.all( + slugs.map((slug) => getNewsBySlug(year, term, slug)) + ); + console.log(slugs); + // Reverse so that we are displaying the most recent news + // of term first + return { props: { year, term, news: news.reverse() } }; +}; + +interface Params extends ParsedUrlQuery { + year: string; + term: Term; + date: string; +} + +export const getStaticPaths: GetStaticPaths = async () => { + const years = await getNewsYears(); + const terms = await Promise.all( + years.map(async (year) => { + const termsInYear = await getNewsTermsByYear(year); + return termsInYear.map((term) => ({ year, term })); + }) + ); + const dates = await Promise.all( + terms.map(async (termInYear) => { + const datesInTerm: Params[] = []; + for (const { year, term } of termInYear) { + const dates = await getNewsDateByTerm(year, term); + dates.map((date) => datesInTerm.push({ year, term, date })); + } + return datesInTerm.flat(); + }) + ); + + return { + paths: dates.flat().map((params) => ({ params })), + fallback: false, + }; +};