208 lines
6.0 KiB
Svelte
208 lines
6.0 KiB
Svelte
<script lang="ts">
|
|
import CouncilCardPopUp from 'components/council/CouncilCardPopUp.svelte';
|
|
import Section from 'components/layout/Section.svelte';
|
|
import CouncilCard from 'components/council/CouncilCard.svelte';
|
|
import Link from 'components/Link.svelte';
|
|
import type { CouncilMember } from '$lib/schemas';
|
|
import SeoMetaTags from 'components/layout/SeoMetaTags.svelte';
|
|
import { onMount } from 'svelte';
|
|
import { tick } from 'svelte';
|
|
|
|
let { data } = $props();
|
|
|
|
const years = ['U4', 'U3', 'U2', 'U1', 'U0'];
|
|
|
|
let president = $derived(
|
|
data.members.filter((member: CouncilMember) => member.position.includes('President'))[0]
|
|
);
|
|
let vps = $derived(
|
|
data.members.filter(
|
|
(member: CouncilMember) =>
|
|
member.position.includes('VP') || member.position.includes('Equity')
|
|
)
|
|
);
|
|
let ureps = $derived(
|
|
(() => {
|
|
const list = data.members.filter((member: CouncilMember) =>
|
|
member.position.includes('Representative')
|
|
);
|
|
return [...list].sort((a, b) => {
|
|
const aYear = years.findIndex((year) => a.position.includes(year));
|
|
const bYear = years.findIndex((year) => b.position.includes(year));
|
|
return aYear - bYear;
|
|
});
|
|
})()
|
|
);
|
|
|
|
let selectedMember = $state<CouncilMember | null>(null);
|
|
let modalRef = $state<HTMLDivElement | null>(null);
|
|
|
|
function handleViewProfile(member: CouncilMember) {
|
|
selectedMember = member;
|
|
}
|
|
function closeModal() {
|
|
selectedMember = null;
|
|
}
|
|
function onBackdropKeydown(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') closeModal();
|
|
}
|
|
|
|
function getYearFromPosition(position: string): string | undefined {
|
|
const match = position.match(/\b(U[0-4])\b/);
|
|
return match?.[1];
|
|
}
|
|
|
|
onMount(() => {
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') closeModal();
|
|
}
|
|
window.addEventListener('keydown', onKeyDown);
|
|
return () => window.removeEventListener('keydown', onKeyDown);
|
|
});
|
|
|
|
$effect(() => {
|
|
if (selectedMember) {
|
|
document.body.style.overflow = 'hidden';
|
|
tick().then(() => modalRef?.focus());
|
|
} else {
|
|
document.body.style.overflow = '';
|
|
}
|
|
return () => {
|
|
document.body.style.overflow = '';
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<SeoMetaTags
|
|
title="Meet the ECSESS student council members!"
|
|
description="Greetings from ECSESS student council!"
|
|
canonical={data.canonical}
|
|
/>
|
|
|
|
<Section
|
|
from="from-ecsess-black"
|
|
to="to-ecsess-black"
|
|
via="via-ecsess-800"
|
|
direction="to-b"
|
|
contentStart
|
|
>
|
|
<div class="w-full max-w-360 px-4">
|
|
<!-- Hero -->
|
|
<h1 class="page-title text-ecsess-50">Meet the ECSESS Council</h1>
|
|
|
|
<figure class="ring-ecsess-400/50 mb-20 overflow-hidden rounded-2xl shadow-2xl ring-2">
|
|
<img
|
|
src={data.councilGoofyPic.url}
|
|
alt="ECSESS Council having fun"
|
|
class="w-full object-cover"
|
|
/>
|
|
</figure>
|
|
|
|
<!-- President -->
|
|
{#if president}
|
|
<section class="mb-12 w-full">
|
|
<div class="flex w-full items-center gap-4">
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
<h2 class="text-ecsess-100 shrink-0 text-xl font-semibold tracking-wider uppercase">
|
|
President
|
|
</h2>
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="mt-4 flex justify-center">
|
|
<CouncilCard
|
|
name={president.name}
|
|
position={president.position}
|
|
image={president.image}
|
|
onViewProfile={() => handleViewProfile(president)}
|
|
featured
|
|
/>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
|
|
<!-- Vice Presidents -->
|
|
<section class="mb-20 w-full">
|
|
<div class="flex w-full items-center gap-4">
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
<h2 class="text-ecsess-100 shrink-0 text-2xl font-semibold tracking-wider uppercase">
|
|
Vice Presidents
|
|
</h2>
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="mt-8 flex flex-wrap justify-center gap-6">
|
|
{#each vps as vp}
|
|
<CouncilCard
|
|
name={vp.name}
|
|
position={vp.position}
|
|
image={vp.image}
|
|
onViewProfile={() => handleViewProfile(vp)}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Year Representatives -->
|
|
<section class="mb-20 w-full">
|
|
<div class="flex w-full items-center gap-4">
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
<h2 class="text-ecsess-100 shrink-0 text-2xl font-semibold tracking-wider uppercase">
|
|
Year Representatives
|
|
</h2>
|
|
<div class="bg-ecsess-300 h-0.5 flex-1" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="mt-8 flex flex-wrap justify-center gap-6">
|
|
{#each ureps as urep}
|
|
<CouncilCard
|
|
name={urep.name}
|
|
position={urep.position}
|
|
image={urep.image}
|
|
onViewProfile={() => handleViewProfile(urep)}
|
|
tag={getYearFromPosition(urep.position)}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Join CTA at bottom -->
|
|
<section class="mt-4 mb-12 text-center">
|
|
<p class="text-ecsess-200 text-lg sm:text-xl">
|
|
Curious to be involved with the Council?
|
|
<Link
|
|
href="/join"
|
|
class="text-ecsess-50 decoration-ecsess-300 hover:text-ecsess-100 hover:decoration-ecsess-200 font-semibold underline underline-offset-4 transition"
|
|
>
|
|
Join ECSESS!
|
|
</Link>
|
|
</p>
|
|
</section>
|
|
</div>
|
|
|
|
{#if selectedMember}
|
|
<div
|
|
bind:this={modalRef}
|
|
tabindex="-1"
|
|
class="focus-visible:ring-ecsess-400 fixed inset-0 z-50 flex items-center justify-center overflow-y-auto bg-black/70 p-4 backdrop-blur-sm outline-none focus-visible:ring-2 focus-visible:ring-inset"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="popup-title"
|
|
onclick={(e) => e.target === e.currentTarget && closeModal()}
|
|
onkeydown={onBackdropKeydown}
|
|
>
|
|
<div
|
|
class="relative my-auto flex w-full max-w-2xl flex-col items-center px-2 md:max-w-3xl md:px-4"
|
|
>
|
|
<CouncilCardPopUp
|
|
id="popup-title"
|
|
name={selectedMember.name}
|
|
position={selectedMember.position}
|
|
email={selectedMember.email}
|
|
positionDescription={selectedMember.positionDescription}
|
|
yearProgram={selectedMember.yearProgram}
|
|
image={selectedMember.image}
|
|
onClose={closeModal}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</Section>
|