Compare commits

...

2 Commits

Author SHA1 Message Date
Aditya Thakral 5b32153613 Create members api 4 months ago
Aditya Thakral 12dc81c34a Add api folder to gitignore 4 months ago
  1. 8
      .drone.yml
  2. 5
      .gitignore
  3. 1
      .vscode/settings.json
  4. 2
      components/EventCard.tsx
  5. 2
      components/MiniEventCard.tsx
  6. 3
      components/WarningHeader.tsx
  7. 176
      lib/events.ts
  8. 2
      lib/members.ts
  9. 39
      lib/news.ts
  10. 89
      lib/team.ts
  11. 70
      package-lock.json
  12. 6
      package.json
  13. 7
      pages/about/members.tsx
  14. 16
      pages/about/team.tsx
  15. 8
      pages/events/[year]/[term]/[event].tsx
  16. 25
      pages/events/[year]/[term]/index.tsx
  17. 4
      pages/events/[year]/index.tsx
  18. 2
      pages/events/archive.tsx
  19. 5
      pages/events/index.tsx
  20. 0
      public/api/.keep
  21. 13
      scripts/api/members.ts
  22. 23
      scripts/change-dates.ts
  23. 2
      scripts/generate-calendar.ts
  24. 65
      utils.ts

@ -37,6 +37,13 @@ steps:
commands:
- npm run build:calendar
- name: generate-api
image: node:16
depends_on:
- install-deps
commands:
- npm run build:api
- name: build
image: node:16
depends_on:
@ -47,6 +54,7 @@ steps:
- name: export
image: node:16
depends_on:
- generate-api
- generate-calendar
- build
commands:

5
.gitignore vendored

@ -28,4 +28,7 @@ yarn-error.log*
/public/events.ics
# Images should be optimized
/public/images
/public/images
# APIs should be automatically generated
/public/api

@ -40,7 +40,6 @@
"files.eol": "\n",
"[markdown]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": false,
"editor.tabSize": 4
}
}

