Co-authored-by: Jared He <66887902+jaredjhe@users.noreply.github.com> Reviewed-on: #361 Reviewed-by: Amy <a258wang@csclub.uwaterloo.ca> Reviewed-by: n3parikh <n3parikh@csclub.uwaterloo.ca> Co-authored-by: j285he <j285he@localhost> Co-committed-by: j285he <j285he@localhost>
This commit is contained in:
parent
ff0594eac7
commit
2264e60852
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { capitalize } from "@/utils";
|
import { capitalize, Term } from "@/utils";
|
||||||
|
|
||||||
import { Link } from "./Link";
|
import { Link } from "./Link";
|
||||||
import {
|
import {
|
||||||
|
@ -16,7 +16,7 @@ export interface Props {
|
||||||
type: "news" | "events";
|
type: "news" | "events";
|
||||||
items: {
|
items: {
|
||||||
year: string;
|
year: string;
|
||||||
terms: string[];
|
terms: Term[];
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ import { MDXRemoteSerializeResult } from "next-mdx-remote";
|
||||||
import { serialize } from "next-mdx-remote/serialize";
|
import { serialize } from "next-mdx-remote/serialize";
|
||||||
|
|
||||||
import type { Props } from "../pages/events/[year]/[term]/index";
|
import type { Props } from "../pages/events/[year]/[term]/index";
|
||||||
|
// do not use alias "@/utils" as generate-calendar imports a function from this file and ts-node is not compatible
|
||||||
|
import { Term, TERMS, isTerm } from "../utils";
|
||||||
|
|
||||||
const EVENTS_PATH = path.join("content", "events");
|
const EVENTS_PATH = path.join("content", "events");
|
||||||
export const TERMS = ["winter", "spring", "fall"];
|
|
||||||
|
|
||||||
export async function getEventYears(): Promise<string[]> {
|
export async function getEventYears(): Promise<string[]> {
|
||||||
return (await fs.readdir(EVENTS_PATH, { withFileTypes: true }))
|
return (await fs.readdir(EVENTS_PATH, { withFileTypes: true }))
|
||||||
|
@ -19,12 +20,12 @@ export async function getEventYears(): Promise<string[]> {
|
||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEventTermsByYear(year: string): Promise<string[]> {
|
export async function getEventTermsByYear(year: string): Promise<Term[]> {
|
||||||
return (
|
return (
|
||||||
await fs.readdir(path.join(EVENTS_PATH, year), { withFileTypes: true })
|
await fs.readdir(path.join(EVENTS_PATH, year), { withFileTypes: true })
|
||||||
)
|
)
|
||||||
.filter((dirent) => dirent.isDirectory() && TERMS.includes(dirent.name))
|
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
|
||||||
.map((dirent) => dirent.name)
|
.map((dirent) => dirent.name as Term)
|
||||||
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ export const DATE_FORMAT = "MMMM dd yyyy HH:mm";
|
||||||
|
|
||||||
export async function getEventBySlug(
|
export async function getEventBySlug(
|
||||||
year: string,
|
year: string,
|
||||||
term: string,
|
term: Term,
|
||||||
slug: string
|
slug: string
|
||||||
): Promise<Event> {
|
): Promise<Event> {
|
||||||
const file = await fs.readFile(
|
const file = await fs.readFile(
|
||||||
|
@ -84,7 +85,7 @@ export async function getEventBySlug(
|
||||||
|
|
||||||
export async function getEventsByTerm(
|
export async function getEventsByTerm(
|
||||||
year: string,
|
year: string,
|
||||||
term: string
|
term: Term
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
return (await fs.readdir(path.join(EVENTS_PATH, year, term)))
|
return (await fs.readdir(path.join(EVENTS_PATH, year, term)))
|
||||||
|
@ -130,7 +131,7 @@ export async function getUpcomingEvents(): Promise<Event[]> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllEvents() {
|
export async function getAllEvents(): Promise<Event[]> {
|
||||||
const events: Event[] = [];
|
const events: Event[] = [];
|
||||||
|
|
||||||
for (const year of await getEventYears()) {
|
for (const year of await getEventYears()) {
|
||||||
|
@ -149,7 +150,7 @@ export async function getEventsPageProps({
|
||||||
term,
|
term,
|
||||||
}: {
|
}: {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
}): Promise<Props> {
|
}): Promise<Props> {
|
||||||
const eventNames = await getEventsByTerm(year, term);
|
const eventNames = await getEventsByTerm(year, term);
|
||||||
|
|
||||||
|
@ -177,7 +178,7 @@ export async function getEventsPageProps({
|
||||||
const eventYears = await getEventYears();
|
const eventYears = await getEventYears();
|
||||||
|
|
||||||
const minYear = eventYears[0];
|
const minYear = eventYears[0];
|
||||||
const pastTerms: { year: string; term: string }[] = [];
|
const pastTerms: { year: string; term: Term }[] = [];
|
||||||
let curPastYear = year;
|
let curPastYear = year;
|
||||||
let curPastTerm = term;
|
let curPastTerm = term;
|
||||||
while (parseInt(curPastYear) >= parseInt(minYear) && pastTerms.length < 2) {
|
while (parseInt(curPastYear) >= parseInt(minYear) && pastTerms.length < 2) {
|
||||||
|
@ -191,7 +192,7 @@ export async function getEventsPageProps({
|
||||||
pastTerms.reverse();
|
pastTerms.reverse();
|
||||||
|
|
||||||
const maxYear = eventYears[eventYears.length - 1];
|
const maxYear = eventYears[eventYears.length - 1];
|
||||||
const futureTerms: { year: string; term: string }[] = [];
|
const futureTerms: { year: string; term: Term }[] = [];
|
||||||
let curFutureYear = year;
|
let curFutureYear = year;
|
||||||
let curFutureTerm = term;
|
let curFutureTerm = term;
|
||||||
while (
|
while (
|
||||||
|
@ -217,7 +218,7 @@ export async function getEventsPageProps({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentTerm() {
|
export function getCurrentTerm(): { year: string; term: Term } {
|
||||||
const today = new Date().toLocaleDateString("en-CA", {
|
const today = new Date().toLocaleDateString("en-CA", {
|
||||||
timeZone: "EST",
|
timeZone: "EST",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
|
@ -241,17 +242,14 @@ export function getCurrentTerm() {
|
||||||
term = "fall";
|
term = "fall";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term === "") {
|
if (!isTerm(term)) {
|
||||||
throw new Error("Error setting the current term");
|
throw new Error("Error setting the current term");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { year, term };
|
return { year, term };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPastTerm(
|
function getPastTerm(year: string, term: Term): { year: string; term: Term } {
|
||||||
year: string,
|
|
||||||
term: string
|
|
||||||
): { year: string; term: string } {
|
|
||||||
const index = TERMS.indexOf(term);
|
const index = TERMS.indexOf(term);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
|
@ -269,10 +267,7 @@ function getPastTerm(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFutureTerm(
|
function getFutureTerm(year: string, term: Term): { year: string; term: Term } {
|
||||||
year: string,
|
|
||||||
term: string
|
|
||||||
): { year: string; term: string } {
|
|
||||||
const index = TERMS.indexOf(term);
|
const index = TERMS.indexOf(term);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
import { Client } from "ldapts";
|
import { Client } from "ldapts";
|
||||||
|
|
||||||
|
import { Term } from "@/utils";
|
||||||
|
|
||||||
export interface Member {
|
export interface Member {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
program: string;
|
program: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMembers(
|
export async function getMembers(year: string, term: Term): Promise<Member[]> {
|
||||||
year: string,
|
|
||||||
term: string
|
|
||||||
): Promise<Member[]> {
|
|
||||||
if (term !== "winter" && term !== "spring" && term !== "fall") {
|
|
||||||
throw new Error(`[getMembers] Not a valid term: "${term}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.USE_LDAP?.toLowerCase() !== "true") {
|
if (process.env.USE_LDAP?.toLowerCase() !== "true") {
|
||||||
return dummyMembers;
|
return dummyMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
let members: Member[] = [];
|
let members: Member[] = [];
|
||||||
|
|
||||||
const url = "ldap://ldap1.csclub.uwaterloo.ca";
|
const url = "ldap://ldap1.csclub.uwaterloo.ca";
|
||||||
|
@ -28,7 +22,10 @@ export async function getMembers(
|
||||||
await client.bind("", "");
|
await client.bind("", "");
|
||||||
const { searchEntries } = await client.search(searchDN, {
|
const { searchEntries } = await client.search(searchDN, {
|
||||||
scope: "sub",
|
scope: "sub",
|
||||||
filter: `(&(objectClass=member)(term=${term.slice(0, 1)}${year}))`,
|
filter: `(&(objectClass=member)(term=${(term as string).slice(
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)}${year}))`,
|
||||||
});
|
});
|
||||||
|
|
||||||
members = searchEntries
|
members = searchEntries
|
||||||
|
|
13
lib/news.ts
13
lib/news.ts
|
@ -6,10 +6,11 @@ import matter from "gray-matter";
|
||||||
import { MDXRemoteSerializeResult } from "next-mdx-remote";
|
import { MDXRemoteSerializeResult } from "next-mdx-remote";
|
||||||
import { serialize } from "next-mdx-remote/serialize";
|
import { serialize } from "next-mdx-remote/serialize";
|
||||||
|
|
||||||
|
import { isTerm, Term, TERMS } from "@/utils";
|
||||||
|
|
||||||
import { DATE_FORMAT, getLocalDateFromEST } from "./events";
|
import { DATE_FORMAT, getLocalDateFromEST } from "./events";
|
||||||
|
|
||||||
export const NEWS_PATH = path.join("content", "news");
|
export const NEWS_PATH = path.join("content", "news");
|
||||||
const TERMS = ["winter", "spring", "fall"];
|
|
||||||
|
|
||||||
export interface Metadata {
|
export interface Metadata {
|
||||||
author: string;
|
author: string;
|
||||||
|
@ -28,20 +29,20 @@ export async function getNewsYears(): Promise<string[]> {
|
||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNewsTermsByYear(year: string): Promise<string[]> {
|
export async function getNewsTermsByYear(year: string): Promise<Term[]> {
|
||||||
return (
|
return (
|
||||||
await fs.readdir(path.join(NEWS_PATH, year), {
|
await fs.readdir(path.join(NEWS_PATH, year), {
|
||||||
withFileTypes: true,
|
withFileTypes: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.filter((dirent) => dirent.isDirectory() && TERMS.includes(dirent.name))
|
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
|
||||||
.map((dirent) => dirent.name)
|
.map((dirent) => dirent.name as Term)
|
||||||
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNewsByTerm(
|
export async function getNewsByTerm(
|
||||||
year: string,
|
year: string,
|
||||||
term: string
|
term: Term
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
return (
|
return (
|
||||||
await fs.readdir(path.join(NEWS_PATH, year, term), {
|
await fs.readdir(path.join(NEWS_PATH, year, term), {
|
||||||
|
@ -54,7 +55,7 @@ export async function getNewsByTerm(
|
||||||
|
|
||||||
export async function getNewsBySlug(
|
export async function getNewsBySlug(
|
||||||
year: string,
|
year: string,
|
||||||
term: string,
|
term: Term,
|
||||||
slug: string
|
slug: string
|
||||||
): Promise<News> {
|
): Promise<News> {
|
||||||
const raw = await fs.readFile(
|
const raw = await fs.readFile(
|
||||||
|
|
|
@ -6,14 +6,14 @@ import { Table } from "@/components/Table";
|
||||||
import { Title } from "@/components/Title";
|
import { Title } from "@/components/Title";
|
||||||
import { getCurrentTerm } from "@/lib/events";
|
import { getCurrentTerm } from "@/lib/events";
|
||||||
import { getMembers, Member } from "@/lib/members";
|
import { getMembers, Member } from "@/lib/members";
|
||||||
import { capitalize } from "@/utils";
|
import { Term, capitalize } from "@/utils";
|
||||||
|
|
||||||
import styles from "./members.module.css";
|
import styles from "./members.module.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
members: Member[];
|
members: Member[];
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Members(props: Props) {
|
export default function Members(props: Props) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
getEventsByTerm,
|
getEventsByTerm,
|
||||||
getEventBySlug,
|
getEventBySlug,
|
||||||
} from "@/lib/events";
|
} from "@/lib/events";
|
||||||
import { capitalize } from "@/utils";
|
import { capitalize, Term } from "@/utils";
|
||||||
|
|
||||||
export default function EventInfoPage({ year, term, event }: Props) {
|
export default function EventInfoPage({ year, term, event }: Props) {
|
||||||
return (
|
return (
|
||||||
|
@ -43,13 +43,13 @@ EventInfoPage.getShapesConfig = ((width, height) => {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
event: Event;
|
event: Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Params extends ParsedUrlQuery {
|
interface Params extends ParsedUrlQuery {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
event: string;
|
event: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,21 +14,21 @@ import {
|
||||||
getEventYears,
|
getEventYears,
|
||||||
getEventTermsByYear,
|
getEventTermsByYear,
|
||||||
} from "@/lib/events";
|
} from "@/lib/events";
|
||||||
import { capitalize } from "@/utils";
|
import { capitalize, Term } from "@/utils";
|
||||||
|
|
||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
pastEvents: Event[];
|
pastEvents: Event[];
|
||||||
futureEvents: Event[];
|
futureEvents: Event[];
|
||||||
isCurrentTerm: boolean;
|
isCurrentTerm: boolean;
|
||||||
pastTerms: { year: string; term: string }[];
|
pastTerms: { year: string; term: Term }[];
|
||||||
futureTerms: { year: string; term: string }[];
|
futureTerms: { year: string; term: Term }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Term(props: Props) {
|
export default function TermPage(props: Props) {
|
||||||
let headerTerms = [{ year: props.year, term: props.term }];
|
let headerTerms = [{ year: props.year, term: props.term }];
|
||||||
|
|
||||||
// p, Current, f
|
// p, Current, f
|
||||||
|
@ -120,7 +120,7 @@ export default function Term(props: Props) {
|
||||||
|
|
||||||
function HeaderLink(props: {
|
function HeaderLink(props: {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
isCurrentTerm?: boolean;
|
isCurrentTerm?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
@ -134,7 +134,7 @@ function HeaderLink(props: {
|
||||||
|
|
||||||
interface Params extends ParsedUrlQuery {
|
interface Params extends ParsedUrlQuery {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps<Props, Params> = async (
|
export const getStaticProps: GetStaticProps<Props, Params> = async (
|
||||||
|
|
|
@ -6,12 +6,13 @@ import React from "react";
|
||||||
import { Link } from "@/components/Link";
|
import { Link } from "@/components/Link";
|
||||||
import { Title } from "@/components/Title";
|
import { Title } from "@/components/Title";
|
||||||
import { getEventYears, getEventTermsByYear } from "@/lib/events";
|
import { getEventYears, getEventTermsByYear } from "@/lib/events";
|
||||||
|
import { Term } from "@/utils";
|
||||||
|
|
||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
year: string;
|
year: string;
|
||||||
terms: string[];
|
terms: Term[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Year(props: Props) {
|
export default function Year(props: Props) {
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { GetStaticProps } from "next";
|
||||||
|
|
||||||
import { getCurrentTerm, getEventsPageProps } from "@/lib/events";
|
import { getCurrentTerm, getEventsPageProps } from "@/lib/events";
|
||||||
|
|
||||||
import Term, { Props } from "./[year]/[term]";
|
import TermPage, { Props } from "./[year]/[term]";
|
||||||
|
|
||||||
export default Term;
|
export default TermPage;
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps<Props> = async () => {
|
export const getStaticProps: GetStaticProps<Props> = async () => {
|
||||||
return { props: await getEventsPageProps(getCurrentTerm()) };
|
return { props: await getEventsPageProps(getCurrentTerm()) };
|
||||||
|
|
|
@ -18,13 +18,13 @@ import {
|
||||||
getNewsYears,
|
getNewsYears,
|
||||||
News,
|
News,
|
||||||
} from "@/lib/news";
|
} from "@/lib/news";
|
||||||
import { capitalize } from "@/utils";
|
import { capitalize, Term } from "@/utils";
|
||||||
|
|
||||||
import styles from "./[term].module.css";
|
import styles from "./[term].module.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
news: News[];
|
news: News[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
|
||||||
|
|
||||||
interface Params extends ParsedUrlQuery {
|
interface Params extends ParsedUrlQuery {
|
||||||
year: string;
|
year: string;
|
||||||
term: string;
|
term: Term;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStaticPaths: GetStaticPaths<Params> = async () => {
|
export const getStaticPaths: GetStaticPaths<Params> = async () => {
|
||||||
|
|
8
utils.ts
8
utils.ts
|
@ -1,3 +1,11 @@
|
||||||
|
export const TERMS = ["winter", "spring", "fall"] as const;
|
||||||
|
export type Term = typeof TERMS[number];
|
||||||
|
|
||||||
|
// https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
|
||||||
|
export function isTerm(x: string): x is Term {
|
||||||
|
return TERMS.some((term) => x === term);
|
||||||
|
}
|
||||||
|
|
||||||
export function capitalize(str: string) {
|
export function capitalize(str: string) {
|
||||||
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue