Skip to content

Commit

Permalink
Update event logic ✨
Browse files Browse the repository at this point in the history
  • Loading branch information
Freymaurer committed Oct 14, 2024
1 parent 9fa89fa commit 95f78ce
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 93 deletions.
84 changes: 70 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,30 +249,38 @@ The frontmatter for an event looks like this:

```yaml
---
date: 2024-10-24
title: ARCify your research project
category: Training
excerpt: 'Learn how to move your datasets into ARCs, share them via the DataHUB, and annotate them with metadata.'
start: 2024-10-24T09:00:00
end: 2024-10-24T12:00:00
category: Training
mode: On-site
audience: [Users, DataStewards]
location:
short: HHU
url: https://www.ceplas.eu/en/contact/how-to-get-there
image: ~/assets/images/events/ceplas-ARCs.drawio.png
when:
start: 2024-10-24T09:00:00
end: 2024-10-24T12:00:00
# -- or for repeating events --
# -
# start: 2024-10-24
# end: 2024-10-25
# -
# start: 2024-11-24
# end: 2024-11-25
tutors:
- Dominik Brilhaus (CEPLAS)
- Sabrina Zander (MibiNet)
image: ~/assets/images/events/ceplas-ARCs.drawio.png
organizer:
name: Dominik Brilhaus
affiliation: CEPLAS Data
url: https://www.ceplas.eu/en/research/data-science-and-data-management
location:
short: HHU
url: https://www.ceplas.eu/en/contact/how-to-get-there
registration:
description: 'First-come-first-serve. Members of CEPLAS have priority. Everyone else is welcome, if seats are available.'
url: 'https://terminplaner6.dfn.de/en/b/551776b4130c40357ea030db0142f472-910401'
deadline: 2024-10-17
seats: 12
external: https://en.wikipedia.org/wiki/URL#:~:text=A%20typical%20URL%20could%20have,html%20).
---
```

Expand All @@ -281,12 +289,10 @@ with these fields:

| Field name | description | Note |
| ------------- | --------------------------------------------------------------------- | ------------ |
| `date` | The date of the event | **Mandatory** |
| `title` | The title of the event | **Mandatory** |
| `category` | The category of the event | one of Conference', 'Hackathon', 'Webinar', 'Training', **Mandatory** |
| `excerpt` | A brief description of the event | **Mandatory** |
| `start` | The start date and time of the event in ISO 8601 format | **Mandatory** |
| `end` | The end date and time of the event in ISO 8601 format | **Mandatory** |
| `category` | The category of the event | one of Conference', 'Hackathon', 'Webinar', 'Training', **Mandatory** |
| `when` | Event instantiation(s) | Either a single object or an array of 'when's , containing the start and end date and time of the event in ISO 8601 format. **Mandatory** |
| `mode` | The mode of the event (e.g., online, on-site) | one of 'On-site', 'Online', 'Hybrid', **Mandatory** |
| `audience` | The intended audience of the event | array of 'Users', 'DataStewards', 'Developers', 'Everyone', **Mandatory** |
| `location` | The location information for the event | **Mandatory** |
Expand Down Expand Up @@ -322,6 +328,54 @@ with these fields:
| `deadline` | The deadline for registration in YYYY-MM-DD format | **Optional** |
| `seats` | The number of available seats | **Optional** |

`when` can be either a single object or an array of objects, each containing the start and end date and time of the event in ISO 8601 format.

Single:

```yaml
when:
start: 2024-10-24T09:00:00
end: 2024-10-24T12:00:00
```

Repeating:

Using a list of objects will create multiple instances of the event. This is useful for events that repeat on a regular basis.

```yaml
when:
-
start: 2024-10-24T09:00:00
end: 2024-10-25T12:00:00
-
start: 2024-11-24T09:00:00
end: 2024-11-25T12:00:00
```

You can give optional props to single instantiantions of a event using the syntax below. The `props` object can have any field the base type has, but it is not mandatory to provide all fields.

