added affiliated clubs and updated events page

This commit is contained in:
paololahoud2004@gmail.com
2025-10-16 23:33:03 -04:00
parent 37077f654d
commit d4fb141517
6 changed files with 534 additions and 234 deletions

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { PortableText } from '@portabletext/svelte'; import { PortableText } from '@portabletext/svelte';
import { CalendarDays, MapPin, Link as LinkIcon, FilePen } from '@lucide/svelte'; import { CalendarDays, MapPin, Link as LinkIcon, FilePen, CalendarPlus } from '@lucide/svelte';
let { let {
eventTitle, eventTitle,
@@ -10,96 +10,220 @@
thumbnail, thumbnail,
registrationLink, registrationLink,
paymentLink, paymentLink,
eventCategory eventCategory,
isPastEvent = false
} = $props(); } = $props();
// Function to generate .ics file for calendar
const addToCalendar = () => {
const eventDate = new Date(date);
const endDate = new Date(eventDate.getTime() + 2 * 60 * 60 * 1000); // 2 hours duration
const formatDate = (d: Date) => {
return d.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
};
const icsContent = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//ECSESS//Events//EN',
'BEGIN:VEVENT',
`DTSTART:${formatDate(eventDate)}`,
`DTEND:${formatDate(endDate)}`,
`SUMMARY:${eventTitle}`,
`LOCATION:${location || 'TBA'}`,
`DESCRIPTION:${eventTitle} - ECSESS Event`,
'END:VEVENT',
'END:VCALENDAR'
].join('\n');
const blob = new Blob([icsContent], { type: 'text/calendar' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${eventTitle.replace(/\s+/g, '_')}.ics`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
</script> </script>
<div class="bg-ecsess-50 text-ecsess-800 mx-auto w-[100%] rounded-md p-5 lg:w-[64%] lg:max-w-3xl">
<div class="bg-ecsess-150 rounded-md">
<div <div
class="bg-ecsess-400 grid h-[200px] place-items-center overflow-hidden rounded-md" class="group dark:bg-ecsess-950 dark:shadow-ecsess-950/50 relative flex h-full flex-col overflow-hidden rounded-2xl bg-white shadow-lg transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl"
aria-label="Event banner"
> >
<!-- Image Container with Gradient Overlay -->
<div class="relative h-64 overflow-hidden">
{#if thumbnail} {#if thumbnail}
<img class="h-full object-cover" src={thumbnail} alt="Event banner" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src={thumbnail}
alt={eventTitle}
/>
{:else if eventCategory?.[0] === 'social'} {:else if eventCategory?.[0] === 'social'}
<img class="h-full object-cover" src="/Social.jpg" alt="Social Placeholder" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src="/Social.jpg"
alt="Social Event"
/>
{:else if eventCategory?.[0] === 'technical'} {:else if eventCategory?.[0] === 'technical'}
<img class="h-full object-cover" src="/Technical.jpg" alt="Technical Placeholder" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src="/Technical.jpg"
alt="Technical Event"
/>
{:else if eventCategory?.[0] === 'professional'} {:else if eventCategory?.[0] === 'professional'}
<img class="h-full object-cover" src="/Professional.jpg" alt="Professional Placeholder" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src="/Professional.jpg"
alt="Professional Event"
/>
{:else if eventCategory?.[0] === 'academic'} {:else if eventCategory?.[0] === 'academic'}
<img class="h-full object-cover" src="/Academic.jpg" alt="Academic Placeholder" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src="/Academic.jpg"
alt="Academic Event"
/>
{:else} {:else}
<img class="h-full object-cover" src="/ECSESS.png" alt="Default Placeholder" /> <img
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
src="/ECSESS.png"
alt="ECSESS Event"
/>
{/if}
<!-- Gradient overlay -->
<div
class="absolute inset-0 bg-gradient-to-b from-transparent via-black/30 to-black/80 dark:to-black/90"
></div>
<!-- Badges -->
<div class="absolute top-0 right-0 left-0 flex items-start justify-between gap-2 p-4">
{#if isPastEvent}
<span
class="dark:bg-ecsess-900/95 rounded-full bg-gray-800/95 px-4 py-1.5 text-xs font-bold tracking-wider text-gray-300 uppercase backdrop-blur-sm"
>
Past Event
</span>
{:else}
<span
class="bg-ecsess-500/95 dark:bg-ecsess-400/95 rounded-full px-4 py-1.5 text-xs font-bold tracking-wider text-white uppercase backdrop-blur-sm"
>
Upcoming
</span>
{/if}
{#if eventCategory && eventCategory.length > 0}
<div class="flex flex-wrap justify-end gap-2">
{#each eventCategory as category}
<span
class="bg-ecsess-600/95 dark:bg-ecsess-500/95 rounded-full px-3 py-1.5 text-xs font-bold tracking-wider text-white uppercase backdrop-blur-sm"
>
{category}
</span>
{/each}
</div>
{/if} {/if}
</div> </div>
<!-- Event Title Overlay -->
<div class="absolute right-0 bottom-0 left-0 p-6">
<h3 class="text-2xl leading-tight font-bold text-white drop-shadow-2xl">
{eventTitle}
</h3>
</div>
</div> </div>
<!-- content --> <!-- Content Section -->
<div class="mt-[22px] grid gap-[18px]"> <div class="flex flex-1 flex-col p-6">
<p <!-- Description -->
class="text-ecsess-800 my-0 text-center text-xl leading-6 tracking-[0.3px] text-wrap lg:text-2xl lg:leading-8"
>
{eventTitle}
</p>
{#if eventDescription} {#if eventDescription}
<div class="text-ecsess-700 mx-auto max-w-[75ch] leading-relaxed"> <div
class="text-ecsess-800 dark:text-ecsess-100 mb-6 line-clamp-3 flex-1 text-sm leading-relaxed"
>
<PortableText value={eventDescription} /> <PortableText value={eventDescription} />
</div> </div>
{/if} {/if}
<div class="mt-[6px] grid gap-4 md:grid-cols-2"> <!-- Info Grid -->
<div class="bg-ecsess-100 grid gap-[10px] rounded-md px-4 py-[14px]"> <div class="bg-ecsess-50 dark:bg-ecsess-900/40 mb-6 space-y-3 rounded-xl p-4">
<div class="text-ecsess-800 flex items-center gap-2"> <div class="flex items-center gap-3">
<CalendarDays class="shrink-0" strokeWidth={2.5} /> <div
<span class="font-bold tracking-[0.2px]">Datetime:</span> class="bg-ecsess-500 dark:bg-ecsess-400 flex h-10 w-10 items-center justify-center rounded-full shadow-md"
<p class="m-0 text-left">{date}</p> >
<CalendarDays class="h-5 w-5 text-white" strokeWidth={2.5} />
</div> </div>
<div class="flex-1">
<div class="text-ecsess-800 flex items-center gap-2"> <p class="text-ecsess-900 dark:text-ecsess-50 text-sm font-semibold">{date}</p>
<MapPin class="shrink-0" strokeWidth={2.5} />
<span class="font-bold tracking-[0.2px]">Location:</span>
<p class="m-0 text-left">{location ?? 'TBA'}</p>
</div> </div>
</div> </div>
<div class="bg-ecsess-100 grid gap-[10px] rounded-md px-4 py-[14px]"> <div class="flex items-center gap-3">
<div class="text-ecsess-800 flex items-center gap-2"> <div
<FilePen class="shrink-0" strokeWidth={2.5} /> class="bg-ecsess-500 dark:bg-ecsess-400 flex h-10 w-10 items-center justify-center rounded-full shadow-md"
<span class="font-bold tracking-[0.2px]">Registration:</span> >
<MapPin class="h-5 w-5 text-white" strokeWidth={2.5} />
</div>
<div class="flex-1">
<p class="text-ecsess-900 dark:text-ecsess-50 text-sm font-semibold">
{location ?? 'TBA'}
</p>
</div>
</div>
</div>
<!-- Action Buttons -->
{#if !isPastEvent}
<div class="space-y-2">
<!-- Add to Calendar Button -->
<button
onclick={addToCalendar}
class="bg-ecsess-500 hover:bg-ecsess-600 dark:bg-ecsess-400 dark:hover:bg-ecsess-500 flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg active:scale-95"
>
<CalendarPlus class="h-5 w-5" strokeWidth={2.5} />
Add to Calendar
</button>
<!-- Registration & Payment Row -->
<div class="grid grid-cols-2 gap-2">
{#if registrationLink} {#if registrationLink}
<a <a
href={registrationLink} href={registrationLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-ecsess-800 text-left underline-offset-4 hover:underline" class="bg-ecsess-600 hover:bg-ecsess-700 dark:bg-ecsess-500 dark:hover:bg-ecsess-600 flex items-center justify-center gap-2 rounded-xl px-4 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg active:scale-95"
> >
Register Here <FilePen class="h-4 w-4" strokeWidth={2.5} />
Register
</a> </a>
{:else} {:else}
<p class="m-0 text-left">Just drop in!</p> <div
{/if} class="bg-ecsess-150 text-ecsess-800 dark:bg-ecsess-900 dark:text-ecsess-200 flex items-center justify-center gap-2 rounded-xl px-4 py-3 text-sm font-bold"
>
<FilePen class="h-4 w-4" strokeWidth={2.5} />
Drop In
</div> </div>
{/if}
<div class="text-ecsess-800 flex items-center gap-2">
<LinkIcon class="shrink-0" strokeWidth={2.5} />
<span class="font-bold tracking-[0.2px]">Payment:</span>
{#if paymentLink} {#if paymentLink}
<a <a
href={paymentLink} href={paymentLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-ecsess-800 text-left underline-offset-4 hover:underline" class="bg-ecsess-700 hover:bg-ecsess-800 dark:bg-ecsess-600 dark:hover:bg-ecsess-700 flex items-center justify-center gap-2 rounded-xl px-4 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg active:scale-95"
> >
Pay Here <LinkIcon class="h-4 w-4" strokeWidth={2.5} />
Pay
</a> </a>
{:else} {:else}
<p class="m-0 text-left">Free!</p> <div
class="bg-ecsess-400 dark:bg-ecsess-500 flex items-center justify-center gap-2 rounded-xl px-4 py-3 text-sm font-bold text-white shadow-md"
>
Free!
</div>
{/if} {/if}
</div> </div>
</div> </div>
</div> {/if}
</div> </div>
</div> </div>

View File

@@ -17,22 +17,121 @@
return Array.isArray(c) ? c.includes(category) : (c as string) === category; return Array.isArray(c) ? c.includes(category) : (c as string) === category;
}; };
const parseEventDate = (dateString: string): Date => {
// Try to parse various date formats
const parsed = new Date(dateString);
return isNaN(parsed.getTime()) ? new Date() : parsed;
};
const formatEventDate = (dateString: string): string => {
const date = parseEventDate(dateString);
// Check if the time is midnight (00:00) which likely means no time was specified
const isMidnight = date.getUTCHours() === 0 && date.getUTCMinutes() === 0;
if (isMidnight) {
// Format without time - just the date
return date.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
} else {
// Format with time
return date.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
});
}
};
const isPastEvent = (dateString: string): boolean => {
const eventDate = parseEventDate(dateString);
const now = new Date();
return eventDate < now;
};
const filtered = $derived((events ?? []).filter(matchCategory)); const filtered = $derived((events ?? []).filter(matchCategory));
const upcomingEvents = $derived(
filtered
.filter((e) => !isPastEvent(e.date))
.sort((a, b) => parseEventDate(a.date).getTime() - parseEventDate(b.date).getTime())
);
const finishedEvents = $derived(
filtered
.filter((e) => isPastEvent(e.date))
.sort((a, b) => parseEventDate(b.date).getTime() - parseEventDate(a.date).getTime())
);
</script> </script>
<Tabs.Panel {value}> <Tabs.Panel {value}>
<div class="m-1 flex flex-wrap gap-4 lg:m-4"> <div class="space-y-12 px-4 py-8 lg:px-8">
{#each filtered as e (e._id ?? e.name)} <!-- Upcoming Events -->
{#if upcomingEvents.length > 0}
<section>
<div class="mb-6 flex items-center gap-3">
<div class="bg-ecsess-500 h-1 w-12 rounded-full"></div>
<h2 class="text-ecsess-600 text-3xl font-bold">Upcoming Events</h2>
</div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{#each upcomingEvents as e (e._id ?? e.name)}
<EventBlock <EventBlock
eventTitle={e.name} eventTitle={e.name}
date={e.date} date={formatEventDate(e.date)}
location={e.location} location={e.location}
eventDescription={e.description} eventDescription={e.description}
thumbnail={e.thumbnail} thumbnail={e.thumbnail}
registrationLink={e.reglink} registrationLink={e.reglink}
paymentLink={e.paylink} paymentLink={e.paylink}
eventCategory={e.category} eventCategory={e.category}
isPastEvent={false}
/> />
{/each} {/each}
</div> </div>
</section>
{/if}
<!-- Finished Events -->
{#if finishedEvents.length > 0}
<section>
<div class="mb-6 flex items-center gap-3">
<div class="h-1 w-12 rounded-full bg-gray-400"></div>
<h2 class="text-3xl font-bold text-gray-700">Past Events</h2>
</div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{#each finishedEvents as e (e._id ?? e.name)}
<EventBlock
eventTitle={e.name}
date={formatEventDate(e.date)}
location={e.location}
eventDescription={e.description}
thumbnail={e.thumbnail}
registrationLink={e.reglink}
paymentLink={e.paylink}
eventCategory={e.category}
isPastEvent={true}
/>
{/each}
</div>
</section>
{/if}
<!-- No events message -->
{#if upcomingEvents.length === 0 && finishedEvents.length === 0}
<div class="flex min-h-[400px] items-center justify-center">
<div class="text-center">
<p class="text-xl font-semibold text-gray-600">No events in this category yet</p>
<p class="mt-2 text-gray-500">Check back soon for updates!</p>
</div>
</div>
{/if}
</div>
</Tabs.Panel> </Tabs.Panel>

View File

@@ -0,0 +1,123 @@
<script lang="ts">
import { ExternalLink, Wrench, Zap, Code2 } from '@lucide/svelte';
const clubs = [
{
name: 'The Factory',
description:
'A hardware design lab run by students, for students. A dedicated space for developing personal projects, gaining experience with cutting-edge hardware, and fostering innovation.',
website: 'https://factory.mcgilleus.ca/',
icon: Wrench,
features: ['PCB Printer', '3D Printing', 'Arduino & Raspberry Pi', 'Hardware Workshops']
},
{
name: 'IEEE McGill',
description:
'One of the largest IEEE student branches in Eastern Canada. Dedicated to professional development through networking, workshops, competitions, and industry connections.',
website: 'https://ieeemcgill.com/',
icon: Zap,
features: ['Technical Talks', 'Company Tours', 'IEEEXtreme Competition', 'Networking Events']
},
{
name: 'CodeJam',
description:
"McGill Engineering's largest and longest-running hackathon. A 36-hour programming competition where students create innovative solutions to real-world problems.",
website: 'https://codejam.mcgilleus.ca/',
icon: Code2,
features: ['Annual Hackathon', 'Industry Mentors', 'Prize Pool', 'Networking with Sponsors']
}
];
</script>
<section
class="to-ecsess-50 dark:from-ecsess-950 dark:to-ecsess-900 w-full bg-gradient-to-b from-white py-16"
>
<div class="container mx-auto px-4">
<!-- Section Header -->
<div class="mb-12 text-center">
<h2 class="text-ecsess-800 dark:text-ecsess-100 mb-4 text-4xl font-bold md:text-5xl">
Affiliated Clubs & Labs
</h2>
<p class="text-ecsess-700 dark:text-ecsess-200 mx-auto max-w-2xl text-lg">
Explore opportunities to enhance your skills, build innovative projects, and connect with
the engineering community through our affiliated organizations.
</p>
</div>
<!-- Clubs Grid -->
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{#each clubs as club}
<div
class="group dark:bg-ecsess-950 relative flex flex-col overflow-hidden rounded-2xl bg-white shadow-lg transition-all duration-300 hover:-translate-y-2 hover:shadow-2xl"
>
<!-- Decorative gradient bar -->
<div class="from-ecsess-400 via-ecsess-500 to-ecsess-600 h-2 bg-gradient-to-r"></div>
<div class="flex flex-1 flex-col p-6">
<!-- Icon and Name -->
<div class="mb-4 flex items-center gap-4">
<div
class="bg-ecsess-100 group-hover:bg-ecsess-500 dark:bg-ecsess-800 flex h-14 w-14 items-center justify-center rounded-xl shadow-md transition-all duration-300 group-hover:scale-110"
>
{#if club.icon === Wrench}
<Wrench
class="text-ecsess-600 dark:text-ecsess-300 h-7 w-7 transition-colors group-hover:text-white"
strokeWidth={2.5}
/>
{:else if club.icon === Zap}
<Zap
class="text-ecsess-600 dark:text-ecsess-300 h-7 w-7 transition-colors group-hover:text-white"
strokeWidth={2.5}
/>
{:else if club.icon === Code2}
<Code2
class="text-ecsess-600 dark:text-ecsess-300 h-7 w-7 transition-colors group-hover:text-white"
strokeWidth={2.5}
/>
{/if}
</div>
<h3 class="text-ecsess-800 dark:text-ecsess-50 text-2xl font-bold">
{club.name}
</h3>
</div>
<!-- Description -->
<p class="text-ecsess-700 dark:text-ecsess-200 mb-6 flex-1 text-sm leading-relaxed">
{club.description}
</p>
<!-- Features -->
<div class="mb-6 space-y-2">
{#each club.features as feature}
<div class="flex items-center gap-2">
<div class="bg-ecsess-500 h-1.5 w-1.5 rounded-full"></div>
<span class="text-ecsess-600 dark:text-ecsess-300 text-xs font-semibold">
{feature}
</span>
</div>
{/each}
</div>
<!-- Visit Button -->
<a
href={club.website}
target="_blank"
rel="noopener noreferrer"
class="bg-ecsess-500 hover:bg-ecsess-600 dark:bg-ecsess-400 dark:hover:bg-ecsess-500 flex items-center justify-center gap-2 rounded-xl px-6 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg active:scale-95"
>
Visit Website
<ExternalLink class="h-4 w-4" strokeWidth={2.5} />
</a>
</div>
</div>
{/each}
</div>
<!-- Bottom CTA -->
<div class="mt-12 text-center">
<p class="text-ecsess-600 dark:text-ecsess-300 text-sm">
Want to get involved? Visit their websites to learn about upcoming events and how to join!
</p>
</div>
</div>
</section>

View File

@@ -1,166 +0,0 @@
<script lang="ts">
// Social links data
const socialLinks = [
{
name: 'Instagram',
url: 'https://www.instagram.com/mcgill_ecsess/',
icon: 'instagram',
ariaLabel: 'Follow us on Instagram'
},
{
name: 'Facebook',
url: 'https://www.facebook.com/ECSESS',
icon: 'facebook',
ariaLabel: 'Follow us on Facebook'
},
{
name: 'LinkedIn',
url: 'https://www.linkedin.com/company/ecsess/',
icon: 'linkedin',
ariaLabel: 'Connect with us on LinkedIn'
},
{
name: 'Linktree',
url: 'https://linktr.ee/ecsess',
icon: 'linktree',
ariaLabel: 'Visit our Linktree'
},
{
name: 'Contact Us',
url: 'mailto:ecsess.president@mcgilleus.ca',
icon: 'email',
ariaLabel: 'Send us an email'
},
{
name: 'Lounge Location',
url: 'https://maps.app.goo.gl/4RHKGJEE8FfcDs1H8',
icon: 'location',
ariaLabel: 'View lounge location on map',
address: 'Trottier Building, ECSESS Lounge'
}
];
</script>
<section class="bg-ecsess-800 w-full px-4 pb-12 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{#each socialLinks as link}
<a
href={link.url}
target={link.icon === 'email' ? '_self' : '_blank'}
rel={link.icon === 'email' ? '' : 'noopener noreferrer'}
aria-label={link.ariaLabel}
class="group border-ecsess-400 bg-ecsess-600 hover:border-ecsess-200 hover:bg-ecsess-400 flex items-center gap-4 rounded-lg border-2 p-6 transition-all hover:scale-105"
>
<!-- Icon -->
<div
class="bg-ecsess-800 group-hover:bg-ecsess-black flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full transition-all"
>
{#if link.icon === 'instagram'}
<svg
class="h-6 w-6 text-white"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z"
clip-rule="evenodd"
/>
</svg>
{:else if link.icon === 'facebook'}
<svg
class="h-6 w-6 text-white"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z"
clip-rule="evenodd"
/>
</svg>
{:else if link.icon === 'linkedin'}
<svg
class="h-6 w-6 text-white"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
/>
</svg>
{:else if link.icon === 'linktree'}
<svg
class="h-6 w-6 text-white"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M13.5108 5.85343L17.5158 1.73642L19.8404 4.11701L15.6393 8.12199H21.5488V11.4268H15.6113L19.8404 15.5345L17.5158 17.8684L11.7744 12.099L6.03299 17.8684L3.70842 15.5438L7.93745 11.4361H2V8.12199H7.90944L3.70842 4.11701L6.03299 1.73642L10.038 5.85343V0H13.5108V5.85343ZM10.038 16.16H13.5108V24H10.038V16.16Z"
/>
</svg>
{:else if link.icon === 'email'}
<svg
class="h-6 w-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
{:else if link.icon === 'location'}
<svg
class="h-6 w-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
{/if}
</div>
<!-- Content -->
<div class="flex-1">
<h3 class="text-lg font-semibold text-white">{link.name}</h3>
{#if link.address}
<p class="text-ecsess-200 text-sm">{link.address}</p>
{/if}
</div>
<!-- Arrow -->
<svg
class="text-ecsess-200 h-5 w-5 flex-shrink-0 transition-transform group-hover:translate-x-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{/each}
</div>
</section>

View File

@@ -5,7 +5,7 @@
import OhSchedule from 'components/officehour/OHSchedule.svelte'; import OhSchedule from 'components/officehour/OHSchedule.svelte';
import Link from 'components/Link.svelte'; import Link from 'components/Link.svelte';
import SeoMetaTags from 'components/layout/SeoMetaTags.svelte'; import SeoMetaTags from 'components/layout/SeoMetaTags.svelte';
import SocialLinks from 'components/homepage/SocialLinks.svelte'; import AffiliatedClubs from 'components/homepage/AffiliatedClubs.svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
/** loading things from the server side */ /** loading things from the server side */
@@ -55,7 +55,6 @@
</div> </div>
</Section> </Section>
<!-- Picture, FAQ -->
<!-- Office Hours Calendar --> <!-- Office Hours Calendar -->
<Section black> <Section black>
<div class="w-full"> <div class="w-full">
@@ -64,6 +63,10 @@
</div> </div>
</Section> </Section>
<!-- Affiliated Clubs -->
<AffiliatedClubs />
<!-- FAQs and Sponsors -->
<Section> <Section>
<div class="grid w-full max-w-[80vw] grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-24"> <div class="grid w-full max-w-[80vw] grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-24">
<div> <div>

View File

@@ -1,3 +1,31 @@
// import type { EventPost } from '$lib/schemas';
// import { getFromCMS } from '$lib/utils.js';
// const eventQuery = `*[_type == "events"]{
// name,
// category,
// date,
// location,
// description,
// reglink,
// paylink,
// "thumbnail": thumbnail.asset->url+"?h=800&fm=webp",
// "lastUpdated": _updatedAt,
// }`;
// export const load = async ({ url }) => {
// let listOfEvents: EventPost[] = await getFromCMS(eventQuery);
// let sortedEvents = listOfEvents.sort(
// (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
// );
// return {
// events: sortedEvents,
// canonical: url.href
// };
// };
import type { EventPost } from '$lib/schemas'; import type { EventPost } from '$lib/schemas';
import { getFromCMS } from '$lib/utils.js'; import { getFromCMS } from '$lib/utils.js';
@@ -13,9 +41,96 @@ const eventQuery = `*[_type == "events"]{
"lastUpdated": _updatedAt, "lastUpdated": _updatedAt,
}`; }`;
// Mock future events for testing
const getMockFutureEvents = (): EventPost[] => {
const today = new Date();
const nextWeek = new Date(today);
nextWeek.setDate(today.getDate() + 7);
const twoWeeks = new Date(today);
twoWeeks.setDate(today.getDate() + 14);
const nextMonth = new Date(today);
nextMonth.setMonth(today.getMonth() + 1);
return [
{
_id: 'mock-1',
name: "Antoine's Smash Workshop",
category: ['technical'],
date: nextWeek.toISOString(),
location: 'ECSESS Lounge',
description: [
{
_type: 'block',
children: [
{
_type: 'span',
text: 'Antoine should reall get gud at smash'
}
]
}
],
reglink: 'https://example.com/register',
paylink: null,
thumbnail: null,
lastUpdated: today.toISOString()
},
{
_id: 'mock-2',
name: 'ECSESS Annual Networking Night',
category: ['professional', 'social'],
date: twoWeeks.toISOString(),
location: 'Trottier ',
description: [
{
_type: 'block',
children: [
{
_type: 'span',
text: 'I love placeholder text!'
}
]
}
],
reglink: 'https://example.com/register',
paylink: 'https://example.com/payment',
thumbnail: null,
lastUpdated: today.toISOString()
},
{
_id: 'mock-3',
name: 'Midterm Study Session',
category: ['academic'],
date: nextMonth.toISOString(),
location: 'Trottier 5th floor',
description: [
{
_type: 'block',
children: [
{
_type: 'span',
text: 'TIME TO LOCK THE FUCK IN'
}
]
}
],
reglink: null,
paylink: null,
thumbnail: null,
lastUpdated: today.toISOString()
}
];
};
export const load = async ({ url }) => { export const load = async ({ url }) => {
let listOfEvents: EventPost[] = await getFromCMS(eventQuery); let listOfEvents: EventPost[] = await getFromCMS(eventQuery);
// TEMPORARY: Add mock future events for testing the UI
const mockEvents = getMockFutureEvents();
listOfEvents = [...listOfEvents, ...mockEvents];
// END TEMPORARY - Remove the above two lines when you have real future events
let sortedEvents = listOfEvents.sort( let sortedEvents = listOfEvents.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
); );
@@ -25,3 +140,5 @@ export const load = async ({ url }) => {
canonical: url.href canonical: url.href
}; };
}; };
// TODO: Remember to remove mock events once real future events are available in the CMS.