feature: event page

This commit is contained in:
Benno Tielen 2024-12-03 14:33:14 +01:00
parent 5d808f6bd3
commit 388ab0b2e6
13 changed files with 298 additions and 72 deletions

View file

@ -1,11 +1,30 @@
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import { Event } from '@/payload-types' import { Event } from '@/payload-types'
import { EventPage } from '@/pageComponents/Event/Event' import { EventPage } from '@/pageComponents/Event/Event'
import { stringify } from 'qs-esm'
export default async function Page({ params }: { params: Promise<{id: string}>}) { export default async function Page({ params }: { params: Promise<{id: string}>}) {
const id = (await params).id; const id = (await params).id;
const res = await fetch(`http://localhost:3000/api/event/${id}?depth=0`); const stringifiedQuery = stringify(
{
select: {
title: true,
date: true,
createdAt: true,
cancelled: true,
isRecurring: true,
location: true,
description: true,
shortDescription: true,
contact: true,
flyer: true,
photo: true
},
},
{ addQueryPrefix: true },
)
const res = await fetch(`http://localhost:3000/api/event/${id}${stringifiedQuery}`);
if (!res.ok) { if (!res.ok) {
notFound() notFound()
@ -24,7 +43,6 @@ export default async function Page({ params }: { params: Promise<{id: string}>})
description={event.description} description={event.description}
shortDescription={event.shortDescription} shortDescription={event.shortDescription}
contact={event.contact || undefined} contact={event.contact || undefined}
address={event.address || undefined}
flyer={typeof event.flyer === 'object' ? event.flyer || undefined : undefined} flyer={typeof event.flyer === 'object' ? event.flyer || undefined : undefined}
/> />
) )

View file

@ -0,0 +1,44 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
export const ContactPerson: CollectionConfig = {
slug: 'contactPerson',
labels: {
singular: {
de: 'Ansprechpartner'
},
plural: {
de: "Ansprechpartner"
}
},
fields: [
{
name: 'name',
type: 'text',
required: true,
label: {
de: "Name"
}
},
{
name: 'email',
type: 'email',
},
{
name: 'telephone',
type: 'text',
label: {
de: 'Telefon'
}
}
],
admin: {
useAsTitle: "name"
},
access: {
read: () => true,
create: isAdminOrEmployee(),
update: isAdminOrEmployee(),
delete: isAdminOrEmployee(),
},
}

View file

@ -43,19 +43,13 @@ export const Events: CollectionConfig = {
}, },
{ {
name: 'location', name: 'location',
type: 'text', type: 'relationship',
relationTo: 'locations',
required: true, required: true,
label: { label: {
de: 'Location' de: 'Location'
}, },
}, },
{
name: 'address',
type: 'textarea',
label: {
de: 'Addresse'
},
},
{ {
name: 'parish', name: 'parish',
type: 'relationship', type: 'relationship',
@ -104,7 +98,8 @@ export const Events: CollectionConfig = {
}, },
{ {
name: 'contact', name: 'contact',
type: 'textarea', type: 'relationship',
relationTo: 'contactPerson',
label: { label: {
de: "Ansprechperson" de: "Ansprechperson"
} }

View file

@ -0,0 +1,62 @@
import { CollectionConfig } from 'payload'
import { isAdmin, isAdminOrEmployee } from '@/collections/access/admin'
export const Locations: CollectionConfig = {
slug: 'locations',
labels: {
singular: {
de: 'Standort'
},
plural: {
de: 'Standorte'
}
},
fields: [
{
name: 'name',
label: {
de: 'Name'
},
type: 'text',
unique: true,
required: true,
},
{
name: 'address',
label: {
de: 'Addresse'
},
type: 'textarea'
},
{
name: 'coordinates',
type: 'point',
label: {
de: 'Koordinaten'
}
},
{
name: 'notes',
type: 'textarea',
label: {
de: "Hinweise"
}
},
{
name: 'barrierFree',
type: 'checkbox',
label: {
de: "Barrierefrei"
},
defaultValue: false
}
],
admin: {
useAsTitle: 'name'
},
access: {
read: () => true,
update: isAdminOrEmployee(),
delete: isAdmin(),
},
}

View file

@ -45,7 +45,8 @@ export async function fetchEvents(parishId: string | undefined, groupId?: string
date: true, date: true,
title: true, title: true,
cancelled: true cancelled: true
} },
depth: 1
}, },
{ addQueryPrefix: true }, { addQueryPrefix: true },
) )

