-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
220 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,226 @@ | ||
import Header from '@/components/Header'; | ||
import Footer from '@/components/Footer'; | ||
import Head from 'next/head'; | ||
import { useEffect, useState } from 'react'; | ||
import { ics } from 'calendar-link'; | ||
|
||
const Meetings = () => ( | ||
<> | ||
<Head> | ||
<title>Meetings - Nebula Labs</title> | ||
<link rel="canonical" href="https://www.utdnebula.com/resources/Meetings" key="canonical" /> | ||
<meta property="og:url" content="https://www.utdnebula.com/resources/Meetings" /> | ||
</Head> | ||
<Header text="Roles" /> | ||
<Footer royalBg={false} /> | ||
</> | ||
); | ||
const timeFormat = new Intl.DateTimeFormat('en-US', { | ||
hour: 'numeric', | ||
minute: 'numeric', | ||
hour12: true, | ||
}); | ||
|
||
const dateFormat = new Intl.DateTimeFormat('en-US', { | ||
weekday: 'long', | ||
}); | ||
|
||
interface MeetingReactProps { | ||
name: string; | ||
found: boolean; | ||
start?: string; | ||
end?: string; | ||
location?: string; | ||
htmlLink?: string; | ||
} | ||
|
||
const Meeting = (props: MeetingReactProps) => { | ||
if ( | ||
props.found === false || | ||
typeof props.start === 'undefined' || | ||
typeof props.end === 'undefined' || | ||
typeof props.location === 'undefined' || | ||
typeof props.htmlLink === 'undefined' | ||
) { | ||
return ( | ||
<div> | ||
<p className="text-4xl font-semibold text-royal">{props.name}</p> | ||
<p>No meetings currently planned.</p> | ||
</div> | ||
); | ||
} | ||
|
||
const start = new Date(props.start); | ||
const end = new Date(props.end); | ||
let startTime = timeFormat.format(start); | ||
if (start.getHours() >= 12 === end.getHours() >= 12) { | ||
//AMPM the same | ||
startTime = startTime.slice(0, -3); | ||
} | ||
|
||
const buttonLinkClasses = | ||
'hover:scale-105 active:scale-95 transition duration-300 ease-in-out px-2 py-1 rounded-lg cursor-pointer border-2 hover:bg-royal hover:text-white border-royal'; | ||
|
||
const iCalFileString = ics({ | ||
title: props.name, | ||
start: props.start, | ||
end: props.end, | ||
location: props.location, | ||
url: props.htmlLink, | ||
}); | ||
|
||
return ( | ||
<div className="flex flex-col md:flex-row items-center"> | ||
<div className="mr-auto"> | ||
<p className="text-4xl font-semibold text-royal">{props.name}</p> | ||
<p> | ||
{dateFormat.format(start)} {startTime} - {timeFormat.format(end)} | ||
</p> | ||
<p>{props.location === 'Unknown' ? 'TBD' : props.location}</p> | ||
</div> | ||
<div className="mt-2 flex flex-row md:flex-col items-end gap-2"> | ||
<a | ||
className={buttonLinkClasses} | ||
target="_blank" | ||
href={ | ||
'https://accounts.google.com/AccountChooser?continue=https://calendar.google.com/calendar/r/eventedit/copy/' + | ||
props.htmlLink.split('eid=')[1] | ||
} | ||
> | ||
Copy to Google Calendar | ||
</a> | ||
<a download={props.name} className={buttonLinkClasses} href={iCalFileString}> | ||
Copy with iCal | ||
</a> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
const Meetings = () => { | ||
const [events, setEvents] = useState<EventFetchProps[]>([]); | ||
const [state, setState] = useState('loading'); | ||
|
||
interface EventFetchProps { | ||
status: string; | ||
id: string; | ||
summary: string; | ||
start: { | ||
dateTime: string; | ||
}; | ||
end: { | ||
dateTime: string; | ||
}; | ||
location: string; | ||
htmlLink: string; | ||
} | ||
|
||
interface FetchProps { | ||
message: string; | ||
data: { | ||
data: { | ||
items: EventFetchProps[]; | ||
}; | ||
}; | ||
} | ||
|
||
useEffect(() => { | ||
fetch('/api/getCalendar') | ||
.then((response) => { | ||
if (!response.ok) { | ||
throw new Error(response.statusText); | ||
} | ||
return response.json() as Promise<FetchProps>; | ||
}) | ||
.then((data) => { | ||
if (data.message !== 'success') { | ||
throw new Error(data.message); | ||
} | ||
console.log(data.data); | ||
setEvents(data.data.data.items); | ||
setState('done'); | ||
}) | ||
.catch((error) => { | ||
setState('error'); | ||
console.error('Feedback', error); | ||
}); | ||
}, []); | ||
|
||
interface MeetingProps { | ||
[key: string]: { | ||
name: string; | ||
found: boolean; | ||
start?: string; | ||
end?: string; | ||
location?: string; | ||
htmlLink?: string; | ||
}; | ||
} | ||
const meetings: MeetingProps = { | ||
planner: { | ||
name: 'Planner', | ||
found: false, | ||
}, | ||
jupiter: { | ||
name: 'Jupiter', | ||
found: false, | ||
}, | ||
trends: { | ||
name: 'Trends & Skedge', | ||
found: false, | ||
}, | ||
api: { | ||
name: 'API & Platform', | ||
found: false, | ||
}, | ||
}; | ||
const elements = []; | ||
if (typeof events !== 'undefined') { | ||
for (const project in meetings) { | ||
for (const event of events) { | ||
if (event.status !== 'confirmed') { | ||
continue; | ||
} | ||
const name = event.summary.toLowerCase(); | ||
if (name.includes('project meeting') && name.includes(project)) { | ||
meetings[project] = { | ||
...meetings[project], | ||
found: true, | ||
start: event.start.dateTime, | ||
end: event.end.dateTime, | ||
location: event.location, | ||
htmlLink: event.htmlLink, | ||
}; | ||
} | ||
} | ||
elements.push(<Meeting key={project} {...meetings[project]} />); | ||
} | ||
} | ||
|
||
const buttonLinkClasses = | ||
'hover:scale-105 active:scale-95 transition duration-300 ease-in-out px-4 py-2 rounded-lg cursor-pointer hover:bg-royal hover:text-white border-royal border-2'; | ||
|
||
//error state | ||
let result = ( | ||
<div className="px-8 lg:px-16 xl:px-32 flex flex-col items-center"> | ||
<h2 className="text-5xl font-bold pb-4 text-center">Error loading calendar</h2> | ||
<button className={buttonLinkClasses} onClick={() => location.reload()}> | ||
Reload | ||
</button> | ||
</div> | ||
); | ||
|
||
if (state === 'loading') { | ||
result = <h2 className="text-5xl font-bold pb-4 text-center">Loading...</h2>; | ||
} else if (state === 'done') { | ||
result = ( | ||
<div className="px-8 lg:px-16 xl:px-32 flex flex-col items-center"> | ||
<div className="flex flex-col gap-16 w-full">{...elements}</div> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<> | ||
<Head> | ||
<title>Meetings - Nebula Labs</title> | ||
<link rel="canonical" href="https://www.utdnebula.com/resources/meetings" key="canonical" /> | ||
<meta property="og:url" content="https://www.utdnebula.com/resources/meetings" /> | ||
</Head> | ||
<Header text="Meetings" /> | ||
{result} | ||
<Footer royalBg={false} /> | ||
</> | ||
); | ||
}; | ||
|
||
export default Meetings; |