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

Modularize General Meeting page #686

Merged
merged 13 commits into from
May 15, 2024
Merged
3 changes: 3 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
DIRECTORY_SPREADSHEET_ID: ${{ secrets.DIRECTORY_SPREADSHEET_ID }}
TOWNHALL_SPREADSHEET_ID: ${{ secrets.TOWNHALL_SPREADSHEET_ID }}
GM_SPREADSHEET_ID: ${{ secrets.GM_SPREADSHEET_ID }}

steps:
- uses: actions/checkout@v2
Expand All @@ -42,4 +43,6 @@ jobs:

- run: npm run thp

ArshMalik02 marked this conversation as resolved.
Show resolved Hide resolved
- run: npm run gmp

- run: npm run lint
3 changes: 3 additions & 0 deletions .github/workflows/node-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
DIRECTORY_SPREADSHEET_ID: ${{ secrets.DIRECTORY_SPREADSHEET_ID }}
TOWNHALL_SPREADSHEET_ID: ${{ secrets.TOWNHALL_SPREADSHEET_ID }}
GM_SPREADSHEET_ID: ${{ secrets.GM_SPREADSHEET_ID }}

steps:
- uses: actions/checkout@v2
Expand All @@ -42,6 +43,8 @@ jobs:

- run: npm run thp

ArshMalik02 marked this conversation as resolved.
Show resolved Hide resolved
- run: npm run gmp

- run: npm run build

- run: npm test
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ output.json
offoutput.json
townhall.json
past-townhall.json
gmData.json
2 changes: 1 addition & 1 deletion components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SocialMedia from './SocialMedia';
const footerACMLinks = [
{ title: 'About', path: '/about' },
{ title: 'Events', path: '/events' },
{ title: 'General Meeting', path: '/gm/w24' },
{ title: 'General Meeting', path: '/gm' },
{ title: 'CS Town Hall', path: '/town-hall' },
{ title: 'Internship Program', path: '/internship' },
{ title: 'Dev Team', path: '/dev'},
Expand Down
2 changes: 1 addition & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build]
command = "npm run ofp; npm run thp; npm run build"
command = "npm run ofp; npm run thp; npm run gmp; npm run build"
publish = "build"

