style: prettier

This commit is contained in:
Benno Tielen 2024-08-22 11:04:22 +02:00
parent 6036af1099
commit b3262d8700
68 changed files with 1047 additions and 1007 deletions

View file

@ -5,9 +5,4 @@ module.exports = {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
plugins: ['eslint-plugin-prettier'],
rules: {
"prefer-const": "error",
"prettier": "error"
}
}

View file

@ -6,10 +6,10 @@ const preview: Preview = {
(Story) => {
return (
<div className={lato.className}>
<Story/>
<Story />
</div>
)
}
},
],
parameters: {
controls: {

View file

@ -14,9 +14,13 @@ type Args = {
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
export const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams })
export default NotFound

View file

@ -14,9 +14,13 @@ type Args = {
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
export const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams })
export default Page

View file

@ -1,7 +1,13 @@
/* 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 { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
} from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)

View file

@ -11,6 +11,8 @@ type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
const Layout = ({ children }: Args) => (
<RootLayout config={configPromise}>{children}</RootLayout>
)
export default Layout

View file

@ -1,29 +1,31 @@
"use client"
'use client'
import {Menu} from "@/components/Menu/Menu";
import {BannerText} from "@/components/BannerText/BannerText";
import {HomeBanner, HomeBannerHandle} from "@/components/HomeBanner/HomeBanner";
import {useRef} from "react";
import { Menu } from '@/components/Menu/Menu'
import { BannerText } from '@/components/BannerText/BannerText'
import {
HomeBanner,
HomeBannerHandle,
} from '@/components/HomeBanner/HomeBanner'
import { useRef } from 'react'
import { Worship } from '@/payload-types'
type BannerWithMenuProps = {
nextMass?: Worship
nextMass?: Worship
}
export const BannerWithMenu = ({nextMass}: BannerWithMenuProps) => {
export const BannerWithMenu = ({ nextMass }: BannerWithMenuProps) => {
const bannerRef = useRef<HomeBannerHandle>(null)
const bannerRef = useRef<HomeBannerHandle>(null);
function addThreeNewStars() {
bannerRef.current?.newStar()
bannerRef.current?.newStar()
bannerRef.current?.newStar()
}
function addThreeNewStars() {
bannerRef.current?.newStar();
bannerRef.current?.newStar();
bannerRef.current?.newStar();
}
return (
<HomeBanner stars={40} ref={bannerRef}>
<Menu starClick={addThreeNewStars} nextMass={nextMass} />
<BannerText/>
</HomeBanner>
)
return (
<HomeBanner stars={40} ref={bannerRef}>
<Menu starClick={addThreeNewStars} nextMass={nextMass} />
<BannerText />
</HomeBanner>
)
}

View file

@ -1,12 +1,12 @@
import { Faustina, Lato } from 'next/font/google';
import { Faustina, Lato } from 'next/font/google'
export const faustina = Faustina({
subsets: ['latin'],
display: "swap"
display: 'swap',
})
export const lato = Lato({
subsets: ['latin'],
weight: ["400"],
display: 'swap'
weight: ['400'],
display: 'swap',
})

View file

@ -1,13 +1,13 @@
.mass {
padding-top: 30px;
padding-bottom: 300px;
padding-top: 30px;
padding-bottom: 300px;
}
.mass h2 {
text-align: center;
text-align: center;
}
.table {
display: flex;
justify-content: center;
display: flex;
justify-content: center;
}

View file

@ -1,20 +1,19 @@
import type { Metadata } from "next";
import {lato} from "./fonts";
import type { Metadata } from 'next'
import { lato } from './fonts'
export const metadata: Metadata = {
title: "Katholische Pfarrei Heilige drei Könige Berlin",
description: "Generated by create next app",
};
title: 'Katholische Pfarrei Heilige drei Könige Berlin',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en" className={lato.className}>
<body>{children}</body>
<body>{children}</body>
</html>
);
)
}

View file

@ -4,7 +4,11 @@ export default function Custom404() {
<h1>404 - Seite nicht gefunden</h1>
<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.
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>
</>
)

View file

@ -1,7 +1,7 @@
import configPromise from "@payload-config";
import configPromise from '@payload-config'
import { BannerWithMenu } from '@/app/BannerWithMenu'
import { Worship } from '@/payload-types'
import styles from "./home.module.css"
import styles from './home.module.css'
import { MassTable } from '@/components/MassTable/MassTable'
import { getPayloadHMR } from '@payloadcms/next/utilities'
@ -9,13 +9,12 @@ const extractWorshipHours = (worships: Worship[]) => {
let worshipByDate = new Map<string, Worship[]>()
for (let worship of worships) {
const date = worship.date.substring(0, 10);
const date = worship.date.substring(0, 10)
if (worshipByDate.has(date)) {
worshipByDate.get(date)?.push(worship);
worshipByDate.get(date)?.push(worship)
} else {
worshipByDate.set(date, [worship]);
worshipByDate.set(date, [worship])
}
}
@ -23,29 +22,28 @@ const extractWorshipHours = (worships: Worship[]) => {
}
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 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)
}
greater_than_equal: today.toISOString().substring(0, 10),
},
},
{
date: {
less_than: nextWeek.toISOString().substring(0, 10)
}
less_than: nextWeek.toISOString().substring(0, 10),
},
},
]
],
},
limit: 30,
sort: 'date'
});
sort: 'date',
})
const nextMass = await payload.find({
collection: 'worship',
@ -53,33 +51,34 @@ export default async function Home() {
and: [
{
date: {
greater_than_equal: today.toISOString().substring(0, 10)
}
greater_than_equal: today.toISOString().substring(0, 10),
},
},
{
cancelled: {
equals: false
}
}
]
equals: false,
},
},
],
},
limit: 1,
sort: "date"
sort: 'date',
})
const worshipByDate = [...extractWorshipHours(worship.docs).entries()];
const worshipByDate = [...extractWorshipHours(worship.docs).entries()]
return (
<>
<BannerWithMenu nextMass={nextMass.docs[0]} />
<div className={styles.mass}>
<h2>Kommen Sie vorbei, in unsere Heilige Messe!</h2>
<div className={styles.table}>
{worshipByDate.map(([date, worships]) => <MassTable key={date} date={date} masses={worships} />)}
{worshipByDate.map(([date, worships]) => (
<MassTable key={date} date={date} masses={worships} />
))}
</div>
</div>
</>
)
)
}

View file

@ -5,29 +5,29 @@ import { useLocation } from '@/hooks/useLocation'
import { useLiturgyCalendarTitle } from '@/hooks/useLiturgyCalendarTitle'
import { Container } from '@/components/Container/Container'
import { Card } from '@/components/Card/Card'
import styles from "./styles.module.css"
import styles from './styles.module.css'
import { MassTitle } from '@/components/MassTitle/MassTitle'
import { useCompactDate, useDate } from '@/hooks/useCompactDate'
import { useTime } from '@/hooks/useTime'
import { Pill } from '@/components/Pill/Pill'
import { useMassType } from '@/hooks/useMassType'
import Image from 'next/image'
import bell from "./bell.svg";
import locationIcon from "./location.svg"
import question from "./question.svg"
import bell from './bell.svg'
import locationIcon from './location.svg'
import question from './question.svg'
import { LocationMap } from '@/components/Map/Map'
import { Testimony } from '@/components/Testimony/Testimony'
export default async function Page({params}: {params: {id: string}}) {
const payload = await getPayloadHMR({ config: configPromise });
export default async function Page({ params }: { params: { id: string } }) {
const payload = await getPayloadHMR({ config: configPromise })
const worship = await payload.findByID({
id: params.id,
collection: 'worship'
});
collection: 'worship',
})
const location = useLocation(worship.location)
const title = useLiturgyCalendarTitle(worship.date);
const date = useDate(worship.date);
const time = useTime(worship.date);
const title = useLiturgyCalendarTitle(worship.date)
const date = useDate(worship.date)
const time = useTime(worship.date)
const type = useMassType(worship.type)
return (
@ -37,11 +37,15 @@ export default async function Page({params}: {params: {id: string}}) {
<Container>
<MassTitle title={title} cancelled={worship.cancelled} />
<div className={styles.info}>
<Card>
<div className={styles.centerIcon}>
<Image src={bell} alt={'Location'} width={60} className={styles.cardIcon} />
<Image
src={bell}
alt={'Location'}
width={60}
className={styles.cardIcon}
/>
</div>
<div className={styles.cardContent}>
<div className={styles.marginBottom}>
@ -55,7 +59,12 @@ export default async function Page({params}: {params: {id: string}}) {
<Card>
<div className={styles.centerIcon}>
<Image src={locationIcon} alt={'Location'} width={60} className={styles.cardIcon} />
<Image
src={locationIcon}
alt={'Location'}
width={60}
className={styles.cardIcon}
/>
</div>
<div className={styles.cardContent}>
<div className={styles.address}>
@ -65,22 +74,30 @@ export default async function Page({params}: {params: {id: string}}) {
</div>
</Card>
{worship.description &&
{worship.description && (
<Card>
<div className={styles.centerIcon}>
<Image src={question} alt={'Location'} width={60} className={styles.cardIcon} />
<Image
src={question}
alt={'Location'}
width={60}
className={styles.cardIcon}
/>
</div>
<div className={styles.cardText}>
{worship.description}
</div>
<div className={styles.cardText}>{worship.description}</div>
</Card>
}
)}
</div>
</Container>
<LocationMap />
<Testimony name={"Johan Shafer"} testimony={"\"Die Eucharistie ist für mich wie ein spiritueller Boost. Wenn ich die Hostie empfange, fühle ich mich krass verbunden mit Jesus. Es ist wie ein Reminder, dass ich nicht allein bin, egal was abgeht. Dieser Moment gibt mir richtig Power und lässt mich mit einem starken Gefühl von Frieden und Hoffnung rausgehen.\""}/>
<Testimony
name={'Johan Shafer'}
testimony={
'"Die Eucharistie ist für mich wie ein spiritueller Boost. Wenn ich die Hostie empfange, fühle ich mich krass verbunden mit Jesus. Es ist wie ein Reminder, dass ich nicht allein bin, egal was abgeht. Dieser Moment gibt mir richtig Power und lässt mich mit einem starken Gefühl von Frieden und Hoffnung rausgehen."'
}
/>
</>
)
}

View file

@ -1,40 +1,40 @@
.info {
display: flex;
gap: 30px;
margin-bottom: 80px;
display: flex;
gap: 30px;
margin-bottom: 80px;
}
.cardContent {
padding-top: 30px;
padding-left: 50px;
padding-top: 30px;
padding-left: 50px;
}
.cardText {
padding-top: 30px;
padding-left: 20px;
padding-right: 20px;
padding-top: 30px;
padding-left: 20px;
padding-right: 20px;
}
.marginBottom {
margin-bottom: 20px;
margin-bottom: 20px;
}
.address {
white-space: pre;
white-space: pre;
}
.cardIcon {
margin-bottom: 10px;
margin-left: auto;
margin-right: auto;
transition: transform 100ms;
margin-bottom: 10px;
margin-left: auto;
margin-right: auto;
transition: transform 100ms;
}
.cardIcon:hover {
transform: rotateZ(-20deg);
transform: rotateZ(-20deg);
}
.centerIcon {
padding-top: 30px;
text-align: center;
padding-top: 30px;
text-align: center;
}

View file

@ -1,37 +1,37 @@
import { CollectionConfig } from 'payload'
export const Churches: CollectionConfig = {
slug: 'church',
labels: {
singular: {
de: 'Kirche'
},
plural: {
de: 'Kirchen'
}
slug: 'church',
labels: {
singular: {
de: 'Kirche',
},
fields: [
{
name: 'name',
label: {
de: 'Name'
},
type: 'text',
required: true,
},
{
name: 'address',
label: {
de: 'Addresse'
},
type: 'textarea',
required: true,
}
],
admin: {
useAsTitle: 'name'
plural: {
de: 'Kirchen',
},
access: {
read: () => true
}
},
fields: [
{
name: 'name',
label: {
de: 'Name',
},
type: 'text',
required: true,
},
{
name: 'address',
label: {
de: 'Addresse',
},
type: 'textarea',
required: true,
},
],
admin: {
useAsTitle: 'name',
},
access: {
read: () => true,
},
}

View file

@ -4,17 +4,17 @@ export const Testimony: CollectionConfig = {
slug: 'testimony',
labels: {
singular: {
de: 'Zeugnis'
de: 'Zeugnis',
},
plural: {
de: 'Zeugnisse'
}
de: 'Zeugnisse',
},
},
fields: [
{
name: 'testimony',
label: {
de: 'Zeugnis'
de: 'Zeugnis',
},
type: 'textarea',
required: true,
@ -30,7 +30,7 @@ export const Testimony: CollectionConfig = {
{
name: 'occupation',
label: {
de: 'Beschäftigung'
de: 'Beschäftigung',
},
type: 'text',
required: false,
@ -38,16 +38,16 @@ export const Testimony: CollectionConfig = {
{
name: 'category',
label: {
de: 'Kategorie'
de: 'Kategorie',
},
type: 'select',
options: [
{
value: 'EUCHARIST',
label: 'Eucharistie'
}
label: 'Eucharistie',
},
],
required: true,
}
]
},
],
}

View file

@ -1,86 +1,86 @@
import { CollectionConfig } from 'payload'
export const Worship: CollectionConfig = {
slug: 'worship',
labels: {
singular: {
de: 'Gottesdienst',
},
plural: {
de: 'Gottesdienst',
}
slug: 'worship',
labels: {
singular: {
de: 'Gottesdienst',
},
fields: [
plural: {
de: 'Gottesdienst',
},
},
fields: [
{
name: 'date',
label: {
de: 'Datum',
},
type: 'date',
required: true,
admin: {
date: {
pickerAppearance: 'dayAndTime',
},
},
},
{
name: 'location',
label: {
de: 'Location',
},
type: 'relationship',
relationTo: 'church',
required: true,
},
{
name: 'type',
label: {
de: 'Categorie',
},
type: 'radio',
options: [
{
name: 'date',
label: {
de: 'Datum'
},
type: 'date',
required: true,
admin: {
date: {
pickerAppearance: 'dayAndTime'
}
}
label: 'Heilige Messe',
value: 'MASS',
},
{
name: 'location',
label: {
de: 'Location'
},
type: "relationship",
relationTo: 'church',
required: true,
label: 'Familien Messe',
value: 'FAMILY',
},
{
name: 'type',
label: {
de: 'Categorie'
},
type: 'radio',
options: [
{
label: 'Heilige Messe',
value: "MASS"
},
{
label: 'Familien Messe',
value: "FAMILY"
},
{
label: "Wort-Gottes-Feier",
value: "WORD"
}
],
required: true,
label: 'Wort-Gottes-Feier',
value: 'WORD',
},
{
name: 'cancelled',
type: 'checkbox',
required: true,
defaultValue: false,
label: {
de: 'Abgesagt'
}
},
{
name: 'title',
type: 'text',
required: false,
label: {
de: 'Liturgischer Tag'
}
},
{
name: 'description',
type: 'textarea',
label: {
de: 'Hinweise'
}
},
],
access: {
read: () => true
}
],
required: true,
},
{
name: 'cancelled',
type: 'checkbox',
required: true,
defaultValue: false,
label: {
de: 'Abgesagt',
},
},
{
name: 'title',
type: 'text',
required: false,
label: {
de: 'Liturgischer Tag',
},
},
{
name: 'description',
type: 'textarea',
label: {
de: 'Hinweise',
},
},
],
access: {
read: () => true,
},
}

View file

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

View file

@ -1,18 +1,18 @@
import styles from "./bannerText.module.css"
import {faustina, lato} from "@/app/fonts";
import styles from './bannerText.module.css'
import { faustina, lato } from '@/app/fonts'
export const BannerText = () => {
return (
<div className={faustina.className + " " + styles.container}>
<div className={styles.catholic}>KATHOLISCHE</div>
<div className={styles.name}>
PFARREI <br/>
HEILIGE <br/>
DREI KÖNIGE <br/>
</div>
<div className={styles.berlin + " " + lato.className}>
Berlin Nord-Neukölln
</div>
</div>
)
return (
<div className={faustina.className + ' ' + styles.container}>
<div className={styles.catholic}>KATHOLISCHE</div>
<div className={styles.name}>
PFARREI <br />
HEILIGE <br />
DREI KÖNIGE <br />
</div>
<div className={styles.berlin + ' ' + lato.className}>
Berlin Nord-Neukölln
</div>
</div>
)
}

View file

@ -1,42 +1,42 @@
.catholic {
font-weight: 600;
font-size: 35px;
position: relative;
bottom: -15px;
left: 10px;
font-weight: 600;
font-size: 35px;
position: relative;
bottom: -15px;
left: 10px;
}
.container {
position: absolute;
bottom: 100px;
position: absolute;
bottom: 100px;
}
.name {
font-weight: 600;
font-size: 120px;
line-height: 105px;
padding: 20px 0;
font-weight: 600;
font-size: 120px;
line-height: 105px;
padding: 20px 0;
}
.berlin {
position: relative;
left: 10px;
position: relative;
left: 10px;
}
@media screen and (max-width: 420px) {
.name {
font-size: 42px;
line-height: 42px;
padding: 5px 0;
}
.name {
font-size: 42px;
line-height: 42px;
padding: 5px 0;
}
.catholic {
font-size: 18px;
bottom: 0;
left: 0;
}
.catholic {
font-size: 18px;
bottom: 0;
left: 0;
}
.berlin {
left: 0;
}
.berlin {
left: 0;
}
}

View file

@ -5,11 +5,11 @@ const meta: Meta<typeof Card> = {
component: Card,
}
type Story = StoryObj<typeof Card>;
type Story = StoryObj<typeof Card>
export default meta
export const Default: Story = {
args: {
children: <>Some content</>
children: <>Some content</>,
},
}

View file

@ -1,13 +1,9 @@
import styles from "./styles.module.css";
import styles from './styles.module.css'
type CardProps = {
children?: JSX.Element | JSX.Element[];
children?: JSX.Element | JSX.Element[]
}
export const Card = ({children}: CardProps) => {
return (
<div className={styles.card}>
{children}
</div>
)
export const Card = ({ children }: CardProps) => {
return <div className={styles.card}>{children}</div>
}

View file

@ -1,6 +1,6 @@
.card {
height: 260px;
width: 235px;
box-shadow: 0 0 11px 0 rgba(79,66,79,0.26);
background-color: #ffffff;
height: 260px;
width: 235px;
box-shadow: 0 0 11px 0 rgba(79, 66, 79, 0.26);
background-color: #ffffff;
}

View file

@ -1,22 +1,22 @@
import {Meta, StoryObj} from "@storybook/react";
import { Container } from "./Container";
import { Meta, StoryObj } from '@storybook/react'
import { Container } from './Container'
const meta: Meta<typeof Container> = {
component: Container
component: Container,
}
type Story = StoryObj<typeof Container>;
export default meta;
type Story = StoryObj<typeof Container>
export default meta
export const Default: Story = {
args: {
children: <>Some content</>
}
children: <>Some content</>,
},
}
export const Yellow: Story = {
args: {
background: "yellow",
children: <>Some content</>
}
background: 'yellow',
children: <>Some content</>,
},
}

View file

@ -1,17 +1,15 @@
import styles from "./styles.module.css"
import styles from './styles.module.css'
import classNames from 'classnames'
type ContainerProps = {
background?: "yellow"
background?: 'yellow'
children: JSX.Element | JSX.Element[]
}
export const Container = ({children, background}: ContainerProps) => {
export const Container = ({ children, background }: ContainerProps) => {
return (
<div className={classNames({[styles.yellow]: background === "yellow"})}>
<div className={styles.container}>
{children}
</div>
<div className={classNames({ [styles.yellow]: background === 'yellow' })}>
<div className={styles.container}>{children}</div>
</div>
)
}

View file

@ -1,11 +1,15 @@
.container {
width: 800px;
margin: 0 auto;
width: 800px;
margin: 0 auto;
}
.yellow {
background: rgb(255,255,97);
background: linear-gradient(180deg, rgba(255,255,97,0.0) 0%, rgb(255 250 163) 20%);
position: relative;
top: -70px;
background: rgb(255, 255, 97);
background: linear-gradient(
180deg,
rgba(255, 255, 97, 0) 0%,
rgb(255 250 163) 20%
);
position: relative;
top: -70px;
}

View file

@ -1,19 +1,19 @@
.splash-bg {
background-color: #8d5fd3;
background-color: #8d5fd3;
}
.splash {
height: 80vh;
background: url("bg.svg") center center;
background-size: cover;
color: #FFFFFF;
padding: 20px;
position: relative;
height: 80vh;
background: url('bg.svg') center center;
background-size: cover;
color: #ffffff;
padding: 20px;
position: relative;
}
.stars {
position: absolute;
top: 0;
left: 0;
z-index: 0;
position: absolute;
top: 0;
left: 0;
z-index: 0;
}

View file

@ -1,15 +1,15 @@
import type { Meta, StoryObj} from '@storybook/react';
import {HomeBanner} from "@/components/HomeBanner/HomeBanner";
import type { Meta, StoryObj } from '@storybook/react'
import { HomeBanner } from '@/components/HomeBanner/HomeBanner'
const meta: Meta<typeof HomeBanner> = {
component: HomeBanner,
component: HomeBanner,
}
type Story = StoryObj<typeof HomeBanner>;
export default meta;
type Story = StoryObj<typeof HomeBanner>
export default meta
export const Default: Story = {
args: {
stars: 50
}
args: {
stars: 50,
},
}

View file

@ -1,72 +1,82 @@
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef} from "react";
import "./HomeBanner.css"
import React, {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
} from 'react'
import './HomeBanner.css'
type HomeBannerProps = {
children?: React.ReactNode,
stars: number
children?: React.ReactNode
stars: number
}
export type HomeBannerHandle = {
newStar: () => void;
newStar: () => void
}
export const HomeBanner = forwardRef<HomeBannerHandle, HomeBannerProps>(function HomeBanner({children, stars}: HomeBannerProps, ref) {
const canvasRef = useRef<HTMLCanvasElement>(null);
export const HomeBanner = forwardRef<HomeBannerHandle, HomeBannerProps>(
function HomeBanner({ children, stars }: HomeBannerProps, ref) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const drawStarAtPosition = useCallback((ctx: CanvasRenderingContext2D, x: number, y: number) => {
let r = 2.5 * Math.random();
const drawStarAtPosition = useCallback(
(ctx: CanvasRenderingContext2D, x: number, y: number) => {
let r = 2.5 * Math.random()
//Draw the stars;
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}, []);
ctx.beginPath()
ctx.fillStyle = 'white'
ctx.arc(x, y, r, 0, Math.PI * 2)
ctx.fill()
},
[],
)
const drawStar = useCallback((ctx: CanvasRenderingContext2D) => {
const drawStar = useCallback(
(ctx: CanvasRenderingContext2D) => {
//Random position and size of stars;
let x = ctx.canvas.width * Math.random();
let y = ctx.canvas.height * Math.random();
let x = ctx.canvas.width * Math.random()
let y = ctx.canvas.height * Math.random()
drawStarAtPosition(ctx, x, y);
}, [drawStarAtPosition]);
drawStarAtPosition(ctx, x, y)
},
[drawStarAtPosition],
)
useImperativeHandle(ref, () => {
return {
newStar() {
const context = canvasRef.current?.getContext("2d")
if (context) {
drawStar(context)
}
}
}
}, [drawStar]);
return {
newStar() {
const context = canvasRef.current?.getContext('2d')
if (context) {
drawStar(context)
}
},
}
}, [drawStar])
useEffect(() => {
if (canvasRef.current) {
canvasRef.current.width = window.innerWidth;
canvasRef.current.height = 0.8 * window.innerHeight;
}
const context = canvasRef.current?.getContext("2d");
if(context) {
for (let i = 0; i < stars; i++) {
//Glow effect;
context.shadowBlur = 10;
context.shadowColor = "white";
if (canvasRef.current) {
canvasRef.current.width = window.innerWidth
canvasRef.current.height = 0.8 * window.innerHeight
}
const context = canvasRef.current?.getContext('2d')
if (context) {
for (let i = 0; i < stars; i++) {
//Glow effect;
context.shadowBlur = 10
context.shadowColor = 'white'
drawStar(context)
}
drawStar(context)
}
}, [drawStar, stars]);
}
}, [drawStar, stars])
return (
<div className="splash-bg">
<canvas ref={canvasRef} className="stars"></canvas>
<div className="splash">
{children}
</div>
</div>
<div className="splash-bg">
<canvas ref={canvasRef} className="stars"></canvas>
<div className="splash">{children}</div>
</div>
)
});
},
)

View file

@ -1,38 +1,35 @@
"use client"
'use client'
import { useEffect, useRef, useState } from 'react'
import mapboxgl from 'mapbox-gl'
import styles from "./styles.module.css"
import styles from './styles.module.css'
import 'mapbox-gl/dist/mapbox-gl.css'
// todo: as env variable
mapboxgl.accessToken ='pk.eyJ1IjoiYnRpZWxlbiIsImEiOiJjbHpzNmNoNjAxdmxqMmpzaWtxOGsxNnY2In0.4XrA_ZlvlmKZ7MG_tLo-mQ'
mapboxgl.accessToken =
'pk.eyJ1IjoiYnRpZWxlbiIsImEiOiJjbHpzNmNoNjAxdmxqMmpzaWtxOGsxNnY2In0.4XrA_ZlvlmKZ7MG_tLo-mQ'
export const LocationMap = () => {
const mapContainer = useRef<HTMLDivElement>(null);
const map = useRef<mapboxgl.Map>(null);
const [lng, setLng] = useState(13.436093);
const [lat, setLat] = useState(52.477608);
const [zoom, setZoom] = useState(15.42);
const mapContainer = useRef<HTMLDivElement>(null)
const map = useRef<mapboxgl.Map>(null)
const [lng, setLng] = useState(13.436093)
const [lat, setLat] = useState(52.477608)
const [zoom, setZoom] = useState(15.42)
useEffect(() => {
if (map.current) return; // initialize map only once
if (map.current) return // initialize map only once
if(mapContainer.current) {
if (mapContainer.current) {
map.current = new mapboxgl.Map({
container: mapContainer.current,
logoPosition: "top-left",
logoPosition: 'top-left',
attributionControl: false,
style: 'mapbox://styles/btielen/clzs6etam008801qu6hpn9qbo',
center: [lng, lat],
zoom: zoom,
});
})
}
})
});
return (
<div ref={mapContainer} className={styles.map}></div>
)
return <div ref={mapContainer} className={styles.map}></div>
}

View file

@ -1,3 +1,3 @@
.map {
height: 300px;
height: 300px;
}

View file

@ -1,41 +1,41 @@
import { Meta, StoryObj } from '@storybook/react';
import { MassTable } from './MassTable';
import { Meta, StoryObj } from '@storybook/react'
import { MassTable } from './MassTable'
const meta: Meta<typeof MassTable> = {
component: MassTable,
};
component: MassTable,
}
export default meta;
type Story = StoryObj<typeof meta>;
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
date: "2024-08-04",
masses: [
{
id: "1",
date: "10:00",
locationName: "St. Christopherus",
type: null
},
{
id: "1",
date: "11:00",
locationName: "St. Richard",
type: "FAMILY"
},
{
id: "1",
date: "11:00",
locationName: "St. Clara",
type: "WORD"
},
{
id: "1",
date: "19:00",
locationName: "St. Clara",
type: null
}
]
}
args: {
date: '2024-08-04',
masses: [
{
id: '1',
date: '10:00',
locationName: 'St. Christopherus',
type: null,
},
{
id: '1',
date: '11:00',
locationName: 'St. Richard',
type: 'FAMILY',
},
{
id: '1',
date: '11:00',
locationName: 'St. Clara',
type: 'WORD',
},
{
id: '1',
date: '19:00',
locationName: 'St. Clara',
type: null,
},
],
},
}

View file

@ -1,33 +1,40 @@
import {MassTableRow} from "@/components/MassTable/MassTableRow";
import {useMemo} from "react";
import styles from "./styles.module.css"
import {faustina} from "@/app/fonts";
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'
type MassTableProps = {
date: string,
masses: Worship[]
date: string
masses: Worship[]
}
export const MassTable = ({date, masses}: MassTableProps) => {
let dateObj = useMemo(() => new Date(date), [date]);
let compactDate = useCompactDate(date);
export const MassTable = ({ date, masses }: MassTableProps) => {
let dateObj = useMemo(() => new Date(date), [date])
let compactDate = useCompactDate(date)
return (
<div className={styles.table}>
<h3 className={faustina.className}>{dateObj.toLocaleDateString("de-DE", {weekday: 'long'})} <small>{compactDate}</small></h3>
return (
<div className={styles.table}>
<h3 className={faustina.className}>
{dateObj.toLocaleDateString('de-DE', { weekday: 'long' })}{' '}
<small>{compactDate}</small>
</h3>
{ masses.map(mass =>
<MassTableRow
key={mass.id}
id={mass.id}
locationName={typeof mass.location == "string" ? mass.location : mass.location.name}
date={mass.date}
type={mass.type}
cancelled={mass.cancelled}
/>
)}
</div>
)
{masses.map((mass) => (
<MassTableRow
key={mass.id}
id={mass.id}
locationName={
typeof mass.location == 'string'
? mass.location
: mass.location.name
}
date={mass.date}
type={mass.type}
cancelled={mass.cancelled}
/>
))}
</div>
)
}

View file

@ -1,49 +1,49 @@
import { Meta, StoryObj } from '@storybook/react';
import { MassTableRow } from './MassTableRow';
import { Meta, StoryObj } from '@storybook/react'
import { MassTableRow } from './MassTableRow'
const meta: Meta<typeof MassTableRow> = {
component: MassTableRow,
};
component: MassTableRow,
}
export default meta;
type Story = StoryObj<typeof meta>;
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
id: "1",
locationName: "St. Clara",
date: "2024-08-23T15:00:00.000Z",
type: 'MASS',
cancelled: false,
}
args: {
id: '1',
locationName: 'St. Clara',
date: '2024-08-23T15:00:00.000Z',
type: 'MASS',
cancelled: false,
},
}
export const FamilyMass: Story = {
args: {
id: "1",
locationName: "St. Christopherus",
date: "2024-08-23T15:00:00.000Z",
type: "FAMILY",
cancelled: false,
}
args: {
id: '1',
locationName: 'St. Christopherus',
date: '2024-08-23T15:00:00.000Z',
type: 'FAMILY',
cancelled: false,
},
}
export const LiturgyOfTheWord: Story = {
args: {
id: "1",
locationName: "St. Richard",
date: "2024-08-23T15:00:00.000Z",
type: "WORD",
cancelled: false,
}
args: {
id: '1',
locationName: 'St. Richard',
date: '2024-08-23T15:00:00.000Z',
type: 'WORD',
cancelled: false,
},
}
export const Cancelled: Story = {
args: {
id: "1",
locationName: "St. Richard",
date: "2024-08-23T15:00:00.000Z",
type: "WORD",
cancelled: true,
}
args: {
id: '1',
locationName: 'St. Richard',
date: '2024-08-23T15:00:00.000Z',
type: 'WORD',
cancelled: true,
},
}

View file

@ -1,49 +1,54 @@
"use client"
'use client'
import styles from "./styles.module.css"
import Image from "next/image";
import family from "./family.svg"
import bible from "./bible.svg"
import {useState} from "react";
import styles from './styles.module.css'
import Image from 'next/image'
import family from './family.svg'
import bible from './bible.svg'
import { useState } from 'react'
import Link from 'next/link'
import classNames from 'classnames'
import { useTime } from '@/hooks/useTime'
export type MassTableRowProps = {
id: string,
locationName: string,
date: string,
type: 'MASS' | 'FAMILY' | 'WORD',
cancelled: boolean
id: string
locationName: string
date: string
type: 'MASS' | 'FAMILY' | 'WORD'
cancelled: boolean
}
export const MassTableRow = ({id, locationName, date, type, cancelled}: MassTableRowProps) => {
const [symbol, setSymbol] = useState("-");
const time = useTime(date);
return (
<Link href={`/worship/${id}`} className={classNames({ [styles.cancelled]: cancelled }, styles.link)}>
<div
className={styles.row}
onMouseEnter={() => setSymbol("†")}
onMouseLeave={() => setSymbol("-")}
>
<div className={styles.time}>{time}</div>
<div className={styles.symbol}>
{symbol}
</div>
<div>
{locationName}
</div>
<div>
{ type === "FAMILY" &&
<Image src={family} width={18} height={18} alt={"Familien Messe"} />
}
export const MassTableRow = ({
id,
locationName,
date,
type,
cancelled,
}: MassTableRowProps) => {
const [symbol, setSymbol] = useState('-')
const time = useTime(date)
return (
<Link
href={`/worship/${id}`}
className={classNames({ [styles.cancelled]: cancelled }, styles.link)}
>
<div
className={styles.row}
onMouseEnter={() => setSymbol('†')}
onMouseLeave={() => setSymbol('-')}
>
<div className={styles.time}>{time}</div>
<div className={styles.symbol}>{symbol}</div>
<div>{locationName}</div>
<div>
{type === 'FAMILY' && (
<Image src={family} width={18} height={18} alt={'Familien Messe'} />
)}
{ type === "WORD" &&
<Image src={bible} width={18} height={18} alt={"Wortgottesfeier"} />
}
</div>
{type === 'WORD' && (
<Image src={bible} width={18} height={18} alt={'Wortgottesfeier'} />
)}
</div>
</Link>
)
</div>
</Link>
)
}

View file

@ -1,28 +1,28 @@
.row {
display: flex;
gap: 10px;
height: 24px;
cursor: pointer;
display: flex;
gap: 10px;
height: 24px;
cursor: pointer;
}
.link {
text-decoration: none;
color: inherit;
text-decoration: none;
color: inherit;
}
.cancelled {
text-decoration: line-through;
text-decoration: line-through;
}
.symbol {
width: 14px;
text-align: center;
width: 14px;
text-align: center;
}
.time {
width: 40px;
width: 40px;
}
.table {
width: 300px;
width: 300px;
}

View file

@ -1,25 +1,24 @@
import type { Meta, StoryObj} from '@storybook/react';
import { MassTimer } from './MassTimer';
import {fn} from "@storybook/test";
import type { Meta, StoryObj } from '@storybook/react'
import { MassTimer } from './MassTimer'
import { fn } from '@storybook/test'
const meta: Meta<typeof MassTimer> = {
component: MassTimer
};
component: MassTimer,
}
type Story = StoryObj<typeof MassTimer>;
type Story = StoryObj<typeof MassTimer>
export default meta
export const OneDay: Story = {
args: {
timeout: new Date().getTime() + 1000 * 60 * 60 * 24,
onStarClick: fn()
}
args: {
timeout: new Date().getTime() + 1000 * 60 * 60 * 24,
onStarClick: fn(),
},
}
export const TimeOut: Story = {
args: {
timeout: new Date().getTime(),
onStarClick: fn()
}
args: {
timeout: new Date().getTime(),
onStarClick: fn(),
},
}

View file

@ -1,50 +1,53 @@
"use client"
'use client'
import {useCountdown} from "@/components/MassTimer/useCountdown";
import styles from "./masstimer.module.css"
import {useState} from "react";
import {MassTimerTooltip} from "@/components/MassTimerTooltip/MassTimerTooltip";
import { useCountdown } from '@/components/MassTimer/useCountdown'
import styles from './masstimer.module.css'
import { useState } from 'react'
import { MassTimerTooltip } from '@/components/MassTimerTooltip/MassTimerTooltip'
import { Worship } from '@/payload-types'
type MassTimerProps = {
nextMass: Worship
nextMass: Worship
/**
* Optional click handler
*/
onStarClick?: () => void
/**
* Optional click handler
*/
onStarClick?: () => void
}
export const MassTimer = ({nextMass, onStarClick}: MassTimerProps) => {
export const MassTimer = ({ nextMass, onStarClick }: MassTimerProps) => {
const [displayTooltip, setDisplayTooltip] = useState<boolean>(false)
const [days, hours, minutes, seconds] = useCountdown(
new Date(nextMass.date).getTime(),
)
const [displayTooltip, setDisplayTooltip] = useState<boolean>(false);
const [days, hours, minutes, seconds] = useCountdown(new Date(nextMass.date).getTime());
return (
<div className={styles.container}>
<div>
<button
className={styles.starButton}
type={'button'}
onClick={onStarClick}
>
🌟
</button>
return (
<div className={styles.container}>
<div>
<button
className={styles.starButton}
type={"button"}
onClick={onStarClick}
>🌟
</button>
<span onMouseEnter={() => setDisplayTooltip(true)}>
{days}T {hours}S {minutes}M {seconds}S
</span>
</div>
<span onMouseEnter={() => setDisplayTooltip(true)}>
{days}T {hours}S {minutes}M {seconds}S
</span>
</div>
<div
className={styles.tooltip}
style={{
visibility: displayTooltip ? "visible" : "hidden",
opacity: displayTooltip ? 1 : 0,
transition: "ease-out 0.2s",
}} onMouseLeave={() => setDisplayTooltip(false)}>
<MassTimerTooltip nextMass={nextMass}/>
</div>
</div>
)
<div
className={styles.tooltip}
style={{
visibility: displayTooltip ? 'visible' : 'hidden',
opacity: displayTooltip ? 1 : 0,
transition: 'ease-out 0.2s',
}}
onMouseLeave={() => setDisplayTooltip(false)}
>
<MassTimerTooltip nextMass={nextMass} />
</div>
</div>
)
}

View file

@ -1,19 +1,19 @@
.container {
position: relative;
position: relative;
}
.starButton {
background: none;
border: none;
margin-right: 5px;
background: none;
border: none;
margin-right: 5px;
}
.starButton:hover {
cursor: pointer;
cursor: pointer;
}
.tooltip {
position: absolute;
right: 0;
top: 35px;
position: absolute;
right: 0;
top: 35px;
}

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState } from 'react'
/**
* Countdown until targetDate,
@ -7,36 +7,33 @@ import { useEffect, useState } from 'react';
*
*/
const useCountdown = (targetDate: number) => {
const [countDown, setCountDown] = useState(targetDate - new Date().getTime())
const [countDown, setCountDown] = useState(
targetDate - new Date().getTime()
);
useEffect(() => {
if (countDown > 0) {
setTimeout(() => setCountDown(countDown - 1000), 1000)
} else {
setCountDown(0)
}
}, [countDown])
useEffect(() => {
if (countDown > 0) {
setTimeout(() => setCountDown(countDown - 1000), 1000);
} else {
setCountDown(0)
}
}, [countDown]);
return getDaysHoursMinutesAndSeconds(countDown);
};
return getDaysHoursMinutesAndSeconds(countDown)
}
/**
* Return an array of four numbers, representing the
* numbers days, hours, minutes and seconds
*/
const getDaysHoursMinutesAndSeconds = (countDown: number) => {
// calculate time left
const days = Math.floor(countDown / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((countDown % (1000 * 60)) / 1000);
// calculate time left
const days = Math.floor(countDown / (1000 * 60 * 60 * 24))
const hours = Math.floor(
(countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
)
const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((countDown % (1000 * 60)) / 1000)
return [days, hours, minutes, seconds];
};
return [days, hours, minutes, seconds]
}
export { useCountdown };
export { useCountdown }

View file

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

View file

@ -1,6 +1,6 @@
import styles from "./massTimerTooltip.module.css";
import clara from "./clara.svg"
import Image from "next/image";
import styles from './massTimerTooltip.module.css'
import clara from './clara.svg'
import Image from 'next/image'
import { Worship } from '@/payload-types'
import { useTime } from '@/hooks/useTime'
import { useLocationName } from '@/hooks/useLocationName'
@ -10,22 +10,20 @@ type MassTimerTooltipProps = {
nextMass: Worship
}
export const MassTimerTooltip = ({nextMass}: MassTimerTooltipProps) => {
const time = useTime(nextMass.date);
const location = useLocationName(nextMass.location);
const date = useCompactDate(nextMass.date);
export const MassTimerTooltip = ({ nextMass }: MassTimerTooltipProps) => {
const time = useTime(nextMass.date)
const location = useLocationName(nextMass.location)
const date = useCompactDate(nextMass.date)
return (
<div
className={styles.tooltip}>
<div className={styles.church}>
<Image src={clara} width={75} height={75} alt={""}/>
</div>
<div className={styles.tooltip}>
<div className={styles.church}>
<Image src={clara} width={75} height={75} alt={''} />
</div>
<div>
Die nächste Messe is am {date} um {time} Uhr in {location}.
</div>
</div>
)
<div>
Die nächste Messe is am {date} um {time} Uhr in {location}.
</div>
</div>
)
}

View file

@ -1,28 +1,32 @@
.tooltip {
width: 300px;
padding: 20px;
color: #4d4d4d;
background: rgb(244,244,244);
background: linear-gradient(40deg, rgba(244,244,244,1) 0%, rgba(228,228,228,1) 100%);
border: solid 1px #dddddd;
border-radius: 6px;
display: flex;
gap: 20px;
align-items: center;
width: 300px;
padding: 20px;
color: #4d4d4d;
background: rgb(244, 244, 244);
background: linear-gradient(
40deg,
rgba(244, 244, 244, 1) 0%,
rgba(228, 228, 228, 1) 100%
);
border: solid 1px #dddddd;
border-radius: 6px;
display: flex;
gap: 20px;
align-items: center;
}
.church {
width: 75px;
height: 75px;
border-radius: 50%;
flex-shrink: 0;
background-color: #f6f6f6;
border: solid 2px #c2c2c2;
transition: background-color 200ms ease-in;
overflow: clip;
width: 75px;
height: 75px;
border-radius: 50%;
flex-shrink: 0;
background-color: #f6f6f6;
border: solid 2px #c2c2c2;
transition: background-color 200ms ease-in;
overflow: clip;
}
.church:hover {
cursor: pointer;
background-color: #fff318;
cursor: pointer;
background-color: #fff318;
}

View file

@ -5,19 +5,19 @@ const meta: Meta<typeof MassTitle> = {
component: MassTitle,
}
type Story = StoryObj<typeof MassTitle>;
type Story = StoryObj<typeof MassTitle>
export default meta
export const Default: Story = {
args: {
title: 'Mittwoch der 19. Woche im Jahreskreis',
cancelled: false
cancelled: false,
},
}
export const Cancelled: Story = {
args: {
title: 'Mittwoch der 19. Woche im Jahreskreis',
cancelled: true
cancelled: true,
},
}

View file

@ -1,18 +1,26 @@
import classNames from 'classnames'
import { faustina } from '@/app/fonts'
import styles from "./styles.module.css"
import styles from './styles.module.css'
import { Pill } from '@/components/Pill/Pill'
type MassTitleProps = {
title: string,
title: string
cancelled: boolean
}
export const MassTitle = ({title, cancelled}: MassTitleProps) => {
export const MassTitle = ({ title, cancelled }: MassTitleProps) => {
return (
<div>
<div className={styles.mass}>Gottesdienst { cancelled && <Pill>ABGESAGT</Pill> }</div>
<h1 className={classNames(faustina.className, styles.title, { [styles.cancelled]: cancelled})}>{title}</h1>
<div className={styles.mass}>
Gottesdienst {cancelled && <Pill>ABGESAGT</Pill>}
</div>
<h1
className={classNames(faustina.className, styles.title, {
[styles.cancelled]: cancelled,
})}
>
{title}
</h1>
</div>
)
}

View file

@ -1,14 +1,14 @@
.title {
font-size: 48px;
font-weight: 700;
margin-block-start: 0;
font-size: 48px;
font-weight: 700;
margin-block-start: 0;
}
.mass {
font-size: 16px;
font-weight: 700;
font-size: 16px;
font-weight: 700;
}
.cancelled {
text-decoration: line-through;
text-decoration: line-through;
}

View file

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

View file

@ -1,41 +1,51 @@
import {MassTimer} from "@/components/MassTimer/MassTimer";
import styles from "./styles.module.css"
import MenuIcon from "./menu.svg"
import Image from "next/image";
import { MassTimer } from '@/components/MassTimer/MassTimer'
import styles from './styles.module.css'
import MenuIcon from './menu.svg'
import Image from 'next/image'
import { Worship } from '@/payload-types'
import { MenuBaseLayer } from '@/components/MenuBaseLayer/MenuBaseLayer'
type MenuProps = {
starClick?: () => void
nextMass?: Worship,
starClick?: () => void
nextMass?: Worship
}
export const Menu = (props: MenuProps) => {
return (
<nav className={styles.nav}>
<MenuBaseLayer />
<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</a>
<a className={styles.menuLink} href={""}>Sakramenten</a>
<a className={styles.menuLink} href={""}>Kontakt</a>
</div>
<nav className={styles.nav}>
<MenuBaseLayer />
<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
</a>
<a className={styles.menuLink} href={''}>
Sakramenten
</a>
<a className={styles.menuLink} href={''}>
Kontakt
</a>
</div>
<div className={styles.itemsRight}>
<div>
<a className={styles.menuLink} href={""}>Spenden</a>
</div>
<div>
<button className={styles.button}>Neu hier?</button>
</div>
<div className={styles.itemsRight}>
<div>
<a className={styles.menuLink} href={''}>
Spenden
</a>
</div>
<div>
<button className={styles.button}>Neu hier?</button>
</div>
{ props.nextMass &&
<MassTimer nextMass={props.nextMass} onStarClick={props.starClick}/>
}
</div>
</nav>
)
{props.nextMass && (
<MassTimer nextMass={props.nextMass} onStarClick={props.starClick} />
)}
</div>
</nav>
)
}

View file

@ -1,63 +1,62 @@
.nav {
display: flex;
align-items: baseline;
gap: 20px;
color: #3d3d3d;
padding-top: 15px;
height: 50px;
border-bottom: 1px solid rgba(217, 217, 217, 0.19);
display: flex;
align-items: baseline;
gap: 20px;
color: #3d3d3d;
padding-top: 15px;
height: 50px;
border-bottom: 1px solid rgba(217, 217, 217, 0.19);
}
.navMobile {
display: none;
display: none;
}
.itemsLeft {
display: flex;
gap: 20px;
display: flex;
gap: 20px;
}
.menuLink {
color: inherit;
text-decoration: none;
font-weight: 600;
transition: opacity 100ms ease-in;
color: inherit;
text-decoration: none;
font-weight: 600;
transition: opacity 100ms ease-in;
}
.menuLink:hover {
opacity: 0.7;
opacity: 0.7;
}
.itemsRight {
margin-left: auto;
display: flex;
gap: 20px;
justify-content: flex-end;
align-items: baseline;
margin-left: auto;
display: flex;
gap: 20px;
justify-content: flex-end;
align-items: baseline;
}
.button {
padding: 10px;
border-radius: 10px;
border: none;
background-color: #eeeeee;
transition: background-color 0.1s ease-in-out;
font-family: inherit;
font-weight: 600;
padding: 10px;
border-radius: 10px;
border: none;
background-color: #eeeeee;
transition: background-color 0.1s ease-in-out;
font-family: inherit;
font-weight: 600;
}
.button:hover {
background-color: #fff318;
cursor: pointer;
background-color: #fff318;
cursor: pointer;
}
@media screen and (max-width: 800px) {
.navMobile {
display: block;
}
.navMobile {
display: block;
}
.itemsLeft {
display: none;
}
.itemsLeft {
display: none;
}
}

View file

@ -1,15 +1,13 @@
import {Meta, StoryObj} from "@storybook/react";
import { Meta, StoryObj } from '@storybook/react'
import { MenuBaseLayer } from '@/components/MenuBaseLayer/MenuBaseLayer'
const meta: Meta<typeof MenuBaseLayer> = {
component: MenuBaseLayer
component: MenuBaseLayer,
}
type Story = StoryObj<typeof MenuBaseLayer>;
export default meta;
type Story = StoryObj<typeof MenuBaseLayer>
export default meta
export const Default: Story = {
args: {
}
args: {},
}

View file

@ -1,4 +1,4 @@
import styles from "./style.module.css"
import styles from './style.module.css'
export const MenuBaseLayer = () => {
return <div className={styles.background}></div>

View file

@ -1,10 +1,10 @@
.background {
width: 100%;
height: 340px;
background-image: url("./bg.svg");
background-size: 100% 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 340px;
background-image: url('./bg.svg');
background-size: 100% 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}

View file

@ -5,11 +5,11 @@ const meta: Meta<typeof Pill> = {
component: Pill,
}
type Story = StoryObj<typeof Pill>;
type Story = StoryObj<typeof Pill>
export default meta
export const Default: Story = {
args: {
children: "Default"
children: 'Default',
},
}

View file

@ -1,13 +1,9 @@
import styles from "./styles.module.css"
import styles from './styles.module.css'
type PillProps = {
children: JSX.Element | string | JSX.Element[]
}
export const Pill = ({children}: PillProps) => {
return (
<div className={styles.pill}>
{children}
</div>
)
export const Pill = ({ children }: PillProps) => {
return <div className={styles.pill}>{children}</div>
}

View file

@ -1,6 +1,6 @@
.pill {
padding: 8px 12px;
background-color: #c2c2c2;
border-radius: 20px;
display: inline-block;
padding: 8px 12px;
background-color: #c2c2c2;
border-radius: 20px;
display: inline-block;
}

View file

@ -5,12 +5,13 @@ const meta: Meta<typeof Testimony> = {
component: Testimony,
}
type Story = StoryObj<typeof Testimony>;
type Story = StoryObj<typeof Testimony>
export default meta
export const Default: Story = {
args: {
name: 'Johan Schäfer',
testimony: 'Die Eucharistie ist für mich wie ein spiritueller Boost. Wenn ich die Hostie empfange, fühle ich mich krass verbunden mit Jesus. Es ist wie ein Reminder, dass ich nicht allein bin, egal was abgeht. Dieser Moment gibt mir richtig Power und lässt mich mit einem starken Gefühl von Frieden und Hoffnung rausgehen.'
testimony:
'Die Eucharistie ist für mich wie ein spiritueller Boost. Wenn ich die Hostie empfange, fühle ich mich krass verbunden mit Jesus. Es ist wie ein Reminder, dass ich nicht allein bin, egal was abgeht. Dieser Moment gibt mir richtig Power und lässt mich mit einem starken Gefühl von Frieden und Hoffnung rausgehen.',
},
}

View file

@ -1,27 +1,23 @@
import styles from "./styles.module.css";
import styles from './styles.module.css'
import { Container } from '@/components/Container/Container'
import classNames from 'classnames'
import { faustina } from '@/app/fonts'
type TestimonyProps = {
name: string,
testimony: string,
name: string
testimony: string
}
export const Testimony = ({name, testimony}: TestimonyProps) => {
export const Testimony = ({ name, testimony }: TestimonyProps) => {
return (
<Container background={"yellow"}>
<Container background={'yellow'}>
<div className={styles.testimony}>
<div className={styles.person}>
</div>
<div className={styles.person}></div>
<div>
<p className={classNames(styles.testimonyText, faustina.className)}>
{testimony}
</p>
<p>
{name}
</p>
<p>{name}</p>
</div>
</div>
</Container>

View file

@ -1,17 +1,17 @@
.testimony {
display: flex;
padding: 80px 0 50px 0;
gap: 40px;
align-items: center;
display: flex;
padding: 80px 0 50px 0;
gap: 40px;
align-items: center;
}
.testimonyText {
font-style: italic;
font-style: italic;
}
.person {
height: 150px;
width: 150px;
background-color: white;
flex-shrink: 0;
height: 150px;
width: 150px;
background-color: white;
flex-shrink: 0;
}

View file

@ -2,11 +2,10 @@
* This data was "hacked" together with the romcal package
*/
type LiturgyData = {
name: string;
color: string;
};
name: string
color: string
}
export const calendar: Record<string, LiturgyData> = {
'2024-01-01': {
@ -29230,4 +29229,3 @@ export const calendar: Record<string, LiturgyData> = {
color: 'weiß',
},
}

View file

@ -10,5 +10,11 @@ export const useCompactDate = (date: string) => {
*
*/
export const useDate = (date: string) => {
return date.substring(8, 10) + '.' + date.substring(5, 7) + '.' + date.substring(0, 4)
return (
date.substring(8, 10) +
'.' +
date.substring(5, 7) +
'.' +
date.substring(0, 4)
)
}

View file

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

View file

@ -1,14 +1,13 @@
import {Church} from '@/payload-types'
import { Church } from '@/payload-types'
export const useLocation = (location: string | Church) : Church => {
if(typeof location === 'string') {
export const useLocation = (location: string | Church): Church => {
if (typeof location === 'string') {
return {
address: '',
createdAt: '',
name: 'Unknown',
updatedAt: '',
id: location
id: location,
}
} else {
return location

View file

@ -4,7 +4,7 @@ import { Church } from '@/payload-types'
* Get user friendly location name
*/
export const useLocationName = (location: string | Church) => {
if (typeof location == "string") {
if (typeof location == 'string') {
return location
} else {
return location.name

View file

@ -1,12 +1,11 @@
export const useMassType = (type: "MASS" | "FAMILY" | "WORD") => {
export const useMassType = (type: 'MASS' | 'FAMILY' | 'WORD') => {
switch (type) {
case "FAMILY":
return "Familien Messe";
case "WORD":
return "Wort-Gottes-Feier";
case 'FAMILY':
return 'Familien Messe'
case 'WORD':
return 'Wort-Gottes-Feier'
case 'MASS':
default:
return "Heilige Messe";
return 'Heilige Messe'
}
}

View file

@ -2,6 +2,6 @@
* From a UTC datetime, return the time in HH:MM format
*/
export const useTime = (datetime: string) => {
let date = new Date(datetime);
return date.toLocaleTimeString('de-De', { timeStyle: "short"});
let date = new Date(datetime)
return date.toLocaleTimeString('de-De', { timeStyle: 'short' })
}

View file

@ -8,162 +8,161 @@
export interface Config {
auth: {
users: UserAuthOperations;
};
users: UserAuthOperations
}
collections: {
users: User;
media: Media;
worship: Worship;
church: Church;
testimony: Testimony;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
users: User
media: Media
worship: Worship
church: Church
testimony: Testimony
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
db: {
defaultIDType: string;
};
globals: {};
locale: null;
defaultIDType: string
}
globals: {}
locale: null
user: User & {
collection: 'users';
};
collection: 'users'
}
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
email: string
password: string
}
login: {
email: string;
password: string;
};
email: string
password: string
}
registerFirstUser: {
email: string;
password: string;
};
email: string
password: string
}
unlock: {
email: string;
password: string;
};
email: string
password: string
}
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
password?: string | null;
id: string
updatedAt: string
createdAt: string
email: string
resetPasswordToken?: string | null
resetPasswordExpiration?: string | null
salt?: string | null
hash?: string | null
loginAttempts?: number | null
lockUntil?: string | null
password?: string | null
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
alt: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
id: string
alt: string
updatedAt: string
createdAt: string
url?: string | null
thumbnailURL?: string | null
filename?: string | null
mimeType?: string | null
filesize?: number | null
width?: number | null
height?: number | null
focalX?: number | null
focalY?: number | null
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "worship".
*/
export interface Worship {
id: string;
date: string;
location: string | Church;
type: 'MASS' | 'FAMILY' | 'WORD';
cancelled: boolean;
title?: string | null;
description?: string | null;
updatedAt: string;
createdAt: string;
id: string
date: string
location: string | Church
type: 'MASS' | 'FAMILY' | 'WORD'
cancelled: boolean
title?: string | null
description?: string | null
updatedAt: string
createdAt: string
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "church".
*/
export interface Church {
id: string;
name: string;
address: string;
updatedAt: string;
createdAt: string;
id: string
name: string
address: string
updatedAt: string
createdAt: string
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "testimony".
*/
export interface Testimony {
id: string;
testimony: string;
name: string;
occupation?: string | null;
category: 'EUCHARIST';
updatedAt: string;
createdAt: string;
id: string
testimony: string
name: string
occupation?: string | null
category: 'EUCHARIST'
updatedAt: string
createdAt: string
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: string;
id: string
user: {
relationTo: 'users';
value: string | User;
};
key?: string | null;
relationTo: 'users'
value: string | User
}
key?: string | null
value?:
| {
[k: string]: unknown;
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
| null
updatedAt: string
createdAt: string
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: string;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
[k: string]: unknown
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}

View file

@ -27,7 +27,7 @@ export default buildConfig({
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
i18n: {
supportedLanguages: { de }
supportedLanguages: { de },
},
db: mongooseAdapter({
url: process.env.DATABASE_URI || '',

View file

@ -1,11 +1,7 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -23,22 +19,11 @@
}
],
"paths": {
"@/*": [
"./src/*"
],
"@payload-config": [
"./src/payload.config.ts"
]
"@/*": ["./src/*"],
"@payload-config": ["./src/payload.config.ts"]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}