feature: some work done

This commit is contained in:
Benno Tielen 2024-11-19 10:58:33 +01:00
parent 87a9cdd07c
commit 8884f31b9c
102 changed files with 3160 additions and 905 deletions

View file

@ -1 +0,0 @@
export const importMap = {}

View file

@ -1,6 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)

View file

@ -1,21 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import configPromise from '@payload-config'
import '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'
import { importMap } from './admin/importMap'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => (
<RootLayout importMap={importMap} config={configPromise}>
{children}
</RootLayout>
)
export default Layout

View file

@ -2,6 +2,7 @@ $base-color: #426156;
$shade1: #728F8D;
$shade2: #CBD6D5;
$shade3: #E3E9E8;
$contrast-color: #9785A9;
$contrast-shade1: #E0DAE5;
$text-color: #000000;
$contrast-color: #ffffff;
$border-radius: 13px;

View file

@ -2,10 +2,6 @@ import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
images: {
unoptimized: true
},
// Your Next.js config here
eslint: {
// Warning: This allows production builds to successfully complete even if

View file

@ -17,18 +17,19 @@
"chromatic": "npx chromatic --project-token=chpt_70d6a2e05af185a"
},
"dependencies": {
"@payloadcms/db-mongodb": "3.0.0-beta.99",
"@payloadcms/next": "3.0.0-beta.99",
"@payloadcms/plugin-cloud": "3.0.0-beta.99",
"@payloadcms/richtext-lexical": "3.0.0-beta.99",
"@payloadcms/db-mongodb": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"classnames": "^2.5.1",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"mapbox-gl": "^3.5.2",
"next": "^15.0.2",
"payload": "3.0.0-beta.99",
"react": "19.0.0-rc-f65ac7bd-20240826",
"react-dom": "19.0.0-rc-f65ac7bd-20240826",
"next": "15.0.0",
"payload": "beta",
"qs-esm": "^7.0.2",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"sharp": "0.32.6"
},
"devDependencies": {

View file

@ -1,18 +1,18 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: {
params: Promise<{
segments: string[]
}
searchParams: {
}>
searchParams: Promise<{
[key: string]: string | string[]
}
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>

View file

@ -1,18 +1,18 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: {
params: Promise<{
segments: string[]
}
searchParams: {
}>
searchParams: Promise<{
[key: string]: string | string[]
}
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>

View file

@ -0,0 +1,47 @@
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
export const importMap = {
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864
}

View file

@ -1,10 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
import '@payloadcms/next/css'
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const PUT = REST_PUT(config)
export const OPTIONS = REST_OPTIONS(config)

View file

@ -1,6 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View file

@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)
export const OPTIONS = REST_OPTIONS(config)

View file

@ -0,0 +1,31 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import type { ServerFunctionClient } from 'payload'
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
const Layout = ({ children }: Args) => (
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
)
export default Layout

View file

@ -0,0 +1,45 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { Title } from '@/components/Title/Title'
import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider'
import { TextDiv } from '@/components/Text/TextDiv'
import { Blog } from '@/payload-types'
import { notFound } from 'next/navigation'
async function fetchBlog(id: string) {
const res = await fetch(`http://localhost:3000/api/blog/${id}`)
if (!res.ok) return undefined
return res.json();
}
export default async function BlogPage({ params }: { params: Promise<{id: string}>}){
const id = (await params).id;
const data = await fetchBlog(id) as Blog;
if(!data) {
notFound();
}
return (
<Section>
<Container>
<Title title={data.title}></Title>
</Container>
<div>
{data.content && data.content.map(item => {
if (item.blockType === "ContentWithSlider") {
return (
<ContentWithSlider key={item.id} slider={item.sliderContent}>
<Container position={"right"}>
<TextDiv text={item.content} />
</Container>
</ContentWithSlider>
);
}
})}
</div>
</Section>
)
}

BIN
src/app/bread.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
src/app/candle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 KiB

View file

@ -7,6 +7,6 @@ export const faustina = Faustina({
export const defaultFont = Cairo({
subsets: ['latin'],
weight: ['400'],
weight: ['400', '300'],
display: 'swap',
})

View file

@ -0,0 +1,53 @@
import { PaginatedDocs } from 'payload'
import { Parish as ParishType } from '@/payload-types'
import { notFound } from 'next/navigation'
import { Parish } from "@/pageComponents/Parish/Parish"
import { stringify } from 'qs-esm'
import { fetchEvents } from '@/fetch/events'
import { fetchWorship } from '@/fetch/worship'
async function fetchParish(slug: string) {
const query = {
slug: {
equals: slug
}
}
const stringifiedQuery = stringify(
{
where: query,
},
{ addQueryPrefix: true },
)
const res = await fetch(`http://localhost:3000/api/parish${stringifiedQuery}`)
if (!res.ok) return undefined
return res.json();
}
export default async function ParishPage ({ params }: { params: Promise<{slug: string}>}) {
const slug = (await params).slug;
const parish = await fetchParish(slug) as PaginatedDocs<ParishType>
if(!parish.docs[0]) {
notFound();
}
const { id, name, description, history, contactPersons, contact, photo, churches } = parish.docs[0]
const events = await fetchEvents(id)
const worship = await fetchWorship(churches.map(c => typeof c === "string" ? c : c.id))
return (
<Parish
title={name}
slug={slug}
image={typeof photo === "string" ? photo : photo.url || "notfound"}
description={description}
history={history}
contactPersons={contactPersons || []}
contact={contact}
events={events?.docs || []}
worship={worship?.docs || []}
/>
)
}

View file

@ -1,4 +1,11 @@
html,
body {
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}

View file

@ -0,0 +1,70 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { notFound } from 'next/navigation'
import { Worship } from '@/payload-types'
import { Title } from '@/components/Title/Title'
import { HR } from '@/components/HorizontalRule/HorizontalRule'
import { liturgicalDayName } from '@/hooks/liturgicalDayName'
import { ChurchWithContact } from '@/compositions/ChurchWithContact/ChurchWithContact'
import { transformCategory } from '@/utils/dto/worship'
import { readableDateTime } from '@/utils/readableDate'
export default async function WorshipPage({ params }: { params: Promise<{id: string}>}) {
const id = (await params).id;
const res = await fetch(`http://localhost:3000/api/worship/${id}`);
if (!res.ok) {
notFound()
}
const event = await res.json() as Worship;
const subtitle = event.title ? event.title : transformCategory(event.type);
const title = event.liturgicalDay ? event.liturgicalDay : liturgicalDayName(event.date);
const church = typeof event.location === "string" ? { name: "Unkown", address: "unknown"} : event.location;
return (
<>
<Section>
<Container>
<Title title={title} subtitle={subtitle} />
</Container>
<HR/>
</Section>
<Section backgroundColor={"off-white"}>
<Container textAlign="center">
<>
<p>
<strong>{church.name}</strong> <br/>
{readableDateTime(event.date)}
</p>
{ event.celebrant &&
<p>
<strong>Zelebrant</strong><br/>
{event.celebrant}
</p>
}
{event.description &&
<p>
<strong>Hinweis</strong><br />
{event.description}
</p>
}
</>
</Container>
</Section>
<Section>
<Container>
<ChurchWithContact
church={church.name}
contact={church.address}
/>
</Container>
</Section>
</>
)
}

View file

@ -1,6 +1,8 @@
import type { Metadata } from 'next'
import { defaultFont } from './fonts'
import './globals.css'
import { Menu } from '@/components/Menu/Menu'
import { Footer } from '@/compositions/Footer/Footer'
export const metadata: Metadata = {
title: 'Katholische Pfarrei Heilige drei Könige Berlin',
@ -13,7 +15,13 @@ export default function RootLayout({
}>) {
return (
<html lang="en" className={defaultFont.className}>
<body>{children}</body>
<body>
<Menu />
<main>
{children}
</main>
<Footer />
</body>
</html>
)
}

BIN
src/app/mons.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View file

@ -1,15 +1,49 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { Title } from '@/components/Title/Title'
import { Row } from '@/components/Flex/Row'
import { Col } from '@/components/Flex/Col'
export default function Custom404() {
return (
<>
<h1>404 - Seite nicht gefunden</h1>
<Section>
<Container>
<Title title={"Seite nicht gefunden"} />
<p>
Glorreicher heiliger Antonius, du hast die göttliche Macht ausgeübt,
verlorene Dinge wiederzufinden. Hilf uns, die Gnade Gottes
wiederzuerlangen und mach mich stark im Dienst an Gott und an den
Tugenden. Lass&apos; mich das Verlorene wiederfinden und zeige mir so
deine Güte.
</p>
<p>
Error <strong>404</strong>: Die von Ihnen angeforderte Seite existiert leider nicht.
</p>
<Title title={"Mögliche Gründe"} size={"sm"} fontStyle={"sans-serif"}/>
<ul>
<li>Die URL wurde falsch eingegeben.</li>
<li>Die Seite wurde verschoben oder entfernt.</li>
<li>Der Link ist veraltet.</li>
</ul>
</Container>
</Section>
<Section backgroundColor={"off-white"}>
<Container>
<Row>
<Col>
<p>
Glorreicher heiliger Antonius, du hast die göttliche Macht ausgeübt,
verlorene Dinge wiederzufinden. Hilf uns, die Gnade Gottes
wiederzuerlangen und mach mich stark im Dienst an Gott und an den
Tugenden. Lass&apos; mich das Verlorene wiederfinden und zeige mir so
deine Güte.
</p>
</Col>
<Col>
<></>
</Col>
</Row>
</Container>
</Section>
</>
)
}

View file

@ -1,85 +1,71 @@
import { Menu } from '@/components/Menu/Menu'
import { Banner } from '@/components/Banner/Banner'
import { Section } from '@/components/Section/Section'
import { MainText } from '@/components/MainText/MainText'
import { Title } from '@/components/Title/Title'
import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider'
import { ImageWithText } from '@/compositions/ImageWithText/ImageWithText'
import { Container } from '@/components/Container/Container'
import { ContactSection } from '@/compositions/ContactSection/ContactSection'
import { Footer } from '@/compositions/Footer/Footer'
import { EventRow } from '@/components/EventRow/EventRow'
import monst from "./mons.jpg"
import bread from "./bread.jpg"
import candle from "./candle.png"
import { SideSlider } from '@/components/SideSlider/SideSlider'
import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider'
import { Events } from '@/compositions/Events/Events'
import { fetchEvents } from '@/fetch/events'
import forest from "../assets/forest.jpeg"
import { fetchWorship } from '@/fetch/worship'
import { Worship } from '@/payload-types'
import { MassTable } from '@/components/MassTable/MassTable'
import { Row } from '@/components/Flex/Row'
import { transformEvents } from '@/utils/dto/events'
import { fetchBlog } from '@/fetch/blog'
import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider'
import { blogToSlides } from '@/utils/dto/blog'
// const extractWorshipHours = (worships: Worship[]) => {
// let worshipByDate = new Map<string, Worship[]>()
//
// for (let worship of worships) {
// const date = worship.date.substring(0, 10)
//
// if (worshipByDate.has(date)) {
// worshipByDate.get(date)?.push(worship)
// } else {
// worshipByDate.set(date, [worship])
// }
// }
//
// return worshipByDate
// }
const sortWorship = (worship: Worship[]) => {
const map = new Map<string, Worship[]>();
worship.map(w => {
if(typeof w.location === "object") {
const title = w.location.name;
if(map.has(title)) {
map.get(title)?.push(w);
} else {
map.set(title, [w])
}
}
})
return map
}
export const dynamic = 'force-dynamic'
export default async function Home() {
// const today = new Date()
// const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000)
// const payload = await getPayloadHMR({ config: configPromise })
// const worship = await payload.find({
// collection: 'worship',
// where: {
// and: [
// {
// date: {
// greater_than_equal: today.toISOString().substring(0, 10),
// },
// },
// {
// date: {
// less_than: nextWeek.toISOString().substring(0, 10),
// },
// },
// ],
// },
// limit: 30,
// sort: 'date',
// })
//
// const nextMass = await payload.find({
// collection: 'worship',
// where: {
// and: [
// {
// date: {
// greater_than_equal: today.toISOString(),
// },
// },
// {
// cancelled: {
// equals: false,
// },
// },
// ],
// },
// limit: 1,
// sort: 'date',
// })
//
// const worshipByDate = [...extractWorshipHours(worship.docs).entries()]
const events = await fetchEvents(undefined)
const worship = await fetchWorship()
const worshipPerLocation = Array.from(
sortWorship(worship?.docs || []).entries()
).sort(
(a, b) => {
const nameA = a[0];
const nameB = b[0];
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
}
)
const blog = await fetchBlog();
return (
<>
<Menu/>
<Banner/>
<Container>
@ -88,32 +74,23 @@ export default async function Home() {
"Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen."} />
</Section>
<Section>
<Title title={"Unsere Gottesdiensten"} align={"center"}/>
<Row>
{worshipPerLocation.map(value => <MassTable key={value[0]} location={value[0]} masses={value[1]} />)}
</Row>
</Section>
<Section>
<Title title={"Aktuelles"} />
<ImageCardSlider slides={[
{
id: "id1",
src: monst,
title: "Anbetung in Oktober",
href: "https://somelink"
},
{
id: "id2",
src: candle,
title: "Allerseelen",
href: "https://somelink"
},
{
id: "id3",
src: bread,
title: "Erntedankfest",
href: "https://somelink"
}
]} />
<ImageCardSlider slides={blogToSlides(blog?.docs || [])} />
</Section>
</Container>
<ImageWithText />
{<ImageWithText backgroundColor={"soft"} title={"Über uns"} text={'Wir begrüßen Sie herzlich in unserer Pfarrei Hl. Drei Könige und im bunten Neukölln mit einer Vielfalt von Kulturen und Nationalitäten.\n' +
'\n' +
'Unsere Pfarrei Hl. Drei Könige wurde am 01.01.2020 gegründet. Am 12.01.2020 feierte Erzbischof Dr. Heiner Koch mit den Gemeinden die Gründung in einer feierlichen Hl. Messe in der katholischen Marienschule. Anwesende Gäste waren Bürgermeister Martin Hikel, Christian Nottmeier, der Superintendent des evangelischen Kirchenkreises Neukölln und vielen Akteuren aus Kiez und Ökumene. Die Vielfalt der Glaubenswege in unserer Pfarrei sehen wir als Schatz. Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen.'} image={forest} />}
<ContentWithSlider slider={<>
<Title title={"Akutelle Highlights"} size={"md"} fontStyle={"sans-serif"} />
@ -121,50 +98,12 @@ export default async function Home() {
<Container position={"right"}>
<Section>
<Title title={"Veranstaltungen"} />
<EventRow
date={"2024-10-23T16:00:00"}
title={"Gemeinsam beten"}
href={"https://link"}
location={"St. Christophorus"}
/>
<EventRow
date={"2024-10-28T19:45:00"}
title={"Rosenkranz"}
href={"https://link"}
location={"St. Clara"}
/>
<EventRow
date={"2024-11-02T19:00:00"}
title={"Allerseelen"}
href={"https://link"}
location={"St. Michael"}
/>
<EventRow
date={"2024-11-11T18:00:00"}
title={"St. Martin"}
href={"https://link"}
location={"Sportplatz St. Christophorus"}
/>
<EventRow
date={"2024-11-11T18:00:00"}
title={"St. Martin"}
href={"https://link"}
location={"Sportplatz St. Christophorus"}
/>
<EventRow
date={"2024-11-11T18:00:00"}
title={"St. Martin"}
href={"https://link"}
location={"Sportplatz St. Christophorus"}
/>
<Events events={transformEvents(events?.docs || [])} n={6}/>
</Section>
</Container>
</ContentWithSlider>
<ContactSection />
<Footer />
</>
)
}

View file

@ -0,0 +1,47 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { notFound } from 'next/navigation'
import { Event } from '@/payload-types'
import { Title } from '@/components/Title/Title'
import { HR } from '@/components/HorizontalRule/HorizontalRule'
import { readableDateTime } from '@/utils/readableDate'
import { TextDiv } from '@/components/Text/TextDiv'
export default async function EventPage({ params }: { params: Promise<{id: string}>}) {
const id = (await params).id;
const res = await fetch(`http://localhost:3000/api/event/${id}?depth=0`);
if (!res.ok) {
notFound()
}
const event = await res.json() as Event;
return (
<>
<Section>
<Container>
<Title
title={event.title}
subtitle={readableDateTime(event.date)}
size={"xl"}/>
</Container>
<HR />
</Section>
<Section>
<Container textAlign={"center"}>
<TextDiv text={event.shortDescription} />
<p>
<strong>Location</strong> <br/>
{event.location}
</p>
</Container>
</Section>
</>
)
}

View file

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

View file

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

View file

@ -1,6 +1,37 @@
import { CollectionConfig } from 'payload'
import { Block, CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
const QuoteBlock: Block = {
slug: 'Quote',
labels: {
singular: "Zitat",
plural: "Zitaten"
},
fields: [
{
name: 'quoteText',
type: 'text',
required: true
},
],
}
const ContentWithSlider: Block = {
slug: 'ContentWithSlider',
fields: [
{
name: 'sliderContent',
type: 'textarea',
required: true
},
{
name: 'content',
type: 'textarea',
required: true
}
]
}
export const Blog: CollectionConfig = {
slug: 'blog',
labels: {
@ -35,21 +66,14 @@ export const Blog: CollectionConfig = {
}
},
{
name: 'isHighlight',
type: 'checkbox',
required: true,
label: {
de: "Highlight"
},
defaultValue: false
},
{
name: 'text',
type: 'richText',
required: true,
label: {
de: "Post"
}
name: 'content',
type: 'blocks',
minRows: 1,
maxRows: 20,
blocks: [
QuoteBlock,
ContentWithSlider
]
}
],
admin: {

View file

@ -29,7 +29,7 @@ export const Events: CollectionConfig = {
},
},
{
name: 'datum',
name: 'date',
type: 'date',
required: true,
label: {
@ -41,6 +41,14 @@ export const Events: CollectionConfig = {
},
},
},
{
name: 'location',
type: 'text',
required: true,
label: {
de: 'Location'
},
},
{
name: 'parish',
type: 'relationship',
@ -70,7 +78,7 @@ export const Events: CollectionConfig = {
label: {
de: 'Gruppe',
},
validate: (value, options) => {
validate: (value: any, options: { req: { user: any } }) => {
let user = options.req.user
if (!user) {

View file

@ -51,23 +51,29 @@ export const Parish: CollectionConfig = {
hasMany: true,
},
{
name: 'contact',
name: 'contactPersons',
label: {
de: 'Kontaktinformation',
de: "Ansprechpartner"
},
type: 'textarea',
required: true,
admin: {
rows: 10,
},
},
{
name: 'title',
label: {
de: 'Titel',
},
type: 'text',
required: true,
type: 'array',
fields: [
{
name: 'title',
label: {
de: 'Titel'
},
type: 'text',
required: true
},
{
name: 'description',
label: {
de: 'Umschreibung'
},
type: 'textarea',
required: true
}
]
},
{
name: 'description',
@ -80,10 +86,33 @@ export const Parish: CollectionConfig = {
rows: 15,
},
},
{
name: 'history',
label: {
de: 'Geschichte',
},
type: 'textarea',
required: true,
admin: {
rows: 15,
},
},
{
name: 'contact',
label: {
de: 'Kontaktinformation',
},
type: 'textarea',
required: true,
admin: {
rows: 10,
},
},
{
name: 'photo',
type: 'upload',
relationTo: 'media',
required: true
},
],
admin: {

View file

@ -56,6 +56,14 @@ export const Worship: CollectionConfig = {
],
required: true,
},
{
name: 'title',
type: 'text',
required: false,
label: {
de: 'Titel'
}
},
{
name: 'cancelled',
type: 'checkbox',
@ -66,13 +74,21 @@ export const Worship: CollectionConfig = {
},
},
{
name: 'title',
name: 'liturgicalDay',
type: 'text',
required: false,
label: {
de: 'Liturgischer Tag',
},
},
{
name: 'celebrant',
type: 'text',
required: false,
label: {
de: 'Zelebrant'
}
},
{
name: 'description',
type: 'textarea',
@ -81,6 +97,10 @@ export const Worship: CollectionConfig = {
},
},
],
admin: {
defaultColumns: ["date", 'location', 'type', 'celebrant'],
listSearchableFields: ['date', 'location']
},
access: {
read: () => true,
create: isAdminOrEmployee(),

View file

@ -2,7 +2,7 @@
.button {
background: $shade1;
color: $contrast-color;
color: $shade3;
border-radius: $border-radius;
text-align: center;
border: 0;

View file

@ -0,0 +1,24 @@
import { Meta, StoryObj } from '@storybook/react'
import { ChurchCard } from './ChurchCard'
const meta: Meta<typeof ChurchCard> = {
component: ChurchCard,
}
type Story = StoryObj<typeof ChurchCard>;
export default meta
export const Default: Story = {
args: {
church: "anna",
backgroundColor: "#E0DAE5"
},
}
export const Small: Story = {
args: {
church: "anna",
backgroundColor: "#E0DAE5",
width: 100
},
}

View file

@ -1,3 +1,14 @@
export const ChurchCard = () => {
import styles from './styles.module.scss'
import { Church, ChurchIcon } from '@/components/ChurchIcon/ChurchIcon'
type ChurchCardProps = {
church: Church,
width?: number,
backgroundColor: string,
}
export const ChurchCard = ({church, backgroundColor, width = 286 }: ChurchCardProps) => {
return <div className={styles.card} style={{backgroundColor, width, height: width}}>
<ChurchIcon church={church} style={"outline"} stroke={0.5} color={"#000000"} />
</div>
}

View file

@ -0,0 +1,12 @@
@import "template.scss";
.card {
border-radius: $border-radius;
border: 1px solid $shade2;
box-shadow: 3px 7px 26px 0 rgba(0, 0, 0, 0.15);
}
.card svg {
width: 100%;
height: 100%;
}

View file

@ -3,6 +3,11 @@ import { ChurchIcon } from './ChurchIcon'
const meta: Meta<typeof ChurchIcon> = {
component: ChurchIcon,
decorators: [
(Story) => <div style={{width: "50px", height: "50px"}} >
<Story/>
</div>
]
}
type Story = StoryObj<typeof ChurchIcon>;
@ -10,30 +15,45 @@ export default meta
export const StClara: Story = {
args: {
church: "clara"
church: "clara",
style: "outline",
stroke: 3,
color: "#426156",
},
}
export const StChristophorus: Story = {
args: {
church: "christophorus"
church: "christophorus",
style: "outline",
stroke: 3,
color: "#426156",
},
}
export const StRichard: Story = {
args: {
church: "richard"
church: "richard",
style: "outline",
stroke: 3,
color: "#426156",
},
}
export const StEduard: Story = {
args: {
church: "eduard"
church: "eduard",
style: "outline",
stroke: 3,
color: "#426156",
},
}
export const StAnna: Story = {
args: {
church: "anna"
church: "anna",
style: "outline",
stroke: 3,
color: "#426156",
},
}

View file

@ -1,32 +1,56 @@
import christophorus from "./christophorus_full.svg"
import clara from "./clara_full.svg"
import anna from "./anna_full.svg"
import richard from "./richard_full.svg"
import eduard from "./eduard_full.svg"
import Image from 'next/image'
export type Church = "clara" | "christophorus" | "richard" | "eduard" | "anna"
type ChurchIconProps = {
church: "clara" | "christophorus" | "richard" | "eduard" | "anna"
church: Church
style: "outline" | "filled"
stroke: number
color: string
}
export const ChurchIcon = ({church}: ChurchIconProps) => {
export const ChurchIcon = ({church, style, stroke, color}: ChurchIconProps) => {
if (church === "clara") {
return <Image src={clara} alt={""} />
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g fill={style === 'outline' ? '#ffffff' : color} stroke={color} strokeWidth={stroke}>
<polygon
points="152.25 185.47 103.11 145.09 103.11 134.29 105.39 134.29 105.39 133.29 103.11 133.29 103.11 128.18 102.11 128.18 102.11 133.29 99.68 133.29 99.68 134.29 102.11 134.29 102.11 145.09 86.5 158.05 86.5 128.09 60.44 42.14 60.44 32.72 62.72 32.72 62.72 31.72 60.44 31.72 60.44 26.61 59.44 26.61 59.44 31.72 57.01 31.72 57.01 32.72 59.44 32.72 59.44 42.12 38.88 111.48 38.86 248.62 86 248.22 169.49 248.22 169.49 185.47 152.25 185.47" />
</g>
</svg>
}
if (church === "anna") {
return <Image src={anna} alt={""} />
if (church === 'anna') {
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g fill={style === 'outline' ? '#ffffff' : color} stroke={color} strokeWidth={stroke}>
<polygon
points="143.86 114.51 143.86 73.97 112.05 56.84 112.05 46.53 114.33 46.53 114.33 45.53 112.05 45.53 112.05 40.42 111.05 40.42 111.05 45.53 108.62 45.53 108.62 46.53 111.05 46.53 111.05 56.83 79.7 74.46 79.7 114.76 33.67 126.02 33.43 217.49 33.42 217.99 190.52 217.99 190.52 125.78 143.86 114.51" />
</g>
</svg>
}
if (church === "richard") {
return <Image src={richard} alt={""} />
if (church === 'richard') {
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g fill={style === "outline" ? "#ffffff" : color} stroke={color} strokeWidth={stroke}>
<path
d="M87,80.23l-.23-.06L32.13,120.86v74.19H204.49V109.29Zm18,24.13h-2.28v11h-1v-11H99.32v-1h2.43V98.25h1v5.11H105Z" />
</g>
</svg>
}
if (church === "eduard") {
return <Image src={eduard} alt={""} />
if (church === 'eduard') {
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g fill={style === "outline" ? "#ffffff" : color} stroke={color} strokeWidth={stroke}>
<polygon
points="152.15 168.04 132.61 179.16 132.61 134.31 109.83 37.44 109.83 26.46 112.11 26.46 112.11 25.46 109.83 25.46 109.83 20.35 108.83 20.35 108.83 25.46 106.4 25.46 106.4 26.46 108.83 26.46 108.83 37.44 85.57 133.71 85.56 168.01 74.7 168.01 58.33 191.25 58.24 191.38 58.24 254.87 184.49 254.87 184.49 196.72 152.15 168.04" />
</g>
</svg>
}
return (
<Image src={christophorus} alt={"chris"} />
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g fill={style === "outline" ? "#ffffff" : color} stroke={color} strokeWidth={stroke}>
<polygon
points="138.03 54.42 138.03 44.29 140.3 44.29 140.3 43.29 138.03 43.29 138.03 38.18 137.03 38.18 137.03 43.29 134.6 43.29 134.6 44.29 137.03 44.29 137.03 54.44 94.55 71.65 94.55 186.65 71.4 203.88 71.4 209.82 42.03 209.9 42.07 232.89 174.94 232.89 174.94 72.21 138.03 54.42" />
</g>
</svg>
)
}

View file

@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 239.12 255.74"><defs><style>.cls-1{fill:#426156;}</style></defs><g id="St._Anna" data-name="St. Anna"><path class="cls-1" d="M152.23,122.54V82L120.42,64.87V54.56h2.28v-1h-2.28V48.45h-1v5.11H117v1h2.43v10.3L88.07,82.49v40.3L42,134.05l-.24,91.47v.5h157.1V133.81ZM88.07,225H42.8L43,134.84l45-11Zm63.16,0H89.07V83.08l30.84-17.35,31.32,16.86Zm46.66,0H152.23V123.57l45.66,11Z"/><polygon class="cls-1" points="197.89 134.59 197.89 225.02 152.23 225.02 152.23 123.57 197.89 134.59"/><polygon class="cls-1" points="151.23 82.59 151.23 225.02 89.07 225.02 89.07 83.08 119.91 65.73 151.23 82.59"/><polygon class="cls-1" points="88.07 123.82 88.07 225.02 42.8 225.02 43.04 134.84 88.07 123.82"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g id="Full_Violet" data-name="Full Violet">
<polygon class="cls-1"
points="143.86 114.51 143.86 73.97 112.05 56.84 112.05 46.53 114.33 46.53 114.33 45.53 112.05 45.53 112.05 40.42 111.05 40.42 111.05 45.53 108.62 45.53 108.62 46.53 111.05 46.53 111.05 56.83 79.7 74.46 79.7 114.76 33.67 126.02 33.43 217.49 33.42 217.99 190.52 217.99 190.52 125.78 143.86 114.51"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 746 B

After

Width:  |  Height:  |  Size: 479 B

View file

@ -1,30 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 175.75 265.12"
version="1.1"
id="svg34"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs27">
<style
id="style25">.cls-1{fill:#426156;}</style>
</defs>
<g
id="St._Christophorus"
data-name="St. Christophorus">
<path
class="cls-1"
d="M 114.44,72.08 V 62 h 2.27 v -1 h -2.27 v -5.16 h -1 V 61 H 111 v 1 h 2.43 V 72.1 L 71,89.31 v 115 l -23.19,17.23 v 5.94 l -29.37,0.08 v 23 H 151.35 V 89.87 Z M 113.93,73 c -75.953333,-48.666667 -37.976667,-24.333333 0,0 z"
id="path29" />
</g>
<g
id="fill">
<polygon
class="cls-1"
points="113.89,72.99 114.02,72.99 150.35,90.49 150.35,249.55 19.48,249.55 19.44,228.56 48.81,228.48 48.81,222.04 71.96,204.81 71.96,89.98 "
id="polygon31"
style="fill:#426156"
transform="translate(0.04,0.01)" />
</g>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g id="White_Mask" data-name="White Mask">
<polygon class="cls-3"
points="138.03 54.42 138.03 44.29 140.3 44.29 140.3 43.29 138.03 43.29 138.03 38.18 137.03 38.18 137.03 43.29 134.6 43.29 134.6 44.29 137.03 44.29 137.03 54.44 94.55 71.65 94.55 186.65 71.4 203.88 71.4 209.82 42.03 209.9 42.07 232.89 174.94 232.89 174.94 72.21 138.03 54.42"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 972 B

After

Width:  |  Height:  |  Size: 456 B

View file

@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 175.75 265.12"><defs><style>.cls-1{fill:#426156;}</style></defs><g id="St._Eduard" data-name="St. Eduard"><path class="cls-1" d="M136.16,177.51,87,137.13v-10.8H89.3v-1H87v-5.11H86v5.11H83.59v1H86v10.8l-15.61,13v-30L44.35,34.18V24.76h2.28v-1H44.35V18.65h-1v5.11H40.92v1h2.43v9.4L22.79,103.52l0,137.14,47.14-.4H153.4V177.51ZM69.41,239.27l-45.64.38V103.74L43.85,36,69.41,120.2Zm83,0h-82V151.39L86.52,138l49.28,40.49h16.6Z"/><polygon class="cls-1" points="152.4 178.51 152.4 239.26 70.41 239.26 70.41 151.39 86.52 138.02 135.8 178.51 152.4 178.51"/><polygon class="cls-1" points="69.41 120.2 69.41 239.27 23.77 239.65 23.77 103.74 43.85 35.98 69.41 120.2"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g>
<polygon
points="152.25 185.47 103.11 145.09 103.11 134.29 105.39 134.29 105.39 133.29 103.11 133.29 103.11 128.18 102.11 128.18 102.11 133.29 99.68 133.29 99.68 134.29 102.11 134.29 102.11 145.09 86.5 158.05 86.5 128.09 60.44 42.14 60.44 32.72 62.72 32.72 62.72 31.72 60.44 31.72 60.44 26.61 59.44 26.61 59.44 31.72 57.01 31.72 57.01 32.72 59.44 32.72 59.44 42.12 38.88 111.48 38.86 248.62 86 248.22 169.49 248.22 169.49 185.47 152.25 185.47"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 563 B

View file

@ -1,14 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 175.75 265.12">
<defs>
<style>.cls-1{fill:#426156;}</style>
</defs>
<g id="St._Eduard" data-name="St. Eduard">
<path class="cls-1"
d="M117.83,161.61,98.29,172.73V127.88L75.51,31V20h2.28V19H75.51V13.92h-1V19H72.08v1h2.43V31L51.25,127.28v34.3H40.38L24,184.82l-.09.13v63.49H150.17V190.29ZM51.24,247.44H24.92V185.26l16-22.68H51.24Zm46.05,0H52.24v-120L75,33.22l22.29,94.72Zm51.88,0H98.29V173.88l19.41-11,31.47,27.91Z"/>
<polygon class="cls-1"
points="149.17 190.74 149.17 247.44 98.29 247.44 98.29 173.88 117.7 162.83 149.17 190.74"/>
<polygon class="cls-1" points="97.29 127.94 97.29 247.44 52.24 247.44 52.24 127.45 75 33.22 97.29 127.94"/>
<polygon class="cls-1" points="51.24 162.58 51.24 247.44 24.92 247.44 24.92 185.26 40.9 162.58 51.24 162.58"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g>
<polygon
points="152.15 168.04 132.61 179.16 132.61 134.31 109.83 37.44 109.83 26.46 112.11 26.46 112.11 25.46 109.83 25.46 109.83 20.35 108.83 20.35 108.83 25.46 106.4 25.46 106.4 26.46 108.83 26.46 108.83 37.44 85.57 133.71 85.56 168.01 74.7 168.01 58.33 191.25 58.24 191.38 58.24 254.87 184.49 254.87 184.49 196.72 152.15 168.04"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 905 B

After

Width:  |  Height:  |  Size: 452 B

View file

@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 221.52 164.2"><defs><style>.cls-1{fill:#426156;}.cls-2{fill:#728f8d;}.cls-3{fill:#fff;}</style></defs><g id="St._Christophorus" data-name="St. Christophorus"><path class="cls-1" d="M74.29,20.71l-.23-.06L19.41,61.34v74.19H191.77V49.77Zm-.62,113.82H20.41V61.84L73.67,22.19Zm117.1,0H74.67V21.83l116.1,28.73Z"/><path class="cls-2" d="M74.67,21.83v112.7h116.1v-84Zm17.64,23.1H90V56H89v-11H86.6v-1H89V38.82h1v5.11h2.28Z"/><polygon class="cls-1" points="73.67 22.19 73.67 134.53 20.41 134.53 20.41 61.84 73.67 22.19"/><polygon class="cls-3" points="92.31 43.93 92.31 44.93 90.03 44.93 90.03 55.97 89.03 55.97 89.03 44.93 86.6 44.93 86.6 43.93 89.03 43.93 89.03 38.82 90.03 38.82 90.03 43.93 92.31 43.93"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223.94 276.25">
<g>
<path
d="M87,80.23l-.23-.06L32.13,120.86v74.19H204.49V109.29Zm18,24.13h-2.28v11h-1v-11H99.32v-1h2.43V98.25h1v5.11H105Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 761 B

After

Width:  |  Height:  |  Size: 236 B

View file

@ -0,0 +1,17 @@
import { Meta, StoryObj } from '@storybook/react'
import { ContactPerson } from './ContactPerson'
const meta: Meta<typeof ContactPerson> = {
component: ContactPerson,
}
type Story = StoryObj<typeof ContactPerson>;
export default meta
export const Default: Story = {
args: {
title: "Kirechenvorstand",
description: `Stellv. Vorsitzender: Michael Wolter
Vorsitzender: Pfarrer Ulrich Kotzur`
},
};

View file

@ -0,0 +1,18 @@
import styles from './styles.module.scss'
import { Title } from '@/components/Title/Title'
type ContactPersonProps = {
title: string,
description: string
}
export const ContactPerson = ({ title, description }: ContactPersonProps) => {
return (
<div>
<Title title={title} size={"sm"} color={"contrast"} fontStyle={"sans-serif"} />
<div className={styles.description}>
{description}
</div>
</div>
)
}

View file

@ -0,0 +1,33 @@
import { Meta, StoryObj } from '@storybook/react'
import { ContactPersonList } from './ContactPersonList'
const meta: Meta<typeof ContactPersonList> = {
component: ContactPersonList,
}
type Story = StoryObj<typeof ContactPersonList>;
export default meta
export const Default: Story = {
args: {
persons: [
{
title: "Seelsorgeteam",
description: `Pater Karl Hermann Lenz SAC (Pfarrvikar)
pallottis@christophorus-berlin.de
Lissy Eichert UAC
l.eichert@christophorus-berlin.
`
},
{
title: "Pallottinische Gemeinschaft",
description: `Lissy Eichert UAC
l.eichert@christophorus-berlin.de`
},
{
title: "Kirechenvorstand",
description: "Some contactinfo"
}
]
},
}

View file

@ -0,0 +1,17 @@
import styles from "./list.module.scss"
import { ContactPerson } from '@/components/ContactPerson/ContactPerson'
type ContactPersonListProps = {
persons: {
title: string,
description: string,
}[]
}
export const ContactPersonList = ({ persons }: ContactPersonListProps) => {
return (
<div className={styles.list}>
{persons.map(person => <ContactPerson key={person.title} title={person.title} description={person.description} />)}
</div>
)
}

View file

@ -0,0 +1,5 @@
.list {
display: flex;
flex-direction: column;
gap: 20px;
}

View file

@ -0,0 +1,5 @@
@import "template.scss";
.description {
white-space: preserve;
}

View file

@ -4,13 +4,13 @@ import classNames from 'classnames'
type ContainerProps = {
children: JSX.Element | JSX.Element[],
flex?: boolean
position?: "center" | "left" | "right"
position?: "center" | "left" | "right",
textAlign?: "left" | "center" | "right",
}
export const Container = ({ children, flex = false, position = "center" }: ContainerProps) => {
export const Container = ({ children, position = "center", textAlign="left" }: ContainerProps) => {
return <div className={classNames({
[styles.container]: true,
[styles.flex]: flex,
[styles.right]: position === "right",
})}>{children}</div>
})} style={{ textAlign }}>{children}</div>
}

View file

@ -5,13 +5,6 @@ $width: 1100px;
margin: 0 auto;
}
.flex {
display: flex;
gap: 80px;
align-items: center;
flex-wrap: wrap;
}
.right {
max-width: inherit;
margin-left: calc((100vw - $width) / 2);

View file

@ -1,8 +1,9 @@
import { useMemo } from 'react'
import styles from "./styles.module.scss"
import classNames from 'classnames'
import Link from 'next/link'
type EventRowProps = {
export type EventRowProps = {
/** datetime 8601 format */
date: string,
title: string,
@ -37,29 +38,31 @@ const shortMonth = (date: string) => {
export const EventRow = ({date, title, location, cancelled}: EventRowProps) => {
export const EventRow = ({date, title, location, cancelled, href}: EventRowProps) => {
const day = useMemo(() => date.substring(8, 10), [date]);
const dateObj = useMemo(() => new Date(date), [date]);
const month = useMemo(() => shortMonth(date), [date]);
return (
<div className={styles.container}>
<div className={styles.day}>
{day} <br/>
{month}
</div>
<Link href={href} className={styles.link}>
<div className={styles.container}>
<div className={styles.day}>
{day} <br />
{month}
</div>
<div className={styles.line}></div>
<div className={styles.line}></div>
<div className={classNames({
[styles.details]: true,
[styles.cancelled]: cancelled
<div className={classNames({
[styles.details]: true,
[styles.cancelled]: cancelled
})}>
{title} <br/>
{dateObj.toLocaleDateString("de-DE", { weekday: "long"})} {dateObj.toLocaleDateString("de-DE", {dateStyle: "medium"})}, {dateObj.toLocaleTimeString("de-DE", {timeStyle: "short"})} Uhr
<br/>
{location}
{title} <br />
{dateObj.toLocaleDateString("de-DE", { weekday: "long" })} {dateObj.toLocaleDateString("de-DE", { dateStyle: "medium" })}, {dateObj.toLocaleTimeString("de-DE", { timeStyle: "short" })} Uhr
<br />
{location}
</div>
</div>
</div>
</Link>
);
}

View file

@ -28,12 +28,17 @@
margin: 20px 0;
}
.link {
text-decoration: none;
color: inherit;
}
.cancelled {
text-decoration: line-through;
}
.container:hover .day {
color: $shade1;
color: $contrast-color;
}
@media screen and (max-width: 576px) {

View file

@ -0,0 +1,14 @@
import styles from "./styles.module.scss"
type RowProps = {
children: React.ReactNode
alignItems?: "center"
}
export const Row = ({ children, alignItems }: RowProps) => {
return (
<div className={styles.row} style={{ alignItems: alignItems }}>
{children}
</div>
)
}

View file

@ -1,5 +1,12 @@
.row {
display: flex;
flex-wrap: wrap;
gap: 80px;
}
.col {
width: calc(50% - 40px);
flex: 1 1 0;
width: 0;
}
@media screen and (max-width: 576px) {

View file

@ -0,0 +1,13 @@
import { Meta, StoryObj } from '@storybook/react'
import { HR } from './HorizontalRule'
const meta: Meta<typeof HR> = {
component: HR,
}
type Story = StoryObj<typeof HR>;
export default meta
export const Default: Story = {
args: {},
}

View file

@ -0,0 +1,13 @@
import styles from "./styles.module.scss"
import Image from "next/image"
import cross from "./cross.svg"
export const HR = () => {
return (
<div className={styles.container}>
<div className={styles.line}></div>
<Image src={cross} alt={"Cross"} className={styles.cross} />
<div className={styles.line}></div>
</div>
)
}

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="40"
height="70"
viewBox="0 0 10.583333 18.520834"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<g
id="layer1">
<rect
style="fill:#426156FF;stroke-width:0.473399"
id="rect846"
width="0.80836952"
height="16.864483"
x="5.1093903"
y="0.83949941" />
<rect
style="fill:#426156FF;stroke-width:0.454843"
id="rect848"
width="10"
height="0.7728833"
x="0.35068181"
y="4.9346447" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 712 B

View file

@ -0,0 +1,13 @@
@import "template.scss";
.container {
display: flex;
padding: 10px 5%;
gap: 10px;
}
.line {
border-bottom: 1.5px solid $base-color;
flex: 1 1 0;
height: 35px;
}

View file

@ -1,7 +1,8 @@
import styles from "./styles.module.scss"
import { StaticImageData } from 'next/image'
import Link from 'next/link'
type ImageCardProps = {
export type ImageCardProps = {
src: string | StaticImageData,
title: string,
href: string
@ -9,10 +10,12 @@ type ImageCardProps = {
export const ImageCard = ({src, title, href}: ImageCardProps) => {
return (
<div className={styles.container} style={{backgroundImage: `url(${src})`}}>
<div className={styles.title}>
{title}
<Link href={href}>
<div className={styles.container} style={{ backgroundImage: `url(${src})` }}>
<div className={styles.title}>
{title}
</div>
</div>
</div>
</Link>
)
}

View file

@ -4,12 +4,13 @@ type InputProps = {
name: string;
type: 'textarea' | 'text' | 'email'
placeholder?: string
rows?: number
}
export const Input = ({name, type, placeholder}: InputProps) => {
export const Input = ({name, type, placeholder, rows = 5}: InputProps) => {
if (type === 'textarea') {
return (
<textarea name={name} className={styles.input} placeholder={placeholder}>
<textarea name={name} className={styles.input} placeholder={placeholder} rows={rows}>
</textarea>
)

View file

@ -13,7 +13,7 @@
@media screen and (max-width: 576px) {
.input {
padding: 5px 10px;
font-size: 16px;
padding: 10px 15px;
font-size: 18px;
}
}

View file

@ -0,0 +1,3 @@
export const MarginBottom = () => {
return <div style={{marginBottom: "70px"}}></div>
}

View file

@ -1,9 +1,9 @@
import { MassTableRow } from '@/components/MassTable/MassTableRow'
import { useMemo } from 'react'
import styles from './styles.module.css'
import { faustina } from '@/app/fonts'
import { Worship } from '@/payload-types'
import { useCompactDate } from '@/hooks/useCompactDate'
import { ChurchCard } from '@/components/ChurchCard/ChurchCard'
import { church } from '@/utils/church'
type MassTableProps = {
location: string
@ -18,6 +18,10 @@ export const MassTable = ({ location, masses }: MassTableProps) => {
{location}
</h3>
<div className={styles.church}>
<ChurchCard church={church(location)} backgroundColor={"#fffff"} width={200} />
</div>
{masses.map((mass) => (
<MassTableRow
key={mass.id}

View file

@ -30,7 +30,7 @@ export const MassTableRow = ({
return (
<Link
href={`/worship/${id}`}
href={`/gottesdienst/${id}`}
className={classNames({ [styles.cancelled]: cancelled }, styles.link)}
>
<div

View file

@ -28,3 +28,7 @@
width: 200px;
text-align: center;
}
.church {
margin-bottom: 20px;
}

View file

@ -0,0 +1,157 @@
import { Meta, StoryObj } from '@storybook/react'
import { MegaMenu } from './MegaMenu'
const meta: Meta<typeof MegaMenu> = {
component: MegaMenu,
}
type Story = StoryObj<typeof MegaMenu>;
export default meta
export const TwoColumns: Story = {
args: {
bibleText: "Ich will den HERRN loben allezeit; sein Lob soll immerdar in meinem Munde sein. Meine Seele soll sich rühmen des HERRN, dass es die Elenden hören und sich freuen.",
bibleBook: "Psalm 34 2,3",
groups: [
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
},
}
export const ThreeColumns = {
args: {
bibleText: "„Denn wo zwei oder drei in meinem Namen versammelt sind, da bin ich mitten unter ihnen.“",
bibleBook: "Jacc 45",
groups: [
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "https://"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "https://"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "https://"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://",
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
}

View file

@ -0,0 +1,64 @@
import styles from "./styles.module.scss"
import { faustina } from '@/app/fonts'
import Link from 'next/link'
type MegaMenuProps = {
bibleText: string,
bibleBook: string,
groups: ItemGroupProps[],
onClick?: () => void
}
type ItemProps = {
title: string;
description: string
href: string
}
type ItemGroupProps = {
title: string,
items: ItemProps[]
}
const Item = ({href, title, description}: ItemProps) => {
return (
<Link href={href} className={styles.item}>
<div className={styles.itemIcon}>
</div>
<div>
<div className={styles.itemTitle}>
{title}
</div>
<div className={styles.itemDescription}>
{description}
</div>
</div>
</Link>
)
}
const ItemGroup = ({title, items}: ItemGroupProps) => {
return (
<div className={styles.itemGroup}>
<div className={styles.groupTitle}>{title}</div>
{items.map(item => <Item key={item.title} title={item.title} href={item.href} description={item.description} />)}
</div>
)
}
export const MegaMenu = ({ bibleText, bibleBook, groups, onClick }: MegaMenuProps) => {
return (
<div className={styles.menu} onClick={onClick}>
<div className={styles.bibleText}>
<div className={faustina.className}>
{bibleText}
</div>
<div className={styles.book}>
{bibleBook}
</div>
</div>
{groups.map(group => <ItemGroup key={group.title} title={group.title} items={group.items} />)}
</div>
)
}

View file

@ -0,0 +1,71 @@
@import "template.scss";
$width: 220px;
.menu {
background-color: $shade3;
display: inline-flex;
gap: 20px;
}
.bibleText {
background-color: $base-color;
color: $shade3;
width: 200px;
font-size: 24px;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.book {
text-align: right;
font-size: 14px;
}
.itemIcon {
height: 50px;
width: 50px;
flex-shrink: 0;
border-radius: 13px;
border: 1px solid $base-color;
box-shadow: 3px 7px 26px -5px rgba(0, 0, 0, 0.15);
transition: background-color 0.2s;
}
.itemGroup {
padding: 40px;
width: $width;
}
.groupTitle {
font-size: 22px;
font-weight: bold;
margin-bottom: 10px;
}
.item {
color: inherit;
text-decoration: none;
display: flex;
gap: 10px;
align-items: center;
margin: 25px 0;
cursor: pointer;
}
.item:hover .itemIcon {
background-color: $base-color;
}
.itemTitle {
font-size: 18px;
line-height: 120%;
}
.itemDescription {
font-size: 16px;
font-weight: 300;
line-height: 95%;
}

View file

@ -1,40 +1,220 @@
"use client"
import styles from './styles.module.scss'
import MenuIcon from './menu.svg'
import Image from 'next/image'
import classNames from 'classnames'
import { useState } from 'react'
import { MegaMenu } from '@/components/MegaMenu/MegaMenu'
export const Menu = () => {
return (
<nav className={classNames(styles.nav)}>
<div className={styles.navMobile}>
<Image src={MenuIcon} width={25} height={25} alt={'Menu'} />
</div>
<div className={styles.itemsLeft}>
<a className={styles.menuLink} href={'/'}>
Home
</a>
<a className={styles.menuLink} href={''}>
Gemeinschaft finden
</a>
<a className={styles.menuLink} href={''}>
Glauben leben
</a>
<a className={styles.menuLink} href={''}>
Kontakt
</a>
</div>
const [displayMenu2, setDisplayMenu2] = useState(false);
const [displayMenu1, setDisplayMenu1] = useState(false);
<div className={styles.itemsRight}>
<div>
<a className={styles.menuLink} href={''}>
Mithelfen
const displayFirstMenu = () => {
setDisplayMenu1(true);
setDisplayMenu2(false);
}
const displaySecondMenu = () => {
setDisplayMenu2(true)
setDisplayMenu1(false)
}
const displayNothing = () => {
setDisplayMenu1(false);
setDisplayMenu2(false);
}
return (
<>
<nav className={classNames(styles.nav)}>
<div className={styles.navMobile}>
<Image src={MenuIcon} width={25} height={25} alt={'Menu'} />
</div>
<div className={styles.itemsLeft}>
<a className={styles.menuLink} href={'/'} onMouseEnter={displayNothing}>
Home
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displayFirstMenu}>
Gemeinschaft finden
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displaySecondMenu}>
Glauben leben
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displayNothing}>
Kontakt
</a>
</div>
<div>
<button className={styles.button}>Neu hier?</button>
<div className={styles.itemsRight}>
<div>
<a className={styles.menuLink} href={''}>
Mithelfen
</a>
</div>
<div>
<button className={styles.button}>Neu hier?</button>
</div>
</div>
</nav>
<div
className={styles.megaMenu}
style={{ display: displayMenu2 ? 'block' : 'none', opacity: displayMenu2 ? 1 : 0 }}
onMouseLeave={() => setDisplayMenu2(false)}
>
<MegaMenu
bibleText={"Ich will den HERRN loben allezeit; sein Lob soll immerdar in meinem Munde sein. Meine Seele soll sich rühmen des HERRN, dass es die Elenden hören und sich freuen."}
bibleBook={"Psalm 34 2,3"}
onClick={() => setDisplayMenu2(false)}
groups={[
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]} />
</div>
</nav>
<div
className={styles.megaMenu}
style={{ display: displayMenu1 ? 'block' : 'none', opacity: displayMenu1 ? 1 : 0 }}
onMouseLeave={() => setDisplayMenu1(false)}
>
<MegaMenu
bibleText={"„Denn wo zwei oder drei in meinem Namen versammelt sind, da bin ich mitten unter ihnen.“"}
bibleBook={"Matt 18-2"}
onClick={() => setDisplayMenu1(false)}
groups={[
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]} />
</div>
</>
)
}

View file

@ -59,6 +59,14 @@
cursor: pointer;
}
.megaMenu {
position: fixed;
top: 76px;
z-index: 8;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
@media screen and (max-width: 800px) {
.navMobile {
display: block;

View file

@ -1,7 +1,7 @@
import classNames from 'classnames'
import styles from "./styles.module.scss"
export type BackgroundColor = "soft" | undefined
export type BackgroundColor = "soft" | "off-white" | undefined
type SectionProps = {
backgroundColor?: BackgroundColor
@ -12,7 +12,8 @@ export const Section = ({ children, backgroundColor }: SectionProps) => {
return (
<section className={classNames({
[styles.section]: true,
[styles.shade2]: backgroundColor === "soft"
[styles.shade2]: backgroundColor === "soft",
[styles.shade3]: backgroundColor === "off-white"
})}>
{children}
</section>

View file

@ -8,6 +8,10 @@
background-color: $shade2;
}
.shade3 {
background-color: $shade3;
}
@media screen and (max-width: 576px) {
.section {
padding: 70px 0;

View file

@ -0,0 +1,11 @@
type TextProps = {
text: string
}
export const TextDiv = ({text}: TextProps) => {
return (
<div style={{whiteSpace: 'preserve'}}>
{text}
</div>
)
}

View file

@ -14,6 +14,20 @@ export const Default: Story = {
},
}
export const WithSubtitle: Story = {
args: {
title: 'Wahlfahrt Lourdes',
subtitle: "Sei dabei!"
},
}
export const Contrast: Story = {
args: {
title: 'Aktuelles',
color: "contrast"
},
}
export const BigTitle: Story = {
args: {
title: 'Veranstaltungen',

View file

@ -4,21 +4,43 @@ import { faustina } from '@/app/fonts'
type TitleProps = {
title: string;
subtitle?: string;
align?: 'left' | 'center';
size?: 'xl' | 'lg' | 'md';
size?: 'xl' | 'lg' | 'md' | "sm";
fontStyle?: 'serif' | 'sans-serif'
color?: "base" | "contrast" | "white"
}
export const Title = ({title, align = "left", size = "lg", fontStyle = "serif"}: TitleProps) => {
export const Title = ({title, subtitle, align = "left", size = "lg", fontStyle = "serif", color = "base"}: TitleProps) => {
return (
<h2 className={classNames({
[styles.title]: true,
[styles.extraLarge]: size === "xl",
[styles.large]: size === "lg",
[styles.medium]: size === "md",
[styles.left]: align === "left",
[styles.center]: align === "center",
[faustina.className]: fontStyle == "serif",
})}>{title}</h2>
<>
<h2 className={classNames({
[styles.title]: true,
[styles.base]: color === "base",
[styles.contrast]: color === "contrast",
[styles.white]: color === "white",
[styles.extraLarge]: size === "xl",
[styles.large]: size === "lg",
[styles.medium]: size === "md",
[styles.small]: size === "sm",
[styles.left]: align === "left",
[styles.center]: align === "center",
[faustina.className]: fontStyle == "serif",
})}>{title}</h2>
{subtitle &&
<div className={classNames({
[styles.subtitle]: true,
[styles.base]: color === "contrast",
[styles.contrast]: color === "base",
[styles.white]: color === "white",
[styles.small]: true,
[styles.left]: align === "left",
[styles.center]: align === "center",
[faustina.className]: fontStyle == "sans-serif"
})}>
{subtitle}
</div>
}
</>
)
}

View file

@ -1,8 +1,26 @@
@import "template.scss";
.title {
color: $base-color;
margin-top: 20px;
margin-bottom: 0.5em;
line-height: 1em;
}
.subtitle {
position: relative;
top: -30px;
}
.base {
color: $base-color;
}
.contrast {
color: $contrast-color;
}
.white {
color: #ffffff;
}
.extraLarge {
@ -20,6 +38,12 @@
font-weight: 700;
}
.small {
font-size: 25px;
font-weight: 700;
margin: 10px 0 5px 0;
}
.left {
text-align: left;
}
@ -40,4 +64,8 @@
.medium {
font-size: 24px;
}
.small {
font-size: 20px;
}
}

View file

@ -0,0 +1,31 @@
import { Col } from '@/components/Flex/Col'
import styles from '@/pageComponents/Parish/styles.module.scss'
import { ChurchCard } from '@/components/ChurchCard/ChurchCard'
import { Title } from '@/components/Title/Title'
import { TextDiv } from '@/components/Text/TextDiv'
import { Row } from '@/components/Flex/Row'
import { church as transform } from '@/utils/church'
type ChurchWithContactProps = {
church: string,
contact: string
}
export const ChurchWithContact = ({church, contact}: ChurchWithContactProps) => {
const c = transform(church)
return (
<Row alignItems={"center"}>
<Col>
<div className={styles.churchIcon}>
<ChurchCard church={c} backgroundColor={'#E0DAE5'} />
</div>
</Col>
<Col>
<Title title={"Kontakt"} size={"md"} color={"contrast"} />
<TextDiv text={contact} />
</Col>
</Row>
)
}

View file

@ -2,29 +2,30 @@ import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { ContactForm } from '@/compositions/ContactForm/ContactForm'
import { Title } from '@/components/Title/Title'
import { Col } from '@/components/Flex/Col'
import { Row } from '@/components/Flex/Row'
type Col50Props = {
children?: React.ReactNode
}
const Col50 = ({children}: Col50Props) => {
return <div>{children}</div>
}
export const ContactSection = () => {
return (
<Section>
<Container flex={true}>
<Col50>
<Title title="Kontakt" size={"lg"} />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque lacinia efficitur sapien, vel molestie ligula fermentum rutrum. Maecenas vel mattis leo. Donec dictum convallis mi ac fermentum. Praesent imperdiet dictum arcu, vel lacinia est accumsan ut. Mauris ultrices leo ut mi eleifend, at porttitor mauris condimentum. Quisque a viverra tellus. Cras eu euismod augue. Morbi tristique a nisi vitae tincidunt. Nullam eget dignissim risus, ut tempor nulla. Proin suscipit mi sed leo posuere scelerisque. Sed vel semper ligula.
</p>
<Container>
<Row>
<Col>
<Title title="Kontakt" size={"lg"} />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque lacinia efficitur sapien, vel molestie ligula fermentum rutrum. Maecenas vel mattis leo. Donec dictum convallis mi ac fermentum. Praesent imperdiet dictum arcu, vel lacinia est accumsan ut. Mauris ultrices leo ut mi eleifend, at porttitor mauris condimentum. Quisque a viverra tellus. Cras eu euismod augue. Morbi tristique a nisi vitae tincidunt. Nullam eget dignissim risus, ut tempor nulla. Proin suscipit mi sed leo posuere scelerisque. Sed vel semper ligula.
</p>
</Col50>
<Col50>
<ContactForm />
</Col50>
</Col>
<Col>
<ContactForm />
</Col>
</Row>
</Container>
</Section>
)

View file

@ -0,0 +1,66 @@
import { Meta, StoryObj } from '@storybook/react'
import { Events } from './Events'
const meta: Meta<typeof Events> = {
component: Events,
}
type Story = StoryObj<typeof Events>;
export default meta
export const Default: Story = {
args: {
events: [
{
title: "Event 1",
date: "2024-01-06T15:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 2",
date: "2024-01-08T19:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 3",
date: "2024-01-09T12:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 4",
date: "2024-01-12T10:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 5",
date: "2024-01-23T09:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 6",
date: "2024-02-01T14:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
{
title: "Event 7",
date: "2024-03-12T18:00:00+01:00",
location: "St Richard",
cancelled: false,
href: "https://some_url"
},
],
n: 3
},
}

View file

@ -0,0 +1,70 @@
"use client"
import { EventRow, EventRowProps } from '@/components/EventRow/EventRow'
import { useMemo, useState } from 'react'
import styles from "./events.module.scss"
type EventsProps = {
/**
* All the events
*/
events: EventRowProps[],
/**
* Display `n` number of events per
* page
*/
n: number,
}
export const Events = ({ events, n }: EventsProps) => {
const [page, setPage] = useState<number>(0)
const eventsPerPage = useMemo(() => {
return events.slice(n * page, n * (page+1))
}, [events, page, n])
return (
<>
{
eventsPerPage.map(event =>
<EventRow
key={event.href}
date={event.date}
title={event.title}
href={event.href}
location={event.location}
cancelled={event.cancelled}
/>,
)}
<div className={styles.buttons}>
<button
type={"button"}
disabled={page === 0}
className={styles.button}
onClick={() => setPage(page - 1)}
>
<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" height="30px"
width="30px" version="1.1" id="Layer_1" viewBox="0 0 330 330">
<path id="XMLID_6_"
d="M165,0C74.019,0,0,74.019,0,165s74.019,165,165,165s165-74.019,165-165S255.981,0,165,0z M205.606,234.394 c5.858,5.857,5.858,15.355,0,21.213C202.678,258.535,198.839,260,195,260s-7.678-1.464-10.606-4.394l-80-79.998 c-2.813-2.813-4.394-6.628-4.394-10.606c0-3.978,1.58-7.794,4.394-10.607l80-80.002c5.857-5.858,15.355-5.858,21.213,0 c5.858,5.857,5.858,15.355,0,21.213l-69.393,69.396L205.606,234.394z" />
</svg>
</button>
<button
type={"button"}
disabled={n * (page + 1) >= events.length} onClick={() => setPage(page + 1)}
className={styles.button}
>
<svg fill="#000000" height="30px" width="30px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 330 330">
<path id="XMLID_2_" d="M165,0C74.019,0,0,74.019,0,165s74.019,165,165,165s165-74.019,165-165S255.981,0,165,0z M225.606,175.605
l-80,80.002C142.678,258.535,138.839,260,135,260s-7.678-1.464-10.606-4.394c-5.858-5.857-5.858-15.355,0-21.213l69.393-69.396
l-69.393-69.392c-5.858-5.857-5.858-15.355,0-21.213c5.857-5.858,15.355-5.858,21.213,0l80,79.998
c2.814,2.813,4.394,6.628,4.394,10.606C230,168.976,228.42,172.792,225.606,175.605z" />
</svg>
</button>
</div>
</>
)
}

View file

@ -0,0 +1,28 @@
@import "template.scss";
.button {
border: none;
background: none;
cursor: pointer;
}
.button svg {
fill: $shade2;
}
.button:hover svg {
fill: $base-color;
}
.button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.button:disabled:hover svg {
fill: $shade2;
}
.buttons {
padding-left: 130px;
}

View file

@ -2,29 +2,38 @@ import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { Logo } from '@/components/Logo/Logo'
import styles from "./styles.module.scss"
import { Row } from '@/components/Flex/Row'
import { Col } from '@/components/Flex/Col'
export const Footer = () => {
return (
<Section backgroundColor="soft">
<Container flex={true}>
<Logo
color={"#ffffff"}
textColor={"#426156"}
withText={true}
height={120}
/>
<div className={styles.container}>
<p>
Briesestraße 17 <br/>
12053 Berlin-Neukölln
</p>
<Container>
<Row>
<Col>
<Logo
color={"#ffffff"}
textColor={"#426156"}
withText={true}
height={120}
/>
</Col>
<p>
T: 030-6889120 <br/>
E: pfarrer@dreikoenige.berlin
</p>
</div>
<Col>
<div className={styles.container}>
<p>
Briesestraße 17 <br />
12053 Berlin-Neukölln
</p>
<p>
T: 030-6889120 <br />
E: pfarrer@dreikoenige.berlin
</p>
</div>
</Col>
</Row>
</Container>
</Section>
);

View file

@ -3,7 +3,7 @@ import styles from "./styles.module.scss"
import { StaticImageData } from 'next/image'
import { Arrow } from '@/components/Arrow/Arrow'
type Slide = {
export type Slide = {
id: string,
src: string | StaticImageData,
title: string,

View file

@ -1,6 +1,6 @@
import { Meta, StoryObj } from '@storybook/react'
import forest from "./forest.jpeg"
import chris from "./christophorus.jpeg"
import forest from "../../assets/forest.jpeg"
import chris from "../../assets/christophorus.jpeg"
import { ImageWithText } from './ImageWithText'
import { Button } from '@/components/Button/Button'

View file

@ -1,10 +1,10 @@
import { BackgroundColor, Section } from '@/components/Section/Section'
import { Title } from '@/components/Title/Title'
import { Button } from '@/components/Button/Button'
import { Container } from '@/components/Container/Container'
import Image, { StaticImageData } from 'next/image'
import styles from "./styles.module.scss"
import classNames from 'classnames'
import { Row } from '@/components/Flex/Row'
type ImageWithTextProps = {
backgroundColor?: BackgroundColor,
@ -15,27 +15,30 @@ type ImageWithTextProps = {
}
export const ImageWithText = ({backgroundColor, title, image, text, link}: ImageWithTextProps) => {
console.log(image)
return (
<Section backgroundColor={backgroundColor}>
<Container flex={true}>
<div className={classNames(styles.col, styles.imageCol)}>
<Image className={styles.image} src={image} objectFit={"cover"} alt={""} />
</div>
<div className={styles.col}>
<Title title={title} size={"lg"} />
<Image className={styles.imageMobile} src={image} objectFit={"cover"} alt={""} />
<div>
{text}
<Container>
<Row alignItems={"center"}>
<div className={classNames(styles.col, styles.imageCol)}>
<Image className={styles.image} width={800} height={800} src={image} objectFit={"cover"} alt={""} />
</div>
<div className={styles.col}>
<Title title={title} size={"lg"} />
{link &&
<div className={styles.right}>
{link}
<Image className={styles.imageMobile} width={500} height={500} src={image} objectFit={"cover"} alt={""} />
<div>
{text}
</div>
}
</div>
{link &&
<div className={styles.right}>
{link}
</div>
}
</div>
</Row>
</Container>
</Section>
)

21
src/fetch/blog.ts Normal file
View file

@ -0,0 +1,21 @@
import { Blog } from '@/payload-types'
import { PaginatedDocs } from 'payload'
import { stringify } from 'qs-esm'
export const fetchBlog = async (): Promise<PaginatedDocs<Blog> | undefined> => {
const stringifiedQuery = stringify(
{
sort: "-date",
select: {
title: true,
date: true,
photo: true
}
},
{ addQueryPrefix: true },
)
const resp = await fetch(`http://localhost:3000/api/blog`);
if (!resp.ok) return undefined;
return resp.json();
}

46
src/fetch/events.ts Normal file
View file

@ -0,0 +1,46 @@
import { stringify } from 'qs-esm'
import { PaginatedDocs } from 'payload'
import { Event } from '@/payload-types'
/**
* Fetch a list of events
*
*/
export async function fetchEvents(parishId: string | undefined): Promise<PaginatedDocs<Event> | undefined> {
const date = new Date()
const query: any = {
and: [
{
date: {
greater_than_equal: date.toISOString(),
},
}
],
}
if (parishId) {
query.and.push({
"parish": {
equals: parishId
},
})
}
const stringifiedQuery = stringify(
{
sort: "date",
where: query,
select: {
location: true,
date: true,
title: true
}
},
{ addQueryPrefix: true },
)
const response = await fetch(`http://localhost:3000/api/event${stringifiedQuery}`)
if (!response.ok) return undefined
return response.json()
}

45
src/fetch/worship.ts Normal file
View file

@ -0,0 +1,45 @@
import { stringify } from 'qs-esm'
import { PaginatedDocs } from 'payload'
import { Worship } from '@/payload-types'
export const fetchWorship = async (locations?: string[]): Promise<PaginatedDocs<Worship> | undefined> => {
const date = new Date();
date.setHours(0, 0, 0, 0);
const query: any = {
and: [
{
date: {
greater_than_equal: date.toISOString(),
},
}
],
}
if (locations ) {
query.and.push({
location: {
in: locations
}
})
}
const stringifiedQuery = stringify(
{
sort: "date",
where: query,
select: {
type: true,
date: true,
cancelled: true,
location: true
},
limit: 15
},
{ addQueryPrefix: true },
)
const response = await fetch(`http://localhost:3000/api/worship${stringifiedQuery}`)
if (!response.ok) return undefined
return response.json()
}

View file

@ -4,7 +4,7 @@ import { calendar } from '@/hooks/calendars'
* Return liturgical name of the date
* e.G. "2024-12-25" => Christmas
*/
export const useLiturgyCalendarTitle = (date: string) => {
export const liturgicalDayName = (date: string) => {
const day = calendar[date.substring(0, 10)]
return day.name
}

View file

@ -0,0 +1,24 @@
import { Meta, StoryObj } from '@storybook/react'
import { Home } from './Home'
import { Menu } from '@/components/Menu/Menu'
import { Footer } from '@/compositions/Footer/Footer'
const meta: Meta<typeof Home> = {
component: Home,
decorators: [
(Story) => (
<>
<Menu/>
<Story />
<Footer />
</>
)
]
}
type Story = StoryObj<typeof Home>;
export default meta
export const Default: Story = {
args: {},
}

View file

@ -0,0 +1,72 @@
import { Banner } from '@/components/Banner/Banner'
import { Container } from '@/components/Container/Container'
import { Section } from '@/components/Section/Section'
import { MainText } from '@/components/MainText/MainText'
import { Title } from '@/components/Title/Title'
import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider'
import monst from '@/app/mons.jpg'
import candle from '@/app/candle.png'
import bread from '@/app/bread.jpg'
import forest from "../../assets/forest.jpeg"
import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider'
import { ContactSection } from '@/compositions/ContactSection/ContactSection'
import { Events } from '@/compositions/Events/Events'
import { ImageWithText } from '@/compositions/ImageWithText/ImageWithText'
export const Home = () => {
return (
<>
<Banner/>
<Container>
<Section>
<MainText text={"Wir begrüßen Sie herzlich in unserer Pfarrei Hl. Drei Könige und im bunten Neukölln mit einer Vielfalt von Kulturen und Nationalitäten. \n" +
"Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen."} />
</Section>
<Section>
<Title title={"Aktuelles"} />
<ImageCardSlider slides={[
{
id: "id1",
src: monst,
title: "Anbetung in Oktober",
href: "https://somelink"
},
{
id: "id2",
src: candle,
title: "Allerseelen",
href: "https://somelink"
},
{
id: "id3",
src: bread,
title: "Erntedankfest",
href: "https://somelink"
}
]} />
</Section>
</Container>
<ContentWithSlider slider={<>
<Title title={"Akutelle Highlights"} size={"md"} fontStyle={"sans-serif"} color={"white"} />
</>}>
<Container position={"right"}>
<Section>
<Title title={"Veranstaltungen"} />
</Section>
</Container>
</ContentWithSlider>
<ImageWithText
image={forest}
title={"Über uns"}
text={"yolo"}
backgroundColor={"soft"}
/>
<ContactSection />
</>
)
}

View file

@ -0,0 +1,65 @@
import { Meta, StoryObj } from '@storybook/react'
import chris from "../../assets/christophorus.jpeg"
import { Parish } from './Parish'
import { Menu } from '@/components/Menu/Menu'
import { Footer } from '@/compositions/Footer/Footer'
const meta: Meta<typeof Parish> = {
component: Parish,
decorators: [
(Story) => (
<>
<Menu/>
<Story />
<Footer />
</>
)
]
}
type Story = StoryObj<typeof Parish>;
export default meta
export const Default: Story = {
args: {
title: "St. Christophorus",
image: chris,
description: "Die St. Christophorus Kirche in Berlin-Neukölln ist ein bedeutendes Beispiel für modernen Kirchenbau in der Hauptstadt. Erbaut in den 1960er Jahren, spiegelt das Gebäude die Architektur und künstlerische Gestaltung dieser Zeit wider und zeichnet sich durch schlichte, klare Linien und einen funktionalen Stil aus. Die Kirche ist nach dem heiligen Christophorus benannt, dem Schutzpatron der Reisenden, und bietet den Gemeindemitgliedern und Besuchern einen Ort der Ruhe und Besinnung im lebhaften Stadtteil Neukölln. Neben Gottesdiensten finden hier regelmäßig kulturelle Veranstaltungen und soziale Projekte statt, die die Kirche zu einem wichtigen Treffpunkt im Kiez machen.",
history: `Am 27.Juni 1929 erschien folgende Niederschrift in der Märkischen Volkszeitung, die eine berechtigte Freude über die Nachricht von dem Bau der neuen Kirche am Reuterplatz auslöste:
Eine neue katholische Kirche in Neukölln.
Den eifrigen Bemühungen des hochw. Herrn Pfarrers Trawnik ist es endlich gelungen, den Plan einer neuen Kirche am schönen Reuterplatz in Neukölln zu verwirklichen. Der Krieg hatte bisher den Kirchbau verhindert, obwohl die Vorbereitungen dazu beendet waren. Die Bank, welche den Bau finanzieren wollte, durfte im Juni 1914 die Bausumme nicht hergeben, weil sie zum Kriegführen benötigt wurde. Nun endlich soll es Wahrheit werden, es wird ein stattliches Gotteshaus erstehen, und sein Turm wird himmelan weisen.
Durch die sprunghafte Entwicklung Neuköllns hat die Gemeinde rapide an Ausdehnung gewonnen. Das rasche Wachstum der Einwohnerzahl hatte zur Folge, daß die Mietskasernen wie Pilze aus der Erde schossen.
`,
contactPersons: [
{
title: "Seelsorgeteam",
description: `Pater Karl Hermann Lenz SAC (Pfarrvikar)
pallottis@christophorus-berlin.de
Lissy Eichert UAC
l.eichert@christophorus-berlin.
`
},
{
title: "Pallottinische Gemeinschaft",
description: `Lissy Eichert UAC
l.eichert@christophorus-berlin.de`
},
{
title: "Kirechenvorstand",
description: "Some contactinfo"
}
],
contact: `Nansenstraße 4-7
12047 Berlin
Tel. 6 27 30 69-210
Fax 6 27 30 69-299
pfarramt@christophorus-berlin.de
Bürozeiten:
Freitags 09:00 - 12:00 Uhr `
}
};

View file

@ -0,0 +1,75 @@
import { ImageWithText } from '@/compositions/ImageWithText/ImageWithText'
import { StaticImageData } from 'next/image'
import { Section } from '@/components/Section/Section'
import { Row } from '@/components/Flex/Row'
import { Col } from '@/components/Flex/Col'
import { Container } from '@/components/Container/Container'
import { Title } from '@/components/Title/Title'
import { TwoColumnText } from '@/components/TwoColumnText/TwoColumnText'
import { Events } from '@/compositions/Events/Events'
import { MarginBottom } from '@/components/Margin/MarbinBottom'
import { ContactPersonList } from '@/components/ContactPerson/ContactPersonList'
import { Event, Worship } from '@/payload-types'
import { transformEvents } from '@/utils/dto/events'
import { ChurchWithContact } from '@/compositions/ChurchWithContact/ChurchWithContact'
import { tranformWorship } from '@/utils/dto/worship'
type ParishProps = {
title: string,
slug: string,
image: string | StaticImageData,
description: string,
history: string
contactPersons: {
title: string,
description: string
}[],
contact: string
events: Event[],
worship: Worship[]
}
export const Parish = ({title, slug, image, description, history, contactPersons, contact, events, worship}: ParishProps) => {
return (
<>
<ImageWithText title={title} image={image} text={description} />
<Section>
<Container>
<Row>
<Col>
<Title title={"Veranstaltungen"} size={"md"}/>
<Events events={transformEvents(events)} n={4}/>
<MarginBottom />
<Title title={"Gottesdienste"} size={"md"} />
<Events events={tranformWorship(worship)} n={4}/>
</Col>
<Col>
<Title title={"Ansprechpersonen"} size={"md"} />
<ContactPersonList persons={contactPersons} />
</Col>
</Row>
</Container>
</Section>
<Section backgroundColor={"soft"}>
<Container>
<Title title={"Unsere Geschichte"} />
<TwoColumnText text={history} />
</Container>
</Section>
<Section>
<Container>
<ChurchWithContact church={title} contact={contact} />
</Container>
</Section>
</>
)
}

View file

@ -0,0 +1,10 @@
.churchIcon {
display: flex;
justify-content: flex-end;
}
@media screen and (max-width: 576px) {
.churchIcon {
justify-content: center;
}
}

View file

@ -24,17 +24,42 @@ export interface Config {
page: Page;
users: User;
media: Media;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
parish: ParishSelect<false> | ParishSelect<true>;
church: ChurchSelect<false> | ChurchSelect<true>;
worship: WorshipSelect<false> | WorshipSelect<true>;
vermeldungen: VermeldungenSelect<false> | VermeldungenSelect<true>;
blog: BlogSelect<false> | BlogSelect<true>;
tweet: TweetSelect<false> | TweetSelect<true>;
event: EventSelect<false> | EventSelect<true>;
group: GroupSelect<false> | GroupSelect<true>;
employees: EmployeesSelect<false> | EmployeesSelect<true>;
testimony: TestimonySelect<false> | TestimonySelect<true>;
page: PageSelect<false> | PageSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: string;
};
globals: {};
globalsSelect: {};
locale: null;
user: User & {
collection: 'users';
};
jobs?: {
tasks: unknown;
workflows?: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
@ -64,10 +89,17 @@ export interface Parish {
slug: string;
churches: (string | Church)[];
employees?: (string | Employee)[] | null;
contact: string;
title: string;
contactPersons?:
| {
title: string;
description: string;
id?: string | null;
}[]
| null;
description: string;
photo?: (string | null) | Media;
history: string;
contact: string;
photo: string | Media;
updatedAt: string;
createdAt: string;
}
@ -124,8 +156,10 @@ export interface Worship {
date: string;
location: string | Church;
type: 'MASS' | 'FAMILY' | 'WORD';
cancelled: boolean;
title?: string | null;
cancelled: boolean;
liturgicalDay?: string | null;
celebrant?: string | null;
description?: string | null;
updatedAt: string;
createdAt: string;
@ -150,22 +184,23 @@ export interface Blog {
photo?: (string | null) | Media;
title: string;
parish?: (string | Parish)[] | null;
isHighlight: boolean;
text: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
content?:
| (
| {
quoteText: string;
id?: string | null;
blockName?: string | null;
blockType: 'Quote';
}
| {
sliderContent: string;
content: string;
id?: string | null;
blockName?: string | null;
blockType: 'ContentWithSlider';
}
)[]
| null;
updatedAt: string;
createdAt: string;
}
@ -188,7 +223,8 @@ export interface Event {
id: string;
photo?: (string | null) | Media;
title: string;
datum: string;
date: string;
location: string;
parish?: (string | Parish)[] | null;
group?: (string | null) | Group;
shortDescription: string;
@ -301,6 +337,73 @@ export interface User {
lockUntil?: string | null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: string;
document?:
| ({
relationTo: 'parish';
value: string | Parish;
} | null)
| ({
relationTo: 'church';
value: string | Church;
} | null)
| ({
relationTo: 'worship';
value: string | Worship;
} | null)
| ({
relationTo: 'vermeldungen';
value: string | Vermeldungen;
} | null)
| ({
relationTo: 'blog';
value: string | Blog;
} | null)
| ({
relationTo: 'tweet';
value: string | Tweet;
} | null)
| ({
relationTo: 'event';
value: string | Event;
} | null)
| ({
relationTo: 'group';
value: string | Group;
} | null)
| ({
relationTo: 'employees';
value: string | Employee;
} | null)
| ({
relationTo: 'testimony';
value: string | Testimony;
} | null)
| ({
relationTo: 'page';
value: string | Page;
} | null)
| ({
relationTo: 'users';
value: string | User;
} | null)
| ({
relationTo: 'media';
value: string | Media;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: string | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
@ -335,6 +438,261 @@ export interface PayloadMigration {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "parish_select".
*/
export interface ParishSelect<T extends boolean = true> {
name?: T;
slug?: T;
churches?: T;
employees?: T;
contactPersons?:
| T
| {
title?: T;
description?: T;
id?: T;
};
description?: T;
history?: T;
contact?: T;
photo?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "church_select".
*/
export interface ChurchSelect<T extends boolean = true> {
name?: T;
address?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "worship_select".
*/
export interface WorshipSelect<T extends boolean = true> {
date?: T;
location?: T;
type?: T;
title?: T;
cancelled?: T;
liturgicalDay?: T;
celebrant?: T;
description?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "vermeldungen_select".
*/
export interface VermeldungenSelect<T extends boolean = true> {
date?: T;
parish?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "blog_select".
*/
export interface BlogSelect<T extends boolean = true> {
photo?: T;
title?: T;
parish?: T;
content?:
| T
| {
Quote?:
| T
| {
quoteText?: T;
id?: T;
blockName?: T;
};
ContentWithSlider?:
| T
| {
sliderContent?: T;
content?: T;
id?: T;
blockName?: T;
};
};
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "tweet_select".
*/
export interface TweetSelect<T extends boolean = true> {
parish?: T;
text?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "event_select".
*/
export interface EventSelect<T extends boolean = true> {
photo?: T;
title?: T;
date?: T;
location?: T;
parish?: T;
group?: T;
shortDescription?: T;
description?: T;
cancelled?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group_select".
*/
export interface GroupSelect<T extends boolean = true> {
photo?: T;
name?: T;
description?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "employees_select".
*/
export interface EmployeesSelect<T extends boolean = true> {
photo?: T;
name?: T;
occupation?: T;
email?: T;
telephone?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "testimony_select".
*/
export interface TestimonySelect<T extends boolean = true> {
testimony?: T;
name?: T;
occupation?: T;
category?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "page_select".
*/
export interface PageSelect<T extends boolean = true> {
title?: T;
slug?: T;
content?:
| T
| {
content?:
| T
| {
content?: T;
id?: T;
blockName?: T;
};
title?:
| T
| {
title?: T;
id?: T;
blockName?: T;
};
testimony?:
| T
| {
testimony?: T;
id?: T;
blockName?: T;
};
};
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
name?: T;
roles?: T;
groups?: T;
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
alt?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".

28
src/utils/church.ts Normal file
View file

@ -0,0 +1,28 @@
/**
* Convert string to a church
* @param s
*/
export const church = (s: string) : "anna" | "christophorus" | "richard" | "eduard" | "clara" => {
if (s.toLowerCase().includes("anna")) {
return "anna"
}
if (s.toLowerCase().includes("christophorus")) {
return "christophorus"
}
if (s.toLowerCase().includes("richard")) {
return "richard"
}
if (s.toLowerCase().includes("eduard")) {
return "eduard"
}
if (s.toLowerCase().includes("clara")) {
return "clara"
}
return "clara";
}

13
src/utils/dto/blog.ts Normal file
View file

@ -0,0 +1,13 @@
import { Blog } from '@/payload-types'
import { Slide } from '@/compositions/ImageCardSlider/ImageCardSlider'
export const blogToSlides = (blog: Blog[]): Slide[] => {
return blog.map(b => {
return {
id: b.id,
title: b.title,
href: `/blog/${b.id}`,
src: typeof b.photo === "string" ? b.photo : b.photo?.url || ""
}
})
}

15
src/utils/dto/events.ts Normal file
View file

@ -0,0 +1,15 @@
import { Event } from '@/payload-types'
import { EventRowProps } from '@/components/EventRow/EventRow'
export const transformEvents = (events: Event[]): EventRowProps[] => {
return events.map(e => {
return {
id: e.id,
title: e.title,
date: e.date,
href: `/veranstaltungen/${e.id}`,
location: e.location,
cancelled: e.cancelled,
}
})
}

32
src/utils/dto/worship.ts Normal file
View file

@ -0,0 +1,32 @@
import { Worship } from '@/payload-types'
import { EventRowProps } from '@/components/EventRow/EventRow'
/**
* Transform the worship category to a readable string
*/
export const transformCategory = (category: "MASS" | "FAMILY" | "WORD"): string => {
const type = {
'MASS': 'Eucharistiefeier',
'FAMILY': 'Familienmesse',
'WORD': 'Wort-Gottes-Feier',
}
return type[category]
}
/**
* Transform worship data to `EventRow` component properties
*/
export const tranformWorship = (worship: Worship[]): EventRowProps[] => {
return worship.map(w => {
return {
id: w.id,
title: transformCategory(w.type),
date: w.date,
href: `/gottesdienst/${w.id}`,
location: typeof w.location === 'string' ? w.location : w.location.name,
cancelled: w.cancelled,
}
})
}

Some files were not shown because too many files have changed in this diff Show more