Merge branch 'main' into SEO

This commit is contained in:
Antoine Phan
2025-09-06 10:12:20 -04:00
19 changed files with 923 additions and 192 deletions

View File

@@ -0,0 +1,103 @@
<script lang="ts">
import { PortableText } from '@portabletext/svelte';
import { CalendarDays, MapPin, Link as LinkIcon, FilePen } from '@lucide/svelte';
let {
eventTitle,
date,
location,
eventDescription,
thumbnail,
registrationLink,
paymentLink,
eventCategory
} = $props();
</script>
<div class="mx-auto w-[100%] rounded-2xl bg-[#E8FFD9] p-5 text-[#0A3D2A] lg:w-[64%] lg:max-w-3xl">
<div class="rounded-[20px] bg-[#A6D6B8]">
<div
class="grid h-[200px] place-items-center overflow-hidden rounded-[16px] bg-[#5CAF95]"
aria-label="Event banner"
>
{#if thumbnail}
<img class="h-full object-cover" src={thumbnail} alt="Event banner" />
{:else if eventCategory?.[0] === 'social'}
<img class="h-full object-cover" src="/Social.jpg" alt="Social Placeholder" />
{:else if eventCategory?.[0] === 'technical'}
<img class="h-full object-cover" src="/Technical.jpg" alt="Technical Placeholder" />
{:else if eventCategory?.[0] === 'professional'}
<img class="h-full object-cover" src="/Professional.jpg" alt="Professional Placeholder" />
{:else if eventCategory?.[0] === 'academic'}
<img class="h-full object-cover" src="/Academic.jpg" alt="Academic Placeholder" />
{:else}
<img class="h-full object-cover" src="/ECSESS.png" alt="Default Placeholder" />
{/if}
</div>
</div>
<!-- content -->
<div class="mt-[22px] grid gap-[18px]">
<p class="text-ecsess-800 my-0 text-center text-xl lg:text-2xl text-wrap lg:leading-8 leading-6 tracking-[0.3px]">
{eventTitle}
</p>
{#if eventDescription}
<div class="mx-auto max-w-[75ch] leading-relaxed text-[#5E8174]">
<PortableText value={eventDescription} />
</div>
{/if}
<div class="mt-[6px] grid gap-4 md:grid-cols-2">
<div class="grid gap-[10px] rounded-2xl bg-[#CCE7BA] px-4 py-[14px]">
<div class="flex items-center gap-2 text-[#0A3D2A]">
<CalendarDays class="shrink-0" strokeWidth={2.5} />
<span class="font-bold tracking-[0.2px]">Datetime:</span>
<p class="m-0 text-left">{date}</p>
</div>
<div class="flex items-center gap-2 text-[#0A3D2A]">
<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 class="grid gap-[10px] rounded-2xl bg-[#CCE7BA] px-4 py-[14px]">
<div class="flex items-center gap-2 text-[#0A3D2A]">
<FilePen class="shrink-0" strokeWidth={2.5} />
<span class="font-bold tracking-[0.2px]">Registration:</span>
{#if registrationLink}
<a
href={registrationLink}
target="_blank"
rel="noopener noreferrer"
class="text-left text-[#0A3D2A] underline-offset-4 hover:underline"
>
Register Here
</a>
{:else}
<p class="m-0 text-left">Just drop in!</p>
{/if}
</div>
<div class="flex items-center gap-2 text-[#0A3D2A]">
<LinkIcon class="shrink-0" strokeWidth={2.5} />
<span class="font-bold tracking-[0.2px]">Payment:</span>
{#if paymentLink}
<a
href={paymentLink}
target="_blank"
rel="noopener noreferrer"
class="text-left text-[#0A3D2A] underline-offset-4 hover:underline"
>
Pay Here
</a>
{:else}
<p class="m-0 text-left">Free!</p>
{/if}
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,12 @@
<script>
import { Tabs } from '@skeletonlabs/skeleton-svelte';
let { value, children } = $props();
</script>
<Tabs.Control
{value}
classes="hover:border-b-ecsess-200 border-b-4 transition-all ease-in-out pb-2 text-lg active:border-b-ecsess-600 px-2"
stateActive="border-b-ecsess-400"
>
{@render children()}
</Tabs.Control>

View File

@@ -0,0 +1,38 @@
<script lang="ts">
import { Tabs } from '@skeletonlabs/skeleton-svelte';
import EventBlock from 'components/EventBlock.svelte';
import type { EventPost } from '$lib/schemas';
type Category = 'allEvents' | 'academic' | 'professional' | 'social' | 'technical';
let { value, category, events } = $props<{
value: Category;
category: Category;
events: EventPost[];
}>();
const matchCategory = (e: EventPost): boolean => {
if (category === 'allEvents') return true;
const c: unknown = e.category ?? [];
return Array.isArray(c) ? c.includes(category) : (c as string) === category;
};
const filtered = $derived((events ?? []).filter(matchCategory));
</script>
<Tabs.Panel {value}>
<div class="flex flex-wrap gap-4 m-1 lg:m-4">
{#each filtered as e (e._id ?? e.name)}
<EventBlock
eventTitle={e.name}
date={e.date}
location={e.location}
eventDescription={e.description}
thumbnail={e.thumbnail}
registrationLink={e.reglink}
paymentLink={e.paylink}
eventCategory={e.category}
/>
{/each}
</div>
</Tabs.Panel>

View File

@@ -1,18 +1,26 @@
<script>
import { slide } from 'svelte/transition';
import Button from './Button.svelte';
import Link from './Link.svelte';
let { title = '_Resource Title_', children, link = 'https://example.com' } = $props();
import { CircleArrowRight } from '@lucide/svelte';
import Link from 'components/Link.svelte';
let { title = '_Resource Title_', description = "Lorem ipsum", link = 'https://example.com' } = $props();
</script>
<div class="bg-ecsess-200 ring-6 ring-ecsess-600 hover:ring-ecsess-200/60 hover:shadow-3xl max-w-sm rounded-lg w-full transition-all" transition:slide>
<Link href={link}>
<div class="px-8 py-4">
<p class="text-ecsess-800 px-2 text-xl font-semibold lg:text-2xl">
<div class="bg-ecsess-50 relative h-fit max-w-xl min-w-12 rounded-lg px-4 py-2 md:py-0">
<div class="grid grid-cols-1 md:grid-cols-[7fr_1fr]">
<div class="flex flex-col items-start p-4">
<p class="text-ecsess-900 my-1 pb-1 text-left text-xl font-extrabold">
{title}
</p>
<p class="text-ecsess-black pt-3 text-base font-normal lg:text-lg">{@render children()}</p>
<p class="text-ecsess-600 text-left text-base">
{description}
</p>
</div>
</Link>
<div class="m-4 place-self-center">
<Link href={link}>
<CircleArrowRight
size="42"
class="stroke-ecsess-800 hover:stroke-ecsess-400 cursor-pointer transition duration-200 active:scale-90"
/>
</Link>
</div>
</div>
</div>