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'], project: ['./tsconfig.json'],
tsconfigRootDir: __dirname, tsconfigRootDir: __dirname,
}, },
plugins: ['eslint-plugin-prettier'],
rules: {
"prefer-const": "error",
"prettier": "error"
}
} }

View file

@ -6,10 +6,10 @@ const preview: Preview = {
(Story) => { (Story) => {
return ( return (
<div className={lato.className}> <div className={lato.className}>
<Story/> <Story />
</div> </div>
) )
} },
], ],
parameters: { parameters: {
controls: { 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 }) 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 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 }) 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 export default Page

View file

@ -1,7 +1,13 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */ /* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config' 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 GET = REST_GET(config)
export const POST = REST_POST(config) export const POST = REST_POST(config)

View file

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

View file

@ -1,29 +1,31 @@
"use client" 'use client'
import {Menu} from "@/components/Menu/Menu"; import { Menu } from '@/components/Menu/Menu'
import {BannerText} from "@/components/BannerText/BannerText"; import { BannerText } from '@/components/BannerText/BannerText'
import {HomeBanner, HomeBannerHandle} from "@/components/HomeBanner/HomeBanner"; import {
import {useRef} from "react"; HomeBanner,
HomeBannerHandle,
} from '@/components/HomeBanner/HomeBanner'
import { useRef } from 'react'
import { Worship } from '@/payload-types' import { Worship } from '@/payload-types'
type BannerWithMenuProps = { 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() { return (
bannerRef.current?.newStar(); <HomeBanner stars={40} ref={bannerRef}>
bannerRef.current?.newStar(); <Menu starClick={addThreeNewStars} nextMass={nextMass} />
bannerRef.current?.newStar(); <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({ export const faustina = Faustina({
subsets: ['latin'], subsets: ['latin'],
display: "swap" display: 'swap',
}) })
export const lato = Lato({ export const lato = Lato({
subsets: ['latin'], subsets: ['latin'],
weight: ["400"], weight: ['400'],
display: 'swap' display: 'swap',
}) })

View file

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

View file

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

View file

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

View file

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

View file

@ -5,29 +5,29 @@ import { useLocation } from '@/hooks/useLocation'
import { useLiturgyCalendarTitle } from '@/hooks/useLiturgyCalendarTitle' import { useLiturgyCalendarTitle } from '@/hooks/useLiturgyCalendarTitle'
import { Container } from '@/components/Container/Container' import { Container } from '@/components/Container/Container'
import { Card } from '@/components/Card/Card' 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 { MassTitle } from '@/components/MassTitle/MassTitle'
import { useCompactDate, useDate } from '@/hooks/useCompactDate' import { useCompactDate, useDate } from '@/hooks/useCompactDate'
import { useTime } from '@/hooks/useTime' import { useTime } from '@/hooks/useTime'
import { Pill } from '@/components/Pill/Pill' import { Pill } from '@/components/Pill/Pill'
import { useMassType } from '@/hooks/useMassType' import { useMassType } from '@/hooks/useMassType'
import Image from 'next/image' import Image from 'next/image'
import bell from "./bell.svg"; import bell from './bell.svg'
import locationIcon from "./location.svg" import locationIcon from './location.svg'
import question from "./question.svg" import question from './question.svg'
import { LocationMap } from '@/components/Map/Map' import { LocationMap } from '@/components/Map/Map'
import { Testimony } from '@/components/Testimony/Testimony' import { Testimony } from '@/components/Testimony/Testimony'
export default async function Page({params}: {params: {id: string}}) { export default async function Page({ params }: { params: { id: string } }) {
const payload = await getPayloadHMR({ config: configPromise }); const payload = await getPayloadHMR({ config: configPromise })
const worship = await payload.findByID({ const worship = await payload.findByID({
id: params.id, id: params.id,
collection: 'worship' collection: 'worship',
}); })
const location = useLocation(worship.location) const location = useLocation(worship.location)
const title = useLiturgyCalendarTitle(worship.date); const title = useLiturgyCalendarTitle(worship.date)
const date = useDate(worship.date); const date = useDate(worship.date)
const time = useTime(worship.date); const time = useTime(worship.date)
const type = useMassType(worship.type) const type = useMassType(worship.type)
return ( return (
@ -37,11 +37,15 @@ export default async function Page({params}: {params: {id: string}}) {
<Container> <Container>
<MassTitle title={title} cancelled={worship.cancelled} /> <MassTitle title={title} cancelled={worship.cancelled} />
<div className={styles.info}> <div className={styles.info}>
<Card> <Card>
<div className={styles.centerIcon}> <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>
<div className={styles.cardContent}> <div className={styles.cardContent}>
<div className={styles.marginBottom}> <div className={styles.marginBottom}>
@ -55,7 +59,12 @@ export default async function Page({params}: {params: {id: string}}) {
<Card> <Card>
<div className={styles.centerIcon}> <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>
<div className={styles.cardContent}> <div className={styles.cardContent}>
<div className={styles.address}> <div className={styles.address}>
@ -65,22 +74,30 @@ export default async function Page({params}: {params: {id: string}}) {
</div> </div>
</Card> </Card>
{worship.description && {worship.description && (
<Card> <Card>
<div className={styles.centerIcon}> <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>
<div className={styles.cardText}> <div className={styles.cardText}>{worship.description}</div>
{worship.description}
</div>
</Card> </Card>
} )}
</div> </div>
</Container> </Container>
<LocationMap /> <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 { .info {
display: flex; display: flex;
gap: 30px; gap: 30px;
margin-bottom: 80px; margin-bottom: 80px;
} }
.cardContent { .cardContent {
padding-top: 30px; padding-top: 30px;
padding-left: 50px; padding-left: 50px;
} }
.cardText { .cardText {
padding-top: 30px; padding-top: 30px;
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;
} }
.marginBottom { .marginBottom {
margin-bottom: 20px; margin-bottom: 20px;
} }
.address { .address {
white-space: pre; white-space: pre;
} }
.cardIcon { .cardIcon {
margin-bottom: 10px; margin-bottom: 10px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
transition: transform 100ms; transition: transform 100ms;
} }
.cardIcon:hover { .cardIcon:hover {
transform: rotateZ(-20deg); transform: rotateZ(-20deg);
} }
.centerIcon { .centerIcon {
padding-top: 30px; padding-top: 30px;
text-align: center; text-align: center;
} }

View file

@ -1,37 +1,37 @@
import { CollectionConfig } from 'payload' import { CollectionConfig } from 'payload'
export const Churches: CollectionConfig = { export const Churches: CollectionConfig = {
slug: 'church', slug: 'church',
labels: { labels: {
singular: { singular: {
de: 'Kirche' de: 'Kirche',
},
plural: {
de: 'Kirchen'
}
}, },
fields: [ plural: {
{ de: 'Kirchen',
name: 'name',
label: {
de: 'Name'
},
type: 'text',
required: true,
},
{
name: 'address',
label: {
de: 'Addresse'
},
type: 'textarea',
required: true,
}
],
admin: {
useAsTitle: 'name'
}, },
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', slug: 'testimony',
labels: { labels: {
singular: { singular: {
de: 'Zeugnis' de: 'Zeugnis',
}, },
plural: { plural: {
de: 'Zeugnisse' de: 'Zeugnisse',
} },
}, },
fields: [ fields: [
{ {
name: 'testimony', name: 'testimony',
label: { label: {
de: 'Zeugnis' de: 'Zeugnis',
}, },
type: 'textarea', type: 'textarea',
required: true, required: true,
@ -30,7 +30,7 @@ export const Testimony: CollectionConfig = {
{ {
name: 'occupation', name: 'occupation',
label: { label: {
de: 'Beschäftigung' de: 'Beschäftigung',
}, },
type: 'text', type: 'text',
required: false, required: false,
@ -38,16 +38,16 @@ export const Testimony: CollectionConfig = {
{ {
name: 'category', name: 'category',
label: { label: {
de: 'Kategorie' de: 'Kategorie',
}, },
type: 'select', type: 'select',
options: [ options: [
{ {
value: 'EUCHARIST', value: 'EUCHARIST',
label: 'Eucharistie' label: 'Eucharistie',
} },
], ],
required: true, required: true,
} },
] ],
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -5,11 +5,11 @@ const meta: Meta<typeof Card> = {
component: Card, component: Card,
} }
type Story = StoryObj<typeof Card>; type Story = StoryObj<typeof Card>
export default meta export default meta
export const Default: Story = { export const Default: Story = {
args: { 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 = { type CardProps = {
children?: JSX.Element | JSX.Element[]; children?: JSX.Element | JSX.Element[]
} }
export const Card = ({children}: CardProps) => { export const Card = ({ children }: CardProps) => {
return ( return <div className={styles.card}>{children}</div>
<div className={styles.card}>
{children}
</div>
)
} }

View file

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

View file

@ -1,22 +1,22 @@
import {Meta, StoryObj} from "@storybook/react"; import { Meta, StoryObj } from '@storybook/react'
import { Container } from "./Container"; import { Container } from './Container'
const meta: Meta<typeof Container> = { const meta: Meta<typeof Container> = {
component: Container component: Container,
} }
type Story = StoryObj<typeof Container>; type Story = StoryObj<typeof Container>
export default meta; export default meta
export const Default: Story = { export const Default: Story = {
args: { args: {
children: <>Some content</> children: <>Some content</>,
} },
} }
export const Yellow: Story = { export const Yellow: Story = {
args: { args: {
background: "yellow", background: 'yellow',
children: <>Some content</> 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' import classNames from 'classnames'
type ContainerProps = { type ContainerProps = {
background?: "yellow" background?: 'yellow'
children: JSX.Element | JSX.Element[] children: JSX.Element | JSX.Element[]
} }
export const Container = ({children, background}: ContainerProps) => { export const Container = ({ children, background }: ContainerProps) => {
return ( return (
<div className={classNames({[styles.yellow]: background === "yellow"})}> <div className={classNames({ [styles.yellow]: background === 'yellow' })}>
<div className={styles.container}> <div className={styles.container}>{children}</div>
{children}
</div>
</div> </div>
) )
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react'
/** /**
* Countdown until targetDate, * Countdown until targetDate,
@ -7,36 +7,33 @@ import { useEffect, useState } from 'react';
* *
*/ */
const useCountdown = (targetDate: number) => { const useCountdown = (targetDate: number) => {
const [countDown, setCountDown] = useState(targetDate - new Date().getTime())
const [countDown, setCountDown] = useState( useEffect(() => {
targetDate - new Date().getTime() if (countDown > 0) {
); setTimeout(() => setCountDown(countDown - 1000), 1000)
} else {
setCountDown(0)
}
}, [countDown])
useEffect(() => { return getDaysHoursMinutesAndSeconds(countDown)
if (countDown > 0) { }
setTimeout(() => setCountDown(countDown - 1000), 1000);
} else {
setCountDown(0)
}
}, [countDown]);
return getDaysHoursMinutesAndSeconds(countDown);
};
/** /**
* Return an array of four numbers, representing the * Return an array of four numbers, representing the
* numbers days, hours, minutes and seconds * numbers days, hours, minutes and seconds
*/ */
const getDaysHoursMinutesAndSeconds = (countDown: number) => { const getDaysHoursMinutesAndSeconds = (countDown: number) => {
// calculate time left // calculate time left
const days = Math.floor(countDown / (1000 * 60 * 60 * 24)); const days = Math.floor(countDown / (1000 * 60 * 60 * 24))
const hours = Math.floor( const hours = Math.floor(
(countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) (countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
); )
const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60)); const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((countDown % (1000 * 60)) / 1000); 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 type { Meta, StoryObj } from '@storybook/react'
import {MassTimerTooltip} from "./MassTimerTooltip"; import { MassTimerTooltip } from './MassTimerTooltip'
const meta: Meta<typeof MassTimerTooltip> = { const meta: Meta<typeof MassTimerTooltip> = {
component: MassTimerTooltip component: MassTimerTooltip,
} }
type Story = StoryObj<typeof MassTimerTooltip>; type Story = StoryObj<typeof MassTimerTooltip>
export default meta; export default meta
export const Default: Story = { export const Default: Story = {
args: { args: {},
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,63 +1,62 @@
.nav { .nav {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
gap: 20px; gap: 20px;
color: #3d3d3d; color: #3d3d3d;
padding-top: 15px; padding-top: 15px;
height: 50px; height: 50px;
border-bottom: 1px solid rgba(217, 217, 217, 0.19); border-bottom: 1px solid rgba(217, 217, 217, 0.19);
} }
.navMobile { .navMobile {
display: none; display: none;
} }
.itemsLeft { .itemsLeft {
display: flex; display: flex;
gap: 20px; gap: 20px;
} }
.menuLink { .menuLink {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
font-weight: 600; font-weight: 600;
transition: opacity 100ms ease-in; transition: opacity 100ms ease-in;
} }
.menuLink:hover { .menuLink:hover {
opacity: 0.7; opacity: 0.7;
} }
.itemsRight { .itemsRight {
margin-left: auto; margin-left: auto;
display: flex; display: flex;
gap: 20px; gap: 20px;
justify-content: flex-end; justify-content: flex-end;
align-items: baseline; align-items: baseline;
} }
.button { .button {
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
border: none; border: none;
background-color: #eeeeee; background-color: #eeeeee;
transition: background-color 0.1s ease-in-out; transition: background-color 0.1s ease-in-out;
font-family: inherit; font-family: inherit;
font-weight: 600; font-weight: 600;
} }
.button:hover { .button:hover {
background-color: #fff318; background-color: #fff318;
cursor: pointer; cursor: pointer;
} }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.navMobile {
display: block;
}
.navMobile { .itemsLeft {
display: block; 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' import { MenuBaseLayer } from '@/components/MenuBaseLayer/MenuBaseLayer'
const meta: Meta<typeof MenuBaseLayer> = { const meta: Meta<typeof MenuBaseLayer> = {
component: MenuBaseLayer component: MenuBaseLayer,
} }
type Story = StoryObj<typeof MenuBaseLayer>; type Story = StoryObj<typeof MenuBaseLayer>
export default meta; export default meta
export const Default: Story = { 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 = () => { export const MenuBaseLayer = () => {
return <div className={styles.background}></div> return <div className={styles.background}></div>

View file

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

View file

@ -5,11 +5,11 @@ const meta: Meta<typeof Pill> = {
component: Pill, component: Pill,
} }
type Story = StoryObj<typeof Pill>; type Story = StoryObj<typeof Pill>
export default meta export default meta
export const Default: Story = { export const Default: Story = {
args: { 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 = { type PillProps = {
children: JSX.Element | string | JSX.Element[] children: JSX.Element | string | JSX.Element[]
} }
export const Pill = ({children}: PillProps) => { export const Pill = ({ children }: PillProps) => {
return ( return <div className={styles.pill}>{children}</div>
<div className={styles.pill}>
{children}
</div>
)
} }

View file

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

View file

@ -5,12 +5,13 @@ const meta: Meta<typeof Testimony> = {
component: Testimony, component: Testimony,
} }
type Story = StoryObj<typeof Testimony>; type Story = StoryObj<typeof Testimony>
export default meta export default meta
export const Default: Story = { export const Default: Story = {
args: { args: {
name: 'Johan Schäfer', 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 { Container } from '@/components/Container/Container'
import classNames from 'classnames' import classNames from 'classnames'
import { faustina } from '@/app/fonts' import { faustina } from '@/app/fonts'
type TestimonyProps = { type TestimonyProps = {
name: string, name: string
testimony: string, testimony: string
} }
export const Testimony = ({name, testimony}: TestimonyProps) => { export const Testimony = ({ name, testimony }: TestimonyProps) => {
return ( return (
<Container background={"yellow"}> <Container background={'yellow'}>
<div className={styles.testimony}> <div className={styles.testimony}>
<div className={styles.person}> <div className={styles.person}></div>
</div>
<div> <div>
<p className={classNames(styles.testimonyText, faustina.className)}> <p className={classNames(styles.testimonyText, faustina.className)}>
{testimony} {testimony}
</p> </p>
<p> <p>{name}</p>
{name}
</p>
</div> </div>
</div> </div>
</Container> </Container>

View file

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

View file

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

View file

@ -10,5 +10,11 @@ export const useCompactDate = (date: string) => {
* *
*/ */
export const useDate = (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 * e.G. "2024-12-25" => Christmas
*/ */
export const useLiturgyCalendarTitle = (date: string) => { export const useLiturgyCalendarTitle = (date: string) => {
const day = calendar[date.substring(0, 10)]; const day = calendar[date.substring(0, 10)]
return day.name; 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 => { export const useLocation = (location: string | Church): Church => {
if (typeof location === 'string') {
if(typeof location === 'string') {
return { return {
address: '', address: '',
createdAt: '', createdAt: '',
name: 'Unknown', name: 'Unknown',
updatedAt: '', updatedAt: '',
id: location id: location,
} }
} else { } else {
return location return location

View file

@ -4,7 +4,7 @@ import { Church } from '@/payload-types'
* Get user friendly location name * Get user friendly location name
*/ */
export const useLocationName = (location: string | Church) => { export const useLocationName = (location: string | Church) => {
if (typeof location == "string") { if (typeof location == 'string') {
return location return location
} else { } else {
return location.name 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) { switch (type) {
case "FAMILY": case 'FAMILY':
return "Familien Messe"; return 'Familien Messe'
case "WORD": case 'WORD':
return "Wort-Gottes-Feier"; return 'Wort-Gottes-Feier'
case 'MASS': case 'MASS':
default: default:
return "Heilige Messe"; return 'Heilige Messe'
} }
} }

View file

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

View file

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

View file

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

View file

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