added affiliated clubs and updated events page
This commit is contained in:
@@ -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
|
||||||
<div class="bg-ecsess-150 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"
|
||||||
|
>
|
||||||
|
<!-- Image Container with Gradient Overlay -->
|
||||||
|
<div class="relative h-64 overflow-hidden">
|
||||||
|
{#if thumbnail}
|
||||||
|
<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'}
|
||||||
|
<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'}
|
||||||
|
<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'}
|
||||||
|
<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'}
|
||||||
|
<img
|
||||||
|
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||||
|
src="/Academic.jpg"
|
||||||
|
alt="Academic Event"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<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
|
<div
|
||||||
class="bg-ecsess-400 grid h-[200px] place-items-center overflow-hidden rounded-md"
|
class="absolute inset-0 bg-gradient-to-b from-transparent via-black/30 to-black/80 dark:to-black/90"
|
||||||
aria-label="Event banner"
|
></div>
|
||||||
>
|
|
||||||
{#if thumbnail}
|
<!-- Badges -->
|
||||||
<img class="h-full object-cover" src={thumbnail} alt="Event banner" />
|
<div class="absolute top-0 right-0 left-0 flex items-start justify-between gap-2 p-4">
|
||||||
{:else if eventCategory?.[0] === 'social'}
|
{#if isPastEvent}
|
||||||
<img class="h-full object-cover" src="/Social.jpg" alt="Social Placeholder" />
|
<span
|
||||||
{:else if eventCategory?.[0] === 'technical'}
|
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"
|
||||||
<img class="h-full object-cover" src="/Technical.jpg" alt="Technical Placeholder" />
|
>
|
||||||
{:else if eventCategory?.[0] === 'professional'}
|
Past Event
|
||||||
<img class="h-full object-cover" src="/Professional.jpg" alt="Professional Placeholder" />
|
</span>
|
||||||
{:else if eventCategory?.[0] === 'academic'}
|
|
||||||
<img class="h-full object-cover" src="/Academic.jpg" alt="Academic Placeholder" />
|
|
||||||
{:else}
|
{:else}
|
||||||
<img class="h-full object-cover" src="/ECSESS.png" alt="Default Placeholder" />
|
<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}
|
||||||
|
|
||||||
|
{#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}
|
||||||
|
</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>
|
</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
|
||||||
|
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>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -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 -->
|
||||||
<EventBlock
|
{#if upcomingEvents.length > 0}
|
||||||
eventTitle={e.name}
|
<section>
|
||||||
date={e.date}
|
<div class="mb-6 flex items-center gap-3">
|
||||||
location={e.location}
|
<div class="bg-ecsess-500 h-1 w-12 rounded-full"></div>
|
||||||
eventDescription={e.description}
|
<h2 class="text-ecsess-600 text-3xl font-bold">Upcoming Events</h2>
|
||||||
thumbnail={e.thumbnail}
|
</div>
|
||||||
registrationLink={e.reglink}
|
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
paymentLink={e.paylink}
|
{#each upcomingEvents as e (e._id ?? e.name)}
|
||||||
eventCategory={e.category}
|
<EventBlock
|
||||||
/>
|
eventTitle={e.name}
|
||||||
{/each}
|
date={formatEventDate(e.date)}
|
||||||
|
location={e.location}
|
||||||
|
eventDescription={e.description}
|
||||||
|
thumbnail={e.thumbnail}
|
||||||
|
registrationLink={e.reglink}
|
||||||
|
paymentLink={e.paylink}
|
||||||
|
eventCategory={e.category}
|
||||||
|
isPastEvent={false}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</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>
|
</div>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|||||||
123
src/components/homepage/AffiliatedClubs.svelte
Normal file
123
src/components/homepage/AffiliatedClubs.svelte
Normal 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>
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user