diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index daa264b..799ce69 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -19,6 +19,9 @@ const preview: Preview = { }, ], parameters: { + nextjs: { + appDirectory: true, + }, controls: { matchers: { color: /(background|color)$/i, diff --git a/next.config.mjs b/next.config.mjs index 4942e4a..6bfa9db 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -4,9 +4,6 @@ import { withPayload } from '@payloadcms/next/withPayload' const nextConfig = { // Your Next.js config here output: 'standalone', - eslint: { - ignoreDuringBuilds: true, - }, images: { remotePatterns: [ { diff --git a/src/app/(home)/old/home2/page.tsx b/src/app/(home)/old/home2/page.tsx index 8f43789..db20949 100644 --- a/src/app/(home)/old/home2/page.tsx +++ b/src/app/(home)/old/home2/page.tsx @@ -1,40 +1,7 @@ -import { fetchEvents } from '@/fetch/events' -import { fetchWorship } from '@/fetch/worship' -import { fetchBlogPosts } from '@/fetch/blog' -import { fetchHighlights } from '@/fetch/highlights' import { Home } from '@/pageComponents/Home/Home' -import moment from 'moment' -import { fetchLastAnnouncement, fetchLastAnnouncements } from '@/fetch/announcement' -import { perParish } from '@/utils/dto/perParish' -import { fetchLastCalendars } from '@/fetch/calendar' export const dynamic = 'force-dynamic' export default async function HomePage() { - - const fromDate = moment().isoWeekday(1).hours(0).minutes(0); - const tillDate = moment().isoWeekday(7).hours(23).minutes(59); - const events = await fetchEvents() - const worship = await fetchWorship({ - fromDate: fromDate.toDate(), - tillDate: tillDate.toDate(), - }); - const blog = await fetchBlogPosts(true) - const highlights = await fetchHighlights() - const announcements = await fetchLastAnnouncements(); - const announcementsLinks = announcements ? perParish(announcements) : []; - const calendars = await fetchLastCalendars(); - const calendarsLinks = calendars ? perParish(calendars) : []; - - - return ( - - ) + return } diff --git a/src/components/Classifieds/Ad.stories.tsx b/src/components/Classifieds/Ad.stories.tsx index 4c2065c..e2051d7 100644 --- a/src/components/Classifieds/Ad.stories.tsx +++ b/src/components/Classifieds/Ad.stories.tsx @@ -40,7 +40,7 @@ const makeAdText = (text: string): SerializedEditorState => }, ], }, - }) as SerializedEditorState + }) as unknown as SerializedEditorState export const Default: Story = { args: { diff --git a/src/components/Classifieds/Classifieds.stories.tsx b/src/components/Classifieds/Classifieds.stories.tsx index 4250b87..8841df8 100644 --- a/src/components/Classifieds/Classifieds.stories.tsx +++ b/src/components/Classifieds/Classifieds.stories.tsx @@ -40,7 +40,7 @@ const makeAdText = (text: string): SerializedEditorState => }, ], }, - }) as SerializedEditorState + }) as unknown as SerializedEditorState export const Default: Story = { args: { diff --git a/src/components/Text/HTMLText.stories.tsx b/src/components/Text/HTMLText.stories.tsx index 37f30d7..250f7d5 100644 --- a/src/components/Text/HTMLText.stories.tsx +++ b/src/components/Text/HTMLText.stories.tsx @@ -39,7 +39,7 @@ const sampleState: SerializedEditorState = { }, ], }, -} as SerializedEditorState +} as unknown as SerializedEditorState export const ThreeFourth: Story = { args: { diff --git a/src/compositions/ContactForm/ContactForm.stories.tsx b/src/compositions/ContactForm/ContactForm.stories.tsx index 0d1b0e4..e9386ba 100644 --- a/src/compositions/ContactForm/ContactForm.stories.tsx +++ b/src/compositions/ContactForm/ContactForm.stories.tsx @@ -1,23 +1,28 @@ import { Meta, StoryObj } from '@storybook/nextjs-vite' -import { ContactForm } from './ContactForm' +import { ContactFormView, ContactFormState } from './ContactFormView' -const meta: Meta = { - component: ContactForm, +const noopAction = async (): Promise => ({ + message: 'Storybook: form submission is a no-op.', +}) + +const meta: Meta = { + component: ContactFormView, + args: { + action: noopAction, + }, } -type Story = StoryObj; +type Story = StoryObj export default meta export const Default: Story = { args: { schema: 'base', - toEmail: 'test@test.com' }, } export const Schema: Story = { args: { schema: 'contrast', - toEmail: 'test@test.com' - } -} \ No newline at end of file + }, +} diff --git a/src/compositions/ContactForm/ContactForm.tsx b/src/compositions/ContactForm/ContactForm.tsx index e70bf11..4057c0e 100644 --- a/src/compositions/ContactForm/ContactForm.tsx +++ b/src/compositions/ContactForm/ContactForm.tsx @@ -1,44 +1,14 @@ 'use client' -import { Input } from '@/components/Input/Input' -import { Button } from '@/components/Button/Button' -import styles from "./styles.module.scss" -import classNames from 'classnames' import { send } from '@/utils/actions' -import { useActionState } from 'react' +import { ContactFormView } from './ContactFormView' type ContactFormProps = { - schema?: "base" | "contrast", + schema?: 'base' | 'contrast' toEmail: string } -const initialState = { - message: '', +export const ContactForm = ({ schema, toEmail }: ContactFormProps) => { + const sendWithEmail = send.bind(null, toEmail) + return } - -export const ContactForm = ({schema, toEmail}: ContactFormProps) => { - - const sendWithEmail = send.bind(null, toEmail); - const [state, formAction, pending] = useActionState(sendWithEmail, initialState) - - return ( -
-
- - -
-
- -
-
- -
-

- {state.message} -

-
- -
-
- ) -} \ No newline at end of file diff --git a/src/compositions/ContactForm/ContactFormView.tsx b/src/compositions/ContactForm/ContactFormView.tsx new file mode 100644 index 0000000..e221798 --- /dev/null +++ b/src/compositions/ContactForm/ContactFormView.tsx @@ -0,0 +1,51 @@ +'use client' + +import { Input } from '@/components/Input/Input' +import { Button } from '@/components/Button/Button' +import styles from './styles.module.scss' +import classNames from 'classnames' +import { useActionState } from 'react' + +export type ContactFormState = { + message: string + errors?: Record +} + +export type ContactFormAction = ( + prevState: ContactFormState, + formData: FormData, +) => Promise | ContactFormState + +type ContactFormViewProps = { + schema?: 'base' | 'contrast' + action: ContactFormAction +} + +const initialState: ContactFormState = { + message: '', +} + +export const ContactFormView = ({ schema, action }: ContactFormViewProps) => { + const [state, formAction, pending] = useActionState(action, initialState) + + return ( +
+
+ + +
+
+ +
+
+ +
+

{state.message}

+
+ +
+
+ ) +} diff --git a/src/compositions/ContactSection/ContactSection.stories.tsx b/src/compositions/ContactSection/ContactSection.stories.tsx index 69a3872..b013d07 100644 --- a/src/compositions/ContactSection/ContactSection.stories.tsx +++ b/src/compositions/ContactSection/ContactSection.stories.tsx @@ -1,17 +1,23 @@ import { Meta, StoryObj } from '@storybook/nextjs-vite' -import { ContactSection } from './ContactSection' +import { ContactSectionView } from './ContactSectionView' +import { ContactFormView, ContactFormState } from '@/compositions/ContactForm/ContactFormView' -const meta: Meta = { - component: ContactSection, +const noopAction = async (): Promise => ({ + message: 'Storybook: form submission is a no-op.', +}) + +const meta: Meta = { + component: ContactSectionView, } -type Story = StoryObj; +type Story = StoryObj export default meta export const Default: Story = { args: { - title: "Kontakt", - description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vel dolor semper, consectetur augue quis, elementum tellus. Nulla ut porta lorem. Nulla posuere quam nisi, ut porttitor diam dignissim eget. Morbi imperdiet et lectus quis dapibus. Cras sollicitudin est augue, vel rhoncus massa elementum vitae. Donec sagittis pulvinar nibh ultrices tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus.', - toEmail: 'kontak@test.com' + title: 'Kontakt', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vel dolor semper, consectetur augue quis, elementum tellus. Nulla ut porta lorem. Nulla posuere quam nisi, ut porttitor diam dignissim eget. Morbi imperdiet et lectus quis dapibus. Cras sollicitudin est augue, vel rhoncus massa elementum vitae. Donec sagittis pulvinar nibh ultrices tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus.', + form: , }, -} \ No newline at end of file +} diff --git a/src/compositions/ContactSection/ContactSection.tsx b/src/compositions/ContactSection/ContactSection.tsx index 8606bd2..e820398 100644 --- a/src/compositions/ContactSection/ContactSection.tsx +++ b/src/compositions/ContactSection/ContactSection.tsx @@ -1,34 +1,28 @@ -import { Section } from '@/components/Section/Section' -import { Container } from '@/components/Container/Container' import { ContactForm } from '@/compositions/ContactForm/ContactForm' -import { Title } from '@/components/Title/Title' -import { Col } from '@/components/Flex/Col' -import { Row } from '@/components/Flex/Row' -import { TextDiv } from '@/components/Text/TextDiv' +import { ContactSectionView } from './ContactSectionView' type ContactSectionProps = { - title: string, - description: string, + title: string + description: string backgroundColor?: 'off-white' schema?: 'base' | 'contrast' toEmail: string } - -export const ContactSection = ({title, description, schema, backgroundColor, toEmail}: ContactSectionProps) => { +export const ContactSection = ({ + title, + description, + schema, + backgroundColor, + toEmail, +}: ContactSectionProps) => { return ( -
- - - - - <TextDiv text={description} /> - </Col> - <Col> - <ContactForm schema={schema} toEmail={toEmail}/> - </Col> - </Row> - </Container> - </Section> + <ContactSectionView + title={title} + description={description} + schema={schema} + backgroundColor={backgroundColor} + form={<ContactForm schema={schema} toEmail={toEmail} />} + /> ) -} \ No newline at end of file +} diff --git a/src/compositions/ContactSection/ContactSectionView.tsx b/src/compositions/ContactSection/ContactSectionView.tsx new file mode 100644 index 0000000..2380183 --- /dev/null +++ b/src/compositions/ContactSection/ContactSectionView.tsx @@ -0,0 +1,37 @@ +import { Section } from '@/components/Section/Section' +import { Container } from '@/components/Container/Container' +import { Title } from '@/components/Title/Title' +import { Col } from '@/components/Flex/Col' +import { Row } from '@/components/Flex/Row' +import { TextDiv } from '@/components/Text/TextDiv' +import type { ReactNode } from 'react' + +type ContactSectionViewProps = { + title: string + description: string + backgroundColor?: 'off-white' + schema?: 'base' | 'contrast' + form: ReactNode +} + +export const ContactSectionView = ({ + title, + description, + schema, + backgroundColor, + form, +}: ContactSectionViewProps) => { + return ( + <Section backgroundColor={backgroundColor}> + <Container> + <Row> + <Col> + <Title title={title} size={'md'} color={schema} /> + <TextDiv text={description} /> + </Col> + <Col>{form}</Col> + </Row> + </Container> + </Section> + ) +} diff --git a/src/compositions/Footer/Footer.stories.tsx b/src/compositions/Footer/Footer.stories.tsx index 8615da0..2670c50 100644 --- a/src/compositions/Footer/Footer.stories.tsx +++ b/src/compositions/Footer/Footer.stories.tsx @@ -1,13 +1,45 @@ import { Meta, StoryObj } from '@storybook/nextjs-vite' -import { Footer } from './Footer' +import type { Footer } from '@/payload-types' +import { FooterView } from './FooterView' -const meta: Meta<typeof Footer> = { - component: Footer, +const mockFooter: Footer = { + id: 'mock-footer', + groups: [ + { + title: 'Pfarrei', + links: [ + { label: 'Über uns', href: '/ueber-uns' }, + { label: 'Kontakt', href: '/kontakt' }, + { label: 'Gemeinden', href: '/gemeinden' }, + ], + }, + { + title: 'Service', + links: [ + { label: 'Gottesdienste', href: '/gottesdienst' }, + { label: 'Veranstaltungen', href: '/veranstaltungen' }, + { label: 'Blog', href: '/blog' }, + ], + }, + ], } -type Story = StoryObj<typeof Footer>; +const mockPrayers = [ + 'Herr, gib mir heute die Gelassenheit, Dinge hinzunehmen, die ich nicht ändern kann.', + 'Vater unser im Himmel, geheiligt werde dein Name.', + 'Gegrüßet seist du, Maria, voll der Gnade.', +] + +const meta: Meta<typeof FooterView> = { + component: FooterView, +} + +type Story = StoryObj<typeof FooterView> export default meta export const Default: Story = { - args: {}, -} \ No newline at end of file + args: { + prayers: mockPrayers, + footer: mockFooter, + }, +} diff --git a/src/compositions/Footer/Footer.tsx b/src/compositions/Footer/Footer.tsx index 36ab206..29c1b82 100644 --- a/src/compositions/Footer/Footer.tsx +++ b/src/compositions/Footer/Footer.tsx @@ -1,13 +1,6 @@ -import { Section } from '@/components/Section/Section' -import { Container } from '@/components/Container/Container' -import { Logo } from '@/components/Logo/Logo' -import styles from './styles.module.scss' -import { Row } from '@/components/Flex/Row' -import { Col } from '@/components/Flex/Col' -import { RandomPrayer } from '@/components/RandomPrayer/RandomPrayer' -import Link from 'next/link' import { fetchPrayers } from '@/fetch/prayers' import { fetchFooter } from '@/fetch/footer' +import { FooterView } from './FooterView' export const Footer = async () => { const [prayers, footer] = await Promise.all([ @@ -15,48 +8,5 @@ export const Footer = async () => { fetchFooter(), ]) - return ( - <div className={styles.container}> - <Section backgroundColor="soft"> - <Container> - <Row> - <Col> - <br /> - <Logo - color={'#ffffff'} - textColor={'var(--base-color)'} - withText={true} - height={100} - /> - </Col> - - <Col> - <Row gap={25}> - {footer.groups?.map((group, i) => ( - <Col key={i}> - <p> - <strong>{group.title}</strong> - </p> - <ul className={styles.list}> - {group.links?.map((link, j) => ( - <li key={j}> - <Link href={link.href}>{link.label}</Link> - </li> - ))} - </ul> - </Col> - ))} - <Col> - <p> - <strong>Stoßgebet</strong> - </p> - <RandomPrayer prayers={prayers} /> - </Col> - </Row> - </Col> - </Row> - </Container> - </Section> - </div> - ) + return <FooterView prayers={prayers} footer={footer} /> } diff --git a/src/compositions/Footer/FooterView.tsx b/src/compositions/Footer/FooterView.tsx new file mode 100644 index 0000000..58488ac --- /dev/null +++ b/src/compositions/Footer/FooterView.tsx @@ -0,0 +1,61 @@ +import { Section } from '@/components/Section/Section' +import { Container } from '@/components/Container/Container' +import { Logo } from '@/components/Logo/Logo' +import styles from './styles.module.scss' +import { Row } from '@/components/Flex/Row' +import { Col } from '@/components/Flex/Col' +import { RandomPrayer } from '@/components/RandomPrayer/RandomPrayer' +import Link from 'next/link' +import type { Footer } from '@/payload-types' + +type FooterViewProps = { + prayers: string[] + footer: Footer +} + +export const FooterView = ({ prayers, footer }: FooterViewProps) => { + return ( + <div className={styles.container}> + <Section backgroundColor="soft"> + <Container> + <Row> + <Col> + <br /> + <Logo + color={'#ffffff'} + textColor={'var(--base-color)'} + withText={true} + height={100} + /> + </Col> + + <Col> + <Row gap={25}> + {footer.groups?.map((group, i) => ( + <Col key={i}> + <p> + <strong>{group.title}</strong> + </p> + <ul className={styles.list}> + {group.links?.map((link, j) => ( + <li key={j}> + <Link href={link.href}>{link.label}</Link> + </li> + ))} + </ul> + </Col> + ))} + <Col> + <p> + <strong>Stoßgebet</strong> + </p> + <RandomPrayer prayers={prayers} /> + </Col> + </Row> + </Col> + </Row> + </Container> + </Section> + </div> + ) +} diff --git a/src/compositions/PublicationAndNewsletter/PublicationAndNewsletter.tsx b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletter.tsx index c30084c..3e19542 100644 --- a/src/compositions/PublicationAndNewsletter/PublicationAndNewsletter.tsx +++ b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletter.tsx @@ -1,87 +1,45 @@ -import { Section } from '@/components/Section/Section' -import { Container } from '@/components/Container/Container' -import { Row } from '@/components/Flex/Row' -import { Col } from '@/components/Flex/Col' -import { Title } from '@/components/Title/Title' -import { NewsletterItem } from '@/compositions/PublicationAndNewsletter/NewsletterItem' -import styles from "./styles.module.scss" -import Image from 'next/image' -import envelope from "./envelope.svg" import Sandbox from '@nyariv/sandboxjs' import { fetchLastMagazine } from '@/fetch/magazine' - -type NewsletterData = { - guid: string, - title: string, - link: string, - pubDate: string, -} +import { + PublicationAndNewsletterView, + NewsletterArchiveItem, +} from './PublicationAndNewsletterView' export const PublicationAndNewsletter = async () => { - - let archiveData: NewsletterData[] = [] + let archiveData: NewsletterArchiveItem[] = [] try { const response = await fetch( 'https://s3-eu-west-1.amazonaws.com/files.crsend.com/37000/37866/rss/mailings.js', - { cache: "force-cache", next: { revalidate: 3600 }} - ); - const jsContent = await response.text(); + { cache: 'force-cache', next: { revalidate: 3600 } }, + ) + const jsContent = await response.text() - // safe (?) alternative to `eval()` - const sandbox = new Sandbox(); - const scope: {cr_archive: NewsletterData[] } = { cr_archive: [] }; - const exec = sandbox.compile(jsContent); - exec(scope).run(); - - archiveData = scope.cr_archive.slice(0, 3); + const sandbox = new Sandbox() + const scope: { cr_archive: NewsletterArchiveItem[] } = { cr_archive: [] } + const exec = sandbox.compile(jsContent) + exec(scope).run() + archiveData = scope.cr_archive.slice(0, 3) } catch { - console.error("Could not fetch newsletters. Please check PublicationAndNewsletter component") + console.error( + 'Could not fetch newsletters. Please check PublicationAndNewsletter component', + ) } - const magazine = await fetchLastMagazine(); - const magazine_url = magazine && typeof magazine.document === "object" ? magazine.document.url || undefined : undefined; - const magazine_cover = magazine && typeof magazine.cover === "object" ? magazine.cover : undefined; + const magazine = await fetchLastMagazine() + const magazineUrl = + magazine && typeof magazine.document === 'object' + ? magazine.document.url || undefined + : undefined + const magazineCover = + magazine && typeof magazine.cover === 'object' ? magazine.cover : undefined return ( - <Section backgroundColor={"off-white"}> - <Container> - <Row alignItems={"center"}> - <Col> - {magazine_url && magazine_cover && magazine_cover.url && - <a href={magazine_url} target={'_blank'}> - <Image - className={styles.image} - src={magazine_cover.url} - width={magazine_cover.width || 500} - height={magazine_cover.height || 600} - alt={'Pfarreimagazin Ausgabe'} - unoptimized={true} - /> - </a> - } - </Col> - <Col> - <div className={styles.titleContainer}> - <Image src={envelope} alt={'Newsletter icon'} /> - <Title - title={'Newsletter aus dem Bistum'} - size={'md'} - /> - </div> - - {archiveData.map((item) => ( - <NewsletterItem - key={item.guid} - title={item.title} - link={item.link} - pubDate={item.pubDate}> - </NewsletterItem> - ))} - </Col> - </Row> - </Container> - </Section> + <PublicationAndNewsletterView + archiveData={archiveData} + magazineUrl={magazineUrl} + magazineCover={magazineCover} + /> ) -} \ No newline at end of file +} diff --git a/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.stories.tsx b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.stories.tsx new file mode 100644 index 0000000..aa7ae09 --- /dev/null +++ b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.stories.tsx @@ -0,0 +1,66 @@ +import { Meta, StoryObj } from '@storybook/nextjs-vite' +import { PublicationAndNewsletterView } from './PublicationAndNewsletterView' + +const meta: Meta<typeof PublicationAndNewsletterView> = { + component: PublicationAndNewsletterView, +} + +type Story = StoryObj<typeof PublicationAndNewsletterView> +export default meta + +const archiveData = [ + { + guid: '1', + title: 'Newsletter aus dem Bistum – März 2026', + link: 'https://example.com/newsletter/march', + pubDate: '2026-03-01T00:00:00.000Z', + }, + { + guid: '2', + title: 'Newsletter aus dem Bistum – Februar 2026', + link: 'https://example.com/newsletter/february', + pubDate: '2026-02-01T00:00:00.000Z', + }, + { + guid: '3', + title: 'Newsletter aus dem Bistum – Januar 2026', + link: 'https://example.com/newsletter/january', + pubDate: '2026-01-01T00:00:00.000Z', + }, +] + +export const Default: Story = { + args: { + archiveData, + magazineUrl: 'https://example.com/magazine.pdf', + magazineCover: { + url: 'https://placehold.co/500x600/png', + width: 500, + height: 600, + }, + }, +} + +export const NewsletterOnly: Story = { + args: { + archiveData, + }, +} + +export const MagazineOnly: Story = { + args: { + archiveData: [], + magazineUrl: 'https://example.com/magazine.pdf', + magazineCover: { + url: 'https://placehold.co/500x600/png', + width: 500, + height: 600, + }, + }, +} + +export const Empty: Story = { + args: { + archiveData: [], + }, +} \ No newline at end of file diff --git a/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.tsx b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.tsx new file mode 100644 index 0000000..d136984 --- /dev/null +++ b/src/compositions/PublicationAndNewsletter/PublicationAndNewsletterView.tsx @@ -0,0 +1,70 @@ +import { Section } from '@/components/Section/Section' +import { Container } from '@/components/Container/Container' +import { Row } from '@/components/Flex/Row' +import { Col } from '@/components/Flex/Col' +import { Title } from '@/components/Title/Title' +import { NewsletterItem } from '@/compositions/PublicationAndNewsletter/NewsletterItem' +import styles from './styles.module.scss' +import Image from 'next/image' +import envelope from './envelope.svg' + +export type NewsletterArchiveItem = { + guid: string + title: string + link: string + pubDate: string +} + +type PublicationAndNewsletterViewProps = { + archiveData: NewsletterArchiveItem[] + magazineUrl?: string + magazineCover?: { + url?: string | null + width?: number | null + height?: number | null + } +} + +export const PublicationAndNewsletterView = ({ + archiveData, + magazineUrl, + magazineCover, +}: PublicationAndNewsletterViewProps) => { + return ( + <Section backgroundColor={'off-white'}> + <Container> + <Row alignItems={'center'}> + <Col> + {magazineUrl && magazineCover?.url && ( + <a href={magazineUrl} target={'_blank'}> + <Image + className={styles.image} + src={magazineCover.url} + width={magazineCover.width || 500} + height={magazineCover.height || 600} + alt={'Pfarreimagazin Ausgabe'} + unoptimized={true} + /> + </a> + )} + </Col> + <Col> + <div className={styles.titleContainer}> + <Image src={envelope} alt={'Newsletter icon'} /> + <Title title={'Newsletter aus dem Bistum'} size={'md'} /> + </div> + + {archiveData.map((item) => ( + <NewsletterItem + key={item.guid} + title={item.title} + link={item.link} + pubDate={item.pubDate} + ></NewsletterItem> + ))} + </Col> + </Row> + </Container> + </Section> + ) +} diff --git a/src/pageComponents/Home/Home.stories.tsx b/src/pageComponents/Home/Home.stories.tsx deleted file mode 100644 index ff7149d..0000000 --- a/src/pageComponents/Home/Home.stories.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { Meta, StoryObj } from '@storybook/nextjs-vite' -import { Home } from './Home' - -const meta: Meta<typeof Home> = { - component: Home, -} - -type Story = StoryObj<typeof Home>; -export default meta - -export const Default: Story = { - args: { - events: [ - { - id: '1', - title: 'Event 1', - date: '2024-12-02T09:21:24Z', - location: { - id: 'l1', - name: "St. Richard", - updatedAt: "", - createdAt: "" - }, - shortDescription: '', - description: "Some descripton", - cancelled: false, - updatedAt: '2024-12-02T09:21:24Z', - createdAt: '2024-12-02T09:21:24Z', - isRecurring: false - }, - { - id: '2', - title: 'Event 2', - date: '2024-12-05T09:21:24Z', - location: { - id: 'l1', - name: "St. Richard", - updatedAt: "", - createdAt: "" - }, - shortDescription: '', - description: "", - cancelled: false, - updatedAt: '2024-12-02T09:21:24Z', - createdAt: '2024-12-02T09:21:24Z', - isRecurring: false - }, - { - id: '2', - title: 'Event 2', - date: '2024-12-08T09:21:24Z', - location: { - id: 'l2', - name: "St. Hedwig", - updatedAt: "", - createdAt: "" - }, - shortDescription: '', - description: "", - cancelled: true, - updatedAt: '2024-12-02T09:21:24Z', - createdAt: '2024-12-02T09:21:24Z', - isRecurring: false - }, - ], - blog: [ - { - id: 'b1', - title: 'Blog 1', - content: { - excerpt: '', - content: [] - }, - configuration: { - showOnFrontpage: false, - }, - updatedAt: '', - createdAt: '', - }, - { - id: 'b2', - title: 'Blog 2', - content: { - excerpt: '', - content: [] - }, - configuration: { - showOnFrontpage: false, - }, - updatedAt: '', - createdAt: '', - }, - { - id: 'b3', - title: 'Blog 3', - content: { - excerpt: '', - content: [] - }, - configuration: { - showOnFrontpage: false, - }, - updatedAt: '', - createdAt: '', - }, - ], - worship: [ - { - id: 'w1', - date: '2024-12-02T09:21:24Z', - location: { - id: 'c1', - name: 'St Richard', - address: '', - createdAt: '', - updatedAt: '' - }, - type: 'MASS', - cancelled: false, - updatedAt: '', - createdAt: '', - }, - { - id: 'w1', - date: '2024-12-07T10:00:24Z', - location: { - id: 'c1', - name: 'St Richard', - address: '', - createdAt: '', - updatedAt: '' - }, - type: 'MASS', - cancelled: false, - updatedAt: '', - createdAt: '', - }, - ], - highlights: [], - announcements: [ - { - id: "link_1", - text: "St. Clara", - href: "https://disney.com" - }, - { - id: "link_2", - text: "St. Anna", - href: "https://disney.com" - }, - { - id: "link_3", - text: "St. Eduard", - href: "https://disney.com" - } - ], - calendars: [ - { - id: "link_1", - text: "St. Clara", - href: "https://disney.com" - }, - { - id: "link_2", - text: "St. Anna", - href: "https://disney.com" - }, - { - id: "link_3", - text: "St. Eduard", - href: "https://disney.com" - } - ], - } -} \ No newline at end of file diff --git a/src/pageComponents/Home/Home.tsx b/src/pageComponents/Home/Home.tsx index 33607e4..ed5ac63 100644 --- a/src/pageComponents/Home/Home.tsx +++ b/src/pageComponents/Home/Home.tsx @@ -1,198 +1,39 @@ -import { Blog, Worship, Event, Highlight } from '@/payload-types' -import { Banner } from '@/components/Banner/Banner' -import { Container } from '@/components/Container/Container' -import { Section } from '@/components/Section/Section' -import { MainText } from '@/components/MainText/MainText' -import { HR } from '@/components/HorizontalRule/HorizontalRule' -import { Title } from '@/components/Title/Title' -import { MassGrid } from '@/components/MassTable/MassGrid' -import { MassTable } from '@/components/MassTable/MassTable' -import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider' -import { blogToSlides } from '@/utils/dto/blog' -import forest from '@/assets/map.jpg' -import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider' -import { EventRow } from '@/components/EventRow/EventRow' -import { highlightLink } from '@/utils/dto/highlight' -import { Events } from '@/compositions/Events/Events' -import { transformEvents } from '@/utils/dto/events' -import { ContactSection } from '@/compositions/ContactSection/ContactSection' -import { CollapsibleImageWithText } from '@/compositions/CollapsibleImageWithText/CollapsibleImageWithText' -import { MoreInformation } from '@/pageComponents/Home/MoreInformation' -import { Button } from '@/components/Button/Button' -import styles from "./styles.module.scss" +import moment from 'moment' +import { fetchEvents } from '@/fetch/events' +import { fetchWorship } from '@/fetch/worship' +import { fetchBlogPosts } from '@/fetch/blog' +import { fetchHighlights } from '@/fetch/highlights' +import { fetchLastAnnouncements } from '@/fetch/announcement' +import { fetchLastCalendars } from '@/fetch/calendar' +import { perParish } from '@/utils/dto/perParish' import { PublicationAndNewsletter } from '@/compositions/PublicationAndNewsletter/PublicationAndNewsletter' -import { Link, PopupButton } from '@/components/PopupButton/PopupButton' +import { HomeView } from './HomeView' -type HomeProps = { - events: Event[], - worship: Worship[], - blog: Blog[], - highlights: Highlight[], - announcements: Link[], - calendars: Link[] -} +export const Home = async () => { + const fromDate = moment().isoWeekday(1).hours(0).minutes(0) + const tillDate = moment().isoWeekday(7).hours(23).minutes(59) -const sortWorship = (worship: Worship[]) => { - const map = new Map<string, Worship[]>() - - worship.map(w => { - if (typeof w.location === 'object') { - const title = w.location.name - - if (map.has(title)) { - map.get(title)?.push(w) - } else { - map.set(title, [w]) - } - } + const events = await fetchEvents() + const worship = await fetchWorship({ + fromDate: fromDate.toDate(), + tillDate: tillDate.toDate(), }) - - return map -} - - -export const Home = ({ - events, - worship, - blog, - highlights, - announcements, - calendars -}: HomeProps) => { - const worshipPerLocation = Array.from( - sortWorship(worship).entries(), - ).sort( - (a, b) => { - const nameA = a[0] - const nameB = b[0] - - if (nameA < nameB) { - return -1 - } - if (nameA > nameB) { - return 1 - } - - // names must be equal - return 0 - }, - ) + const blog = await fetchBlogPosts(true) + const highlights = await fetchHighlights() + const announcements = await fetchLastAnnouncements() + const announcementsLinks = announcements ? perParish(announcements) : [] + const calendars = await fetchLastCalendars() + const calendarsLinks = calendars ? perParish(calendars) : [] return ( - <> - <Banner /> - - <Container> - <Section> - <MainText - text={`Willkommen bei der Pfarrei Heilige Drei Könige Nord-Neukölln! Hier begegnen wir einander – spirituell, kulturell, sozial – und gestalten gemeinsam eine lebendige Kirche. Trete ein und entdecke einen Ort, der inspiriert, verbindet und bewegt.`} /> - </Section> - </Container> - - <HR /> - - <Container> - {blog && blog.length > 0 && - <Section> - <Title title={'Aktuelles'} color={"contrast"} /> - <ImageCardSlider slides={blogToSlides(blog)} /> - </Section> - } - </Container> - - <Section paddingBottom={'medium'}> - <Title - title={'Nächste Gottesdienste'} - subtitle={'Komm einfach vorbei!'} - color={"contrast"} - align={'center'} - /> - - <Section padding={'small'}> - <MassGrid> - {worshipPerLocation.map(value => <MassTable key={value[0]} location={value[0]} masses={value[1]} />)} - </MassGrid> - </Section> - - <Section padding={'small'}> - <div className={styles.center}> - - { announcements.length > 0 && - <PopupButton - text={"Vermeldungen"} - title={"Vermeldungen"} - links={announcements} - schema={"shade"} - /> - } - - { calendars.length > 0 && - <PopupButton - text={"Liturgischer Kalender"} - title={"Kalender"} - links={calendars} - schema={"shade"} - /> - } - - <Button - href={"/gottesdienst"} - size={"md"} - >Alle Gottesdienste</Button> - </div> - </Section> - - </Section> - - <CollapsibleImageWithText - backgroundColor={'soft'} - title={'Über uns'} - schema={"base"} - text={'Unsere Pfarrei Hl. Drei Könige wurde am 01.01.2020 gegründet. Am 12.01.2020 feierte Erzbischof Dr. Heiner Koch mit den Gemeinden die Gründung in einer feierlichen Hl. Messe in der katholischen Marienschule. Anwesende Gäste waren Bürgermeister Martin Hikel, Christian Nottmeier, der Superintendent des evangelischen Kirchenkreises Neukölln und viele Akteuren aus Kiez und Ökumene. Die Vielfalt der Glaubenswege in unserer Pfarrei sehen wir als Schatz. Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen.'} - image={forest} - content={<MoreInformation />} - /> - - <ContentWithSlider slider={<> - <Title title={'Aktuelle Highlights'} size={'md'} fontStyle={'sans-serif'} color={'white'} /> - {highlights.map(highlight => ( - <EventRow - color={'white'} - key={highlight.id} - date={highlight.date} - showDate={false} - title={highlight.text} - href={highlightLink(highlight)} - cancelled={false} - /> - ))} - </>}> - <Section> - <Title - color={"contrast"} - title={'Veranstaltungen'} - /> - <Events - events={transformEvents(events)} - n={6} - schema={"contrast"} - /> - </Section> - </ContentWithSlider> - - <PublicationAndNewsletter /> - - <ContactSection - title={'Kontakt'} - description={'Haben Sie Fragen zum Glauben, zu den Sakramenten oder unseren Angeboten? Benötigen Sie Hilfe in einer schwierigen Situation oder möchten Sie einfach Ihre Gedanken mit uns teilen?\n' + - '\n' + - 'Zögern Sie nicht, uns über das Kontaktformular zu schreiben. Wir freuen uns über jede Nachricht und sind gerne für Sie da.'} - schema={"base"} - toEmail={"kontakt@dreikoenige.berlin"} - /> - </> - + <HomeView + events={events?.docs || []} + worship={worship?.docs || []} + blog={blog?.docs || []} + highlights={highlights?.docs || []} + announcements={announcementsLinks} + calendars={calendarsLinks} + publicationAndNewsletter={<PublicationAndNewsletter />} + /> ) } - -export default Home; \ No newline at end of file diff --git a/src/pageComponents/Home/HomeView.tsx b/src/pageComponents/Home/HomeView.tsx new file mode 100644 index 0000000..37ef033 --- /dev/null +++ b/src/pageComponents/Home/HomeView.tsx @@ -0,0 +1,199 @@ +import { Blog, Worship, Event, Highlight } from '@/payload-types' +import { Banner } from '@/components/Banner/Banner' +import { Container } from '@/components/Container/Container' +import { Section } from '@/components/Section/Section' +import { MainText } from '@/components/MainText/MainText' +import { HR } from '@/components/HorizontalRule/HorizontalRule' +import { Title } from '@/components/Title/Title' +import { MassGrid } from '@/components/MassTable/MassGrid' +import { MassTable } from '@/components/MassTable/MassTable' +import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider' +import { blogToSlides } from '@/utils/dto/blog' +import forest from '@/assets/map.jpg' +import { ContentWithSlider } from '@/compositions/ContentWithSlider/ContentWithSlider' +import { EventRow } from '@/components/EventRow/EventRow' +import { highlightLink } from '@/utils/dto/highlight' +import { Events } from '@/compositions/Events/Events' +import { transformEvents } from '@/utils/dto/events' +import { ContactSection } from '@/compositions/ContactSection/ContactSection' +import { CollapsibleImageWithText } from '@/compositions/CollapsibleImageWithText/CollapsibleImageWithText' +import { MoreInformation } from '@/pageComponents/Home/MoreInformation' +import { Button } from '@/components/Button/Button' +import styles from "./styles.module.scss" +import { Link, PopupButton } from '@/components/PopupButton/PopupButton' +import type { ReactNode } from 'react' + +type HomeViewProps = { + events: Event[], + worship: Worship[], + blog: Blog[], + highlights: Highlight[], + announcements: Link[], + calendars: Link[], + publicationAndNewsletter?: ReactNode +} + +const sortWorship = (worship: Worship[]) => { + const map = new Map<string, Worship[]>() + + worship.map(w => { + if (typeof w.location === 'object') { + const title = w.location.name + + if (map.has(title)) { + map.get(title)?.push(w) + } else { + map.set(title, [w]) + } + } + }) + + return map +} + + +export const HomeView = ({ + events, + worship, + blog, + highlights, + announcements, + calendars, + publicationAndNewsletter, +}: HomeViewProps) => { + const worshipPerLocation = Array.from( + sortWorship(worship).entries(), + ).sort( + (a, b) => { + const nameA = a[0] + const nameB = b[0] + + if (nameA < nameB) { + return -1 + } + if (nameA > nameB) { + return 1 + } + + // names must be equal + return 0 + }, + ) + + return ( + <> + <Banner /> + + <Container> + <Section> + <MainText + text={`Willkommen bei der Pfarrei Heilige Drei Könige Nord-Neukölln! Hier begegnen wir einander – spirituell, kulturell, sozial – und gestalten gemeinsam eine lebendige Kirche. Trete ein und entdecke einen Ort, der inspiriert, verbindet und bewegt.`} /> + </Section> + </Container> + + <HR /> + + <Container> + {blog && blog.length > 0 && + <Section> + <Title title={'Aktuelles'} color={"contrast"} /> + <ImageCardSlider slides={blogToSlides(blog)} /> + </Section> + } + </Container> + + <Section paddingBottom={'medium'}> + <Title + title={'Nächste Gottesdienste'} + subtitle={'Komm einfach vorbei!'} + color={"contrast"} + align={'center'} + /> + + <Section padding={'small'}> + <MassGrid> + {worshipPerLocation.map(value => <MassTable key={value[0]} location={value[0]} masses={value[1]} />)} + </MassGrid> + </Section> + + <Section padding={'small'}> + <div className={styles.center}> + + { announcements.length > 0 && + <PopupButton + text={"Vermeldungen"} + title={"Vermeldungen"} + links={announcements} + schema={"shade"} + /> + } + + { calendars.length > 0 && + <PopupButton + text={"Liturgischer Kalender"} + title={"Kalender"} + links={calendars} + schema={"shade"} + /> + } + + <Button + href={"/gottesdienst"} + size={"md"} + >Alle Gottesdienste</Button> + </div> + </Section> + + </Section> + + <CollapsibleImageWithText + backgroundColor={'soft'} + title={'Über uns'} + schema={"base"} + text={'Unsere Pfarrei Hl. Drei Könige wurde am 01.01.2020 gegründet. Am 12.01.2020 feierte Erzbischof Dr. Heiner Koch mit den Gemeinden die Gründung in einer feierlichen Hl. Messe in der katholischen Marienschule. Anwesende Gäste waren Bürgermeister Martin Hikel, Christian Nottmeier, der Superintendent des evangelischen Kirchenkreises Neukölln und viele Akteuren aus Kiez und Ökumene. Die Vielfalt der Glaubenswege in unserer Pfarrei sehen wir als Schatz. Wie die drei Weisen aus dem Morgenland wollen wir uns immer wieder neu auf den Weg machen.'} + image={forest} + content={<MoreInformation />} + /> + + <ContentWithSlider slider={<> + <Title title={'Aktuelle Highlights'} size={'md'} fontStyle={'sans-serif'} color={'white'} /> + {highlights.map(highlight => ( + <EventRow + color={'white'} + key={highlight.id} + date={highlight.date} + showDate={false} + title={highlight.text} + href={highlightLink(highlight)} + cancelled={false} + /> + ))} + </>}> + <Section> + <Title + color={"contrast"} + title={'Veranstaltungen'} + /> + <Events + events={transformEvents(events)} + n={6} + schema={"contrast"} + /> + </Section> + </ContentWithSlider> + + {publicationAndNewsletter} + + <ContactSection + title={'Kontakt'} + description={'Haben Sie Fragen zum Glauben, zu den Sakramenten oder unseren Angeboten? Benötigen Sie Hilfe in einer schwierigen Situation oder möchten Sie einfach Ihre Gedanken mit uns teilen?\n' + + '\n' + + 'Zögern Sie nicht, uns über das Kontaktformular zu schreiben. Wir freuen uns über jede Nachricht und sind gerne für Sie da.'} + schema={"base"} + toEmail={"kontakt@dreikoenige.berlin"} + /> + </> + + ) +} + diff --git a/src/pageComponents/Parish/Parish.stories.tsx b/src/pageComponents/Parish/Parish.stories.tsx index 2f7004f..a647743 100644 --- a/src/pageComponents/Parish/Parish.stories.tsx +++ b/src/pageComponents/Parish/Parish.stories.tsx @@ -2,7 +2,7 @@ import { Meta, StoryObj } from '@storybook/nextjs-vite' import chris from "../../assets/christophorus.jpeg" import { Parish } from './Parish' import { Menu } from '@/components/Menu/Menu' -import { Footer } from '@/compositions/Footer/Footer' +import { FooterView } from '@/compositions/Footer/FooterView' const meta: Meta<typeof Parish> = { component: Parish, @@ -28,7 +28,18 @@ const meta: Meta<typeof Parish> = { ] }}/> <Story /> - <Footer /> + <FooterView + prayers={['Herr, gib uns deinen Frieden.']} + footer={{ + id: 'mock-footer', + groups: [ + { + title: 'Pfarrei', + links: [{ label: 'Kontakt', href: '/kontakt' }], + }, + ], + }} + /> </> ) ] diff --git a/src/pageComponents/Search/Search.stories.tsx b/src/pageComponents/Search/Search.stories.tsx index cb10179..4ea649a 100644 --- a/src/pageComponents/Search/Search.stories.tsx +++ b/src/pageComponents/Search/Search.stories.tsx @@ -27,26 +27,20 @@ export const WithResults: Story = { results: [ makeResult('1', 'Sonntagsgottesdienst', { relationTo: 'pages', - value: { - id: 'p1', - slug: 'gottesdienst', - } as Search['doc']['value'], - }), + value: { id: 'p1', slug: 'gottesdienst' }, + } as unknown as Search['doc']), makeResult('2', 'Ostern 2026 – Liturgische Feier', { relationTo: 'event', - value: { id: 'e1' } as Search['doc']['value'], - }), + value: { id: 'e1' }, + } as unknown as Search['doc']), makeResult('3', 'Ministrantengruppe', { relationTo: 'group', - value: { - id: 'g1', - slug: 'ministranten', - } as Search['doc']['value'], - }), + value: { id: 'g1', slug: 'ministranten' }, + } as unknown as Search['doc']), makeResult('4', 'Rundbrief Januar', { relationTo: 'blog', - value: { id: 'b1' } as Search['doc']['value'], - }), + value: { id: 'b1' }, + } as unknown as Search['doc']), ], }, } @@ -63,4 +57,4 @@ export const EmptyQuery: Story = { query: '', results: [], }, -} \ No newline at end of file +}