> [!CAUTION]
> If you replace any object field, for example `registration`, you must provide all subfields for this object you want to use. You cannot partially replace an object field.

```yaml
when:
-
start: 2024-10-24T09:00:00
end: 2024-10-25T12:00:00
-
start: 2024-11-24T09:00:00
end: 2024-11-25T12:00:00
props:
registration:
description: 'First-come-first-serve. Members of CEPLAS have priority. Everyone else is welcome, if seats are available.'
url: 'https://an-entirely-different-url.com'
deadline: 2024-11-17
seats: 42
```

In this example the event on 2024-11-24 has a specific different registration object.


### News

News are generated from markdown files in `src/content/news`.
Expand Down Expand Up @@ -434,9 +488,11 @@ All files found here will be used to generate a file structure like `/articles/:

#### `src/content/events`

All files found here will be used to generate a file structure like `/events/:slug`. The slug is automatically generated from the filename. The file responsible for generating the respective layout is `/src/pages/events/[...slug].astro`.
All files found here are used to generate the page `/events/...` structure. Multiple pages are created for this content:

All content events are display in `/src/pages/events/index.astro`.
- `/events` is a list of all events (from `/src/pages/events/index.astro`)
- `/events/[slug]`, where slug is the file name or a frontmatter `slug` property. This can be any of two, a detailed view on a single event or a list of events for repeating events (`when` is array, from `/src/pages/[event]/index.astro`).
- `/events/[slug]/[dd-MM-yyyy]`, slug as above, `dd-MM-yyyy` is `when`.`start` of the specific event instantiation (e.g. `13-11-2024`). This is always a detailed view on a single event (from `src/pages/events/[event]/[date].astro`).

#### `src/content/news`

Expand Down
2 changes: 1 addition & 1 deletion src/components/events/EventInfoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function EventInfoList({event, additional}: Props) {
<li className="flex items-center gap-2">
<InlineIcon icon="tabler:calendar-time" className="text-xl"/>
<span>
{event.data.periodic && <span title="repeating" className="inline-block align-middle"><InlineIcon icon="tabler:infinity"></InlineIcon></span>}
{event.data.repeating && <span title="repeating" className="inline-block align-middle"><InlineIcon icon="tabler:infinity"></InlineIcon></span>}
{formatterDate.format(event.data.when.start) + " " + formatterTime.format(event.data.when.start)}
<span></span>
{formatterDate.format(event.data.when.end) + " " + formatterTime.format(event.data.when.end)}
Expand Down
50 changes: 34 additions & 16 deletions src/components/events/EventList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useState } from 'react';
import type { CollectionEntry } from 'astro:content';
import {sortByYear, groupByYear, reducePeriodicEvents, type ReducedEvent } from '~/util/EventUtil.ts';
import {sortByYear, groupByYear, isPastOrWithinNext31Days, type ReducedEvent } from '~/util/EventUtil.ts';
import EventInfoList from './EventInfoList.tsx';

interface Props {
events: CollectionEntry<'events'>[];
events: ReducedEvent[];
showFilter?: boolean;
// This prop determines if all events should be shown, or only those within the next 31 days
showPastFutureLimit?: boolean;
}

interface ConfigState {
Expand Down Expand Up @@ -54,7 +56,7 @@ function EventCard (event: ReducedEvent) {
<div className="shadow border p-3 lg:p-5 rounded lg:max-w-5xl xl:max-w-6xl" key={"event-" + event.slug}>
<div className={"grid lg:grid-rows-1 gap-2 lg:gap-4 lg:grid-cols-2 " + (event.data.image ? 'lg:grid-cols-2' : '')}>
<div className="prose-sm lg:prose">
<h1><a href={"/events/" + event.slug}>{event.data.title}</a></h1>
<h1><a href={"/events/" + event.href}>{event.data.title}</a></h1>
{ event.data.image && <EventInfoList event={event} />}
<p>
{ event.data.excerpt }
Expand All @@ -70,13 +72,25 @@ function EventCard (event: ReducedEvent) {
)
}

export default function EventList( {events: rawEvents}: Props ) {
export default function EventList( {events, showFilter = true, showPastFutureLimit = false}: Props ) {
let now = new Date();
let events: ReducedEvent[] = reducePeriodicEvents(rawEvents);

const upcomingEvents = sortByYear(events.filter((event) => {
return event.data.when.start >= now;
}), (event) => event.data.when.start);
const filterFutureEventsFunction = (events: ReducedEvent[]) => {
return events
.filter((event) => {
return event.data.when.start >= now;
})
.filter((event) =>
showPastFutureLimit
? true
: isPastOrWithinNext31Days(event.data.when.start)
)
}

const upcomingEvents = sortByYear(
filterFutureEventsFunction(events),
(event) => event.data.when.start
);

const pastEvents = events.filter((event) => {
return event.data.when.start < now;
Expand Down Expand Up @@ -115,11 +129,15 @@ export default function EventList( {events: rawEvents}: Props ) {

return (
<>
<div className='grid grid-cols-1 md:grid-cols-[auto_minmax(0,_1fr)] gap-x-4 gap-y-2'>
<ControlButtons label="Mode" options={modes} selectedOption={config.mode} setOption={toggleMode}/>
<ControlButtons label="Audience" options={audiences} selectedOption={config.audience} setOption={toggleAudience}/>
<ControlButtons label="Category" options={categories} selectedOption={config.category} setOption={toggleCategory}/>
</div>
{
showFilter && (
<div className='grid grid-cols-1 md:grid-cols-[auto_minmax(0,_1fr)] gap-x-4 gap-y-2'>
<ControlButtons label="Mode" options={modes} selectedOption={config.mode} setOption={toggleMode}/>
<ControlButtons label="Audience" options={audiences} selectedOption={config.audience} setOption={toggleAudience}/>
<ControlButtons label="Category" options={categories} selectedOption={config.category} setOption={toggleCategory}/>
</div>
)
}

<h2 className="text-5xl lg:text-7xl text-center bg-secondary text-secondary-content p-2">Upcoming</h2>
<div>
Expand All @@ -129,7 +147,7 @@ export default function EventList( {events: rawEvents}: Props ) {
</div>
{
filteredUpcomingEvents.map((event) => (
<EventCard {...event} key={"card-" + event.slug} />
<EventCard {...event} key={"card-" + event.href} />
))
}
<h2 className="text-5xl lg:text-7xl text-center bg-secondary text-secondary-content p-2">Archive</h2>
Expand All @@ -147,7 +165,7 @@ export default function EventList( {events: rawEvents}: Props ) {
<div className="grid grid-cols-1 gap-4 lg:gap-10">
{
events.filter(filterEvents).map((event) => (
<EventCard {...event} key={"card-" + event.slug}/>
<EventCard {...event} key={"card-" + event.href}/>
))
}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/content/events/2024-10-16_DataSteward-Circle.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
slug: datasteward-circle
title: DataSteward Circle
category: Meeting
audience: [DataStewards]
Expand Down
13 changes: 13 additions & 0 deletions src/layouts/CenteredLayout.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
import Layout from "./Layout.astro";
const { title } = Astro.props;
---

<Layout title={title}>
<div class="flex items-center justify-center w-full">
<div class="flex flex-col gap-4 p-2 lg:p-5 lg:py-10 xl:py-20">
<slot />
</div>
</div>
</Layout>
33 changes: 16 additions & 17 deletions src/pages/events/[...slug].astro → src/layouts/EventLayout.astro
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
---
import { type ReducedEvent } from '~/util/EventUtil.ts';
import MarkdownLayout from '~/layouts/MarkdownLayout.astro';
import { getCollection } from 'astro:content';
import { Image } from 'astro:assets';
import SocialShare from '~/components/widgets/SocialShare.astro';
import EventInfoList from '~/components/events/EventInfoList.tsx';
import {Icon} from 'astro-icon/components';
import { reducePeriodicEvents, type ReducedEvent } from '~/util/EventUtil.ts';
export async function getStaticPaths() {
const eventsCollection = await getCollection('events')
const reducedEvents = reducePeriodicEvents(eventsCollection);
return reducedEvents.map((entry) => {
return {
params: { slug: entry.slug },
props: { event: entry }
}
});
}
import EventInfoList from '~/components/events/EventInfoList';
import { Icon } from 'astro-icon/components';
import { Image } from 'astro:assets';
interface Props {
event: ReducedEvent
Expand Down Expand Up @@ -84,6 +72,15 @@ const additionalMetadata: ({
event={event}
client:load
additional={additionalMetadata} />
{
event.data.repeating &&
<div class="text-center prose max-w-none">
<h3>Repeating Event</h3>
<p>
This event is part of a returning series! Check out all dates for this event here: <a href={`/events/${event.slug}`}>All Dates</a>
</p>
</div>
}
{event.data.registration && event.data.registration.description && (
<div class="text-center prose max-w-none bg-secondary text-secondary-content p-4 rounded shadow">
<h3 id="registration" class="!mt-0 text-primary">Registration</h3>
Expand All @@ -101,7 +98,9 @@ const additionalMetadata: ({
)}
</div>
)}
{event.data.image && <Image src={event.data.image} alt={event.data.title}></Image>}
<div class="flex justify-center">
{event.data.image && <Image src={event.data.image} alt={event.data.title}></Image>}
</div>
</section>
</Fragment >
<Content />
Expand Down
26 changes: 26 additions & 0 deletions src/pages/events/[event]/[date].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
import { getCollection } from 'astro:content';
import { reducePeriodicEvents, formatDateToHref, type ReducedEvent } from '~/util/EventUtil.ts';
import EventLayout from '~/layouts/EventLayout.astro';
export async function getStaticPaths() {
const eventsCollection = await getCollection('events')
const reducedEvents = reducePeriodicEvents(eventsCollection);
return reducedEvents.map((entry) => {
const dateString = formatDateToHref(entry.data.when.start);
return {
params: { event: entry.slug, date: dateString},
props: { event: entry }
}
});
}
interface Props {
event: ReducedEvent
}
const { event } = Astro.props;
---


<EventLayout event={event}>
44 changes: 44 additions & 0 deletions src/pages/events/[event]/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
import { getCollection } from 'astro:content';
import { reducePeriodicEvent } from '~/util/EventUtil.ts';
import type { CollectionEntry } from 'astro:content';
import CenteredLayout from '~/layouts/CenteredLayout.astro';
import EventList from '~/components/events/EventList.tsx';
import EventLayout from '~/layouts/EventLayout.astro';
export async function getStaticPaths() {
const eventsCollection = await getCollection('events')
return eventsCollection.map((entry) => {
return {
params: { event: entry.slug },
props: { event: entry }
}
});
}
interface Props {
event: CollectionEntry<'events'>
}
const { event } = Astro.props;
// ⚠️ at this point we do not know if we should show a single event or a list of events (in case of a repeating event)
// Therefore, we reduce the event. This will create a list of multiple events if the event is repeating.
const reducedEvents = reducePeriodicEvent(event);
---
{reducedEvents.length > 1
? <CenteredLayout title="Events">
<div class="prose lg:prose-lg">
<h1 class="!mb-0">{event.data.title}</h1>
<h2 class="!mt-0 text-opacity-20 text-base-content">Periodic event with the following dates!</h2>
</div>
<EventList
client:load
events={reducedEvents}
showFilter={false}
showPastFutureLimit={true} />
</CenteredLayout>
: <EventLayout event={reducedEvents[0]} />
}
Loading

0 comments on commit 95f78ce

Please sign in to comment.