feature: sacrament pages

This commit is contained in:
Benno Tielen 2024-11-27 18:13:20 +01:00
parent 7aa901a355
commit 2479714eaa
46 changed files with 947 additions and 85 deletions

View file

@ -0,0 +1,70 @@
import { ImageWithText } from '@/compositions/ImageWithText/ImageWithText'
import { fetchGroup } from '@/fetch/group'
import { notFound } from 'next/navigation'
import { fetchEvents } from '@/fetch/events'
import { Section } from '@/components/Section/Section'
import { Title } from '@/components/Title/Title'
import { Events } from '@/compositions/Events/Events'
import { transformEvents } from '@/utils/dto/events'
import { Container } from '@/components/Container/Container'
import { HR } from '@/components/HorizontalRule/HorizontalRule'
import { TextDiv } from '@/components/Text/TextDiv'
export default async function GroupPage({ params }: { params: Promise<{slug: string}>}) {
const slug = (await params).slug
const groups = await fetchGroup(slug)
if(!groups || groups.docs.length === 0) {
notFound();
}
const {id, description, photo,name } = groups.docs[0]
const media = {
src: typeof photo === "object" && photo ? photo.url || "" : "",
width: typeof photo === "object" && photo ? photo.width || 0 : 0,
height: typeof photo === "object" && photo ? photo.height || 0 : 0
}
const events = await fetchEvents(undefined, id)
return (
<>
{typeof photo === "object" &&
<ImageWithText
title={name}
backgroundColor={"soft"}
text={description}
image={media}
/>
}
{typeof photo !== "object" &&
<Section paddingBottom={"medium"}>
<Container>
<Title title={name} />
<strong>
<TextDiv text={description} />
</strong>
</Container>
<HR />
</Section>
}
{ events && events.docs.length > 0 &&
<Section>
<Container>
<Title title={"Veranstaltungen"} size={"md"} />
<Events events={transformEvents(events.docs)} n={3} />
</Container>
</Section>
}
</>
)
}

View file

@ -117,32 +117,32 @@ export default function RootLayout({
{ {
title: "Taufe", title: "Taufe",
description: "Neues Leben in Christus", description: "Neues Leben in Christus",
href: "https://" href: "/sakramente/taufe"
}, },
{ {
title: "Eucharistie", title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein", description: "Gemeinschaft durch Brot und Wein",
href: "https://" href: "/sakramente/eucharistie"
}, },
{ {
title: "Firmung", title: "Firmung",
description: "Stärkung im Heiligen Geist", description: "Stärkung im Heiligen Geist",
href: "https://" href: "/sakramente/firmung"
}, },
{ {
title: "Ehe", title: "Ehe",
description: "Bund in Liebe, Treue", description: "Bund in Liebe, Treue",
href: "https://" href: "/sakramente/ehe"
}, },
{ {
title: "Beichte", title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade", description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://" href: "/sakramente/beichte"
}, },
{ {
title: "Krankensalbung", title: "Krankensalbung",
description: "Stärkung und Gottes Beistand", description: "Stärkung und Gottes Beistand",
href: "https://" href: "/sakramente/krankensalbung"
} }
] ]
}, },

View file

