Merge branch 'main' into SEO
This commit is contained in:
16
src/app.css
16
src/app.css
@@ -2,12 +2,20 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
--color-ecsess-50: #e8ffd9;
|
||||
--color-ecsess-100: #CCE7BA;
|
||||
--color-ecsess-200: #a9b7a0;
|
||||
--color-ecsess-300: #82a17f;
|
||||
--color-ecsess-400: #5c8a5c;
|
||||
--color-ecsess-500: #4b7b4b;
|
||||
--color-ecsess-600: #3b6a3a;
|
||||
--color-ecsess-700: #235323;
|
||||
--color-ecsess-800: #0a3d2a;
|
||||
--color-ecsess-black: #1f1f1f;
|
||||
--color-ecsess-black-hover: #2c2c2c;
|
||||
--color-ecsess-900: #062c20;
|
||||
--color-ecsess-950: #031c15;
|
||||
--color-ecsess-teal: #168059;
|
||||
--color-ecsess-black: #1F1F1F;
|
||||
--color-ecsess-black-hover: #161917;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -184,3 +192,7 @@ h2 {
|
||||
@apply mt-3 text-sm leading-5;
|
||||
}
|
||||
}
|
||||
|
||||
.event{
|
||||
@apply grid gap-6;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
12
src/components/EventTabControl.svelte
Normal file
12
src/components/EventTabControl.svelte
Normal 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>
|
||||
38
src/components/EventTabPanel.svelte
Normal file
38
src/components/EventTabPanel.svelte
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export type EventPost = {
|
||||
id: string;
|
||||
title: string;
|
||||
name: string;
|
||||
description: string;
|
||||
date: string;
|
||||
time: string;
|
||||
location: string;
|
||||
image: string;
|
||||
link: string;
|
||||
thumbnail: string;
|
||||
reglink: string;
|
||||
category: string;
|
||||
payment: string; // event payment link (e.g., Zeffy)
|
||||
paylink: string; // event payment link (e.g., Zeffy)
|
||||
};
|
||||
|
||||
export type FAQ = {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<script>
|
||||
import '../app.css';
|
||||
import { dev } from '$app/environment';
|
||||
import { inject } from '@vercel/analytics';
|
||||
import Navbar from 'components/NavBar.svelte';
|
||||
import Footer from 'components/Footer.svelte';
|
||||
|
||||
inject({ mode: dev ? 'development' : 'production' });
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -40,4 +40,4 @@ export const load = async ({ url }) => {
|
||||
sponsors: sponsorsResp,
|
||||
canonical: url.href
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,18 +1,27 @@
|
||||
import type { EventPost } from '$lib/schemas';
|
||||
import { getFromCMS } from '$lib/utils.js';
|
||||
|
||||
// needs to concat and format this text
|
||||
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: await getFromCMS(eventQuery),
|
||||
events: sortedEvents,
|
||||
canonical: url.href
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<script>
|
||||
import { PortableText } from '@portabletext/svelte';
|
||||
<script lang="ts">
|
||||
import Section from 'components/Section.svelte';
|
||||
import SeoMetaTags from 'components/SeoMetaTags.svelte';
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
import EventTabControl from 'components/EventTabControl.svelte';
|
||||
import { Tabs } from '@skeletonlabs/skeleton-svelte';
|
||||
import EventTabPanel from 'components/EventTabPanel.svelte';
|
||||
import type { EventPost } from '$lib/schemas';
|
||||
|
||||
let events: EventPost[] = data.events ?? [];
|
||||
let group = $state('allEvents');
|
||||
</script>
|
||||
|
||||
<SeoMetaTags
|
||||
title="Events by ECSESS"
|
||||
@@ -14,22 +20,25 @@
|
||||
|
||||
<Section>
|
||||
<p class="page-title">Events</p>
|
||||
{#each data.events as event}
|
||||
<div class="rounded-lg border-4 p-4">
|
||||
<p>{event.name}</p>
|
||||
<p>{event.date}</p>
|
||||
<p>{event.location}</p>
|
||||
{#if event.description}
|
||||
<PortableText value={event.description} />
|
||||
{/if}
|
||||
Category:
|
||||
<div class="list">
|
||||
<ul class="list-inside list-disc space-y-2">
|
||||
{#each event.category as cat}
|
||||
<li>{cat}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<Tabs
|
||||
value={group}
|
||||
onValueChange={(e) => (group = e.value)}
|
||||
listClasses="flex-wrap place-content-center"
|
||||
>
|
||||
{#snippet list()}
|
||||
<EventTabControl value="allEvents">All Events</EventTabControl>
|
||||
<EventTabControl value="academic">Academic</EventTabControl>
|
||||
<EventTabControl value="professional">Professional</EventTabControl>
|
||||
<EventTabControl value="social">Social</EventTabControl>
|
||||
<EventTabControl value="technical">Technical</EventTabControl>
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<EventTabPanel value="allEvents" category="allEvents" {events} />
|
||||
<EventTabPanel value="academic" category="academic" {events} />
|
||||
<EventTabPanel value="professional" category="professional" {events} />
|
||||
<EventTabPanel value="social" category="social" {events} />
|
||||
<EventTabPanel value="technical" category="technical" {events} />
|
||||
{/snippet}
|
||||
</Tabs>
|
||||
</Section>
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
<Section>
|
||||
<p class="page-title">Resources</p>
|
||||
|
||||
<div class="flex flex-col gap-8">
|
||||
<div class="grid gap-4">
|
||||
{#each data.resources as re}
|
||||
<ResourceCard title={re.title} link={re.url}>
|
||||
{re.description}
|
||||
</ResourceCard>
|
||||
<ResourceCard
|
||||
title={re.title}
|
||||
link={re.url}
|
||||
description={re.description}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
Reference in New Issue
Block a user