mirror of
https://github.com/tjsga/tj-sga-website-react.git
synced 2025-04-09 22:50:17 -04:00
add officer order, some UI components
This commit is contained in:
parent
0339c587a2
commit
4e93c4a948
|
@ -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<SGA.ArticleDocument[]>([]);
|
||||
|
@ -40,27 +41,30 @@ export default function ArticleList() {
|
|||
return null;
|
||||
}
|
||||
|
||||
let bottomComponent: any;
|
||||
if (reachedEnd) {
|
||||
bottomComponent = <div>No more articles to show</div>;
|
||||
} else {
|
||||
bottomComponent = (
|
||||
<BlueButton
|
||||
onClick={() => {
|
||||
let { publish_date, title } = articles[articles.length - 1];
|
||||
addArticles(publish_date, title);
|
||||
}}
|
||||
>
|
||||
Load more articles
|
||||
</BlueButton>
|
||||
);
|
||||
}
|
||||
|
||||
const articleList = articles.map((article) => (
|
||||
<ArticleRow key={article._id} article={article} />
|
||||
));
|
||||
|
||||
return (
|
||||
<div>
|
||||
{articles.map((article) => {
|
||||
return <ArticleRow key={article._id} article={article} />;
|
||||
})}
|
||||
|
||||
<div className='text-center'>
|
||||
{!reachedEnd ? (
|
||||
<button
|
||||
className='blue-button'
|
||||
onClick={() => {
|
||||
let lastArticle = articles[articles.length - 1];
|
||||
addArticles(lastArticle.publish_date, lastArticle.title);
|
||||
}}
|
||||
>
|
||||
Load more articles
|
||||
</button>
|
||||
) : (
|
||||
<div>No more articles to show</div>
|
||||
)}
|
||||
</div>
|
||||
{articleList}
|
||||
<div className='text-center'>{bottomComponent}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
23
src/components/BlockContentWithExternalLinks.tsx
Normal file
23
src/components/BlockContentWithExternalLinks.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import BlockContent from '@sanity/block-content-to-react';
|
||||
|
||||
export default function BlockContentWithExternalLinks({
|
||||
blocks,
|
||||
}: {
|
||||
blocks: any[];
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
ref={(ref) => {
|
||||
// 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';
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<BlockContent blocks={blocks} />
|
||||
</div>
|
||||
);
|
||||
}
|
15
src/components/BlueButton.tsx
Normal file
15
src/components/BlueButton.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { MouseEventHandler } from 'react';
|
||||
|
||||
export default function BlueButton({
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
onClick?: MouseEventHandler;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<button onClick={onClick} className='blue-button'>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
23
src/components/BlueButtonLink.tsx
Normal file
23
src/components/BlueButtonLink.tsx
Normal file
|
@ -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 (
|
||||
<Link to={href} className='blue-button'>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<a href={href} className='blue-button'>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
3
src/components/Centered.tsx
Normal file
3
src/components/Centered.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function Centered({ children }: { children: React.ReactNode }) {
|
||||
return <div style={{ textAlign: 'center' }}>{children}</div>;
|
||||
}
|
|
@ -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 }) {
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
15
src/components/LocalLinkClickable.tsx
Normal file
15
src/components/LocalLinkClickable.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
export default function LocalLinkClickable({
|
||||
to,
|
||||
children,
|
||||
}: {
|
||||
to: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Link to={to} className='clickable-link'>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
|
@ -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 }) {
|
||||
|
|
9
src/components/ParagraphHeader.tsx
Normal file
9
src/components/ParagraphHeader.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default function ParagraphHeader({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<h2 style={{ marginTop: '4rem', marginBottom: '1.5rem' }}>{children}</h2>
|
||||
);
|
||||
}
|
7
src/components/PrimaryHeader.tsx
Normal file
7
src/components/PrimaryHeader.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function PrimaryHeader({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <h1 className='my-4'>{children}</h1>;
|
||||
}
|
8
src/hooks/useCommittee.tsx
Normal file
8
src/hooks/useCommittee.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import useQuery from './useQuery';
|
||||
|
||||
export default function useCommittee(committee: string) {
|
||||
return useQuery<SGA.MemberDocument[]>(
|
||||
`*[_type == 'member' && committee == $committee]`,
|
||||
{ committee }
|
||||
);
|
||||
}
|
12
src/hooks/useNewsArticle.ts
Normal file
12
src/hooks/useNewsArticle.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import sanity from '../sanity';
|
||||
|
||||
export default function useNewsArticle(articleId: string) {
|
||||
let [article, setArticle] = useState<SGA.ArticleDocument>(null!);
|
||||
|
||||
useEffect(() => {
|
||||
sanity.fetch('*[_id == $articleId] [0]', { articleId }).then(setArticle);
|
||||
}, [articleId]);
|
||||
|
||||
return article;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { default as ImageUrlBuilder } from '@sanity/image-url';
|
||||
import sanity from './sanity';
|
||||
import sanity from '../sanity';
|
||||
|
||||
const builder = ImageUrlBuilder(sanity);
|
||||
|
24
src/lib/sortCommittee.ts
Normal file
24
src/lib/sortCommittee.ts
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
import Centered from '../components/Centered';
|
||||
import PrimaryHeader from '../components/PrimaryHeader';
|
||||
|
||||
export default function NotFoundPage() {
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<h1>404: Not Found</h1>
|
||||
<Centered>
|
||||
<PrimaryHeader>404: Not Found</PrimaryHeader>
|
||||
<p>This page wasn't found...</p>
|
||||
</div>
|
||||
</Centered>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,18 +4,18 @@ import MemberRow from '../components/MemberRow';
|
|||
import useQuery from '../hooks/useQuery';
|
||||
|
||||
export default function ClassCouncil() {
|
||||
let members = useQuery<SGA.MemberDocument[]>(
|
||||
`*[_type == 'member' && role == 'class'] | order (year desc)`
|
||||
);
|
||||
let members =
|
||||
useQuery<SGA.MemberDocument[]>(
|
||||
`*[_type == 'member' && role == 'class'] | order (year desc)`
|
||||
) ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero heading='Class Council' />
|
||||
<main>
|
||||
{members &&
|
||||
members.map((member) => {
|
||||
return <MemberRow key={member._id} member={member}></MemberRow>;
|
||||
})}
|
||||
{members.map((member) => (
|
||||
<MemberRow key={member._id} member={member}></MemberRow>
|
||||
))}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -4,9 +4,10 @@ import MemberRow from '../components/MemberRow';
|
|||
import useQuery from '../hooks/useQuery';
|
||||
|
||||
export default function Committee() {
|
||||
let excomm = useQuery<SGA.MemberDocument[]>(
|
||||
`*[_type == 'member' && committee == 'excomm'] | order (role, year desc)`
|
||||
);
|
||||
let excomm =
|
||||
useQuery<SGA.MemberDocument[]>(
|
||||
`*[_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'
|
||||
/>
|
||||
<main>
|
||||
<div>
|
||||
{excomm &&
|
||||
excomm.map((member) => {
|
||||
return <MemberRow key={member._id} member={member} />;
|
||||
})}
|
||||
</div>
|
||||
{excomm.map((member) => (
|
||||
<MemberRow key={member._id} member={member} />
|
||||
))}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -4,19 +4,17 @@ import InitiativeRow from '../components/InitiativeRow';
|
|||
import useQuery from '../hooks/useQuery';
|
||||
|
||||
export default function Initiatives() {
|
||||
let initiatives = useQuery<SGA.InitiativeDocument[]>(
|
||||
'*[_type == "initiative"]'
|
||||
);
|
||||
let initiatives =
|
||||
useQuery<SGA.InitiativeDocument[]>('*[_type == "initiative"]') ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero heading='Initiatives' />
|
||||
<main>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{initiatives &&
|
||||
initiatives.map((initiative) => {
|
||||
return <InitiativeRow initiative={initiative} />;
|
||||
})}
|
||||
{initiatives.map((initiative) => (
|
||||
<InitiativeRow initiative={initiative} />
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
|
|
|
@ -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<SGA.GetInvolvedDocument> | undefined
|
||||
>();
|
||||
|
||||
React.useEffect(() => {
|
||||
sanity.getDocument<SGA.GetInvolvedDocument>('get_involved').then(setWays);
|
||||
sanity
|
||||
.getDocument<SGA.GetInvolvedDocument>('get_involved')
|
||||
.then(setGetInvolved);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero heading='Get Involved' />
|
||||
<main className='text-center'>
|
||||
<h2 className='my-2'>SGA Calendar</h2>
|
||||
<ParagraphHeader>SGA Calendar</ParagraphHeader>
|
||||
<iframe
|
||||
src='https://calendar.google.com/calendar/u/0/embed?src=mbftfg4hu7i8ueqrgcb5o7hc6k@group.calendar.google.com&ctz=America/New_York'
|
||||
title='SGA Calendar'
|
||||
|
@ -32,19 +35,17 @@ export default function GetInvolved() {
|
|||
idea or concern or get to know your representatives, reach out to us
|
||||
at <b>sga@tjhsst.edu</b>!
|
||||
</p>
|
||||
{ways ? (
|
||||
<>
|
||||
<h2 style={{ marginTop: '4rem', marginBottom: '1.5rem' }}>
|
||||
Here are some ways to connect with SGA:
|
||||
</h2>
|
||||
{ways.ways.map((way) => (
|
||||
<ParagraphHeader>
|
||||
Here are some ways to connect with SGA:
|
||||
</ParagraphHeader>
|
||||
|
||||
{getInvolved
|
||||
? getInvolved.ways.map((way) => (
|
||||
<GetInvolvedRow way={way} key={way._id} />
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
<Link className='blue-button' to='/feedback'>
|
||||
Give Feedback
|
||||
</Link>
|
||||
))
|
||||
: null}
|
||||
|
||||
<BlueButtonLink href='/feedback'>Give Feedback</BlueButtonLink>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<div className='mission-quote'>
|
||||
<span className='mission-quote-text'>“{text}”</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className='mission-quote-author'>— {author}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MissionParagraph({ title, body }: { title: string; body: string }) {
|
||||
return (
|
||||
<div className='d-flex'>
|
||||
<div className='flex-1 p-2'>
|
||||
<span className='mission-header'>{title}</span>
|
||||
</div>
|
||||
<div className='flex-2 p-2'>
|
||||
<p className='mission-para'>{body}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
|||
<Hero heading='Mission and History' />
|
||||
{mission ? (
|
||||
<main>
|
||||
<div className='mission-quote'>
|
||||
<span className='mission-quote-text'>“{mission.quote_text}”</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className='mission-quote-author'>
|
||||
— {mission.quote_author}
|
||||
</span>
|
||||
</div>
|
||||
<div className='d-flex'>
|
||||
<div className='flex-1 p-2'>
|
||||
<span className='mission-header'>Vision</span>
|
||||
</div>
|
||||
<div className='flex-2 p-2'>
|
||||
<p className='mission-para'>{mission.vision}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='d-flex'>
|
||||
<div className='flex-1 p-2'>
|
||||
<span className='mission-header'>Mission</span>
|
||||
</div>
|
||||
<div className='flex-2 p-2'>
|
||||
<p className='mission-para'>{mission.mission}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<a
|
||||
href='https://docs.google.com/spreadsheets/d/1a3RYdqrDi1IPG9BKWQ2xhoX3YCPQKUl_FsRLvIVEMPg/edit?usp=drive_open&ouid=0'
|
||||
className='blue-button'
|
||||
>
|
||||
<MissionQuote
|
||||
author={mission.quote_author}
|
||||
text={mission.quote_text}
|
||||
/>
|
||||
|
||||
<MissionParagraph title='Vision' body={mission.vision} />
|
||||
<MissionParagraph title='Mission' body={mission.mission} />
|
||||
|
||||
<Centered>
|
||||
<BlueButtonLink href={previousLeadershipLink}>
|
||||
Previous Leadership
|
||||
</a>
|
||||
</div>
|
||||
</BlueButtonLink>
|
||||
</Centered>
|
||||
</main>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -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<SGA.ArticleDocument>(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 = (
|
||||
<LocalLinkClickable to='/news'>Go to all news articles</LocalLinkClickable>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero heading='News' imageURL={thumbUrl || undefined} />
|
||||
<Hero heading='News' imageURL={thumbnailUrl || undefined} />
|
||||
<main>
|
||||
{article ? (
|
||||
<div style={{ maxWidth: '640px', margin: '2rem auto' }}>
|
||||
<Link to='/news' className='clickable-link'>
|
||||
Go to all news articles
|
||||
</Link>
|
||||
<h1>{article.title}</h1>
|
||||
{goToAllNewsArticles}
|
||||
<PrimaryHeader>{article.title}</PrimaryHeader>
|
||||
<i>
|
||||
Posted {article.publish_date} by {article.author || 'No author'}
|
||||
</i>
|
||||
<br />
|
||||
{/* Wrap the BlockContent in a div because it expands to <></> */}
|
||||
<div
|
||||
id='article-content'
|
||||
className='article-paragraphs'
|
||||
ref={(ref) => {
|
||||
/*
|
||||
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';
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<BlockContent blocks={article.content} />
|
||||
<div id='article-content' className='article-paragraphs'>
|
||||
<BlockContentWithExternalLinks blocks={article.content} />
|
||||
</div>
|
||||
<br />
|
||||
<Link to='/news' className='clickable-link'>
|
||||
Go to all news articles
|
||||
</Link>
|
||||
{goToAllNewsArticles}
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
|
|
|
@ -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<SGA.MemberDocument[]>(
|
||||
`*[_type == 'member' && committee == 'officer']`
|
||||
);
|
||||
const [officers, setOfficers] = useState<SGA.MemberDocument[]>();
|
||||
|
||||
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) => (
|
||||
<MemberRow member={officer} />
|
||||
));
|
||||
console.log(officersSorted);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero heading='Officers' imageURL='/images/who-we-are/officers.jpg' />
|
||||
<main>
|
||||
{officers
|
||||
? officers.map((officer) => {
|
||||
return <MemberRow member={officer} />;
|
||||
})
|
||||
: null}
|
||||
</main>
|
||||
<main>{officerList}</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user