@ -12,7 +12,6 @@ import forest from "../assets/forest.jpeg"
import { fetchWorship } from '@/fetch/worship' import { fetchWorship } from '@/fetch/worship'
import { Worship } from '@/payload-types' import { Worship } from '@/payload-types'
import { MassTable } from '@/components/MassTable/MassTable' import { MassTable } from '@/components/MassTable/MassTable'
import { Row } from '@/components/Flex/Row'
import { transformEvents } from '@/utils/dto/events' import { transformEvents } from '@/utils/dto/events'
import { fetchBlog } from '@/fetch/blog' import { fetchBlog } from '@/fetch/blog'
import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider' import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider'
@ -20,6 +19,7 @@ import { blogToSlides } from '@/utils/dto/blog'
import { fetchHighlights } from '@/fetch/highlights' import { fetchHighlights } from '@/fetch/highlights'
import { EventRow } from '@/components/EventRow/EventRow' import { EventRow } from '@/components/EventRow/EventRow'
import { highlightLink } from '@/utils/dto/highlight' import { highlightLink } from '@/utils/dto/highlight'
import { MassRow } from '@/components/MassTable/MassRow'
const sortWorship = (worship: Worship[]) => { const sortWorship = (worship: Worship[]) => {
const map = new Map<string, Worship[]>(); const map = new Map<string, Worship[]>();
@ -78,18 +78,28 @@ export default async function Home() {
"Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen."} /> "Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen."} />
</Section> </Section>
<Section> <Section paddingBottom={"medium"}>
<Title title={"Unsere Gottesdiensten"} align={"center"}/> <Title
title={"Unsere Gottesdiensten"}
subtitle={"Komm einfach vorbei!"}
align={"center"}
/>
<Section padding={"small"}>
<MassRow>
{worshipPerLocation.map(value => <MassTable key={value[0]} location={value[0]} masses={value[1]} />)}
</MassRow>
</Section>
<Row>
{worshipPerLocation.map(value => <MassTable key={value[0]} location={value[0]} masses={value[1]} />)}
</Row>
</Section> </Section>
<Section>
<Title title={"Aktuelles"} /> { blog && blog.docs.length > 0 &&
<ImageCardSlider slides={blogToSlides(blog?.docs || [])} /> <Section>
</Section> <Title title={"Aktuelles"} />
<ImageCardSlider slides={blogToSlides(blog.docs)} />
</Section>
}
</Container> </Container>
{<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' + {<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' +

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View file

@ -0,0 +1,71 @@
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import beichte from './beichte.jpg'
import { P } from '@/components/Text/Paragraph'
import { Container } from '@/components/Container/Container'
import { Section } from '@/components/Section/Section'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
export default function ConfessionPage() {
return (
<>
<PageHeader
title={'Beichte'}
description={'Im Leben passiert es immer wieder, dass wir Fehler machen und Dinge tun, die unserer Beziehung zu Gott und zu anderen Menschen schaden. In der Beichte hast du die Möglichkeit, diese Fehler Gott anzuvertrauen und um Vergebung zu bitten.'}
image={beichte}
/>
<Container>
<P width={'3/4'}>
Die Beichte, auch Bußsakrament genannt, bietet dir die Möglichkeit, deine Beziehung zu Gott neu auszurichten.
Im Leben kommt es immer wieder vor, dass wir Fehler machen und Dinge tun, die unserer Beziehung zu Gott und zu
unseren Mitmenschen schaden. In der Beichte kannst du Gott diese Fehler anvertrauen und um Vergebung bitten.
Dazu gehört, dass du deine Fehler bereust und dir wünschst, sie nicht begangen zu haben. Indem du deine Sünden
ehrlich und aufrichtig vor dem Priester aussprichst, erhältst du im Namen Gottes die Lossprechung und
Vergebung. Die Beichte ist somit ein Zeichen der Barmherzigkeit Gottes, der uns unsere Sünden vergibt und uns
einen Neuanfang schenkt. Sie ist ein Weg der Versöhnung, nicht nur mit Gott, sondern auch mit uns selbst und
den Menschen, denen wir Unrecht getan haben. Durch die Beichte werden wir von Schuldgefühlen befreit und
finden inneren Frieden.
</P>
<h3>Beichtzeiten</h3>
<P width={'1/2'}>
<strong>St. Christophorus</strong> <br/>
Samstags 17:30 - 17:50
</P>
<P width={'1/2'}>
Für ein persönliches Beichtgesprach außerhalb dieser Zeit können Sie gerne Kontakt mit uns aufnehmen.
</P>
<h3>Weitere Möglichkeiten</h3>
<P width={'1/2'}>
<strong>St. Clemens</strong> <br/>
Stresemannstraße 66 <br/>
09:00 - 12:00 und 15:00 - 18:00
</P>
<P width={'1/2'}>
<strong>Hedwigs Kathedrale</strong> <br />
Bebelplatz, Berlin <br />
Montag bis Donnerstag: 17.15 - 17.45 Uhr<br />
Freitag: 19.15 - 19.45 Uhr<br />
Samstag: 17.00 - 17.45 Uhr<br />
Sonntag: 17.00 - 17.45 Uhr<br />
</P>
</Container>
<Section padding={"medium"}/>
<Section padding={"small"}>
<NextPrevButtons
prev={{
href: "/sakramente/ehe",
text: "Ehe"
}}
next={{
href: "/sakramente/krankensalbung",
text: "Krankensalbung"
}}
/>
</Section>
</>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -0,0 +1,61 @@
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import ehe from './ehe.jpg'
import { Container } from '@/components/Container/Container'
import { P } from '@/components/Text/Paragraph'
import { ContactSection } from '@/compositions/ContactSection/ContactSection'
import { Section } from '@/components/Section/Section'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
export default function MarriagePage() {
return (
<>
<PageHeader
title={'Die Ehe'}
description={'Die Ehe ist ein Sakrament, in dem sich Mann und Frau vor Gott und der Gemeinde das Ja-Wort geben und sich zu einem lebenslangen Bund der Liebe und Treue verpflichten.'}
image={ehe}
/>
<Container>
<P width={'3/4'}>
Die Ehe in der katholischen Kirche ist weit mehr als nur ein Vertrag oder eine romantische Verbindung. Sie ist
ein Sakrament, ein heiliges Zeichen der Liebe Gottes, und ein Bund fürs Leben, der mit Gottes Segen besiegelt
wird. Dieser Segen begleitet die Partner auf ihrem gemeinsamen Lebensweg und schenkt ihnen Gnade für die
Herausforderungen des Alltags.
</P>
<P width={'1/2'}>
In der Ehe versprechen sich die Partner Treue und Liebe in guten wie in schlechten Zeiten. Dieses
Versprechen ist unauflöslich und zeigt die tiefe Verbundenheit, die die Ehepartner miteinander und mit Gott
eingehen.
</P>
<P width={'1/2'}>
Offenheit für das Geschenk des Lebens ist ein weiterer wichtiger Aspekt der Ehe. Ehepartner sind bereit,
Kinder anzunehmen und in ihrem Glauben zu erziehen
</P>
<P width={'1/2'}>
Gemeinsam bilden sie eine Gemeinschaft des Lebens und der Liebe, in der sie sich gegenseitig unterstützen und
füreinander da sind. Die Ehe ist somit nicht nur ein Zeichen der Liebe Gottes zu den Menschen, sondern auch
ein Weg der Heiligung, auf dem sich die Eheleute gegenseitig unterstützen.
</P>
</Container>
<Section padding={"medium"}/>
<Section padding={"small"}>
<NextPrevButtons
prev={{
href: "/sakramente/firmung",
text: "Firmung"
}}
next={{
href: "/sakramente/beichte",
text: "Beichte"
}}
/>
</Section>
<ContactSection
title={"Bei uns heiraten?"}
description={"Gerne! Wir freuen uns, wenn Sie sich für den Bund der Ehe entscheiden. Der Sakrament der Ehe ist ein besonderes Zeichen der Liebe Gottes und die Kirche bietet Ihnen einen feierlichen und würdevollen Rahmen, um sich das Ja-Wort zu geben. Sprechen Sie uns an, wir begleiten Sie gerne bei den Vorbereitungen und gestalten mit Ihnen einen unvergesslichen Traugottesdienst."}
/>
</>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View file

@ -0,0 +1,77 @@
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import eucharist from './eucharist.jpg'
import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider'
import { P } from '@/components/Text/Paragraph'
import { Section } from '@/components/Section/Section'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
const SliderContent = () => {
return (
<div>
<h3>Kann mann eigentlich die Kommunion empfangen als Nicht-Katholik?</h3>
<p>
Grundsätzlich gilt in der katholischen Kirche: Die Kommunion ist katholischen Christen vorbehalten, die in
voller Gemeinschaft mit der Kirche stehen. Das bedeutet, sie sind getauft, haben Erstkommunion empfangen, und teilen den katholischen
Glauben in seiner Gesamtheit.
</p>
</div>
)
}
export default function EucharistPage() {
return (
<>
<PageHeader
title={'Die Eucharistie'}
description={'In der Eucharistie feiern wir das Herzstück unseres Glaubens: die Gegenwart Jesu Christi in Brot und Wein. Sie ist Gedächtnismahl seines letzten Abendmahls, Danksagung für sein Opfer und Quelle der Gemeinschaft mit ihm und untereinander.'}
image={eucharist}
/>
<ContentWithSlider slider={<SliderContent />}>
<P width={'1/2'}>
<i>
&quot;Und er nahm Brot, sprach das Dankgebet, brach es und reichte es ihnen mit den Worten: Das ist mein
Leib, der für euch hingegeben wird. Tut dies zu meinem Gedächtnis!&quot;
<br />
<small>Lukas 22-19</small>
</i>
</P>
<h3></h3>
<P width={'3/4'}>
Im Brot und Wein begegnen wir Jesus Christus auf einzigartige Weise. Er schenkt sich uns selbst, damit wir
an seinem Leben teilhaben und gestärkt werden für unseren Alltag.
Die Eucharistiefeier ist nicht nur ein persönliches Erlebnis, sondern verbindet uns als Gemeinde. Gemeinsam
versammeln wir uns am Tisch des Herrn, um Gottes Wort zu hören, füreinander zu beten und die heilige
Kommunion zu empfangen.
</P>
<P width={'1/2'}>
Jeder Sonntag ist ein kleines Osterfest, an dem wir die Auferstehung Jesu feiern. In der Eucharistie
erfahren wir die Freude und den Frieden, die von ihm ausgehen. Sie schenkt uns Trost in schweren Zeiten und
Hoffnung für die Zukunft.
</P>
<P width={'1/2'}>
Die Eucharistie ist mehr als nur ein Gottesdienst. Sie ist ein Fest des Glaubens, eine Quelle der Kraft und
Hoffnung. Sie hilft uns, im Alltag standzuhalten und uns auf das Leben nach dem Tod vorzubereiten.
</P>
<Section />
</ContentWithSlider>
<Section padding={"small"}>
<NextPrevButtons
prev={{
href: "/sakramente/taufe",
text: "Taufe"
}}
next={{
href: "/sakramente/firmung",
text: "Firmung"
}}
/>
</Section>
</>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View file

@ -0,0 +1,59 @@
import { Section } from '@/components/Section/Section'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import firmung from './firmung.jpg'
import { Container } from '@/components/Container/Container'
import { P } from '@/components/Text/Paragraph'
export default function ConfirmationPage() {
return (
<>
<PageHeader
title={'Firmung'}
description={'Die Firmung ist ein Sakrament, das den Gläubigen die Fülle des Heiligen Geistes verleiht und sie zur aktiven Teilnahme am Leben der Kirche befähigt.'}
image={firmung}
/>
<Container>
<P width={'3/4'}>
Durch die Firmung werden die Gläubigen mit den Gaben des Heiligen Geistes beschenkt, die ihnen helfen, ihren
Glauben zu leben und zu verkünden. Sie werden zu Zeugen Christi in der Welt und übernehmen Verantwortung für
die Weiterentwicklung der Kirche.
</P>
<P width={'1/2'}>
Die Firmung ist ein wichtiger Schritt im Leben eines Christen. Sie ist ein Zeichen der Reife und der
Bereitschaft, sich für den Glauben einzusetzen. Sie ist eine Einladung, sich aktiv an der Gestaltung der
Kirche zu beteiligen und gemeinsam mit anderen Christen den Weg zu Gott zu gehen.
</P>
<h3>Dein Firmheiliger</h3>
<P width={'1/2'}>
Bei der Firmung wählst du dir einen Firmhelligen eine starke Persönlichkeit aus der Geschichte der Kirche,
die dich auf deinem Glaubensweg begleiten soll. Dein Firmheiliger ist so etwas wie ein spiritueller Freund und
Begleiter. Er ist jemand, der Gott in besonderer Weise erlebt hat und uns durch sein Leben ein Vorbild im
Glauben sein kann.
</P>
<P width={'1/2'}>
<em>
Wir bitten dich, Herr, sende ihnen den Heiligen Geist, den Beistand. Gib ihnen den Geist der Weisheit und
der Einsicht, des Rates, der Erkenntnis und der Stärke, den Geist der Frömmigkeit und der Gottesfurcht.
Durch Christus, unsern Herrn.
Amen.
</em>
</P>
</Container>
<Section padding={"medium"}></Section>
<Section padding={'small'}>
<NextPrevButtons
prev={{
href: '/sakramente/eucharistie',
text: 'Eucharistie',
}}
next={{
href: '/sakramente/ehe',
text: 'Ehe',
}}
/>
</Section>
</>
)
}

View file

@ -0,0 +1,45 @@
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import { Container } from '@/components/Container/Container'
import { P } from '@/components/Text/Paragraph'
import { Section } from '@/components/Section/Section'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
export default function Page() {
return (
<>
<PageHeader
title={'Krankensalbung'}
description={'Die Krankensalbung ist ein Sakrament der Heilung und des Trostes, das Menschen in Zeiten von Krankheit und Schwäche gespendet wird. Es ist ein Zeichen der Nähe Gottes und seiner Liebe, die gerade in Zeiten der körperlichen oder seelischen Not besonders spürbar wird.'}
/>
<Container>
<h3>Was passiert bei der Krankensalbung?</h3>
<P width={'3/4'}>
Der Priester salbt die Stirn und die Hände des Kranken mit geweihtem Öl (Chrisam) und spricht dabei Gebete.
Durch diese Salbung und die Gebete wird Gottes Kraft und sein Segen auf den Kranken herabgerufen.
</P>
<P width={'1/2'}>
Die Krankensalbung kann körperliche und seelische Heilung unterstützen und schenkt dem Kranken Kraft, Trost
und die Vergebung der Sünden. So kann er sich in Frieden mit Gott und sich selbst wissen. Die Krankensalbung
kann auch den Kranken auf den Tod vorbereiten und ihm den Übergang ins ewige Leben erleichtern.
</P>
<P width={'1/2'}>
Zögern Sie nicht, sich an Ihren Pfarrer zu wenden, wenn Sie die Krankensalbung empfangen möchten
oder Fragen dazu haben.
</P>
</Container>
<Section padding={"medium"}/>
<Section padding={"small"}>
<NextPrevButtons
prev={{
href: "/sakramente/beichte",
text: "Beichte"
}}
/>
</Section>
</>
)
}

View file

@ -0,0 +1,71 @@
import { PageHeader } from '@/compositions/PageHeader/PageHeader'
import baptism from './taufe.jpg'
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { P } from '@/components/Text/Paragraph'
import { ContactSection } from '@/compositions/ContactSection/ContactSection'
import { NextPrevButtons } from '@/components/NextPrevButtons/NextPrevButtons'
export default function BaptismPage() {
return (
<>
<PageHeader
title={'Taufe'}
description={'Die Taufe ist ein festlicher Start ins Leben als Christ: Sie wäscht symbolisch alles Alte ab, schenkt neues Leben in der Liebe Gottes und macht uns zu einem Teil der großen Gemeinschaft der Kirche. Mit Wasser und dem Versprechen Gottes, das im Namen des Vaters, des Sohnes und des Heiligen Geistes gegeben wird, zeigt die Taufe: Du bist gewollt, geliebt und Teil von etwas Größerem dem Leben mit Christus.'}
image={baptism}
alt={'Taufe'}
/>
<Container>
<P width="3/4">
Die Taufe ist aber nicht nur ein einmaliges Ereignis, sondern der Beginn eines lebenslangen Weges mit Gott.
Sie ist wie ein Samen, der gepflegt und genährt werden muss, damit er wachsen und Frucht bringen kann. Das
geschieht durch das Gebet, den Besuch von Gottesdiensten, die Gemeinschaft mit anderen Christen und das
Lesen der Bibel. So kann der Glaube gefestigt und vertieft werden und der Täufling immer mehr in die Liebe
Gottes hineinwachsen.
</P>
<P width={'1/2'}>
Gerade in der heutigen Zeit, in der viele Menschen nach Halt und Orientierung suchen, ist die Taufe ein
wertvolles Geschenk. Sie schenkt Geborgenheit in der Gemeinschaft der Kirche und eröffnet den Zugang zu
einem Leben voller Sinn und Hoffnung. Die Taufe ist ein Zeichen dafür, dass Gott uns bedingungslos liebt und
uns auf unserem Lebensweg begleiten möchte.
</P>
<h3>Erwachsenentaufe</h3>
<P width={'1/2'}>
Die Taufe ist zwar oft mit Säuglingen und Kindern verbunden, doch die Tür zum Glauben steht in jedem Alter
offen. Auch als Erwachsener kann man sich entscheiden, diesen wichtigen Schritt zu gehen und sich taufen zu
lassen.
</P>
<P width={'1/2'}>
Die Erwachsenentaufe ist ein besonders bewegende Erfahrung. Sie ist ein öffentliches Bekenntnis zum
christlichen Glauben und drückt den Wunsch aus, ein neues Leben mit Gott zu beginnen. In der Regel findet vor
der Taufe ein Glaubenskurs statt, in dem man sich mit den Grundlagen des christlichen Glaubens
auseinandersetzt und sich auf die Taufe vorbereitet. Die Taufe selbst wird feierlich im Gottesdienst vollzogen
und ist ein unvergessliches Erlebnis für den Täufling und die Gemeinde.
</P>
<P width={'1/2'}>
Es ist nie zu spät, sich für ein Leben mit Gott zu entscheiden. Die Erwachsenentaufe bietet die Möglichkeit,
ganz bewusst Ja zu sagen zum Glauben und zur Gemeinschaft der Christen.
</P>
</Container>
<Section padding={"small"}>
<NextPrevButtons
next={{
href: "/sakramente/eucharistie",
text: "Eucharistie"
}}
/>
</Section>
<ContactSection
title={'Wagst du den Schritt?'}
description={'Vielleicht hast du schon lange mit dem Gedanken gespielt, dich taufen zu lassen. Oder die Frage taucht gerade zum ersten Mal in dir auf. Egal, wo du stehst: Die Taufe ist ein Angebot, eine Einladung. Eine Einladung, Teil der großen Familie der Christen zu werden, Gottes Liebe zu erfahren und deinen Glauben gemeinsam mit anderen zu leben. Nimm Kontakt mit uns auf für ein Gespräch!'}
/>
</>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View file

@ -75,6 +75,7 @@ export const Events: CollectionConfig = {
name: 'group', name: 'group',
type: 'relationship', type: 'relationship',
relationTo: 'group', relationTo: 'group',
hasMany: true,
label: { label: {
de: 'Gruppe', de: 'Gruppe',
}, },

View file

@ -28,6 +28,15 @@ export const Groups: CollectionConfig = {
}, },
required: true, required: true,
}, },
{
name: 'slug',
type: 'text',
label: {
de: 'URL slug',
},
required: true,
unique: true
},
{ {
name: 'description', name: 'description',
type: 'textarea', type: 'textarea',

View file

@ -1,6 +1,3 @@
import arrow from './Arrow.svg'
import Image from 'next/image'
type ArrowProps = { type ArrowProps = {
direction: 'left' | 'right', direction: 'left' | 'right',
onClick?: () => void onClick?: () => void
@ -8,11 +5,17 @@ type ArrowProps = {
export const Arrow = ({ direction, onClick }: ArrowProps) => { export const Arrow = ({ direction, onClick }: ArrowProps) => {
return ( return (
<Image <svg
onClick={onClick} width="21"
src={arrow} height="56"
alt={''} viewBox="0 0 21 56"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ transform: `rotate(${direction === 'left' ? 0 : 180}deg)` }} style={{ transform: `rotate(${direction === 'left' ? 0 : 180}deg)` }}
/> stroke="#426156"
>
<path d="M19 1.53406L2.68974 27.0243C2.26927 27.6814 2.26927 28.5231 2.68974 29.1802L19 54.6704"
strokeWidth="3" />
</svg>
) )
} }

View file

@ -1,13 +1,19 @@
import { Logo } from '@/components/Logo/Logo' import { Logo } from '@/components/Logo/Logo'
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import classNames from 'classnames'
import { faustina } from '@/app/fonts'
export const Banner = () => { export const Banner = () => {
return ( return (
<div className={styles.banner}> <div className={styles.banner}>
<div className={styles.logo}> <div className={styles.logo}>
<Logo color={"#ffffff"} /> <Logo color={"#ffffff33"} height={200} />
</div>
<div className={classNames(faustina.className, styles.nameContainer)}>
<div className={styles.catholic}>Katholische Pfarrei</div>
<div className={styles.name}>Heilige Drei Könige</div>
<div className={styles.location}>Nord-Neukölln</div>
</div> </div>
</div> </div>
) )
} }

View file

@ -1,9 +1,11 @@
.banner { .banner {
position: relative; position: relative;
height: 634px; height: 634px;
background-image: url("banner.jpg"); background-color: #728f8d;
background-image: url("banner2.jpg");
background-size: cover; background-size: cover;
background-position: top center; background-position: center center;
opacity: 0.7;
} }
.logo { .logo {
@ -11,3 +13,53 @@
bottom: 20px; bottom: 20px;
left: 30px; left: 30px;
} }
.nameContainer {
opacity: 1;
color: #ececec;
position: absolute;
bottom: 50px;
right: 0;
width: 50vw;
line-height: 1.7em;
}
.catholic {
font-size: 40px;
font-weight: 700;
margin-bottom: 20px;
}
.name {
font-size: 90px;
font-weight: 700;
margin-bottom: 30px;
line-height: 1em;
}
.location {
font-size: 40px;
font-weight: 700;
}
@media screen and (max-width: 576px) {
.logo {
left: -20px;
bottom: -30px;
}
.nameContainer {
padding: 20px;
position: relative;
top: 100px;
}
.name {
font-size: 50px;
line-height: 0.8em;
}
.catholic, .location {
font-size: 30px;
}
}

View file

@ -9,6 +9,6 @@ type ChurchCardProps = {
export const ChurchCard = ({church, backgroundColor, width = 286 }: ChurchCardProps) => { export const ChurchCard = ({church, backgroundColor, width = 286 }: ChurchCardProps) => {
return <div className={styles.card} style={{backgroundColor, width, height: width}}> return <div className={styles.card} style={{backgroundColor, width, height: width}}>
<ChurchIcon church={church} style={"outline"} stroke={0.5} color={"#000000"} /> <ChurchIcon church={church} style={"outline"} stroke={0.8} color={"#555555"} />
</div> </div>
} }

View file

@ -2,8 +2,7 @@
.card { .card {
border-radius: $border-radius; border-radius: $border-radius;
border: 1px solid $shade2; box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
box-shadow: 3px 7px 26px 0 rgba(0, 0, 0, 0.15);
} }
.card svg { .card svg {

View file

@ -2,7 +2,7 @@ import styles from './styles.module.scss'
import classNames from 'classnames' import classNames from 'classnames'
type ContainerProps = { type ContainerProps = {
children: JSX.Element | JSX.Element[], children: React.ReactNode,
flex?: boolean flex?: boolean
position?: "center" | "left" | "right", position?: "center" | "left" | "right",
textAlign?: "left" | "center" | "right", textAlign?: "left" | "center" | "right",

View file

@ -2,9 +2,9 @@ import Img, { StaticImageData } from 'next/image'
type ImageProps = { type ImageProps = {
src: string | StaticImageData, src: string | StaticImageData,
width: number, width?: number,
height: number, height?: number,
alt: string, alt?: string,
} }
export const Image = ({src, width, height, alt}: ImageProps) => { export const Image = ({src, width, height, alt}: ImageProps) => {
@ -14,7 +14,7 @@ export const Image = ({src, width, height, alt}: ImageProps) => {
src={src} src={src}
width={width} width={width}
height={height} height={height}
alt={alt} alt={alt || ""}
/> />
) )
} }

View file

@ -0,0 +1,13 @@
import styles from "./massrow.module.scss"
type MassRowProps = {
children: React.ReactNode;
}
export const MassRow = ({children}: MassRowProps) => {
return (
<div className={styles.row}>
{children}
</div>
)
}

View file

@ -1,9 +1,10 @@
import { MassTableRow } from '@/components/MassTable/MassTableRow' import { MassTableRow } from '@/components/MassTable/MassTableRow'
import styles from './styles.module.css' import styles from './styles.module.scss'
import { faustina } from '@/app/fonts' import { faustina } from '@/app/fonts'
import { Worship } from '@/payload-types' import { Worship } from '@/payload-types'
import { ChurchCard } from '@/components/ChurchCard/ChurchCard' import { ChurchCard } from '@/components/ChurchCard/ChurchCard'
import { church } from '@/utils/church' import { church } from '@/utils/church'
import classNames from 'classnames'
type MassTableProps = { type MassTableProps = {
location: string location: string
@ -14,12 +15,12 @@ export const MassTable = ({ location, masses }: MassTableProps) => {
return ( return (
<div className={styles.table}> <div className={styles.table}>
<h3 className={faustina.className}> <div className={classNames(styles.location)}>
{location} {location}
</h3> </div>
<div className={styles.church}> <div className={styles.church}>
<ChurchCard church={church(location)} backgroundColor={"#fffff"} width={200} /> <ChurchCard church={church(location)} backgroundColor={"#E3E9E8"} width={200} />
</div> </div>
{masses.map((mass) => ( {masses.map((mass) => (

View file

@ -1,6 +1,6 @@
'use client' 'use client'
import styles from './styles.module.css' import styles from './styles.module.scss'
import Image from 'next/image' import Image from 'next/image'
import family from './family.svg' import family from './family.svg'
import bible from './bible.svg' import bible from './bible.svg'

View file

@ -0,0 +1,6 @@
.row {
display: flex;
gap: 30px;
justify-content: center;
flex-wrap: wrap;
}

View file

@ -1,3 +1,5 @@
@import "template.scss";
.row { .row {
display: flex; display: flex;
gap: 10px; gap: 10px;
@ -12,6 +14,13 @@
color: inherit; color: inherit;
} }
.location {
font-size: 22px;
font-weight: bold;
margin-bottom: 10px;
color: $shade1;
}
.cancelled { .cancelled {
text-decoration: line-through; text-decoration: line-through;
} }

View file

@ -0,0 +1,40 @@
import { Meta, StoryObj } from '@storybook/react'
import { NextPrevButtons } from './NextPrevButtons'
const meta: Meta<typeof NextPrevButtons> = {
component: NextPrevButtons,
}
type Story = StoryObj<typeof NextPrevButtons>;
export default meta
export const Default: Story = {
args: {
prev: {
text: 'Blog 1',
href: 'https://prev'
},
next: {
text: 'Blog 3',
href: 'https://next'
}
},
}
export const NoPrev: Story = {
args: {
next: {
text: 'Blog 3',
href: 'https://next'
}
},
}
export const NoNext: Story = {
args: {
prev: {
text: 'Blog 1',
href: 'https://prev'
}
},
}

View file

@ -0,0 +1,41 @@
import styles from "./styles.module.scss"
import { Arrow } from '@/components/Arrow/Arrow'
import Link from 'next/link'
import classNames from 'classnames'
type NextPrevButtonsProps = {
next?: {
text: string,
href: string
}
prev?: {
text: string,
href: string
}
}
export const NextPrevButtons = (
{
next,
prev
}: NextPrevButtonsProps) => {
return (
<div className={classNames({
[styles.container]: true,
[styles.noPrev]: typeof prev === 'undefined'
})}>
{prev &&
<Link href={prev.href} className={styles.arrow}>
<Arrow direction={"left"} />
{prev.text}
</Link>
}
{next &&
<Link href={next.href} className={classNames(styles.arrow, styles.arrowRight)}>
{next.text}
<Arrow direction={"right"} />
</Link>
}
</div>
)
}

View file

@ -0,0 +1,40 @@
@import "template.scss";
.container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding: 0 30px;
}
.noPrev {
justify-content: flex-end;
}
.arrow {
display: inline-flex;
gap: 10px;
align-items: center;
text-decoration: none;
font-size: 22px;
font-weight: bold;
color: $base-color;
}
.arrow:hover {
color: $shade1;
}
.arrow:hover svg {
stroke: $shade1;
}
@media screen and (max-width: 576px) {
.container {
gap: 20px;
}
.arrowRight {
margin-left: auto;
}
}

View file

@ -0,0 +1,38 @@
"use client"
import { randomPrayer } from '@/utils/randomPrayer'
import { useCallback, useEffect, useState } from 'react'
export const RandomPrayer = () => {
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | undefined>(undefined)
const [prayer, setPrayer] = useState("")
// Set interval to change the prayer
const newPrayerEveryInterval = useCallback(() => {
const i = setInterval(() => {
setPrayer(randomPrayer())
}, 60 * 1000)
setIntervalId(i)
}, [setPrayer, setIntervalId])
// Set new random prayer and reset timer interval
const newPrayer = useCallback(() => {
clearInterval(intervalId)
setPrayer(randomPrayer())
newPrayerEveryInterval()
}, [intervalId, setPrayer, newPrayerEveryInterval])
// Every 30 seconds set a new prayer
useEffect(() => {
setPrayer(randomPrayer())
newPrayerEveryInterval()
return () => clearInterval(intervalId)
}, [newPrayerEveryInterval])
return (
<p onClick={newPrayer} style={{cursor: "pointer"}}>
{prayer}
</p>
)
}

View file

@ -1,7 +1,7 @@
"use client" "use client"
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { useCallback, useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import classNames from 'classnames' import classNames from 'classnames'
type SideSliderProps = { type SideSliderProps = {

View file

@ -43,6 +43,7 @@ $iconSize: 150px;
} }
.padding { .padding {
width: 400px;
padding: 30px 0; padding: 30px 0;
} }
@ -51,6 +52,10 @@ $iconSize: 150px;
display: none; display: none;
} }
.padding {
width: 100%;
}
.container { .container {
right: 0; right: 0;
width: 100%; width: 100%;
@ -58,7 +63,7 @@ $iconSize: 150px;
.content { .content {
border-radius: 0; border-radius: 0;
width: 100%; width: auto;
position: relative; position: relative;
left: 0; left: 0;
padding: 0 20px; padding: 0 20px;

View file

@ -0,0 +1,18 @@
import styles from "./html.module.scss"
import classNames from 'classnames'
type PProps = {
width: "1/2" | "3/4",
children?: React.ReactNode
}
export const P = ({width, children}: PProps) => {
return (
<p className={classNames({
[styles.half]: width === "1/2",
[styles.threeFourth]: width === "3/4"
})}>
{children}
</p>
)
}

View file

@ -1,5 +1,6 @@
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { SideSlider } from '@/components/SideSlider/SideSlider' import { SideSlider } from '@/components/SideSlider/SideSlider'
import { Container } from '@/components/Container/Container'
type ContentWithSliderProps = { type ContentWithSliderProps = {
children: React.ReactNode; children: React.ReactNode;
@ -9,9 +10,9 @@ type ContentWithSliderProps = {
export const ContentWithSlider = ({children, slider}: ContentWithSliderProps) => { export const ContentWithSlider = ({children, slider}: ContentWithSliderProps) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> <Container>
{children} {children}
</div> </Container>
<div className={styles.slider}> <div className={styles.slider}>
<SideSlider> <SideSlider>
{slider} {slider}

View file

@ -2,10 +2,6 @@
position: relative; position: relative;
} }
.content {
max-width: 1100px;
}
.slider { .slider {
position: absolute; position: absolute;
right: 0; right: 0;
@ -20,13 +16,10 @@
} }
@media screen and (max-width: 700px) { @media screen and (max-width: 700px) {
.content {
max-width: 100%;
}
.slider { .slider {
position: relative; position: relative;
top: 20px; top: 20px;
transform: none; transform: none;
margin-bottom: 60px;
} }
} }

View file

@ -4,37 +4,48 @@ import { Logo } from '@/components/Logo/Logo'
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { Row } from '@/components/Flex/Row' import { Row } from '@/components/Flex/Row'
import { Col } from '@/components/Flex/Col' import { Col } from '@/components/Flex/Col'
import { RandomPrayer } from '@/components/RandomPrayer/RandomPrayer'
export const Footer = () => { export const Footer = () => {
return ( return (
<Section backgroundColor="soft"> <div className={styles.container}>
<Container> <Section backgroundColor="soft">
<Row> <Container>
<Col> <Row alignItems={"center"}>
<Logo <Col>
color={"#ffffff"} <Logo
textColor={"#426156"} color={"#ffffff"}
withText={true} textColor={"#426156"}
height={120} withText={true}
/> height={100}
</Col> />
</Col>
<Col> <Col>
<div className={styles.container}> <Row>
<p> <Col>
Briesestraße 17 <br /> <p>
12053 Berlin-Neukölln <strong>Navigation</strong>
</p> </p>
<ul className={styles.list}>
<p> <li>Kontakt</li>
T: 030-6889120 <br /> <li>Gottesdiensten</li>
E: pfarrer@dreikoenige.berlin <li>Impressum</li>
</p> <li>Datenschutz</li>
</div> </ul>
</Col> </Col>
</Row> <Col>
</Container> <p>
</Section> <strong>Stoßgebet</strong>
</p>
<RandomPrayer />
</Col>
</Row>
</Col>
</Row>
</Container>
</Section>
</div>
); );
} }

View file

@ -7,4 +7,9 @@
.container { .container {
color: $base-color; color: $base-color;
line-height: 130%;
}
.list {
list-style: square;
} }

View file

@ -5,6 +5,7 @@ import Image, { StaticImageData } from 'next/image'
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import classNames from 'classnames' import classNames from 'classnames'
import { Row } from '@/components/Flex/Row' import { Row } from '@/components/Flex/Row'
import { TextDiv } from '@/components/Text/TextDiv'
type ImageWithTextProps = { type ImageWithTextProps = {
backgroundColor?: BackgroundColor, backgroundColor?: BackgroundColor,
@ -28,9 +29,7 @@ export const ImageWithText = ({backgroundColor, title, image, text, link}: Image
<Image className={styles.imageMobile} width={500} height={500} src={image} objectFit={"cover"} alt={""} /> <Image className={styles.imageMobile} width={500} height={500} src={image} objectFit={"cover"} alt={""} />
<div> <TextDiv text={text} />
{text}
</div>
{link && {link &&
<div className={styles.right}> <div className={styles.right}>

View file

@ -0,0 +1,42 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { Title } from '@/components/Title/Title'
import { HR } from '@/components/HorizontalRule/HorizontalRule'
import { TextDiv } from '@/components/Text/TextDiv'
import Image from 'next/image'
import { StaticImageData } from 'next/image'
import styles from "./styles.module.scss"
type PageHeaderProps = {
title: string,
description: string,
image?: StaticImageData
alt?: string
}
export const PageHeader = ({ title, description, image, alt }: PageHeaderProps) => {
return (
<Section paddingBottom={"small"}>
<Container>
<Title title={title} />
<strong>
<TextDiv text={description} />
</strong>
</Container>
<HR />
<Container>
{image &&
<Image
unoptimized={true}
className={styles.image}
src={image}
alt={alt || ""}
/>
}
</Container>
</Section>
)
}

View file

@ -0,0 +1,7 @@
@import "template.scss";
.image {
max-width: 100%;
height: auto;
border-radius: $border-radius;
}

View file

@ -6,7 +6,7 @@ import { Event } from '@/payload-types'
* Fetch a list of events * Fetch a list of events
* *
*/ */
export async function fetchEvents(parishId: string | undefined): Promise<PaginatedDocs<Event> | undefined> { export async function fetchEvents(parishId: string | undefined, groupId?: string): Promise<PaginatedDocs<Event> | undefined> {
const date = new Date() const date = new Date()
const query: any = { const query: any = {
@ -27,6 +27,15 @@ export async function fetchEvents(parishId: string | undefined): Promise<Paginat
}) })
} }
if (groupId) {
query.and.push({
"group": {
equals: groupId
}
})
}
const stringifiedQuery = stringify( const stringifiedQuery = stringify(
{ {
sort: "date", sort: "date",

22
src/fetch/group.ts Normal file
View file

@ -0,0 +1,22 @@
import { stringify } from 'qs-esm'
import { PaginatedDocs } from 'payload'
import { Group } from '@/payload-types'
export async function fetchGroup(slug: string): Promise<PaginatedDocs<Group> | undefined> {
const query = {
slug: {
equals: slug,
},
}
const stringifiedQuery = stringify(
{
where: query,
},
{ addQueryPrefix: true },
)
const res = await fetch(`http://localhost:3000/api/group${stringifiedQuery}`)
if (!res.ok) return undefined
return res.json()
}

View file

@ -335,7 +335,7 @@ export interface Event {
date: string; date: string;
location: string; location: string;
parish?: (string | Parish)[] | null; parish?: (string | Parish)[] | null;
group?: (string | null) | Group; group?: (string | Group)[] | null;
shortDescription: string; shortDescription: string;
description: { description: {
root: { root: {
@ -364,6 +364,7 @@ export interface Group {
id: string; id: string;
photo?: (string | null) | Media; photo?: (string | null) | Media;
name: string; name: string;
slug: string;
description: string; description: string;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@ -708,6 +709,7 @@ export interface EventSelect<T extends boolean = true> {
export interface GroupSelect<T extends boolean = true> { export interface GroupSelect<T extends boolean = true> {
photo?: T; photo?: T;
name?: T; name?: T;
slug?: T;
description?: T; description?: T;
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;

26
src/utils/randomPrayer.ts Normal file
View file

@ -0,0 +1,26 @@
const PRAYERS = [
"Herr, erbarme Dich meiner.",
"Herr Jesus Christus, erbarme dich meiner.",
"Ehre sei dem Vater, und dem Sohn und dem Heiligen Geist.",
"Preiset den Herrn zu aller Zeit, denn er ist gut.",
"Mein Herr und mein Gott.",
"Herr, dir in die Hände sei Anfang und Ende, sei alles gelegt.",
"Herr, du weißt alles; du weißt, dass ich dich liebe.",
"Der Herr ist mein Licht und mein Heil, vor wem sollte ich mich fürchten?",
"Herr, dein Wille geschehe.",
"Ich glaube, Herr; hilf meinem Unglauben.",
"O Gott, komm mir zu Hilfe. Herr, eile, mir zu helfen.",
"Jesus, ich vertraue auf Dich.",
"Gegrüßet seist du, Maria, voll der Gnade, der Herr ist mit dir.",
"Gelobt sei Jesus Christus - in Ewigkeit. Amen.",
"Maria mit dem Kinde lieb, uns allen deinen Segen gib.",
"Aus der Tiefe rufe ich Herr zu dir. Herr, höre meine Stimme.",
"Durch sein schmerzhaftes Leiden, habe Erbarmen mit uns und mit der ganzen Welt.",
"Heiliger Gott, habe Erbarmen mit uns und mit der ganzen Welt.",
"Heilige Maria, Mutter Gottes, bitte für uns Sünder.",
"Gepriesen seist du, Herr. Lehre mich deine Gesetze."
]
export const randomPrayer = (): string => {
return PRAYERS[Math.floor(Math.random()*PRAYERS.length)]
}