@ -19,7 +19,7 @@ interface EventCardProps {
permaLink: string;
showDescription?: boolean;
children: ReactNode;
year: string;
year: number;
term: string;
slug: string;
titleLinked: boolean;

@ -14,7 +14,7 @@ interface MiniEventCardProps {
startDate: Date;
endDate?: Date;
background: "dark-bg" | "normal-bg";
year: string;
year: number;
term: string;
slug: string;
}

@ -1,8 +1,9 @@
import { parse } from "date-fns";
import React from "react";
import { DATE_FORMAT, getLocalDateFromEST } from "@/utils";
import warnings from "../content/warnings/warnings.json";
import { DATE_FORMAT, getLocalDateFromEST } from "../utils";
import styles from "./WarningHeader.module.css";

@ -6,28 +6,33 @@ import matter from "gray-matter";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
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,
DATE_FORMAT,
getLocalDateFromEST,
} from "../utils";
TermYear,
getTermYear,
getCurrentTermYear,
} from "@/utils";
import type { Props } from "../pages/events/[year]/[term]";
const EVENTS_PATH = path.join("content", "events");
export async function getEventYears(): Promise<string[]> {
export async function getEventYears(): Promise<number[]> {
return (await fs.readdir(EVENTS_PATH, { withFileTypes: true }))
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.map((dirent) => parseInt(dirent.name))
.sort();
}
export async function getEventTermsByYear(year: string): Promise<Term[]> {
export async function getEventTermsByYear(year: number): Promise<Term[]> {
return (
await fs.readdir(path.join(EVENTS_PATH, year), { withFileTypes: true })
await fs.readdir(path.join(EVENTS_PATH, year.toString()), {
withFileTypes: true,
})
)
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
.map((dirent) => dirent.name as Term)
@ -55,7 +60,7 @@ interface Metadata {
location: string;
permaLink: string;
registerLink?: string;
year: string;
year: number;
term: string;
slug: string;
}
@ -66,12 +71,12 @@ export interface Event {
}
export async function getEventBySlug(
year: string,
year: number,
term: Term,
slug: string
): Promise<Event> {
const file = await fs.readFile(
path.join(EVENTS_PATH, year, term, `${slug}.md`),
path.join(EVENTS_PATH, year.toString(), term, `${slug}.md`),
"utf-8"
);
const { content, data } = matter(file);
@ -95,11 +100,11 @@ export async function getEventBySlug(
}
export async function getEventsByTerm(
year: string,
year: number,
term: Term
): Promise<string[]> {
try {
return (await fs.readdir(path.join(EVENTS_PATH, year, term)))
return (await fs.readdir(path.join(EVENTS_PATH, year.toString(), term)))
.filter((name) => name.endsWith(".md"))
.map((name) => name.slice(0, -".md".length));
} catch {
@ -108,22 +113,24 @@ export async function getEventsByTerm(
}
export async function getUpcomingEvents(): Promise<Event[]> {
const today = new Date();
const currentYear = today.getFullYear();
const currentTerm = Math.trunc(today.getMonth() / 4);
const nextYear = currentTerm < 2 ? currentYear : currentYear + 1;
const nextTerm = (currentTerm + 1) % 3;
const terms: TermYear[] = [];
// Get events for the next two terms
for (const termYear of getTermYear()) {
if (terms.length >= 2) {
break;
}
terms.push(termYear);
}
const events: Event[] = (
await Promise.all(
[
{ year: currentYear.toString(), term: currentTerm },
{ year: nextYear.toString(), term: nextTerm },
].map(async ({ year, term }) => {
terms.map(async ({ year, term }) => {
try {
const eventsInTerm = await getEventsByTerm(year, TERMS[term]);
const eventsInTerm = await getEventsByTerm(year, term);
return await Promise.all(
eventsInTerm.map((slug) => getEventBySlug(year, TERMS[term], slug))
eventsInTerm.map((slug) => getEventBySlug(year, term, slug))
);
} catch (error) {
return [];
@ -162,12 +169,9 @@ export async function getAllEvents(): Promise<Event[]> {
}
export async function getEventsPageProps({
year,
term,
}: {
year: string;
term: Term;
}): Promise<Props> {
year,
}: TermYear): Promise<Props> {
const eventNames = await getEventsByTerm(year, term);
const events: Event[] = (
@ -198,40 +202,43 @@ export async function getEventsPageProps({
currentDate
);
const current = getCurrentTerm();
const eventYears = await getEventYears();
const minYear = eventYears[0];
const pastTerms: { year: string; term: Term }[] = [];
let curPastYear = year;
let curPastTerm = term;
while (parseInt(curPastYear) >= parseInt(minYear) && pastTerms.length < 2) {
const pastTerm = getPastTerm(curPastYear, curPastTerm);
curPastYear = pastTerm.year;
curPastTerm = pastTerm.term;
if ((await getEventsByTerm(curPastYear, curPastTerm)).length !== 0) {
pastTerms.push(pastTerm);
const pastTerms: TermYear[] = [];
for (const current of getTermYear(
{ year, term },
{ goBackwards: true, skipCurrent: true }
)) {
if (pastTerms.length >= 2 || current.year < minYear) {
break;
}
if ((await getEventsByTerm(current.year, current.term)).length !== 0) {
pastTerms.push(current);
}
}
pastTerms.reverse();
const maxYear = eventYears[eventYears.length - 1];
const futureTerms: { year: string; term: Term }[] = [];
let curFutureYear = year;
let curFutureTerm = term;
while (
parseInt(curFutureYear) <= parseInt(maxYear) &&
futureTerms.length < 2
) {
const futureTerm = getFutureTerm(curFutureYear, curFutureTerm);
curFutureYear = futureTerm.year;
curFutureTerm = futureTerm.term;
if ((await getEventsByTerm(curFutureYear, curFutureTerm)).length !== 0) {
futureTerms.push(futureTerm);
const futureTerms: TermYear[] = [];
for (const current of getTermYear(
{ year, term },
{ goBackwards: false, skipCurrent: true }
)) {
if (futureTerms.length >= 2 || maxYear < current.year) {
break;
}
if ((await getEventsByTerm(current.year, current.term)).length !== 0) {
futureTerms.push(current);
}
}
const current = getCurrentTermYear();
return {
year: year,
term: term,
@ -242,70 +249,3 @@ export async function getEventsPageProps({
futureTerms: futureTerms,
};
}
export function getCurrentTerm(): { year: string; term: Term } {
const today = new Date().toLocaleDateString("en-CA", {
timeZone: "EST",
year: "numeric",
month: "numeric",
day: "numeric",
});
const [year] = today.split("-");
let term = "";
if (`${year}-01-01` <= today) {
term = "winter";
}
if (`${year}-05-01` <= today) {
term = "spring";
}
if (`${year}-09-01` <= today) {
term = "fall";
}
if (!isTerm(term)) {
throw new Error("Error setting the current term");
}
return { year, term };
}
function getPastTerm(year: string, term: Term): { year: string; term: Term } {
const index = TERMS.indexOf(term);
if (index === -1) {
throw new Error(`[getPastTerm] Not a valid term: "${term}" "${year}"`);
}
return index === 0
? {
year: (parseInt(year) - 1).toString(),
term: TERMS[TERMS.length - 1],
}
: {
year: year,
term: TERMS[index - 1],
};
}
function getFutureTerm(year: string, term: Term): { year: string; term: Term } {
const index = TERMS.indexOf(term);
if (index === -1) {
throw new Error(`[getFutureTerm] Not a valid term: "${term}" "${year}"`);
}
return index === TERMS.length - 1
? {
year: (parseInt(year) + 1).toString(),
term: TERMS[0],
}
: {
year: year,
term: TERMS[index + 1],
};
}

@ -8,7 +8,7 @@ export interface Member {
program: string;
}
export async function getMembers(year: string, term: Term): Promise<Member[]> {
export async function getMembers(year: number, term: Term): Promise<Member[]> {
if (process.env.USE_LDAP?.toLowerCase() !== "true") {
return dummyMembers;
}

@ -7,9 +7,16 @@ import truncateMarkdown from "markdown-truncate";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import { isTerm, Term, TERMS } from "@/utils";
import { DATE_FORMAT, getLocalDateFromEST } from "../utils";
import {
isTerm,
Term,
TERMS,
DATE_FORMAT,
getLocalDateFromEST,
TermYear,
getTermYear,
getCurrentTermYear,
} from "@/utils";
export const NEWS_PATH = path.join("content", "news");
@ -96,23 +103,27 @@ export async function getNewsBySlug(
}
export async function getRecentNews(): Promise<News[]> {
const today = new Date();
const currentYear = today.getFullYear();
const currentTerm = Math.trunc(today.getMonth() / 4);
const prevYear = currentTerm > 0 ? currentYear : currentYear - 1;
const prevTerm = (currentTerm - 1 + 3) % 3;
const terms: TermYear[] = [];
// Get news for the last two terms
for (const termYear of getTermYear(getCurrentTermYear(), {
goBackwards: true,
})) {
if (terms.length >= 2) {
break;
}
terms.push(termYear);
}
const news: News[] = (
await Promise.all(
[
{ year: currentYear.toString(), term: currentTerm },
{ year: prevYear.toString(), term: prevTerm },
].map(async ({ year, term }) => {
terms.map(async ({ year, term }) => {
try {
const newsInTerm = await getNewsByTerm(year, TERMS[term]);
const newsInTerm = await getNewsByTerm(year.toString(), term);
return await Promise.all(
newsInTerm.map((slug) => {
return getNewsBySlug(year, TERMS[term], slug, true);
return getNewsBySlug(year.toString(), term, slug, true);
})
);
} catch (error) {

@ -5,12 +5,10 @@ import matter from "gray-matter";
import { Client } from "ldapts";
import { serialize } from "next-mdx-remote/serialize";
import { getCurrentTerm } from "@/lib/events";
import { capitalize } from "@/utils";
import { capitalize, TermYear } from "@/utils";
const EXECS_PATH = path.join("content", "team", "execs");
const FILETYPE = ".md";
const { year, term } = getCurrentTerm();
const execPositions: { [position: string]: string } = {
president: "President",
@ -34,7 +32,54 @@ export interface Metadata {
image: string;
}
export async function getExecNamePosPairs() {
export async function getExecs(termYear: TermYear) {
const execNamePosPairs = await getExecNamePosPairs(termYear);
return await Promise.all(
execNamePosPairs.map((namePosPair) =>
getExec(namePosPair[0], namePosPair[1])
)
);
}
async function getExec(name: string, pos: string) {
let content, metadata;
try {
const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`));
({ content, data: metadata } = matter(raw));
const image = await getMemberImagePath(metadata.name as string);
return {
content: await serialize(content),
metadata: { ...metadata, image } as Metadata,
};
} catch (err) {
// Capitalize the first letter of the first name and last name
const firstName = capitalize(name.split("-")[0]);
const lastName = capitalize(name.split("-")[1]);
const posName = execPositions[pos];
content = "Coming Soon!";
metadata = {
name: `${firstName} ${lastName}`,
role: `${posName}`,
};
const image = await getMemberImagePath(metadata.name);
return {
content: await serialize(content),
metadata: { ...metadata, image } as Metadata,
};
}
}
async function getExecNamePosPairs({
term,
year,
}: TermYear): Promise<[person: string, position: string][]> {
if (process.env.USE_LDAP?.toLowerCase() !== "true") {
return [["codey", "mascot"]];
}
@ -45,7 +90,7 @@ export async function getExecNamePosPairs() {
// position: name
const execMembers: { [position: string]: string } = {};
let formattedExec: [string, string][] = [];
let formattedExec: [person: string, position: string][] = [];
try {
await client.bind("", "");
@ -89,40 +134,6 @@ export async function getExecNamePosPairs() {
return formattedExec;
}
export async function getExec(name: string, pos: string, convert = true) {
let content, metadata;
try {
const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`));
({ content, data: metadata } = matter(raw));
const image = await getMemberImagePath(metadata.name as string);
return {
content: convert ? await serialize(content) : content,
metadata: { ...metadata, image } as Metadata,
};
} catch (err) {
// Capitalize the first letter of the first name and last name
const firstName = capitalize(name.split("-")[0]);
const lastName = capitalize(name.split("-")[1]);
const posName = execPositions[pos];
content = "Coming Soon!";
metadata = {
name: `${firstName} ${lastName}`,
role: `${posName}`,
};
const image = await getMemberImagePath(metadata.name);
return {
content: convert ? await serialize(content) : content,
metadata: { ...metadata, image } as Metadata,
};
}
}
async function getImage(imgPath: string) {
try {
await access(path.join("public", imgPath));

70
package-lock.json generated

@ -45,6 +45,7 @@
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-preset-env": "^7.0.0",
"ts-node": "^10.2.1",
"tsconfig-paths": "^4.1.0",
"typescript": "4.6.4"
},
"engines": {
@ -1075,7 +1076,7 @@
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/mdast": {
@ -2883,6 +2884,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-import/node_modules/tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
"integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
@ -7153,15 +7166,29 @@
}
},
"node_modules/tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
"integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz",
"integrity": "sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"json5": "^2.2.1",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tsconfig-paths/node_modules/json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true,
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tslib": {
@ -8550,7 +8577,7 @@
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"@types/mdast": {
@ -10025,6 +10052,18 @@
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
"integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
}
}
},
@ -12975,15 +13014,22 @@
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
},
"tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
"integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz",
"integrity": "sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"json5": "^2.2.1",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true
}
}
},
"tslib": {

@ -8,9 +8,10 @@
"scripts": {
"dev": "next dev",
"build": "npm run build:images && npm run build:web && npm run build:calendar",
"build:images": "ts-node ./scripts/optimize-images",
"build:web": "next build",
"build:calendar": "ts-node ./scripts/generate-calendar",
"build:images": "ts-node -r tsconfig-paths/register ./scripts/optimize-images",
"build:calendar": "ts-node -r tsconfig-paths/register ./scripts/generate-calendar",
"build:api": "ts-node -r tsconfig-paths/register ./scripts/api/members",
"start": "next start",
"export": "next export",
"lint": "eslint \"{pages,components,lib,hooks,scripts}/**/*.{js,ts,tsx,jsx}\" --quiet",
@ -56,6 +57,7 @@
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-preset-env": "^7.0.0",
"ts-node": "^10.2.1",
"tsconfig-paths": "^4.1.0",
"typescript": "4.6.4"
}
}

@ -4,15 +4,14 @@ import React from "react";
import { Link } from "@/components/Link";
import { Table } from "@/components/Table";
import { Title } from "@/components/Title";
import { getCurrentTerm } from "@/lib/events";
import { getMembers, Member } from "@/lib/members";
import { Term, capitalize } from "@/utils";
import { Term, capitalize, getCurrentTermYear } from "@/utils";
import styles from "./members.module.css";
interface Props {
members: Member[];
year: string;
year: number;
term: Term;
}
@ -56,7 +55,7 @@ export default function Members(props: Props) {
}
export const getStaticProps: GetStaticProps<Props> = async () => {
const curTerm = getCurrentTerm();
const curTerm = getCurrentTermYear();
return {
props: {
year: curTerm.year,

@ -9,12 +9,8 @@ import { Link } from "@/components/Link";
import { TeamMember } from "@/components/TeamMember";
import { TeamMemberCard } from "@/components/TeamMemberCard";
import { Title } from "@/components/Title";
import {
getExec,
getExecNamePosPairs,
Metadata,
getMemberImagePath,
} from "@/lib/team";
import { getExecs, Metadata, getMemberImagePath } from "@/lib/team";
import { getCurrentTermYear } from "@/utils";
import coordinatorsData from "../../content/team/coordinators-team.json";
import designData from "../../content/team/design-team.json";
@ -224,13 +220,7 @@ function sortTeam(team: Metadata[]): Metadata[] {
}
export const getStaticProps: GetStaticProps<Props> = async () => {
const execNamePosPairs = await getExecNamePosPairs();
const execs = (await Promise.all(
execNamePosPairs.map((namePosPair) =>
getExec(namePosPair[0], namePosPair[1])
)
)) as SerializedExec[];
const execs = await getExecs(getCurrentTermYear());
let [
coordinators,

@ -63,7 +63,11 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { year, term, event } = context.params!;
return {
props: { year, term, event: await getEventBySlug(year, term, event) },
props: {
year,
term,
event: await getEventBySlug(parseInt(year), term, event),
},
};
};
@ -77,7 +81,7 @@ export const getStaticPaths: GetStaticPaths<Params> = async () => {
termsInYear.map(async (term) => {
const eventsInTerm = await getEventsByTerm(year, term);
return eventsInTerm.map((event) => ({
year,
year: year.toString(),
term,
event,
}));

@ -10,22 +10,22 @@ import { MiniEventCard } from "@/components/MiniEventCard";
import { Title } from "@/components/Title";
import {
Event,
getEventsPageProps,
getEventYears,
getEventTermsByYear,
getEventsPageProps,
} from "@/lib/events";
import { capitalize, Term } from "@/utils";
import { capitalize, Term, TermYear } from "@/utils";
import styles from "./index.module.css";
export interface Props {
year: string;
year: number;
term: Term;
pastEvents: Event[];
futureEvents: Event[];
isCurrentTerm: boolean;
pastTerms: { year: string; term: Term }[];
futureTerms: { year: string; term: Term }[];
pastTerms: TermYear[];
futureTerms: TermYear[];
}
export default function TermPage(props: Props) {
@ -61,7 +61,7 @@ export default function TermPage(props: Props) {
<HeaderLink
{...link}
isCurrentTerm={link.year === props.year && link.term === props.term}
key={link.year + link.term}
key={`${link.year}${link.term}`}
/>
))}
<Link href="/events/archive">Archive</Link>
@ -129,7 +129,7 @@ export default function TermPage(props: Props) {
}
function HeaderLink(props: {
year: string;
year: number;
term: Term;
isCurrentTerm?: boolean;
}) {
@ -151,7 +151,14 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
context
) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return { props: await getEventsPageProps(context.params!) };
const params = context.params!;
return {
props: await getEventsPageProps({
year: parseInt(params.year),
term: params.term,
}),
};
};
export const getStaticPaths: GetStaticPaths<Params> = async () => {
@ -161,7 +168,7 @@ export const getStaticPaths: GetStaticPaths<Params> = async () => {
years.map(async (year) => {
const terms = await getEventTermsByYear(year);
return terms.map((curTerm) => ({
params: { year: year, term: curTerm },
params: { year: year.toString(), term: curTerm },
}));
})
)

@ -47,7 +47,7 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
return {
props: {
year: year,
terms: await getEventTermsByYear(year),
terms: await getEventTermsByYear(parseInt(year)),
},
};
};
@ -55,7 +55,7 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
export const getStaticPaths: GetStaticPaths<Params> = async () => {
const years = await getEventYears();
const paths = years.map((curYear) => ({
params: { year: curYear },
params: { year: curYear.toString() },
}));
return {
paths: paths,

@ -9,7 +9,7 @@ export const getStaticProps: GetStaticProps<Props> = async () => {
const years = (await getEventYears()).reverse();
const yearsWithTerms = await Promise.all(
years.map(async (year) => ({
year,
year: year.toString(),
terms: (await getEventTermsByYear(year)).reverse(),
}))
);

@ -1,11 +1,12 @@
import { GetStaticProps } from "next";
import { getCurrentTerm, getEventsPageProps } from "@/lib/events";
import { getEventsPageProps } from "@/lib/events";
import { getCurrentTermYear } from "@/utils";
import TermPage, { Props } from "./[year]/[term]";
export default TermPage;
export const getStaticProps: GetStaticProps<Props> = async () => {
return { props: await getEventsPageProps(getCurrentTerm()) };
return { props: await getEventsPageProps(getCurrentTermYear()) };
};

@ -0,0 +1,13 @@
import { writeFile } from "fs/promises";
import { getMembers } from "@/lib/members";
import { getCurrentTermYear } from "@/utils";
async function createMembersApi() {
const { term, year } = getCurrentTermYear();
const members = await getMembers(year, term);
await writeFile("public/api/members.json", JSON.stringify(members));
}
void createMembersApi();

@ -3,30 +3,35 @@ import path from "path";
import { format } from "date-fns";
import { DATE_FORMAT } from "@/utils";
/*
Note:
This script will not work for events by default anymore, since events now have startDate instead of endDate
*/
import {
getEventsByTerm,
getEventTermsByYear,
getEventYears,
} from "../lib/events";
} from "@/lib/events";
import {
getNewsByTerm,
getNewsTermsByYear,
getNewsYears,
NEWS_PATH,
} from "../lib/news";
} from "@/lib/news";
import { DATE_FORMAT } from "@/utils";
/*
Note:
This script will not work for events by default anymore, since events now have startDate instead of endDate
*/
const EVENTS_PATH = path.join("content", "events");
export async function main() {
for (const year of await getEventYears()) {
for (const term of await getEventTermsByYear(year)) {
for (const slug of await getEventsByTerm(year, term)) {
const filePath = path.join(EVENTS_PATH, year, term, `${slug}.md`);
const filePath = path.join(
EVENTS_PATH,
year.toString(),
term,
`${slug}.md`
);
const file = await fs.readFile(filePath, "utf-8");
await fs.writeFile(filePath, replaceDate(file));

@ -4,7 +4,7 @@ import path from "path";
import { addHours } from "date-fns";
import ical, { ICalCalendarMethod } from "ical-generator";
import { getAllEvents } from "../lib/events";
import { getAllEvents } from "@/lib/events";
export async function generateCalendar() {
const events = await getAllEvents();

@ -21,3 +21,68 @@ export function getLocalDateFromEST(date: Date): Date {
Intl.DateTimeFormat().resolvedOptions().timeZone
);
}
export interface TermYear {
term: Term;
year: number;
}
export interface GetTermYearOptions {
goBackwards?: boolean;
skipCurrent?: boolean;
}
export function* getTermYear(
start?: number | TermYear,
{ goBackwards = false, skipCurrent = false }: GetTermYearOptions = {}
) {
const today = new Date();
const todayYear = today.getFullYear();
const todayTerm = TERMS[Math.trunc(today.getMonth() / 4)];
start ??= { term: todayTerm, year: todayYear };
if (typeof start === "number") {
start = { term: TERMS[0], year: start };
}
const allTerms = [...TERMS];
if (goBackwards) {
allTerms.reverse();
}
let currentYear = start.year;
while (0 <= currentYear && currentYear <= Number.MAX_SAFE_INTEGER) {
for (const currentTerm of allTerms) {
if (
currentYear === start.year &&
allTerms.indexOf(currentTerm) < allTerms.indexOf(start.term)
) {
continue;
}
if (
skipCurrent &&
currentYear === start.year &&
currentTerm === start.term
) {
continue;
}
yield { term: currentTerm, year: currentYear };
}
currentYear = currentYear + (goBackwards ? -1 : 1);
}
}
export function getCurrentTermYear() {
const result = getTermYear().next();
if (result.done === true) {
throw new Error("Cannot get current term. Iterator is done.");
}
return result.value;
}

Loading…
Cancel
Save