From 388ab0b2e67f33e00db573b715fcaf37b5493c5e Mon Sep 17 00:00:00 2001 From: Benno Tielen Date: Tue, 3 Dec 2024 14:33:14 +0100 Subject: [PATCH] feature: event page --- src/app/veranstaltungen/[id]/page.tsx | 22 ++++++- src/collections/ContactPerson.ts | 44 +++++++++++++ src/collections/Events.ts | 13 ++-- src/collections/Locations.ts | 62 +++++++++++++++++++ src/fetch/events.ts | 3 +- src/pageComponents/Event/Event.stories.tsx | 2 - src/pageComponents/Event/Event.tsx | 36 +++++------ src/pageComponents/Home/Home.stories.tsx | 57 ++++++++--------- src/payload-types.ts | 72 ++++++++++++++++++++-- src/payload.config.ts | 6 +- src/utils/dto/contact.ts | 29 +++++++++ src/utils/dto/events.ts | 2 +- src/utils/dto/location.ts | 22 +++++++ 13 files changed, 298 insertions(+), 72 deletions(-) create mode 100644 src/collections/ContactPerson.ts create mode 100644 src/collections/Locations.ts create mode 100644 src/utils/dto/contact.ts create mode 100644 src/utils/dto/location.ts diff --git a/src/app/veranstaltungen/[id]/page.tsx b/src/app/veranstaltungen/[id]/page.tsx index 0959679..d6e7e47 100644 --- a/src/app/veranstaltungen/[id]/page.tsx +++ b/src/app/veranstaltungen/[id]/page.tsx @@ -1,11 +1,30 @@ import { notFound } from 'next/navigation' import { Event } from '@/payload-types' import { EventPage } from '@/pageComponents/Event/Event' +import { stringify } from 'qs-esm' export default async function Page({ params }: { params: Promise<{id: string}>}) { 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) { notFound() @@ -24,7 +43,6 @@ export default async function Page({ params }: { params: Promise<{id: string}>}) description={event.description} shortDescription={event.shortDescription} contact={event.contact || undefined} - address={event.address || undefined} flyer={typeof event.flyer === 'object' ? event.flyer || undefined : undefined} /> ) diff --git a/src/collections/ContactPerson.ts b/src/collections/ContactPerson.ts new file mode 100644 index 0000000..109246c --- /dev/null +++ b/src/collections/ContactPerson.ts @@ -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(), + }, +} \ No newline at end of file diff --git a/src/collections/Events.ts b/src/collections/Events.ts index 1d51b56..ef25e59 100644 --- a/src/collections/Events.ts +++ b/src/collections/Events.ts @@ -43,19 +43,13 @@ export const Events: CollectionConfig = { }, { name: 'location', - type: 'text', + type: 'relationship', + relationTo: 'locations', required: true, label: { de: 'Location' }, }, - { - name: 'address', - type: 'textarea', - label: { - de: 'Addresse' - }, - }, { name: 'parish', type: 'relationship', @@ -104,7 +98,8 @@ export const Events: CollectionConfig = { }, { name: 'contact', - type: 'textarea', + type: 'relationship', + relationTo: 'contactPerson', label: { de: "Ansprechperson" } diff --git a/src/collections/Locations.ts b/src/collections/Locations.ts new file mode 100644 index 0000000..3ea7584 --- /dev/null +++ b/src/collections/Locations.ts @@ -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(), + }, +} \ No newline at end of file diff --git a/src/fetch/events.ts b/src/fetch/events.ts index 40ebb7d..9e16984 100644 --- a/src/fetch/events.ts +++ b/src/fetch/events.ts @@ -45,7 +45,8 @@ export async function fetchEvents(parishId: string | undefined, groupId?: string date: true, title: true, cancelled: true - } + }, + depth: 1 }, { addQueryPrefix: true }, ) diff --git a/src/pageComponents/Event/Event.stories.tsx b/src/pageComponents/Event/Event.stories.tsx index 79d2ef9..eeb5b3a 100644 --- a/src/pageComponents/Event/Event.stories.tsx +++ b/src/pageComponents/Event/Event.stories.tsx @@ -23,8 +23,6 @@ export const Default: Story = { contact: 'Pfarrei Ulrich Kotzur\n' + 'pfarrer@sankt-clara.de\n' + '+4930 6851042', - address: 'Braunschweiger Str. 18, 12055 Berlin \n' + - 'Anfahrt über S-Sonnenallee oder Mareschtraße', flyer: { id: "1", name: "some flyer", diff --git a/src/pageComponents/Event/Event.tsx b/src/pageComponents/Event/Event.tsx index cedc84d..bfae11f 100644 --- a/src/pageComponents/Event/Event.tsx +++ b/src/pageComponents/Event/Event.tsx @@ -1,5 +1,5 @@ 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 { Title } from '@/components/Title/Title' import { Container } from '@/components/Container/Container' @@ -13,6 +13,8 @@ import { TextDiv } from '@/components/Text/TextDiv' import { EventExcerpt } from '@/components/EventExcerpt/EventExcerpt' import { Button } from '@/components/Button/Button' import { StaticImageData } from 'next/image' +import { locationString } from '@/utils/dto/location' +import { contactPersonString } from '@/utils/dto/contact' type EventProps = { title: string, @@ -20,11 +22,10 @@ type EventProps = { createdAt: string, cancelled: boolean, isRecurring?: boolean, - location: string, + location: string | Location, description: string, shortDescription: string, - contact?: string, - address?: string, + contact?: string | ContactPerson, flyer?: Document, photo?: StaticImageData } @@ -40,19 +41,14 @@ export function EventPage( description, shortDescription, contact, - address, flyer, photo }: EventProps ) { const published = useDate(createdAt) const readableDate = readableDateTime(date) - let where = location; - - if (address) { - where += '\n' + address; - } - + const where = locationString(location); + const who = contactPersonString(contact) return ( <> @@ -98,12 +94,16 @@ export function EventPage( fontStyle={"sans-serif"} align={"center"} /> - + + {typeof location === "object" && + <Title + size={"md"} + title={location.name} + fontStyle={"sans-serif"} + align={"center"} + /> + } + <Section padding={"medium"}> <div className={styles.description}> @@ -121,7 +121,7 @@ export function EventPage( <EventExcerpt what={shortDescription} where={where} - who={contact} + who={who} /> </Section> diff --git a/src/pageComponents/Home/Home.stories.tsx b/src/pageComponents/Home/Home.stories.tsx index 969945a..0f9ac68 100644 --- a/src/pageComponents/Home/Home.stories.tsx +++ b/src/pageComponents/Home/Home.stories.tsx @@ -16,61 +16,52 @@ export const Default: Story = { id: '1', title: 'Event 1', date: '2024-12-02T09:21:24Z', - location: 'St. Richard', - shortDescription: '', - description: { - root: { - type: '', - children: [], - direction: null, - format: '', - indent: 0, - version: 0, - }, + location: { + id: 'l1', + name: "St. Richard", + updatedAt: "", + createdAt: "" }, + shortDescription: '', + description: "Some descripton", cancelled: false, updatedAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z', + isRecurring: false }, { id: '2', title: 'Event 2', date: '2024-12-05T09:21:24Z', - location: 'St. Clara', - shortDescription: '', - description: { - root: { - type: '', - children: [], - direction: null, - format: '', - indent: 0, - version: 0, - }, + location: { + id: 'l1', + name: "St. Richard", + updatedAt: "", + createdAt: "" }, + shortDescription: '', + description: "", cancelled: false, updatedAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z', + isRecurring: false }, { id: '2', title: 'Event 2', date: '2024-12-08T09:21:24Z', - location: 'St. Hedwig', - shortDescription: '', - description: { - root: { - type: '', - children: [], - direction: null, - format: '', - indent: 0, - version: 0, - }, + location: { + id: 'l2', + name: "St. Hedwig", + updatedAt: "", + createdAt: "" }, + shortDescription: '', + description: "", cancelled: true, updatedAt: '2024-12-02T09:21:24Z', createdAt: '2024-12-02T09:21:24Z', + isRecurring: false }, ], blog: [ diff --git a/src/payload-types.ts b/src/payload-types.ts index ef9c8b0..e281214 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -18,6 +18,8 @@ export interface Config { blog: Blog; highlight: Highlight; event: Event; + contactPerson: ContactPerson; + locations: Location; group: Group; employees: Employee; testimony: Testimony; @@ -38,6 +40,8 @@ export interface Config { blog: BlogSelect<false> | BlogSelect<true>; highlight: HighlightSelect<false> | HighlightSelect<true>; event: EventSelect<false> | EventSelect<true>; + contactPerson: ContactPersonSelect<false> | ContactPersonSelect<true>; + locations: LocationsSelect<false> | LocationsSelect<true>; group: GroupSelect<false> | GroupSelect<true>; employees: EmployeesSelect<false> | EmployeesSelect<true>; testimony: TestimonySelect<false> | TestimonySelect<true>; @@ -333,11 +337,10 @@ export interface Event { photo?: (string | null) | Media; title: string; date: string; - location: string; - address?: string | null; + location: string | Location; parish?: (string | Parish)[] | null; group?: (string | Group)[] | null; - contact?: string | null; + contact?: (string | null) | ContactPerson; shortDescription: string; description: string; flyer?: (string | null) | Document; @@ -346,6 +349,24 @@ export interface Event { updatedAt: 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 * via the `definition` "group". @@ -359,6 +380,18 @@ export interface Group { updatedAt: 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 * via the `definition` "testimony". @@ -472,6 +505,14 @@ export interface PayloadLockedDocument { relationTo: 'event'; value: string | Event; } | null) + | ({ + relationTo: 'contactPerson'; + value: string | ContactPerson; + } | null) + | ({ + relationTo: 'locations'; + value: string | Location; + } | null) | ({ relationTo: 'group'; value: string | Group; @@ -684,7 +725,6 @@ export interface EventSelect<T extends boolean = true> { title?: T; date?: T; location?: T; - address?: T; parish?: T; group?: T; contact?: T; @@ -696,6 +736,30 @@ export interface EventSelect<T extends boolean = true> { updatedAt?: 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 * via the `definition` "group_select". diff --git a/src/payload.config.ts b/src/payload.config.ts index 85d9317..686692a 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -33,7 +33,8 @@ import { Blog } from '@/collections/Blog' import { Highlight } from '@/collections/Highlight' import { Pages } from '@/collections/Pages' 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 dirname = path.dirname(filename) @@ -50,6 +51,8 @@ export default buildConfig({ Blog, Highlight, Events, + ContactPerson, + Locations, Groups, Employees, Testimony, @@ -87,6 +90,5 @@ export default buildConfig({ sharp, plugins: [ // storage-adapter-placeholder - payloadCloud() ], }) diff --git a/src/utils/dto/contact.ts b/src/utils/dto/contact.ts new file mode 100644 index 0000000..73b3462 --- /dev/null +++ b/src/utils/dto/contact.ts @@ -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 +} \ No newline at end of file diff --git a/src/utils/dto/events.ts b/src/utils/dto/events.ts index b28a6f4..2fe3e66 100644 --- a/src/utils/dto/events.ts +++ b/src/utils/dto/events.ts @@ -8,7 +8,7 @@ export const transformEvents = (events: Event[]): EventRowProps[] => { title: e.title, date: e.date, href: `/veranstaltungen/${e.id}`, - location: e.location, + location: typeof e.location === "object" ? e.location.name : "Unbekannt", cancelled: e.cancelled, } }) diff --git a/src/utils/dto/location.ts b/src/utils/dto/location.ts new file mode 100644 index 0000000..37982a3 --- /dev/null +++ b/src/utils/dto/location.ts @@ -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; +} \ No newline at end of file