Update Meet The Team page for Winter 2022 (Closes #382) #388

Merged
e26chiu merged 13 commits from update-meet-team-page-winter-2022 into main 2022-01-26 11:00:20 -05:00
19 changed files with 738 additions and 3071 deletions
Showing only changes of commit f2e803330a - Show all commits

View File

@ -1,6 +1,6 @@
import React from "react";
import { capitalize } from "@/utils";
import { capitalize, Term } from "@/utils";
import { Link } from "./Link";
import {
@ -16,7 +16,7 @@ export interface Props {
type: "news" | "events";
items: {
year: string;
terms: string[];
terms: Term[];
}[];
}

View File

@ -75,36 +75,50 @@
.popupBackground {
position: fixed;
z-index: 11;
background-color: var(--navbar-page-overlay);
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 11;
background-color: var(--navbar-page-overlay);
animation: revealBg 0.2s forwards;
}
.popupContainer {
position: fixed;
display: flex;
z-index: 12;
flex-direction: column;
background-color: var(--secondary-background);
padding: calc(20rem / 16) calc(40rem / 16);
left: 0;
right: 0;
top: 50%;
z-index: 12;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
box-sizing: border-box;
padding: calc(40rem / 16);
max-height: 75vh;
overflow: auto;
background-color: var(--secondary-background);
animation: popup 0.7s forwards;
}
.closeBtn {
position: absolute;
align-self: flex-end;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
/* reset default button styling */
width: min-content;
background: transparent;
border: 0px solid transparent;
background: none;
border: none;
padding: 0;
font-family: inherit;
line-height: inherit;
}
.popupContent {
@ -113,25 +127,27 @@
align-items: center;
}
.popupName {
color: var(--primary-accent);
margin: calc(24rem / 16) 0 0 0;
.popupContent .name {
margin-top: calc(24rem / 16);
font-size: calc(18rem / 16);
font-weight: 600;
}
.popupRole {
color: var(--primary-heading);
margin: 0 0 1rem 0;
text-align: center;
.popupContent .role {
margin-bottom: calc(16rem / 16);
font-size: calc(18rem / 16);
font-weight: 600;
}
.popupDescription {
.popupContent .description {
display: block;
font-size: calc(14rem / 16);
}
.popupContent .description > *:last-child {
margin-bottom: 0;
}
@media only screen and (max-width: calc(768rem / 16)) {
.card {
display: flex;

View File

@ -13,35 +13,6 @@ export interface TeamMemberCardProps {
children: React.ReactNode;
}
interface TeamMemberInfoProps extends TeamMemberCardProps {
isPopup?: boolean;
}
function TeamMemberInfo({
name,
role,
image,
children,
isPopup = false,
}: TeamMemberInfoProps) {
return (
<>
<div className={styles.picture}>
<Image
className={styles.image}
src={image}
alt={`Picture of ${name}`}
/>
</div>
<h1 className={isPopup ? styles.popupName : styles.name}>{name}</h1>
<h2 className={isPopup ? styles.popupRole : styles.role}>{role}</h2>
<div className={isPopup ? styles.popupDescription : styles.description}>
{children}
</div>
</>
);
}
export function TeamMemberCard({
name,
role,
@ -50,11 +21,13 @@ export function TeamMemberCard({
}: TeamMemberCardProps) {
const { width } = useWindowDimension();
const [isOpen, setIsOpen] = useState(false);
const handleClick = () => {
if (isOpen || width <= 768) {
setIsOpen(!isOpen);
}
};
return (
<>
<article className={styles.card} onClick={handleClick}>
@ -75,11 +48,28 @@ export function TeamMemberCard({
);
}
interface Propup extends TeamMemberCardProps {
function TeamMemberInfo({ name, role, image, children }: TeamMemberCardProps) {
return (
<>
<div className={styles.picture}>
<Image
className={styles.image}
src={image}
alt={`Picture of ${name}`}
/>
</div>
<h1 className={styles.name}>{name}</h1>
<h2 className={styles.role}>{role}</h2>
<div className={styles.description}>{children}</div>
</>
);
}
interface PopupProps extends TeamMemberCardProps {
handleClick: () => void;
}
function ExecPopup({ name, role, image, children, handleClick }: Propup) {
function ExecPopup({ name, role, image, children, handleClick }: PopupProps) {
return (
<>
<div className={styles.popupBackground} onClick={handleClick} />
@ -88,9 +78,7 @@ function ExecPopup({ name, role, image, children, handleClick }: Propup) {
<Image src="images/team/popup-close.svg" />
</button>
<div className={styles.popupContent}>
<TeamMemberInfo {...{ name, role, image }} isPopup={true}>
{children}
</TeamMemberInfo>
<TeamMemberInfo {...{ name, role, image }}>{children}</TeamMemberInfo>
</div>
</div>
</>

View File

@ -0,0 +1,23 @@
---
name: 'Bootcamp: Mock Interviews'
short: 'Come to practice your interview skills with experienced mentors!'
date: 'January 23 2022 18:00'
online: true
location: 'Discord'
poster: 'images/events/2022/winter/Bootcamp-resume-review-mock-interview.png'
registerLink: https://lnkd.in/eHrs7_Fi
---
📢 Mentee applications for Bootcamp are now OPEN! 📢 CSC is bringing back Bootcamp to gear you up for your next recruiting season, partnered with the UW Tech clubs! 💻 The drop-in mock interview event takes place January 23rd 6:00 - 10:00 PM EST.
💁‍♀️ Sign up as a mentee, and join our experienced mentors in Mock Interviews (virtual 1:1 sessions) to receive feedback from various tech backgrounds 📃 Well be using an SE22 FYDP project called ReviewKit. You will be paired with a mentor who is knowledgeable in the same or a similar career path to yours to ensure relevant feedback! 👌
A mentor will be paired with you based on your career interests to provide insightful feedback and advice to rock your job search - dont miss out! If youre interested, please sign up! We would love to help you feel ready and confident for the upcoming job hunt.
📝After signing up, youll soon receive a link to the Discord server in which this event takes place. Our collaborating clubs are excited to bring you this opportunity to sharpen your job hunting skills
👉 If youre interested, please apply at https://lnkd.in/eHrs7_Fi
Alternatively, you can email us at exec@csclub.uwaterloo.ca with the year and program youre in, along with interested job paths.
📅 Deadline to Apply: January 16th, 2022, 11:59AM EST

View File

@ -0,0 +1,23 @@
---
name: 'Bootcamp: Resume Reviews'
short: 'Come to receive insightful feedback and advice on your resume to rock your job search.'
date: 'January 16 2022 18:00'
online: true
location: 'Discord'
poster: 'images/events/2022/winter/Bootcamp-resume-review-mock-interview.png'
registerLink: https://lnkd.in/eHrs7_Fi
---
📢 Mentee applications for Bootcamp are now OPEN! 📢 CSC is bringing back Bootcamp to gear you up for your next recruiting season, partnered with the UW Tech clubs! 💻 The drop-in resume review event takes place January 16th 6:00 - 10:00 PM EST.
💁‍♀️ Sign up as a mentee, and join our experienced mentors in Resume Reviews (virtual 1:1 sessions) to receive feedback from various tech backgrounds 📃 Well be using an SE22 FYDP project called ReviewKit. You will be paired with a mentor who is knowledgeable in the same or a similar career path to yours to ensure relevant feedback! 👌
A mentor will be paired with you based on your career interests to provide insightful feedback and advice to rock your job search - dont miss out! If youre interested, please sign up! We would love to help you feel ready and confident for the upcoming job hunt.
📝After signing up, youll soon receive a link to the Discord server in which this event takes place. Our collaborating clubs are excited to bring you this opportunity to sharpen your job hunting skills
👉 If youre interested, please apply at https://lnkd.in/eHrs7_Fi
Alternatively, you can email us at exec@csclub.uwaterloo.ca with the year and program youre in, along with interested job paths.
📅 Deadline to Apply: January 16th, 2022, 11:59AM EST

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -8,9 +8,10 @@ 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 } from "../utils";
const EVENTS_PATH = path.join("content", "events");
export const TERMS = ["winter", "spring", "fall"];
export async function getEventYears(): Promise<string[]> {
return (await fs.readdir(EVENTS_PATH, { withFileTypes: true }))
@ -19,12 +20,12 @@ export async function getEventYears(): Promise<string[]> {
.sort();
}
export async function getEventTermsByYear(year: string): Promise<string[]> {
export async function getEventTermsByYear(year: string): Promise<Term[]> {
return (
await fs.readdir(path.join(EVENTS_PATH, year), { withFileTypes: true })
)
.filter((dirent) => dirent.isDirectory() && TERMS.includes(dirent.name))
.map((dirent) => dirent.name)
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
.map((dirent) => dirent.name as Term)
.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(
year: string,
term: string,
term: Term,
slug: string
): Promise<Event> {
const file = await fs.readFile(
@ -84,7 +85,7 @@ export async function getEventBySlug(
export async function getEventsByTerm(
year: string,
term: string
term: Term
): Promise<string[]> {
try {
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[] = [];
for (const year of await getEventYears()) {
@ -149,7 +150,7 @@ export async function getEventsPageProps({
term,
}: {
year: string;
term: string;
term: Term;
}): Promise<Props> {
const eventNames = await getEventsByTerm(year, term);
@ -177,7 +178,7 @@ export async function getEventsPageProps({
const eventYears = await getEventYears();
const minYear = eventYears[0];
const pastTerms: { year: string; term: string }[] = [];
const pastTerms: { year: string; term: Term }[] = [];
let curPastYear = year;
let curPastTerm = term;
while (parseInt(curPastYear) >= parseInt(minYear) && pastTerms.length < 2) {
@ -191,7 +192,7 @@ export async function getEventsPageProps({
pastTerms.reverse();
const maxYear = eventYears[eventYears.length - 1];
const futureTerms: { year: string; term: string }[] = [];
const futureTerms: { year: string; term: Term }[] = [];
let curFutureYear = year;
let curFutureTerm = term;
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", {
timeZone: "EST",
year: "numeric",
@ -241,17 +242,14 @@ export function getCurrentTerm() {
term = "fall";
}
if (term === "") {
if (!isTerm(term)) {
throw new Error("Error setting the current term");
}
return { year, term };
}
function getPastTerm(
year: string,
term: string
): { year: string; term: string } {
function getPastTerm(year: string, term: Term): { year: string; term: Term } {
const index = TERMS.indexOf(term);
if (index === -1) {
@ -269,10 +267,7 @@ function getPastTerm(
};
}
function getFutureTerm(
year: string,
term: string
): { year: string; term: string } {
function getFutureTerm(year: string, term: Term): { year: string; term: Term } {
const index = TERMS.indexOf(term);
if (index === -1) {

View File

@ -1,23 +1,17 @@
import { Client } from "ldapts";
import { Term } from "@/utils";
export interface Member {
name: string;
id: string;
program: string;
}
export async function getMembers(
year: string,
term: string
): Promise<Member[]> {
if (term !== "winter" && term !== "spring" && term !== "fall") {
throw new Error(`[getMembers] Not a valid term: "${term}"`);
}
export async function getMembers(year: string, term: Term): Promise<Member[]> {
if (process.env.USE_LDAP?.toLowerCase() !== "true") {
return dummyMembers;
}
let members: Member[] = [];
const url = "ldap://ldap1.csclub.uwaterloo.ca";
@ -28,7 +22,10 @@ export async function getMembers(
await client.bind("", "");
const { searchEntries } = await client.search(searchDN, {
scope: "sub",
filter: `(&(objectClass=member)(term=${term.slice(0, 1)}${year}))`,
filter: `(&(objectClass=member)(term=${(term as string).slice(
0,
1
)}${year}))`,
});
members = searchEntries
@ -39,7 +36,9 @@ export async function getMembers(
program: item.program === undefined ? "" : (item.program as string),
};
})
.sort((item1: Member, item2: Member) => item1.id.localeCompare(item2.id));
.sort((item1: Member, item2: Member) =>
item1.name.localeCompare(item2.name)
);
} finally {
await client.unbind();
}

View File

@ -6,10 +6,11 @@ import matter from "gray-matter";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import { isTerm, Term, TERMS } from "@/utils";
import { DATE_FORMAT, getLocalDateFromEST } from "./events";
export const NEWS_PATH = path.join("content", "news");
const TERMS = ["winter", "spring", "fall"];
export interface Metadata {
author: string;
@ -28,20 +29,20 @@ export async function getNewsYears(): Promise<string[]> {
.sort();
}
export async function getNewsTermsByYear(year: string): Promise<string[]> {
export async function getNewsTermsByYear(year: string): Promise<Term[]> {
return (
await fs.readdir(path.join(NEWS_PATH, year), {
withFileTypes: true,
})
)
.filter((dirent) => dirent.isDirectory() && TERMS.includes(dirent.name))
.map((dirent) => dirent.name)
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
.map((dirent) => dirent.name as Term)
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
}
export async function getNewsByTerm(
year: string,
term: string
term: Term
): Promise<string[]> {
return (
await fs.readdir(path.join(NEWS_PATH, year, term), {
@ -54,7 +55,7 @@ export async function getNewsByTerm(
export async function getNewsBySlug(
year: string,
term: string,
term: Term,
slug: string
): Promise<News> {
const raw = await fs.readFile(

3525
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"private": true,
"engines": {
"node": "^16",
"npm": "^7"
"npm": "^8.0.0"
},
"scripts": {
"dev": "next dev",
@ -20,7 +20,7 @@
"dependencies": {
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/mdx": "11.0.1",
"@next/mdx": "11.1.3",
"@squoosh/lib": "^0.4.0",
"date-fns": "^2.11.1",
"date-fns-tz": "^1.1.6",
@ -53,7 +53,7 @@
"postcss": "^8.3.0",
"postcss-calc": "^8.0.0",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-preset-env": "^6.7.0",
"postcss-preset-env": "^7.0.0",
"ts-node": "^10.2.1",
"typescript": "4.3.5"
}

View File

@ -6,14 +6,14 @@ import { Table } from "@/components/Table";
import { Title } from "@/components/Title";
import { getCurrentTerm } from "@/lib/events";
import { getMembers, Member } from "@/lib/members";
import { capitalize } from "@/utils";
import { Term, capitalize } from "@/utils";
import styles from "./members.module.css";
interface Props {
members: Member[];
year: string;
term: string;
term: Term;
}
export default function Members(props: Props) {

View File

@ -18,7 +18,7 @@ import {
getEventsByTerm,
getEventBySlug,
} from "@/lib/events";
import { capitalize } from "@/utils";
import { capitalize, Term } from "@/utils";
export default function EventInfoPage({ year, term, event }: Props) {
return (
@ -43,13 +43,13 @@ EventInfoPage.getShapesConfig = ((width, height) => {
interface Props {
year: string;
term: string;
term: Term;
event: Event;
}
interface Params extends ParsedUrlQuery {
year: string;
term: string;
term: Term;
event: string;
}

View File

@ -14,21 +14,21 @@ import {
getEventYears,
getEventTermsByYear,
} from "@/lib/events";
import { capitalize } from "@/utils";
import { capitalize, Term } from "@/utils";
import styles from "./index.module.css";
export interface Props {
year: string;
term: string;
term: Term;
pastEvents: Event[];
futureEvents: Event[];
isCurrentTerm: boolean;
pastTerms: { year: string; term: string }[];
futureTerms: { year: string; term: string }[];
pastTerms: { year: string; term: Term }[];
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 }];
// p, Current, f
@ -120,7 +120,7 @@ export default function Term(props: Props) {
function HeaderLink(props: {
year: string;
term: string;
term: Term;
isCurrentTerm?: boolean;
}) {
return (
@ -134,7 +134,7 @@ function HeaderLink(props: {
interface Params extends ParsedUrlQuery {
year: string;
term: string;
term: Term;
}
export const getStaticProps: GetStaticProps<Props, Params> = async (

View File

@ -6,12 +6,13 @@ import React from "react";
import { Link } from "@/components/Link";
import { Title } from "@/components/Title";
import { getEventYears, getEventTermsByYear } from "@/lib/events";
import { Term } from "@/utils";
import styles from "./index.module.css";
interface Props {
year: string;
terms: string[];
terms: Term[];
}
export default function Year(props: Props) {

View File

@ -2,9 +2,9 @@ import { GetStaticProps } from "next";
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 () => {
return { props: await getEventsPageProps(getCurrentTerm()) };

View File

@ -37,6 +37,7 @@
justify-content: center;
align-items: center;
gap: 1rem;
margin: calc(8rem / 16);
}
.clubTitleWrapper img {
@ -49,6 +50,7 @@
text-align: center;
white-space: nowrap;
color: var(--primary-heading);
margin: 0;
}
.clubDescription {

View File

@ -18,13 +18,13 @@ import {
getNewsYears,
News,
} from "@/lib/news";
import { capitalize } from "@/utils";
import { capitalize, Term } from "@/utils";
import styles from "./[term].module.css";
interface Props {
year: string;
term: string;
term: Term;
news: News[];
}
@ -75,7 +75,7 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
interface Params extends ParsedUrlQuery {
year: string;
term: string;
term: Term;
}
export const getStaticPaths: GetStaticPaths<Params> = async () => {

View File

@ -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) {
return str.slice(0, 1).toUpperCase() + str.slice(1);
}