[[plugins]]
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"scripts": {
"start": "next dev",
"start-production": "next start",
"build": "npm run ofp && npm run thp && next build",
"build": "npm run ofp && npm run thp && npm run gmp && next build",
"lint-js": "eslint \"**/*.js\"",
"lint-js-fix": "eslint --fix \"**/*.js\"",
"lint-css": "stylelint \"**/*.css\" \"**/*.scss\"",
Expand All @@ -35,6 +35,7 @@
"seg": "node scripts/single-event-generator.mjs",
"ofp": "node scripts/officer-parser.mjs",
"thp": "node scripts/town-hall-generator.mjs",
"gmp": "node scripts/gm-generator.mjs",
"test": "jest --env=jsdom"
},
"eslintConfig": {
Expand Down
280 changes: 280 additions & 0 deletions pages/gm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
import { NextSeo } from 'next-seo';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';
import Countdown from 'react-countdown';

import Banner from '../components/Banner';
import Layout from '../components/Layout';
import gmData from '../gmData.json';

import aiLogo from '../public/images/committees/ai_wordmark.svg';
import boardLogo from '../public/images/committees/board_wordmark.svg';
import cyberLogo from '../public/images/committees/cyber_wordmark.svg';
import designLogo from '../public/images/committees/design_wordmark.svg';
import hackLogo from '../public/images/committees/hack_wordmark.svg';
import icpcLogo from '../public/images/committees/icpc_wordmark.svg';
import studioLogo from '../public/images/committees/studio_wordmark.svg';
import teachlaLogo from '../public/images/committees/teachLA_wordmark.svg';
import wLogo from '../public/images/committees/w_wordmark.svg';
import googleSlideLogo from '../public/images/slides.png';
import winterGMgraphic from '../public/images/Winter_GM_2024_graphic.png';


const dayToName = (day) => {
switch (day) {
case 0:
return 'Sunday';
case 1:
return 'Monday';
case 2:
return 'Tuesday';
case 3:
return 'Wednesday';
case 4:
return 'Thursday';
case 5:
return 'Friday';
default:
return 'Saturday';
}
};

const getDateWithSuffix = (date) => {
let suffix = '';
switch (date % 10) {
case 1:
suffix = 'st';
break;
case 2:
suffix = 'nd';
break;
case 3:
suffix = 'rd';
break;
default:
suffix = 'th';
}
return date.toString() + suffix;
};

const calculateTimeStrings = ({days, hours, minutes, seconds}) => {
let dayString = 'Day';
let hourString = 'Hour';
let minuteString = 'Minute';
let secondString = 'Second';
if(days !== 1){ dayString += 's'; }
if(hours !== 1){ hourString += 's'; }
if(minutes !== 1){ minuteString += 's'; }
if(seconds !== 1){ secondString += 's'; }

return {dayString, hourString, minuteString, secondString};
};

const parseGMData = (jsonContent) => {
const data = {};

for (const row of jsonContent) {
data[row.name] = row.description;
}

const startTime = new Date(data?.gm_start_time);

return {
gm_start_time: startTime,
gm_end_time: new Date(data?.gm_end_time),
rsvp_link: data?.rsvp_link,
quarter: data?.quarter,
slides_link: data?.slides_link,
location: data?.location,
day_of_week: dayToName(startTime.getDay()),
date_with_suffix: getDateWithSuffix(startTime.getDate()),
pres: data?.pres,
ivp: data?.ivp,
evp: data?.evp,
studio: data?.studio,
icpc: data?.icpc,
design: data?.design,
cyber: data?.cyber,
teachLA: data?.teachLA,
w: data?.w,
ai: data?.ai,
hack: data?.hack,
initiatives: data?.initiatives.split(';'),
};
};

const GMCountdown = (props) => {
return (
<>
<div id='countdown-wrapper'>
<div className='countdown-cards'>
<div className='square-background'/>
<div className='countdown-numbers'>{props.days}</div>
<div className='countdown-labels'>{props.dayString}</div>
</div>
<div className='countdown-cards'>
<div className='square-background'/>
<div className='countdown-numbers'>{props.hours}</div>
<div className='countdown-labels'>{props.hourString}</div>
</div>
<div className='countdown-cards'>
<div className='square-background'/>
<div className='countdown-numbers'>{props.minutes}</div>
<div className='countdown-labels'>{props.minuteString}</div>
</div>
<div className='countdown-cards'>
<div className='square-background'/>
<div className='countdown-numbers'>{props.seconds}</div>
<div className='countdown-labels'>{props.secondString}</div>
</div>
</div>
<div className='text-center'>
<h1>{props.data.quarter} {props.data.gm_start_time.getFullYear()} General Meeting</h1>
<Link href={props.data.rsvp_link}>
<a className='button' target='_blank' rel='noreferrer noopener'>
RSVP Now!
</a>
</Link>
</div>
</>
);
};

function gm() {
const data = parseGMData(gmData);
function countdownRenderer({ days, hours, minutes, seconds, completed }) {
const {dayString, hourString, minuteString, secondString} =
calculateTimeStrings({days, hours, minutes, seconds});
if (completed) {
return (
<div className='text-center'>
<h1>
ACM&apos;s {data.quarter} GM {data.gm_start_time.getFullYear()} happened on the {data.date_with_suffix}!
</h1>
<div className='should-dim'>
<a href={data.slides_link} target='_blank' rel='noopener noreferrer'>
<div className='button-wrapper'>
<Image
src = {googleSlideLogo}
alt='Google Slides logo'
className='join-links-img'
/>
</div>
<p className='join-us'>View the {data.quarter} GM slides to catch up!</p>
</a>
</div>
</div>
);
}
return <GMCountdown
data={data}
days={days}
hours={hours}
minutes={minutes}
seconds={seconds}
dayString={dayString}
hourString={hourString}
minuteString={minuteString}
secondString={secondString}
/>;
}
return (
<Layout>
<NextSeo
title={`${data.quarter} General Meeting | ACM at UCLA`}
description={
`ACM's ${data.quarter} General Meeting will take place on ${data.gm_start_time.getMonth()} ${data.date_with_suffix} at ${data.gm_start_time.getHours()}:${data.gm_start_time.getMinutes()} PT!`}
openGraph={{
images: [
{
url: 'https://www.uclaacm.com/images/logo.png',
width: 1200,
height: 1200,
alt: 'The ACM at UCLA logo',
},
],
site_name: 'ACM at UCLA',
}}
/>
<Banner decorative />
<div className='text-center'>
<Image
src = {winterGMgraphic}
className='gm-graphic'
alt={`${data.quarter} GM ${data.gm_start_time.getFullYear()} Marketing Graphic. ${data.quarter} GM will happen on ${data.day_of_week}, ${data.gm_start_time.getMonth()} ${data.date_with_suffix} from ${data.gm_start_time.getHours()}:${data.gm_start_time.getMinutes()} to ${data.gm_end_time.getHours()}:${data.gm_end_time.getMinutes()} in ${data.location}`}
/>
</div>
<Countdown date={data.gm_start_time} renderer={countdownRenderer}/>
<div className='content-container-tight text-center'>
<div id='info-wrapper'>
<h2>Relevant information</h2>
<div className='flex'>
<div>
<iframe src='https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3304.8687573568445!2d-118.4437108235007!3d34.07287847314926!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x80c2bd8bbb175573%3A0x46c5c13d4084a4b3!2sHaines%20Hall!5e0!3m2!1sen!2sus!4v1702237752096!5m2!1sen!2sus' width='450' height='400' style={{border: 0}} allowfullscreen='' loading='lazy' title='gogole maps embed of haines 39' referrerPolicy='no-referrer-when-downgrade'></iframe>
</div>
<div className='what-to-bring'>
<h3>How to get there</h3>
<p> {data.quarter} GM will be hosted in {data.location}. </p>
<br></br>
<h3>What to bring</h3>
<p>Encouraged: Phone to scan QR codes, excitement to learn about ACM!</p>
</div>
</div>
<p>
Don&apos;t hesitate to contact us at [email protected] if you any accessiblity concerns for {data.quarter} GM.
</p>

</div>

<div id='gm-program-wrapper'>
<h2>Program</h2>
<div className='gm-program-section'>
<div className='section-header'>
<h3>Welcome</h3>
<p className='sub-heading'>An introduction to ACM by our president {data.pres}.</p>
</div>
</div>
<div className='gm-program-section'>
<div className='section-header'>
<h3>Committee Presentations</h3>
<p className='sub-heading'>Learn what ACM&apos;s eight committees have planned for {data.quarter} quarter.</p>
</div>
<p className='gm-program-row'><Image src= {studioLogo} alt='ACM studio'/> {data.studio}</p>
<p className='gm-program-row'><Image src= {icpcLogo} alt='ACM icpc'/> {data.icpc}</p>
<p className='gm-program-row'><Image src= {designLogo} alt='ACM design'/> {data.design}</p>
<p className='gm-program-row'><Image src= {cyberLogo} alt='ACM cyber'/> {data.cyber}</p>
<p className='gm-program-row'><Image src= {teachlaLogo} alt='ACM teachLA'/> {data.teachLA}</p>
<p className='gm-program-row'><Image src= {wLogo} alt='ACM w'/> {data.w}</p>
<p className='gm-program-row'><Image src= {aiLogo} alt='ACM ai'/> {data.ai}</p>
<p className='gm-program-row'><Image src= {hackLogo} alt='ACM hack'/> {data.hack}</p>
</div>
<div className='gm-program-section'>
<div className='section-header'>
<h3>ACM Board</h3>
<p className='sub-heading'>How to get more involved with ACM beyond attending workshops and events</p>
</div>
<p className='gm-program-row'><Image src= {boardLogo} alt='ACM board'/>&nbsp;&nbsp;External: {data.evp}</p>
<p className='gm-program-row'><Image src= {boardLogo} alt='ACM board'/>&nbsp;&nbsp;Internal: {data.ivp}</p>
</div>
<div className='gm-program-section'>
<div className='section-header'>
<h3>ACM Initatives</h3>
<p className='sub-heading'>See exciting new programs that ACM is trying out</p>
</div>
{data.initiatives.map(item => <p key={item.id} className='gm-program-row'>{item}</p>)}
</div>
<div className='gm-program-section'>
<div className='section-header'>
<h3>Tabling and Social</h3>
<p className='sub-heading'>Interact with ACM&apos;s officers and walk away with new friends!</p>
</div>
<p className='gm-program-row'>All ACM officers</p>
</div>
</div>
</div>
</Layout>
);
}

export default gm;
Loading
Loading