Skip to content

Commit

Permalink
feat: add event details page (#51)
Browse files Browse the repository at this point in the history
This adds the events details page (cards #47-50), not including the
"Related Events" section (card #51).
  • Loading branch information
eyronjuude authored Jan 16, 2025
2 parents b0d18d2 + 4857713 commit db9dcfc
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 11 deletions.
110 changes: 110 additions & 0 deletions website-frontend/src/lib/components/events/DetailsBanner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script lang="ts">
import { deslugify } from '$lib/utils';
import { PUBLIC_APIURL } from '$env/static/public';
import { Clock, MapPin } from 'lucide-svelte';
export let title: string;
export let background_image: string;
export let location: string;
export let start_date: string;
export let end_date: string;
$: deslugify_title = deslugify(title);
// function to extract date
function getDateParts(dateString: string): { day: number; month: string } {
const date = new Date(dateString);
const options: Intl.DateTimeFormatOptions = { month: 'long' };
return {
day: date.getDate(),
month: new Intl.DateTimeFormat('en-US', options).format(date)
};
}
const { day: startDay, month: startMonth } = getDateParts(start_date);
let endDay: number | undefined;
let endMonth: string | undefined;
if (end_date) {
({ day: endDay, month: endMonth } = getDateParts(end_date));
}
// function to extract time
function getTimeParts(dateString: string): { hours: number; minutes: string } {
const date = new Date(dateString);
return {
hours: date.getHours(), // Hours in 24-hour format
minutes: date.getMinutes().toString().padStart(2, '0') // Padded minutes
};
}
const { hours: startHours, minutes: startMinutes } = getTimeParts(start_date);
let endHours: number | undefined;
let endMinutes: string | undefined;
if (end_date) {
({ hours: endHours, minutes: endMinutes } = getTimeParts(end_date));
}
</script>

<div class="relative z-0">
<div
class="h-[40vh] bg-cover bg-center md:h-[70vh]"
style="background-image: linear-gradient(to top, #004420, transparent), url('{PUBLIC_APIURL}/assets/{background_image}')"
></div>

<div
class="-mt-[0.4px] w-full bg-[#004420] pb-10 pt-10 md:absolute md:bottom-10 md:bg-transparent md:pb-0"
>
<div
class="-mt-16 flex w-full
flex-col items-center px-5 text-center
md:flex-row md:justify-between md:px-32 md:text-start"
>
<div class="text-white md:max-w-[60vw]">
<h1 class="text-4xl font-bold md:mb-4 md:text-4xl">{deslugify_title}</h1>
<div class="my-6 space-y-1 text-gray-100 md:my-0 md:flex">
{#if location}
<div class="flex items-center justify-center space-x-2 md:mr-10 md:justify-start">
<MapPin class="h-4 w-4" />
<h4>{@html location}</h4>

Check warning on line 73 in website-frontend/src/lib/components/events/DetailsBanner.svelte

View workflow job for this annotation

GitHub Actions / test

`{@html}` can lead to XSS attack
</div>
{/if}

<div class="flex items-center justify-center space-x-2 md:justify-start">
<Clock class="h-4 w-4" />
{#if end_date}
<h4>{startHours}:{startMinutes} - {endHours}:{endMinutes}</h4>
{:else}
<h4>Whole day event</h4>
{/if}
</div>
</div>
</div>

<!-- Date Display -->

<div class="flex flex-row items-center md:pl-12">
<div
class="flex h-28 w-28 flex-col items-center justify-center rounded-lg bg-white text-gray-900 shadow-xl md:h-36 md:w-36"
>
<h1 class="text-4xl font-bold md:mb-2 md:text-5xl">{startDay}</h1>
<h3 class="font-medium text-[#004420]">{startMonth}</h3>
</div>

{#if end_date}
<div class="mx-3 h-1 w-3 bg-gray-300"></div>
<div
class="flex h-28 w-28 flex-col items-center justify-center rounded-lg bg-white text-gray-900 shadow-xl md:h-36 md:w-36"
>
<h1 class="text-4xl font-bold md:mb-2 md:text-5xl">{endDay}</h1>
<h3 class="font-medium text-[#004420]">{endMonth}</h3>
</div>
{/if}
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
export let event: Event;
</script>

<div class="card h-fit w-72 max-w-72 snap-center p-4 md:w-96 md:max-w-96">
<div class="mb-4 md:flex md:justify-between">
<h3>{event.event_headline}</h3>
<h3>{new Date(event.date_created).toLocaleDateString()}</h3>
<a href="/events/{event.slug}">
<div class="card h-fit w-72 max-w-72 snap-center p-4 md:w-96 md:max-w-96">
<div class="mb-4 md:flex md:justify-between">
<h3>{event.event_headline}</h3>
<h3>{new Date(event.date_created).toLocaleDateString()}</h3>
</div>
<div class="max-h-6 overflow-hidden *:truncate">
{@html event.event_content}

Check warning on line 13 in website-frontend/src/lib/components/events/FeaturedEventCard.svelte

View workflow job for this annotation

GitHub Actions / test

`{@html}` can lead to XSS attack
</div>
<p>View details &#8594;</p>
</div>
<div class="max-h-6 overflow-hidden *:truncate">
{@html event.event_content}
</div>
<p>View details &#8594;</p>
</div>
</a>
18 changes: 16 additions & 2 deletions website-frontend/src/lib/models/event.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { cleanHtml } from '$lib/models-helpers';
import { array, isoTimestamp, nullable, object, pipe, string, type InferOutput } from 'valibot';
import {
array,
isoTimestamp,
nullable,
object,
optional,
pipe,
string,
type InferOutput
} from 'valibot';

export const Event = object({
slug: string(),
date_created: pipe(string(), isoTimestamp()),
event_headline: string(),
hero_image: optional(nullable(string())),
event_content: pipe(string(), cleanHtml),
tags: nullable(array(string()))
tags: nullable(array(string())),
start_date: pipe(string(), isoTimestamp()),
end_date: nullable(pipe(string(), isoTimestamp())),
location: nullable(string())
});

export const Events = array(Event);
Expand Down
29 changes: 29 additions & 0 deletions website-frontend/src/routes/events/[slug]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** @type {import('./$types').PageServerLoad} */
import { readItems } from '@directus/sdk';
import getDirectusInstance from '$lib/directus';
import { error } from '@sveltejs/kit';

export async function load({ params, fetch }) {
const directus = await getDirectusInstance(fetch);
const eventSlug = params.slug;

const events = await directus.request(
readItems('events', {
filter: {
slug: {
_eq: eventSlug
}
}
})
);

if (!events.length) {
throw error(404, 'Event not found');
}

const event = events[0];

return {
event
};
}
41 changes: 41 additions & 0 deletions website-frontend/src/routes/events/[slug]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
/** @type {import('./$types').PageData} */
import DetailsBanner from '$lib/components/events/DetailsBanner.svelte';
export let data;
$: ({ event } = data);
</script>

<body class="bg-white">
{#if event}
<div class="relative">
<DetailsBanner
title={event.event_headline}
background_image={event.hero_image ?? ''}
location={event.location ?? ''}
start_date={event.start_date}
end_date={event.end_date ?? ''}
/>
</div>

<div class="px-4 py-14 text-gray-800 md:px-64 md:py-16">
{event.event_content}
</div>

<h1 class="mb-8 px-4 text-2xl font-bold text-gray-900 md:px-32">Related Events</h1>

<!-- <div
class="mx-auto my-3 grid
max-w-[94vw] grid-cols-2 gap-2 pb-20
md:my-8 md:max-w-[80vw] md:grid-cols-4 md:gap-4"
>
{#each related_events as related_event}
<a href="/events/{related_event.event_headline}">
<FeaturedEventCard {related_event} />
</a>
{/each}
</div> -->
{:else}
<p>Event not found</p>
{/if}
</body>

0 comments on commit db9dcfc

Please sign in to comment.