From 2b6822ac7aa9568f9ba640a6619bebe901ca9621 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Thu, 27 Jul 2023 16:35:50 -0700 Subject: [PATCH 1/4] feat: organizer page now fetching from Sanity --- apps/site/src/app/about/TeamCard.tsx | 4 +-- apps/site/src/app/about/TeamSection.tsx | 16 +++++----- apps/site/src/app/about/getMembers.ts | 42 +++++++++---------------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/apps/site/src/app/about/TeamCard.tsx b/apps/site/src/app/about/TeamCard.tsx index a3c59b1..4b3b088 100644 --- a/apps/site/src/app/about/TeamCard.tsx +++ b/apps/site/src/app/about/TeamCard.tsx @@ -19,8 +19,8 @@ import styles from "./TeamCard.module.scss"; interface TeamCardProps { name: string; position: string; - image: string; - linkedInUrl: string; + image: string | undefined; + linkedInUrl: string | undefined; } const TeamCard = ({ name, position, image, linkedInUrl }: TeamCardProps) => { return ( diff --git a/apps/site/src/app/about/TeamSection.tsx b/apps/site/src/app/about/TeamSection.tsx index bfb0dbb..76cdd20 100644 --- a/apps/site/src/app/about/TeamSection.tsx +++ b/apps/site/src/app/about/TeamSection.tsx @@ -4,10 +4,12 @@ import TeamCard from "./TeamCard"; import styles from "./TeamSection.module.scss"; type Member = { - name: string; + person: { + name: string; + profilePic?: { asset: { url: string } }; + socials?: { link: string }; + }; position: string; - image: string; - linkedInUrl: string; }; interface TeamSection { @@ -21,11 +23,11 @@ const TeamSection = ({ team, members }: TeamSection) => {
{members.map((member) => ( ))}
diff --git a/apps/site/src/app/about/getMembers.ts b/apps/site/src/app/about/getMembers.ts index f763b47..fb59782 100644 --- a/apps/site/src/app/about/getMembers.ts +++ b/apps/site/src/app/about/getMembers.ts @@ -1,28 +1,14 @@ -const FEED_URL = "https://docs.google.com/spreadsheets/d/"; -const SPREADSHEET_KEY = "1DWCOQBlzA3mpa2BXYXPmQrF9_-SpoPFbTdDQuCQ83hU"; -const QUERY = "pub"; -const FORMAT = "output=tsv"; - -const dataURL = FEED_URL + SPREADSHEET_KEY + "/" + QUERY + "?" + FORMAT; - -const getSheetsData = async (page: string) => { - const response = await fetch(dataURL); - - if (response.status !== 200) - console.warn( - "Error ocurred while fetching schedule data:", - response.statusText - ); - - 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); - } - } - - return null; -}; - -export const getMembers = async () => await getSheetsData("members"); +import { cache } from "react"; +import { client } from "@/lib/sanity/sanityClient"; + +export const getMembers = cache(async () => { + return await client + .fetch( + `*[_type == 'boardYear'][0]{corporate[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, + logistics[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, + marketing[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, + tech[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}}` + ) + .then((result) => result) + .catch((error) => console.warn(error)); +}); From ddc7a8a8cf81cfb239b1755cf3c5b418668c674c Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Thu, 27 Jul 2023 20:02:23 -0700 Subject: [PATCH 2/4] fix: switched LinkedIn icons to non-transparent icons --- apps/site/src/app/about/TeamCard.tsx | 2 +- .../site/src/lib/common/assets/linkedin-logo.png | Bin 0 -> 6192 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 apps/site/src/lib/common/assets/linkedin-logo.png diff --git a/apps/site/src/app/about/TeamCard.tsx b/apps/site/src/app/about/TeamCard.tsx index 4b3b088..52f2a0a 100644 --- a/apps/site/src/app/about/TeamCard.tsx +++ b/apps/site/src/app/about/TeamCard.tsx @@ -2,7 +2,7 @@ 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.png"; import blank from "./blank.png"; import styles from "./TeamCard.module.scss"; diff --git a/apps/site/src/lib/common/assets/linkedin-logo.png b/apps/site/src/lib/common/assets/linkedin-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56ba168ae786069bd375298f385211dc9f38901c GIT binary patch literal 6192 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 literal 0 HcmV?d00001 From ccbb98d6183b8f58865d38ce70838cb7c0ee38b3 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Wed, 9 Aug 2023 14:41:51 -0700 Subject: [PATCH 3/4] fix: added zod validation and used @sanity/image-url --- apps/site/src/app/about/TeamCard.tsx | 15 +- apps/site/src/app/about/TeamSection.tsx | 8 +- apps/site/src/app/about/getMembers.ts | 95 ++++++++++-- apps/site/src/app/about/page.tsx | 3 +- .../src/lib/common/assets/linkedin-logo.svg | 142 ++++++++++++++++++ 5 files changed, 245 insertions(+), 18 deletions(-) create mode 100644 apps/site/src/lib/common/assets/linkedin-logo.svg diff --git a/apps/site/src/app/about/TeamCard.tsx b/apps/site/src/app/about/TeamCard.tsx index 52f2a0a..6786269 100644 --- a/apps/site/src/app/about/TeamCard.tsx +++ b/apps/site/src/app/about/TeamCard.tsx @@ -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/linkedin-logo.png"; +import linkedinLogo from "@/lib/common/assets/linkedin-logo.svg"; 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"; @@ -16,18 +22,19 @@ import styles from "./TeamCard.module.scss"; // // + interface TeamCardProps { name: string; position: string; - image: string | undefined; - linkedInUrl: string | undefined; + image: z.infer | null; + linkedInUrl?: string; } const TeamCard = ({ name, position, image, linkedInUrl }: TeamCardProps) => { return (
{name} diff --git a/apps/site/src/app/about/TeamSection.tsx b/apps/site/src/app/about/TeamSection.tsx index 76cdd20..3ef93b1 100644 --- a/apps/site/src/app/about/TeamSection.tsx +++ b/apps/site/src/app/about/TeamSection.tsx @@ -1,13 +1,15 @@ 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 = { person: { name: string; - profilePic?: { asset: { url: string } }; - socials?: { link: string }; + profilePic: z.infer | null; + socials: { link: string } | null; }; position: string; }; @@ -26,7 +28,7 @@ const TeamSection = ({ team, members }: TeamSection) => { key={member.person.name} name={member.person.name} position={member.position} - image={member.person.profilePic?.asset.url} + image={member.person.profilePic} linkedInUrl={member.person.socials?.link} /> ))} diff --git a/apps/site/src/app/about/getMembers.ts b/apps/site/src/app/about/getMembers.ts index fb59782..9614c6a 100644 --- a/apps/site/src/app/about/getMembers.ts +++ b/apps/site/src/app/about/getMembers.ts @@ -1,14 +1,91 @@ +import { z } from "zod"; import { cache } from "react"; import { client } from "@/lib/sanity/sanityClient"; +import { SanityImageReference } from "@/lib/sanity/types"; + +const Person = z.object({ + _type: z.literal("person"), + name: z.string(), + profilePic: SanityImageReference.nullable(), + socials: z + .object({ + link: z.string(), + }) + .nullable(), +}); + +export const Members = z.object({ + corporate: z.array( + z.object({ + person: Person, + position: z.string(), + }) + ), + logistics: z.array( + z.object({ + person: Person, + position: z.string(), + }) + ), + marketing: z.array( + z.object({ + person: Person, + position: z.string(), + }) + ), + tech: z.array( + z.object({ + person: Person, + position: z.string(), + }) + ), +}); export const getMembers = cache(async () => { - return await client - .fetch( - `*[_type == 'boardYear'][0]{corporate[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, - logistics[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, - marketing[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}, - tech[]{person->{name, profilePic{asset->{url}}, socials[0]{link}}, position}}` - ) - .then((result) => result) - .catch((error) => console.warn(error)); + try { + return Members.parse( + await client.fetch( + `*[_type == 'boardYear'] | order(year desc) [0]{ + corporate[]{ + person->{ + _type, + name, + profilePic, + socials[0] {link} + }, + 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 + }, + }` + ) + ); + } catch (error) { + console.error(error); + } }); diff --git a/apps/site/src/app/about/page.tsx b/apps/site/src/app/about/page.tsx index 0cd37c2..ab9f3a3 100644 --- a/apps/site/src/app/about/page.tsx +++ b/apps/site/src/app/about/page.tsx @@ -47,8 +47,7 @@ export default async function Home() {

- {Object.entries(teamMembers).map(([team, members]) => ( - // @ts-expect-error + {Object.entries(teamMembers!).map(([team, members]) => ( ))}
diff --git a/apps/site/src/lib/common/assets/linkedin-logo.svg b/apps/site/src/lib/common/assets/linkedin-logo.svg new file mode 100644 index 0000000..358274a --- /dev/null +++ b/apps/site/src/lib/common/assets/linkedin-logo.svg @@ -0,0 +1,142 @@ + + + + + + + \ No newline at end of file From 0679ca098ff274c405dbe5022be189c478a60d29 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Mon, 21 Aug 2023 13:29:20 -0700 Subject: [PATCH 4/4] fix: throw error if teamMembers is undefined --- apps/site/src/app/about/TeamCard.tsx | 6 ++++- apps/site/src/app/about/getMembers.ts | 35 +++++++++------------------ apps/site/src/app/about/page.tsx | 6 ++++- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/apps/site/src/app/about/TeamCard.tsx b/apps/site/src/app/about/TeamCard.tsx index 6786269..345d74d 100644 --- a/apps/site/src/app/about/TeamCard.tsx +++ b/apps/site/src/app/about/TeamCard.tsx @@ -34,7 +34,11 @@ const TeamCard = ({ name, position, image, linkedInUrl }: TeamCardProps) => {
{name} diff --git a/apps/site/src/app/about/getMembers.ts b/apps/site/src/app/about/getMembers.ts index 9614c6a..c633a56 100644 --- a/apps/site/src/app/about/getMembers.ts +++ b/apps/site/src/app/about/getMembers.ts @@ -14,31 +14,18 @@ const Person = z.object({ .nullable(), }); +const Department = z.array( + z.object({ + person: Person, + position: z.string(), + }) +); + export const Members = z.object({ - corporate: z.array( - z.object({ - person: Person, - position: z.string(), - }) - ), - logistics: z.array( - z.object({ - person: Person, - position: z.string(), - }) - ), - marketing: z.array( - z.object({ - person: Person, - position: z.string(), - }) - ), - tech: z.array( - z.object({ - person: Person, - position: z.string(), - }) - ), + corporate: Department, + logistics: Department, + marketing: Department, + tech: Department, }); export const getMembers = cache(async () => { diff --git a/apps/site/src/app/about/page.tsx b/apps/site/src/app/about/page.tsx index ab9f3a3..a59e1a5 100644 --- a/apps/site/src/app/about/page.tsx +++ b/apps/site/src/app/about/page.tsx @@ -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 (
@@ -47,7 +51,7 @@ export default async function Home() {

- {Object.entries(teamMembers!).map(([team, members]) => ( + {Object.entries(teamMembers).map(([team, members]) => ( ))}