View file

@ -23,8 +23,6 @@ export const Default: Story = {
contact: 'Pfarrei Ulrich Kotzur\n' + contact: 'Pfarrei Ulrich Kotzur\n' +
'pfarrer@sankt-clara.de\n' + 'pfarrer@sankt-clara.de\n' +
'+4930 6851042', '+4930 6851042',
address: 'Braunschweiger Str. 18, 12055 Berlin \n' +
'Anfahrt über S-Sonnenallee oder Mareschtraße',
flyer: { flyer: {
id: "1", id: "1",
name: "some flyer", name: "some flyer",

View file

@ -1,5 +1,5 @@
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { Document } from '@/payload-types' import { ContactPerson, Document, Location } from '@/payload-types'
import { Section } from '@/components/Section/Section' import { Section } from '@/components/Section/Section'
import { Title } from '@/components/Title/Title' import { Title } from '@/components/Title/Title'
import { Container } from '@/components/Container/Container' import { Container } from '@/components/Container/Container'
@ -13,6 +13,8 @@ import { TextDiv } from '@/components/Text/TextDiv'
import { EventExcerpt } from '@/components/EventExcerpt/EventExcerpt' import { EventExcerpt } from '@/components/EventExcerpt/EventExcerpt'
import { Button } from '@/components/Button/Button' import { Button } from '@/components/Button/Button'
import { StaticImageData } from 'next/image' import { StaticImageData } from 'next/image'
import { locationString } from '@/utils/dto/location'
import { contactPersonString } from '@/utils/dto/contact'
type EventProps = { type EventProps = {
title: string, title: string,
@ -20,11 +22,10 @@ type EventProps = {
createdAt: string, createdAt: string,
cancelled: boolean, cancelled: boolean,
isRecurring?: boolean, isRecurring?: boolean,
location: string, location: string | Location,
description: string, description: string,
shortDescription: string, shortDescription: string,
contact?: string, contact?: string | ContactPerson,
address?: string,
flyer?: Document, flyer?: Document,
photo?: StaticImageData photo?: StaticImageData
} }
@ -40,19 +41,14 @@ export function EventPage(
description, description,
shortDescription, shortDescription,
contact, contact,
address,
flyer, flyer,
photo photo
}: EventProps }: EventProps
) { ) {
const published = useDate(createdAt) const published = useDate(createdAt)
const readableDate = readableDateTime(date) const readableDate = readableDateTime(date)
let where = location; const where = locationString(location);
const who = contactPersonString(contact)
if (address) {
where += '\n' + address;
}
return ( return (
<> <>
@ -98,12 +94,16 @@ export function EventPage(
fontStyle={"sans-serif"} fontStyle={"sans-serif"}
align={"center"} align={"center"}
/> />
<Title
size={"md"} {typeof location === "object" &&
title={location} <Title
fontStyle={"sans-serif"} size={"md"}
align={"center"} title={location.name}
/> fontStyle={"sans-serif"}
align={"center"}
/>
}
<Section padding={"medium"}> <Section padding={"medium"}>
<div className={styles.description}> <div className={styles.description}>
@ -121,7 +121,7 @@ export function EventPage(
<EventExcerpt <EventExcerpt
what={shortDescription} what={shortDescription}
where={where} where={where}
who={contact} who={who}
/> />
</Section> </Section>

View file

@ -16,61 +16,52 @@ export const Default: Story = {
id: '1', id: '1',
title: 'Event 1', title: 'Event 1',
date: '2024-12-02T09:21:24Z', date: '2024-12-02T09:21:24Z',
location: 'St. Richard', location: {
shortDescription: '', id: 'l1',
description: { name: "St. Richard",
root: { updatedAt: "",
type: '', createdAt: ""
children: [],
direction: null,
format: '',
indent: 0,
version: 0,
},
}, },
shortDescription: '',
description: "Some descripton",
cancelled: false, cancelled: false,
updatedAt: '2024-12-02T09:21:24Z', updatedAt: '2024-12-02T09:21:24Z',
createdAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z',
isRecurring: false
}, },
{ {
id: '2', id: '2',
title: 'Event 2', title: 'Event 2',
date: '2024-12-05T09:21:24Z', date: '2024-12-05T09:21:24Z',
location: 'St. Clara', location: {
shortDescription: '', id: 'l1',
description: { name: "St. Richard",
root: { updatedAt: "",
type: '', createdAt: ""
children: [],
direction: null,
format: '',
indent: 0,
version: 0,
},
}, },
shortDescription: '',
description: "",
cancelled: false, cancelled: false,
updatedAt: '2024-12-02T09:21:24Z', updatedAt: '2024-12-02T09:21:24Z',
createdAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z',
isRecurring: false
}, },
{ {
id: '2', id: '2',
title: 'Event 2', title: 'Event 2',
date: '2024-12-08T09:21:24Z', date: '2024-12-08T09:21:24Z',
location: 'St. Hedwig', location: {
shortDescription: '', id: 'l2',
description: { name: "St. Hedwig",
root: { updatedAt: "",
type: '', createdAt: ""
children: [],
direction: null,
format: '',
indent: 0,
version: 0,
},
}, },
shortDescription: '',
description: "",
cancelled: true, cancelled: true,
updatedAt: '2024-12-02T09:21:24Z', updatedAt: '2024-12-02T09:21:24Z',
createdAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z',
isRecurring: false
}, },
], ],
blog: [ blog: [

View file

@ -18,6 +18,8 @@ export interface Config {
blog: Blog; blog: Blog;
highlight: Highlight; highlight: Highlight;
event: Event; event: Event;
contactPerson: ContactPerson;
locations: Location;
group: Group; group: Group;
employees: Employee; employees: Employee;
testimony: Testimony; testimony: Testimony;
@ -38,6 +40,8 @@ export interface Config {
blog: BlogSelect<false> | BlogSelect<true>; blog: BlogSelect<false> | BlogSelect<true>;
highlight: HighlightSelect<false> | HighlightSelect<true>; highlight: HighlightSelect<false> | HighlightSelect<true>;
event: EventSelect<false> | EventSelect<true>; event: EventSelect<false> | EventSelect<true>;
contactPerson: ContactPersonSelect<false> | ContactPersonSelect<true>;
locations: LocationsSelect<false> | LocationsSelect<true>;
group: GroupSelect<false> | GroupSelect<true>; group: GroupSelect<false> | GroupSelect<true>;
employees: EmployeesSelect<false> | EmployeesSelect<true>; employees: EmployeesSelect<false> | EmployeesSelect<true>;
testimony: TestimonySelect<false> | TestimonySelect<true>; testimony: TestimonySelect<false> | TestimonySelect<true>;
@ -333,11 +337,10 @@ export interface Event {
photo?: (string | null) | Media; photo?: (string | null) | Media;
title: string; title: string;
date: string; date: string;
location: string; location: string | Location;
address?: string | null;
parish?: (string | Parish)[] | null; parish?: (string | Parish)[] | null;
group?: (string | Group)[] | null; group?: (string | Group)[] | null;
contact?: string | null; contact?: (string | null) | ContactPerson;
shortDescription: string; shortDescription: string;
description: string; description: string;
flyer?: (string | null) | Document; flyer?: (string | null) | Document;
@ -346,6 +349,24 @@ export interface Event {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "locations".
*/
export interface Location {
id: string;
name: string;
address?: string | null;
/**
* @minItems 2
* @maxItems 2
*/
coordinates?: [number, number] | null;
notes?: string | null;
barrierFree?: boolean | null;
updatedAt: string;
createdAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group". * via the `definition` "group".
@ -359,6 +380,18 @@ export interface Group {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "contactPerson".
*/
export interface ContactPerson {
id: string;
name: string;
email?: string | null;
telephone?: string | null;
updatedAt: string;
createdAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "testimony". * via the `definition` "testimony".
@ -472,6 +505,14 @@ export interface PayloadLockedDocument {
relationTo: 'event'; relationTo: 'event';
value: string | Event; value: string | Event;
} | null) } | null)
| ({
relationTo: 'contactPerson';
value: string | ContactPerson;
} | null)
| ({
relationTo: 'locations';
value: string | Location;
} | null)
| ({ | ({
relationTo: 'group'; relationTo: 'group';
value: string | Group; value: string | Group;
@ -684,7 +725,6 @@ export interface EventSelect<T extends boolean = true> {
title?: T; title?: T;
date?: T; date?: T;
location?: T; location?: T;
address?: T;
parish?: T; parish?: T;
group?: T; group?: T;
contact?: T; contact?: T;
@ -696,6 +736,30 @@ export interface EventSelect<T extends boolean = true> {
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "contactPerson_select".
*/
export interface ContactPersonSelect<T extends boolean = true> {
name?: T;
email?: T;
telephone?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "locations_select".
*/
export interface LocationsSelect<T extends boolean = true> {
name?: T;
address?: T;
coordinates?: T;
notes?: T;
barrierFree?: T;
updatedAt?: T;
createdAt?: T;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group_select". * via the `definition` "group_select".

View file

@ -33,7 +33,8 @@ import { Blog } from '@/collections/Blog'
import { Highlight } from '@/collections/Highlight' import { Highlight } from '@/collections/Highlight'
import { Pages } from '@/collections/Pages' import { Pages } from '@/collections/Pages'
import { Documents } from '@/collections/Documents' import { Documents } from '@/collections/Documents'
import { payloadCloud } from '@payloadcms/plugin-cloud' import { Locations } from '@/collections/Locations'
import { ContactPerson } from '@/collections/ContactPerson'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
@ -50,6 +51,8 @@ export default buildConfig({
Blog, Blog,
Highlight, Highlight,
Events, Events,
ContactPerson,
Locations,
Groups, Groups,
Employees, Employees,
Testimony, Testimony,
@ -87,6 +90,5 @@ export default buildConfig({
sharp, sharp,
plugins: [ plugins: [
// storage-adapter-placeholder // storage-adapter-placeholder
payloadCloud()
], ],
}) })

29
src/utils/dto/contact.ts Normal file
View file

@ -0,0 +1,29 @@
import { ContactPerson } from '@/payload-types'
/**
* Return contact person as a readable string
*
* e.G
*
* Hans Mustermann
* hans@mustermann.com
* 030-65625885
*/
export const contactPersonString = (c: string | ContactPerson | undefined) => {
if(typeof c === "string" || !c) {
return "Unbekannt"
}
let s = c.name
if (c.email) {
s += '\n' + c.email;
}
if (c.telephone) {
s += '\n' + c.telephone;
}
return s
}

View file

@ -8,7 +8,7 @@ export const transformEvents = (events: Event[]): EventRowProps[] => {
title: e.title, title: e.title,
date: e.date, date: e.date,
href: `/veranstaltungen/${e.id}`, href: `/veranstaltungen/${e.id}`,
location: e.location, location: typeof e.location === "object" ? e.location.name : "Unbekannt",
cancelled: e.cancelled, cancelled: e.cancelled,
} }
}) })

22
src/utils/dto/location.ts Normal file
View file

@ -0,0 +1,22 @@
import {Location} from '@/payload-types'
/**
* Return location as readable string
*/
export const locationString = (location: Location | string) => {
if (typeof location === 'string') {
return "Unbekannt";
}
let s = location.name;
if(location.address) {
s += '\n' + location.address;
}
if(location.notes) {
s += '\n' + location.notes;
}
return s;
}