add multiple news items on homepage and single news item page
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
84144c10ab
commit
417ad32fba
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<MiniNewsCardProps> = ({
|
||||
date,
|
||||
author,
|
||||
children,
|
||||
permalink,
|
||||
fit = false,
|
||||
}) => {
|
||||
const classes = fit ? [styles.card, styles.fit] : [styles.card];
|
||||
|
||||
return (
|
||||
<article className={classes.join(" ")}>
|
||||
<h1 className={styles.date}>
|
||||
<time dateTime={date.toISOString()}>
|
||||
{date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</time>
|
||||
</h1>
|
||||
<address className={styles.author}>{author}</address>
|
||||
<div className={styles.content}>{children}</div>
|
||||
<Link href={permalink}>
|
||||
<span className={styles.mobileLearnMore}>Learn more</span>
|
||||
</Link>
|
||||
</article>
|
||||
);
|
||||
};
|
|
@ -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 {
|
||||
|
|
12
lib/news.ts
12
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<Term[]> {
|
|||
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
||||
}
|
||||
|
||||
export async function getNewsDateByTerm(
|
||||
year: string,
|
||||
term: Term
|
||||
): Promise<string[]> {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 <Link href="/news/archive">here</Link>
|
||||
</p>
|
||||
<hr className={styles.cardsDividingLine} />
|
||||
{
|
||||
<NewsCard
|
||||
{...props.news.metadata}
|
||||
date={new Date(props.news.metadata.date)}
|
||||
>
|
||||
<MDXRemote {...props.news.content} />
|
||||
</NewsCard>
|
||||
}
|
||||
{props.news.length > 0
|
||||
? props.news.map((news) => (
|
||||
<MiniNewsCard
|
||||
{...news.metadata}
|
||||
date={new Date(news.metadata.date)}
|
||||
key={news.metadata.date.toString()}
|
||||
>
|
||||
<MDXRemote {...news.content} />
|
||||
</MiniNewsCard>
|
||||
))
|
||||
: null}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,7 +119,7 @@ export const getStaticProps: GetStaticProps<Props> = async () => {
|
|||
props: {
|
||||
moreEvents: upcomingEvents.length > 2,
|
||||
events: upcomingEvents.slice(0, 2),
|
||||
news: recentNews[0],
|
||||
news: recentNews.slice(0, 3),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 (
|
||||
<div className={styles.page}>
|
||||
<Title>{["News", `${date}`]}</Title>
|
||||
<h1>
|
||||
News: <span>{date}</span>
|
||||
</h1>
|
||||
{news.map(({ content, metadata }, idx) => (
|
||||
<NewsCard
|
||||
key={idx}
|
||||
{...metadata}
|
||||
date={new Date(metadata.date)}
|
||||
fit={true}
|
||||
>
|
||||
<MDXRemote {...content} />
|
||||
</NewsCard>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
DateNews.getShapesConfig = ((width, height) => {
|
||||
return window.innerWidth <= 768
|
||||
? ({} as ShapesConfig)
|
||||
: defaultGetShapesConfig(width, height);
|
||||
}) as GetShapesConfig;
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props, Params> = 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<Params> = 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,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue