feature: Worship page

This commit is contained in:
Benno Tielen 2024-12-17 14:45:18 +01:00
parent ef913bc500
commit a6a20fb7cc
18 changed files with 261 additions and 160 deletions

3
.gitignore vendored
View file

@ -39,6 +39,9 @@ yarn-error.log*
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
# storybook
/storybook-static
.env .env
/media /media

View file

@ -1,13 +1,6 @@
import { Section } from '@/components/Section/Section'
import { Container } from '@/components/Container/Container'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import { Worship } from '@/payload-types' import { Worship as WorshipType } from '@/payload-types'
import { Title } from '@/components/Title/Title' import { Worship } from '@/pageComponents/Worship/Worship'
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}>}) { export default async function WorshipPage({ params }: { params: Promise<{id: string}>}) {
@ -17,54 +10,9 @@ export default async function WorshipPage({ params }: { params: Promise<{id: str
notFound() notFound()
} }
const event = await res.json() as Worship; const worship = await res.json() as WorshipType;
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 ( return (
<> <Worship worship={worship} />
<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

@ -38,6 +38,8 @@ export const Events: CollectionConfig = {
admin: { admin: {
date: { date: {
pickerAppearance: 'dayAndTime', pickerAppearance: 'dayAndTime',
timeIntervals: 15,
timeFormat: 'HH:mm'
}, },
}, },
}, },

View file

@ -0,0 +1,21 @@
import { Meta, StoryObj } from '@storybook/react'
import { Cross } from './Cross'
const meta: Meta<typeof Cross> = {
component: Cross,
}
type Story = StoryObj<typeof Cross>;
export default meta
export const Default: Story = {
args: {
schema: "base"
},
}
export const Contrast: Story = {
args: {
schema: "contrast"
},
}

View file

@ -0,0 +1,25 @@
import styles from "./styles.module.scss"
import classNames from 'classnames'
type CrossProps = {
schema?: "base" | "contrast"
}
export const Cross = ({schema = "base"}: CrossProps) => {
const style = classNames({
[styles.crossContrast]: schema === "contrast",
[styles.crossBase]: schema === "base",
})
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 39.69 49.61"
className={style}
height={49.61}
>
<polygon
points="33.89 14.81 19.89 14.81 19.89 0.8 18.89 0.8 18.89 14.81 4.89 14.81 4.89 15.81 18.89 15.81 18.89 48.8 19.89 48.8 19.89 15.81 33.89 15.81 33.89 14.81" />
</svg>
)
}

View file

@ -0,0 +1,9 @@
@import "template.scss";
.crossContrast {
fill: $contrast-color;
}
.crossBase {
fill: $base-color;
}

View file

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

View file

@ -1,31 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 712 B

View file

@ -1,7 +0,0 @@
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 39.69 49.61">
<defs>
<style>.cls-1{fill:#426156;}</style>
</defs>
<polygon class="cls-1"
points="33.89 14.81 19.89 14.81 19.89 0.8 18.89 0.8 18.89 14.81 4.89 14.81 4.89 15.81 18.89 15.81 18.89 48.8 19.89 48.8 19.89 15.81 33.89 15.81 33.89 14.81"/>
</svg>

Before

Width:  |  Height:  |  Size: 373 B

View file

@ -1,12 +1,10 @@
import styles from './styles.module.css' import styles from './styles.module.scss'
import { Container } from '@/components/Container/Container' import { Container } from '@/components/Container/Container'
import classNames from 'classnames' import classNames from 'classnames'
import { faustina } from '@/app/fonts' import { faustina } from '@/app/fonts'
import Image from 'next/image'
import quote from './quotes.svg'
type TestimonyProps = { type TestimonyProps = {
name: string name?: string
testimony: string testimony: string
occupation?: string occupation?: string
} }
@ -16,18 +14,14 @@ export const Testimony = ({ name, testimony, occupation }: TestimonyProps) => {
<Container> <Container>
<div className={styles.testimony}> <div className={styles.testimony}>
<div className={styles.container}> <div className={styles.container}>
<Image
src={quote}
alt={'Quote'}
width={100}
className={classNames(styles.quote, styles.slidein)}
/>
<p className={classNames(styles.testimonyText, faustina.className)}> <p className={classNames(styles.testimonyText, faustina.className)}>
{testimony} {testimony}
</p> </p>
<p className={styles.name}> {typeof name === 'string' &&
{name} {occupation && <>- {occupation}</>} <p className={styles.name}>
</p> {name} {occupation && <>- {occupation}</>}
</p>
}
</div> </div>
</div> </div>
</Container> </Container>

View file

@ -1,47 +0,0 @@
.testimony {
display: flex;
padding: 80px 0 50px 0;
gap: 40px;
align-items: center;
}
.testimonyText {
font-style: italic;
line-height: 1.7em;
font-size: 1.1em;
}
.container {
position: relative;
}
.person {
height: 150px;
width: 150px;
background-color: #f6dc66;
flex-shrink: 0;
}
.name {
text-align: right;
font-weight: bold;
}
.quote {
position: absolute;
top: -10px;
left: -10px;
opacity: 0.5;
z-index: -1;
}
.slidein {
transform: translateX(-100%);
animation: slide-in 0.3s forwards;
}
@keyframes slide-in {
100% {
transform: translateX(0%);
}
}

View file

@ -0,0 +1,44 @@
@import "template.scss";
.testimony {
display: flex;
padding: 80px 0 50px 0;
gap: 40px;
align-items: center;
}
.testimonyText {
font-size: 33px;
color: $base-color;
text-align: center;
position: relative;
}
.testimonyText::after {
content: '';
color: $contrast-color;
font-size: 120px;
position: absolute;
bottom: -20px;
line-height: 0;
}
.testimonyText::before {
content: '';
color: $contrast-color;
font-size: 120px;
position: relative;
line-height: 0;
top: 40px;
}
.container {
width: 870px;
margin: 0 auto;
}
.name {
text-align: right;
font-weight: bold;
color: $shade1;
}

View file

@ -53,7 +53,8 @@ export const fetchWorship = async (args?: FetchWorshipArgs): Promise<PaginatedDo
type: true, type: true,
date: true, date: true,
cancelled: true, cancelled: true,
location: true location: true,
title: true,
}, },
limit: 15 limit: 15
}, },

View file

@ -7,6 +7,7 @@ export const useCompactDate = (date: string) => {
/** /**
* Return date in user friendly format * Return date in user friendly format
* in format DD.MM.YYYY
* *
*/ */
export const useDate = (date: string) => { export const useDate = (date: string) => {

View file

@ -0,0 +1,31 @@
import { Meta, StoryObj } from '@storybook/react'
import { Worship } from './Worship'
const meta: Meta<typeof Worship> = {
component: Worship,
}
type Story = StoryObj<typeof Worship>;
export default meta
export const Default: Story = {
args: {
worship: {
id: "",
date: "2024-12-16T10:42:16+0000",
location: {
id: "",
name: "St. Clara",
address: "Schudomastr 12\n12345 Berlin",
createdAt: "",
updatedAt: ""
} ,
type: 'MASS',
title: null,
cancelled: false,
celebrant: "Pfr. M. Mustermann",
updatedAt: "",
createdAt: "",
}
},
}

View file

@ -0,0 +1,85 @@
import styles from "./styles.module.scss"
import { Worship as WorshipType } from '@/payload-types'
import { useDate } from '@/hooks/useCompactDate'
import { Section } from '@/components/Section/Section'
import { Title } from '@/components/Title/Title'
import { liturgicalDayName } from '@/hooks/liturgicalDayName'
import { EventExcerpt, EventExcerptRow } from '@/components/EventExcerpt/EventExcerpt'
import { transformCategory } from '@/utils/dto/worship'
import { TextDiv } from '@/components/Text/TextDiv'
import { church } from '@/utils/church'
import { ChurchIcon } from '@/components/ChurchIcon/ChurchIcon'
import { Cross } from '@/components/Cross/Cross'
import { Testimony } from '@/components/Testimony/Testimony'
type WorshipPageProps = {
worship: WorshipType
}
export const Worship = ({ worship }: WorshipPageProps) => {
const date = new Date(worship.date);
const day = date.toLocaleDateString('de-DE', {weekday: 'long'} )
const localeDate = useDate(worship.date)
const liturgicalDay = worship.liturgicalDay ? worship.liturgicalDay : liturgicalDayName(worship.date);
const what = worship.title ? worship.title : transformCategory(worship.type);
const time = date.toLocaleTimeString("de-DE", { timeStyle: "short" });
return (
<>
<Section>
<div className={styles.textCenter}>
<Cross schema={"contrast"} />
</div>
<Title
title={`${day}, ${localeDate}`}
size={'xl'}
color={'contrast'}
align={"center"}
/>
<p className={styles.liturgicalDay}>
{liturgicalDay}
</p>
</Section>
<Section padding={"medium"}>
<EventExcerpt>
<EventExcerptRow label={"Was:"}>
{what}
</EventExcerptRow>
<EventExcerptRow label={"Wo:"}>
{ typeof worship.location == "object" &&
<TextDiv text={`${worship.location.name}\n${worship.location.address}\n${time} Uhr`} />
}
</EventExcerptRow>
{ worship.celebrant &&
<EventExcerptRow label={"Zelebrant:"}>
{worship.celebrant}
</EventExcerptRow>
}
{ typeof worship.description === "string" && worship.description != "" &&
<EventExcerptRow label={"Hinweise:"}>
<TextDiv text={worship.description} />
</EventExcerptRow>
}
<div>
<div className={styles.church}>
<ChurchIcon
church={church(typeof worship.location == "object" ? worship.location.name : "clara")}
color={"#426156"}
style={"filled"}
stroke={3}
/>
</div>
</div>
</EventExcerpt>
</Section>
<Section>
<Testimony testimony={"Du bringst nichts mit hinein, Du nimmst nichts mit hinausLass eine goldene Spur zurück, Im alten Erdenhaus"} />
</Section>
</>
)
}

View file

@ -0,0 +1,23 @@
@import "template.scss";
.textCenter {
text-align: center;
}
.liturgicalDay {
text-align: center;
margin-top: -20px;
}
.church {
height: 144px;
width: 144px;
margin: 40px auto;
border-radius: $border-radius;
border: 1px solid $base-color;
}
.church svg {
width: 100%;
height: 100%;
}

View file

@ -22,7 +22,7 @@ export const tranformWorship = (worship: Worship[]): (EventRowProps & {id: strin
return worship.map(w => { return worship.map(w => {
return { return {
id: w.id, id: w.id,
title: transformCategory(w.type), title: typeof w.title === "string" ? w.title : transformCategory(w.type),
date: w.date, date: w.date,
href: `/gottesdienst/${w.id}`, href: `/gottesdienst/${w.id}`,
location: typeof w.location === 'string' ? w.location : w.location.name, location: typeof w.location === 'string' ? w.location : w.location.name,