diff --git a/src/components/ArticleList.tsx b/src/components/ArticleList.tsx
index a77eb4c..b451fbd 100644
--- a/src/components/ArticleList.tsx
+++ b/src/components/ArticleList.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import ArticleRow from './ArticleRow';
import sanity from '../sanity';
import '../css/article.css';
+import BlueButton from './BlueButton';
export default function ArticleList() {
let [articles, setArticles] = React.useState([]);
@@ -40,27 +41,30 @@ export default function ArticleList() {
return null;
}
+ let bottomComponent: any;
+ if (reachedEnd) {
+ bottomComponent = No more articles to show
;
+ } else {
+ bottomComponent = (
+ {
+ let { publish_date, title } = articles[articles.length - 1];
+ addArticles(publish_date, title);
+ }}
+ >
+ Load more articles
+
+ );
+ }
+
+ const articleList = articles.map((article) => (
+
+ ));
+
return (
- {articles.map((article) => {
- return
;
- })}
-
-
- {!reachedEnd ? (
-
{
- let lastArticle = articles[articles.length - 1];
- addArticles(lastArticle.publish_date, lastArticle.title);
- }}
- >
- Load more articles
-
- ) : (
-
No more articles to show
- )}
-
+ {articleList}
+
{bottomComponent}
);
}
diff --git a/src/components/ArticleRow.tsx b/src/components/ArticleRow.tsx
index 0f6ef20..bf0ff4e 100644
--- a/src/components/ArticleRow.tsx
+++ b/src/components/ArticleRow.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import imageUrl from '../imageUrl';
+import imageUrl from '../lib/imageUrl';
import { Link } from 'react-router-dom';
import '../css/article.css';
diff --git a/src/components/BlockContentWithExternalLinks.tsx b/src/components/BlockContentWithExternalLinks.tsx
new file mode 100644
index 0000000..fee0822
--- /dev/null
+++ b/src/components/BlockContentWithExternalLinks.tsx
@@ -0,0 +1,23 @@
+import BlockContent from '@sanity/block-content-to-react';
+
+export default function BlockContentWithExternalLinks({
+ blocks,
+}: {
+ blocks: any[];
+}) {
+ return (
+ {
+ // When this element loads, convert all the links to have target="_blank."
+ // This ensures that the links open in a new tab
+ if (ref) {
+ ref.querySelectorAll('a').forEach((link) => {
+ link.target = '_blank';
+ });
+ }
+ }}
+ >
+
+
+ );
+}
diff --git a/src/components/BlueButton.tsx b/src/components/BlueButton.tsx
new file mode 100644
index 0000000..6800cd4
--- /dev/null
+++ b/src/components/BlueButton.tsx
@@ -0,0 +1,15 @@
+import { MouseEventHandler } from 'react';
+
+export default function BlueButton({
+ onClick,
+ children,
+}: {
+ onClick?: MouseEventHandler;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/BlueButtonLink.tsx b/src/components/BlueButtonLink.tsx
new file mode 100644
index 0000000..6d151bb
--- /dev/null
+++ b/src/components/BlueButtonLink.tsx
@@ -0,0 +1,23 @@
+import { Link } from 'react-router-dom';
+
+export default function BlueButtonLink({
+ href,
+ children,
+}: {
+ href: string;
+ children: React.ReactNode;
+}) {
+ if (!href.startsWith('http')) {
+ return (
+
+ {children}
+
+ );
+ } else {
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/src/components/Centered.tsx b/src/components/Centered.tsx
new file mode 100644
index 0000000..5409566
--- /dev/null
+++ b/src/components/Centered.tsx
@@ -0,0 +1,3 @@
+export default function Centered({ children }: { children: React.ReactNode }) {
+ return {children}
;
+}
diff --git a/src/components/InitiativeColumn.tsx b/src/components/InitiativeColumn.tsx
index 6862fe5..8bc109c 100644
--- a/src/components/InitiativeColumn.tsx
+++ b/src/components/InitiativeColumn.tsx
@@ -1,5 +1,5 @@
import BlockContent from '@sanity/block-content-to-react';
-import imageUrl from '../imageUrl';
+import imageUrl from '../lib/imageUrl';
import '../css/initiative.css';
export default function InitiativeColumn({ name, thumbnail, content }) {
diff --git a/src/components/InitiativeRow.tsx b/src/components/InitiativeRow.tsx
index 5e7f2fd..edc7b38 100644
--- a/src/components/InitiativeRow.tsx
+++ b/src/components/InitiativeRow.tsx
@@ -1,5 +1,4 @@
-import React from 'react';
-import imageUrl from '../imageUrl';
+import imageUrl from '../lib/imageUrl';
import BlockContent from '@sanity/block-content-to-react';
import '../css/article.css';
diff --git a/src/components/LocalLinkClickable.tsx b/src/components/LocalLinkClickable.tsx
new file mode 100644
index 0000000..2e0ea9b
--- /dev/null
+++ b/src/components/LocalLinkClickable.tsx
@@ -0,0 +1,15 @@
+import { Link } from 'react-router-dom';
+
+export default function LocalLinkClickable({
+ to,
+ children,
+}: {
+ to: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/MemberRow.tsx b/src/components/MemberRow.tsx
index 6cb1405..e4faac6 100644
--- a/src/components/MemberRow.tsx
+++ b/src/components/MemberRow.tsx
@@ -1,4 +1,4 @@
-import imageUrl from '../imageUrl';
+import imageUrl from '../lib/imageUrl';
import '../css/article.css';
export default function MemberRow({ member }: { member: SGA.MemberDocument }) {
diff --git a/src/components/ParagraphHeader.tsx b/src/components/ParagraphHeader.tsx
new file mode 100644
index 0000000..73bbc7d
--- /dev/null
+++ b/src/components/ParagraphHeader.tsx
@@ -0,0 +1,9 @@
+export default function ParagraphHeader({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+ {children}
+ );
+}
diff --git a/src/components/PrimaryHeader.tsx b/src/components/PrimaryHeader.tsx
new file mode 100644
index 0000000..868b4e6
--- /dev/null
+++ b/src/components/PrimaryHeader.tsx
@@ -0,0 +1,7 @@
+export default function PrimaryHeader({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return {children} ;
+}
diff --git a/src/hooks/useCommittee.tsx b/src/hooks/useCommittee.tsx
new file mode 100644
index 0000000..7553e83
--- /dev/null
+++ b/src/hooks/useCommittee.tsx
@@ -0,0 +1,8 @@
+import useQuery from './useQuery';
+
+export default function useCommittee(committee: string) {
+ return useQuery(
+ `*[_type == 'member' && committee == $committee]`,
+ { committee }
+ );
+}
diff --git a/src/hooks/useNewsArticle.ts b/src/hooks/useNewsArticle.ts
new file mode 100644
index 0000000..c347bf7
--- /dev/null
+++ b/src/hooks/useNewsArticle.ts
@@ -0,0 +1,12 @@
+import { useEffect, useState } from 'react';
+import sanity from '../sanity';
+
+export default function useNewsArticle(articleId: string) {
+ let [article, setArticle] = useState(null!);
+
+ useEffect(() => {
+ sanity.fetch('*[_id == $articleId] [0]', { articleId }).then(setArticle);
+ }, [articleId]);
+
+ return article;
+}
diff --git a/src/imageUrl.ts b/src/lib/imageUrl.ts
similarity index 90%
rename from src/imageUrl.ts
rename to src/lib/imageUrl.ts
index 4b5828e..c9762f9 100644
--- a/src/imageUrl.ts
+++ b/src/lib/imageUrl.ts
@@ -1,5 +1,5 @@
import { default as ImageUrlBuilder } from '@sanity/image-url';
-import sanity from './sanity';
+import sanity from '../sanity';
const builder = ImageUrlBuilder(sanity);
diff --git a/src/lib/sortCommittee.ts b/src/lib/sortCommittee.ts
new file mode 100644
index 0000000..3d9a750
--- /dev/null
+++ b/src/lib/sortCommittee.ts
@@ -0,0 +1,24 @@
+export default function sortCommittee(
+ members: SGA.MemberDocument[],
+ roleOrder: string[]
+) {
+ const roleIndexes = {};
+ for (const { _id, role } of members) {
+ roleIndexes[_id] = roleOrder.findIndex((role_) => role_ === role);
+ }
+
+ return members.sort((a, b) => {
+ let roleDifference = roleIndexes[a._id] - roleIndexes[b._id];
+ if (roleDifference !== 0) {
+ return roleDifference;
+ } else {
+ if (a.name < b.name) {
+ return -1;
+ } else if (a.name > b.name) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+}
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 06487a9..5b060ed 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,8 +1,11 @@
+import Centered from '../components/Centered';
+import PrimaryHeader from '../components/PrimaryHeader';
+
export default function NotFoundPage() {
return (
-
-
404: Not Found
+
+ 404: Not Found
This page wasn't found...
-
+
);
}
diff --git a/src/pages/classcouncil.tsx b/src/pages/classcouncil.tsx
index 361490f..cf2ce4d 100644
--- a/src/pages/classcouncil.tsx
+++ b/src/pages/classcouncil.tsx
@@ -4,18 +4,18 @@ import MemberRow from '../components/MemberRow';
import useQuery from '../hooks/useQuery';
export default function ClassCouncil() {
- let members = useQuery(
- `*[_type == 'member' && role == 'class'] | order (year desc)`
- );
+ let members =
+ useQuery(
+ `*[_type == 'member' && role == 'class'] | order (year desc)`
+ ) ?? [];
return (
<>
- {members &&
- members.map((member) => {
- return ;
- })}
+ {members.map((member) => (
+
+ ))}
>
);
diff --git a/src/pages/committee.tsx b/src/pages/committee.tsx
index b5729d3..2ebb424 100644
--- a/src/pages/committee.tsx
+++ b/src/pages/committee.tsx
@@ -4,9 +4,10 @@ import MemberRow from '../components/MemberRow';
import useQuery from '../hooks/useQuery';
export default function Committee() {
- let excomm = useQuery(
- `*[_type == 'member' && committee == 'excomm'] | order (role, year desc)`
- );
+ let excomm =
+ useQuery(
+ `*[_type == 'member' && committee == 'excomm'] | order (role, year desc)`
+ ) ?? [];
// year desc because seniority 8)
return (
@@ -16,12 +17,9 @@ export default function Committee() {
imageURL='/images/who-we-are/excomm.png'
/>
-
- {excomm &&
- excomm.map((member) => {
- return ;
- })}
-
+ {excomm.map((member) => (
+
+ ))}
>
);
diff --git a/src/pages/initiatives.tsx b/src/pages/initiatives.tsx
index 079b66e..f5138d1 100644
--- a/src/pages/initiatives.tsx
+++ b/src/pages/initiatives.tsx
@@ -4,19 +4,17 @@ import InitiativeRow from '../components/InitiativeRow';
import useQuery from '../hooks/useQuery';
export default function Initiatives() {
- let initiatives = useQuery(
- '*[_type == "initiative"]'
- );
+ let initiatives =
+ useQuery('*[_type == "initiative"]') ?? [];
return (
<>
- {initiatives &&
- initiatives.map((initiative) => {
- return ;
- })}
+ {initiatives.map((initiative) => (
+
+ ))}
>
diff --git a/src/pages/involved.tsx b/src/pages/involved.tsx
index feefb16..9074fbb 100644
--- a/src/pages/involved.tsx
+++ b/src/pages/involved.tsx
@@ -1,25 +1,28 @@
import { SanityDocument } from '@sanity/client';
import React from 'react';
-import { Link } from 'react-router-dom';
+import BlueButtonLink from '../components/BlueButtonLink';
import GetInvolvedRow from '../components/GetInvolvedRow';
import Hero from '../components/Hero';
+import ParagraphHeader from '../components/ParagraphHeader';
import '../css/get-involved.css';
import sanity from '../sanity';
export default function GetInvolved() {
- let [ways, setWays] = React.useState<
+ let [getInvolved, setGetInvolved] = React.useState<
SanityDocument | undefined
>();
React.useEffect(() => {
- sanity.getDocument('get_involved').then(setWays);
+ sanity
+ .getDocument('get_involved')
+ .then(setGetInvolved);
}, []);
return (
<>
- SGA Calendar
+ SGA Calendar
- {ways ? (
- <>
-
- Here are some ways to connect with SGA:
-
- {ways.ways.map((way) => (
+
+ Here are some ways to connect with SGA:
+
+
+ {getInvolved
+ ? getInvolved.ways.map((way) => (
- ))}
- >
- ) : null}
-
- Give Feedback
-
+ ))
+ : null}
+
+ Give Feedback
>
);
diff --git a/src/pages/mission.tsx b/src/pages/mission.tsx
index baa65a4..bf88c7f 100644
--- a/src/pages/mission.tsx
+++ b/src/pages/mission.tsx
@@ -2,6 +2,35 @@ import React from 'react';
import Hero from '../components/Hero';
import useMission from '../hooks/useMission';
import '../css/mission.css';
+import BlueButtonLink from '../components/BlueButtonLink';
+import Centered from '../components/Centered';
+
+function MissionQuote({ text, author }) {
+ return (
+
+ “{text}”
+
+
+ — {author}
+
+ );
+}
+
+function MissionParagraph({ title, body }: { title: string; body: string }) {
+ return (
+
+ );
+}
+
+const previousLeadershipLink =
+ 'https://docs.google.com/spreadsheets/d/1a3RYdqrDi1IPG9BKWQ2xhoX3YCPQKUl_FsRLvIVEMPg/edit?usp=drive_open&ouid=0';
export default function Mission() {
let mission = useMission();
@@ -11,38 +40,19 @@ export default function Mission() {
{mission ? (
-
- “{mission.quote_text}”
-
-
-
- — {mission.quote_author}
-
-
-
-
-
+
+
) : null}
>
diff --git a/src/pages/newsArticle.tsx b/src/pages/newsArticle.tsx
index 284ebb0..70e8473 100644
--- a/src/pages/newsArticle.tsx
+++ b/src/pages/newsArticle.tsx
@@ -1,62 +1,43 @@
import React from 'react';
-import { Link, useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom';
import Hero from '../components/Hero';
-import imageUrl from '../imageUrl';
-import BlockContent from '@sanity/block-content-to-react';
-import sanity from '../sanity';
+import imageUrl from '../lib/imageUrl';
import '../css/article.css';
+import useNewsArticle from '../hooks/useNewsArticle';
+import LocalLinkClickable from '../components/LocalLinkClickable';
+import BlockContentWithExternalLinks from '../components/BlockContentWithExternalLinks';
+import PrimaryHeader from '../components/PrimaryHeader';
export default function NewsArticle() {
let { articleId } = useParams<{ articleId: string }>();
- let [article, setArticle] = React.useState(null!);
+ let article = useNewsArticle(articleId);
- React.useEffect(() => {
- sanity.fetch('*[_id == $articleId] [0]', { articleId }).then(setArticle);
- }, [articleId]);
-
- let thumbUrl: string;
+ let thumbnailUrl = '/images/hero.png';
if (article?.thumbnail) {
- thumbUrl = imageUrl(article.thumbnail).url();
- } else {
- thumbUrl = '/images/hero.png';
+ thumbnailUrl = imageUrl(article.thumbnail).url();
}
+ const goToAllNewsArticles = (
+ Go to all news articles
+ );
+
return (
<>
-
+
{article ? (
-
- Go to all news articles
-
-
{article.title}
+ {goToAllNewsArticles}
+
{article.title}
Posted {article.publish_date} by {article.author || 'No author'}
- {/* Wrap the BlockContent in a div because it expands to <>> */}
-
{
- /*
- When this element loads, convert all the links to have target="_blank."
- This ensures that the links open in a new tab
- */
- if (ref) {
- ref.querySelectorAll('a').forEach((link) => {
- link.target = '_blank';
- });
- }
- }}
- >
-
+
+
-
- Go to all news articles
-
+ {goToAllNewsArticles}
) : null}
diff --git a/src/pages/officers.tsx b/src/pages/officers.tsx
index 2ce803f..a4eef1b 100644
--- a/src/pages/officers.tsx
+++ b/src/pages/officers.tsx
@@ -1,27 +1,37 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import Hero from '../components/Hero';
import MemberRow from '../components/MemberRow';
-import useQuery from '../hooks/useQuery';
+import sortCommittee from '../lib/sortCommittee';
+import sanity from '../sanity';
+
+const officerOrder = [
+ 'SGA President',
+ 'SGA Vice-President',
+ 'SGA Treasurer',
+ 'SGA Secretary',
+];
export default function Officers() {
- let officers = useQuery
(
- `*[_type == 'member' && committee == 'officer']`
- );
+ const [officers, setOfficers] = useState();
- if (!officers) {
- return null;
- }
+ useEffect(() => {
+ sanity
+ .fetch("*[_type == 'member' && committee == $committee]", {
+ committee: 'officer',
+ })
+ .then(setOfficers);
+ }, []);
+
+ const officersSorted = sortCommittee(officers ?? [], officerOrder);
+ const officerList = officersSorted.map((officer) => (
+
+ ));
+ console.log(officersSorted);
return (
<>
-
- {officers
- ? officers.map((officer) => {
- return ;
- })
- : null}
-
+ {officerList}
>
);
}