Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organizer page is now fetching from Sanity #21

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions apps/site/src/app/about/TeamCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { z } from "zod";
/* eslint-disable @next/next/no-img-element */
import type { StaticImageData } from "next/image";
import clsx from "clsx";
// could we replace with something like simple-icons (https://github.com/simple-icons/simple-icons)?
import linkedinLogo from "@/lib/common/assets/icons/linkedin.svg";
import linkedinLogo from "@/lib/common/assets/linkedin-logo.svg";
Comment on lines -5 to +6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chore: if we're replacing the logo icon, could we remove the older version.

import blank from "./blank.png";
import { client } from "@/lib/sanity/sanityClient";
import { SanityImageReference } from "@/lib/sanity/types";
import imageUrlBuilder from "@sanity/image-url";

const urlBuilder = imageUrlBuilder(client);

import styles from "./TeamCard.module.scss";

Expand All @@ -16,18 +22,23 @@ import styles from "./TeamCard.module.scss";
//

//

interface TeamCardProps {
name: string;
position: string;
image: string;
linkedInUrl: string;
image: z.infer<typeof SanityImageReference> | null;
linkedInUrl?: string;
}
const TeamCard = ({ name, position, image, linkedInUrl }: TeamCardProps) => {
return (
<div className={styles.teamCard}>
<div className={styles.imagesContainer}>
<img
src={image || blank.src}
src={
image
? urlBuilder.image(image).size(200, 200).dpr(3).url()
: blank.src
alexanderl19 marked this conversation as resolved.
Show resolved Hide resolved
}
className={styles.profilePicture}
alt={name}
/>
Expand Down
18 changes: 11 additions & 7 deletions apps/site/src/app/about/TeamSection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import clsx from "clsx";
import TeamCard from "./TeamCard";
import { z } from "zod";
import { SanityImageReference } from "@/lib/sanity/types";

import styles from "./TeamSection.module.scss";

type Member = {
name: string;
person: {
name: string;
profilePic: z.infer<typeof SanityImageReference> | null;
socials: { link: string } | null;
};
position: string;
image: string;
linkedInUrl: string;
};

interface TeamSection {
Expand All @@ -21,11 +25,11 @@ const TeamSection = ({ team, members }: TeamSection) => {
<div className={styles.teamGrid}>
{members.map((member) => (
<TeamCard
key={member.name}
name={member.name}
key={member.person.name}
name={member.person.name}
position={member.position}
image={member.image}
linkedInUrl={member.linkedInUrl}
image={member.person.profilePic}
linkedInUrl={member.person.socials?.link}
/>
))}
</div>
Expand Down
96 changes: 73 additions & 23 deletions apps/site/src/app/about/getMembers.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,78 @@
const FEED_URL = "https://docs.google.com/spreadsheets/d/";
const SPREADSHEET_KEY = "1DWCOQBlzA3mpa2BXYXPmQrF9_-SpoPFbTdDQuCQ83hU";
const QUERY = "pub";
const FORMAT = "output=tsv";
import { z } from "zod";
import { cache } from "react";
import { client } from "@/lib/sanity/sanityClient";
import { SanityImageReference } from "@/lib/sanity/types";

const dataURL = FEED_URL + SPREADSHEET_KEY + "/" + QUERY + "?" + FORMAT;
const Person = z.object({
_type: z.literal("person"),
name: z.string(),
profilePic: SanityImageReference.nullable(),
socials: z
.object({
link: z.string(),
})
.nullable(),
});

const getSheetsData = async (page: string) => {
const response = await fetch(dataURL);
const Department = z.array(
z.object({
person: Person,
position: z.string(),
})
Comment on lines +19 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why is position separate from Person? Wouldn't it make more sense for position to be an attribute of the Person? Or is this to handle a person being part of multiple departments and be able to reuse a Person? Probably uncommon, but in such a case, someone might want different profile photos per department?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is focused on pulling content from Sanity, so the Zod schema here is just validating what's returned from Sanity.

As far as I can remember, we decided to separate the Person object from organizers to make it more reusable. For example, people may switch to different departments between years, or we may decided to use the Person object as an author field in the future. The profile photo was something we discussed at the time, but ultimately decided that it wasn't worth the extra complexity. Might be something we have to revisit in the future?

cc @samderanova

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding on to what Alex already mentioned, there might be former organizers that we might want to display on other sections of the site in the future (ex. alumni showcases). In situations like those, it would be better to just have one schema for all of our people and then use them in other schemas where needed instead of having two schemas to do the same.

);

if (response.status !== 200)
console.warn(
"Error ocurred while fetching schedule data:",
response.statusText
);
export const Members = z.object({
corporate: Department,
alexanderl19 marked this conversation as resolved.
Show resolved Hide resolved
logistics: Department,
marketing: Department,
tech: Department,
});

const csv = await response.text();
for (const line of csv.split("\n")) {
const [key, value] = line.split("\t");
if (key === page) {
return JSON.parse(value);
}
export const getMembers = cache(async () => {
try {
return Members.parse(
await client.fetch(
`*[_type == 'boardYear'] | order(year desc) [0]{
corporate[]{
person->{
_type,
name,
profilePic,
socials[0] {link}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing socials[0] with socials[platform == 'linkedin'][0] should ensure that we fetch the member's LinkedIn social.
This is essential filtering the socials array for a value where platform is 'linkedin', then selecting the first result that's found. It's not dissimilar from what we're doing on L35, where we're filtering then selecting the first result.

position
},
logistics[]{
person->{
_type,
name,
profilePic,
socials[0] {link}
},
position
},
marketing[]{
person->{
_type,
name,
profilePic,
socials[0] {link}
},
position
},
tech[]{
person->{
_type,
name,
profilePic,
socials[0] {link}
},
position
},
}`
alexanderl19 marked this conversation as resolved.
Show resolved Hide resolved
)
);
} catch (error) {
console.error(error);
}

return null;
};

export const getMembers = async () => await getSheetsData("members");
});
5 changes: 4 additions & 1 deletion apps/site/src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export const metadata: Metadata = {
export default async function Home() {
const teamMembers = await getMembers();

if (teamMembers === undefined) {
throw new Error("teamMembers is undefined");
}

return (
<div className={styles.about}>
<Header title="About Us" />
Expand Down Expand Up @@ -48,7 +52,6 @@ export default async function Home() {
</div>
<div className={clsx(styles.aboutTeams, "container")}>
{Object.entries(teamMembers).map(([team, members]) => (
// @ts-expect-error
<TeamSection key={team} team={team} members={members} />
))}
</div>
Expand Down
Binary file added apps/site/src/lib/common/assets/linkedin-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
142 changes: 142 additions & 0 deletions apps/site/src/lib/common/assets/linkedin-logo.svg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: this asset seems quite large, is this official or made with a vectorizer? The color is also slightly off from the LinkedIn Blue. Would prefer to avoid diverging from brand guidelines.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading