pbw-explore/src/routes/[entry]/[type]/[slug]/+page.svelte

425 řádky
14 KiB
Svelte

<script>
export let data;
import { page } from '$app/stores';
import CollectionList from '$lib/components/CollectionList.svelte';
import CalendarList from '$lib/components/CalendarList.svelte';
import ItemLogo from '$lib/components/ItemLogo.svelte';
import EventTypeBadge from '$lib/components/EventTypeBadge.svelte';
import Footer from '$lib/components/Footer.svelte';
import Header from '$lib/components/Header.svelte';
import SvelteMarkdown from 'svelte-markdown';
import { formatItemDate, bareDomain, getFlagEmoji } from '$lib/utils.js';
import { config } from '$lib/pbw';
import { format } from 'date-fns';
const colsDef = Object.fromEntries(
Object.keys(config.collections).map((col) => {
return [config.collections[col].model, col];
})
);
const speakerLinks = {
twitter: { col: (x) => (x.twitter ? 'https://twitter.com/' + x.twitter : null) },
web: { col: (x) => x.web?.url },
linkedin: { col: (x) => (x.linkedin ? 'https://linkedin.com/in/' + x.linkedin : null) }
};
function eventDates(event) {
const dates = [];
for (const seg of event.segments) {
const date = format(new Date(seg.startTime), 'yyyy-MM-dd');
if (!dates.includes(date)) {
dates.push(date);
}
}
return dates;
}
$: entry = $page.params.entry;
$: col = $page.params.type;
$: colPlural = colsDef[col];
$: item = data.bundle[colPlural].find((e) => e.id === $page.params.slug);
$: defs = data.schema ? data.schema.definitions[col] : {};
</script>
<svelte:head>
<title>{item.name} | #PBW{$page.params.entry}</title>
</svelte:head>
<Header path={colsDef[$page.params.type]} type={$page.params.type} />
<div class="w-full dark:text-gray-200">
<div class="max-w-7xl mx-auto pt-5 md:pt-10">
<div class="mx-4 xl:mx-0">
<div class="flex flex-wrap md:flex-nowrap w-full">
<ItemLogo
{item}
img={config.collections[colPlural]?.img || 'logo'}
aspect={config[col]?.aspect || 'aspect-square'}
width="w-48 md:w-56 mr-5"
rounded="rounded-xl"
/>
<div class="flex-grow">
<!--div class="font-normal text opacity-50 mt-4 md:mt-0 mb-1" style="line-height: 0.6em;"><a href="/{entry}/{col}">{col.toUpperCase()}</a></div-->
<h2 class="text-4xl md:text-5xl font-bold text-gray-600 dark:text-gray-100 mt-4 md:mt-0">
{item.name}
</h2>
{#if col === 'event'}
<div class="text-2xl flex gap-4 mt-2 flex-wrap">
<div class="flex gap-1 items-center">
{#each item.types as type}
<EventTypeBadge {type} />
{/each}
</div>
<div class="">{formatItemDate(item, { full: true })}</div>
<div>
📍
{#if item.venues}
{@html item.venues
.map((vId) => {
const place = data.bundle.places.find((p) => p.id === vId);
return `<a href="/${$page.params.entry}/place/${place.id}" class=\"underline hover:no-underline\">${place.name}</a>`;
})
.join(', ')}
{:else if item.venueUrl}
<a
href={item.venueUrl}
target="_blank"
class="underline hover:no-underline external">{item.venueName}</a
>
{:else}
{item.venueName}
{/if}
</div>
{#if item.attendees}
<div>👥 {item.attendees}</div>
{/if}
</div>
{/if}
{#if col === 'speaker'}
<div class="text-2xl mt-2 markdown">
<SvelteMarkdown source={item.caption} />
</div>
<div class="flex flex-wrap gap-4 mt-4 text-xl">
{#each Object.keys(speakerLinks) as lk}
{#if speakerLinks[lk].col(item)}
<div>
<span class="opacity-40 text-sm uppercase">{lk} </span>
<a
href={speakerLinks[lk].col(item)}
target="_blank"
class="underline hover:no-underline"
>{bareDomain(speakerLinks[lk].col(item), lk)}</a
>
</div>
{/if}
{/each}
</div>
{/if}
{#if col === 'event'}
<div class="flex flex-wrap gap-6 text-xl mt-4">
{#if item.chains && item.chains.length > 0}
<div>
<div class="uppercase text-sm opacity-40">Chains</div>
<div class="flex gap-2">
{#each item.chains.map((chId) => {
const chItem = data.bundle.chains.find((x) => x.id === chId);
if (!chItem) {
return { name: chain.substr(0, 1).toUpperCase() + chain.substr(1) };
}
return chItem;
}) as chain}
<div class="flex items-center">
{#if chain.id}
<a
href="/{entry}/chain/{chain.id}"
class="flex items-center underline hover:no-underline"
>
{#if chain.logo}
<ItemLogo item={chain} width="w-5 h-5 mr-1" />
{/if}
{chain.name}
</a>
{:else}
{chain.name}
{/if}
</div>
{/each}
</div>
</div>
{/if}
{#if item.tags && item.tags.length > 0}
<div>
<div class="uppercase text-sm opacity-40">Tags</div>
<div class="flex gap-2">
{#each item.tags as tag}
<div>#{tag}</div>
{/each}
</div>
</div>
{/if}
{#if item.languages && item.languages.length > 0}
<div>
<div class="uppercase text-sm opacity-40">Languages</div>
<div class="flex gap-2">
{#each item.languages as lang}
<div>{getFlagEmoji(lang)} {lang}</div>
{/each}
</div>
</div>
{/if}
<div>
<div class="uppercase text-sm opacity-40">Organizator</div>
<div class="markdown"><SvelteMarkdown source={item.org || 'TBD'} /></div>
</div>
{#if item.poc}
<div>
<div class="uppercase text-sm opacity-40">Point of contact</div>
<div class="markdown"><SvelteMarkdown source={item.poc} /></div>
</div>
{/if}
</div>
{/if}
{#if col === 'place'}
<div class="flex flex-wrap gap-6 text-xl mt-4">
{#if item.address}
<div>
<div class="uppercase text-sm opacity-40">Address</div>
<div class="flex gap-2">
{item.address}
</div>
</div>
{/if}
{#if item.capacity}
<div>
<div class="uppercase text-sm opacity-40">Capacity</div>
<div class="flex gap-2">
{item.capacity} ppl
</div>
</div>
{/if}
{#if item.eventTypes && item.eventTypes.length > 0}
<div>
<div class="uppercase text-sm opacity-40">Event Types</div>
<div class="flex gap-2">
{#each item.eventTypes as type}
<div>{type}</div>
{/each}
</div>
</div>
{/if}
</div>
{/if}
{#if item.links}
<div class="flex flex-wrap gap-4 mt-4 text-xl">
{#each Object.keys(item.links) as lk}
<div>
<span class="opacity-40 text-sm uppercase">{lk} </span>
<a href={item.links[lk]} target="_blank" class="underline hover:no-underline"
>{bareDomain(item.links[lk], lk)}</a
>
</div>
{/each}
</div>
{/if}
</div>
{#if item.registration}
<div class="text-xl lg:flex-nowrap mt-6 lg:mt-0">
<div class="w-auto rounded-lg md:text-right gap-4">
{#if item.registration.link}
<div class="pb-2">
<a href={item.registration.link} class="" target="_blank">
<div
class="inline-block py-3 px-6 border border-pbw-red hover:bg-pbw-red hover:text-white text-pbw-red text-xl rounded-lg"
>
{#if item.registration.button}
{item.registration.button}
{:else if item.registration.type === 'tickets'}
Buy tickets!
{:else}
Request tickets!
{/if}
</div>
</a>
</div>
{:else}
<div
class="inline-block py-2 px-4 border border-gray-400 text-gray-400 text-lg rounded-lg mb-2 cursor-not-allowed"
>
{#if item.registration.type === 'tickets'}
Tickets not yet available
{:else if item.registration.type === 'invites'}
Application form is not yet available
{/if}
</div>
{/if}
<div>
{#if item.registration.status}
<div>
<span class="opacity-40 text-sm uppercase mr-1">Status</span>
{#if item.registration.status === 'available'}
<span class="text-green-700"> Available</span>
{:else if item.registration.status === 'sold-out'}
<span class="text-red-700"> Sold Out!</span>
{:else if item.registration.status === 'closed'}
<span class="text-yellow-700"> Registration closed</span>
{/if}
</div>
{/if}
<div>
<span class="opacity-40 text-sm uppercase mr-1">Price</span>
{#if item.registration.type === 'tickets'}
{item.registration.price || 'TBA'}
{:else}
Free!
{/if}
</div>
</div>
</div>
</div>
{/if}
</div>
{#if item.desc || item.description}
<div class="mt-4 text-xl markdown">
<div><SvelteMarkdown source={item.desc || item.description} /></div>
</div>
{/if}
{#if col === 'event'}
{#if item.tracks}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Tracks
</h2>
<div class="flex flex-wrap gap-2 mt-4">
{#each item.tracks as track}
<div
class="border rounded-lg bg-gray-50 dark:text-gray-300 dark:border-gray-300 dark:bg-transparent items-center py-1.5 px-2"
>
<div class="text-xl" title={track.name}>{track.shortname || track.name}</div>
<!--div class="text-lg markdown"><SvelteMarkdown source={track.examples} /></div-->
</div>
{/each}
</div>
{/if}
{#if item.segments}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Schedule
</h2>
{#each eventDates(item) as date}
<div class="mb-6">
<h3 class="mt-4 text-xl uppercase text-gray-500 dark:text-gray-400">
<a href="/{entry}/day/{date}">{format(new Date(date), 'EEEE - MMMM d, yyyy')}</a>
</h3>
<div class="mt-4">
<CalendarList
{date}
segments={item.segments.filter((s) => s.startTime.match(new RegExp('^' + date)))}
{entry}
bundle={data.bundle}
event={item}
/>
</div>
</div>
{/each}
{/if}
{#if item.speakers}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Speakers ({item.speakers?.length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center text-xl"
>
<CollectionList arr={item.speakers} bundle={data.bundle} currentItem={item} />
</div>
{/if}
{#if item.events}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Sub-Events ({item.events?.length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center"
>
<CollectionList arr={item.events} />
</div>
{/if}
{#if item.venues}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Venues ({item.venues?.length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center text-xl"
>
<CollectionList
arr={data.bundle.places.filter((p) => item.venues.includes(p.id))}
col="place"
img="photo"
/>
</div>
{/if}
{/if}
{#if col === 'union'}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Big events ({item.events?.map((eId) => data.bundle.events.find((e) => e.id === eId))
.length})
</h2>
<div
class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-6 mt-4 text-2xl text-center"
>
<CollectionList
arr={item.events?.map((eId) => data.bundle.events.find((e) => e.id === eId))}
col="event"
img="logo"
/>
</div>
{/if}
{#if col === 'speaker'}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Events ({data.bundle.events.filter((e) => e.speakers?.find((s) => s.id === item.id))
.length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center text-xl"
>
<CollectionList
arr={data.bundle.events.filter((e) => e.speakers?.find((s) => s.id === item.id))}
col="event"
img="logo"
/>
</div>
{/if}
{#if col === 'place'}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Events ({data.bundle.events.filter((e) => e.venues?.includes(item.id)).length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center text-xl"
>
<CollectionList
arr={data.bundle.events.filter((e) => e.venues?.includes(item.id))}
col="event"
img="logo"
/>
</div>
{/if}
{#if col === 'chain'}
<h2 class="text-2xl uppercase font-bold mt-10 text-gray-500 dark:text-gray-300">
Events ({data.bundle.events.filter((e) => e.chains?.includes(item.id)).length || 0})
</h2>
<div
class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 mt-4 text-center text-xl"
>
<CollectionList
arr={data.bundle.events.filter((e) => e.chains?.includes(item.id))}
col="event"
img="logo"
/>
</div>
{/if}
</div>
</div>
</div>
<Footer {col} {item} bundle={data.bundle} />