diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index f008d91..8fdf0bc 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -18,7 +18,15 @@ const preview: Preview = { date: /Date$/i, }, }, - }, + backgrounds: { + values: [ + { name: "light", value: "#ffffff" }, + { name: "shade2", value: "#CBD6D5" }, + { name: 'dark', value: "#728F8D" } + ], + default: "light" + } + } } export default preview diff --git a/src/app/page.tsx b/src/app/page.tsx index 3408e31..598f828 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -17,6 +17,9 @@ import { transformEvents } from '@/utils/dto/events' import { fetchBlog } from '@/fetch/blog' import { ImageCardSlider } from '@/compositions/ImageCardSlider/ImageCardSlider' import { blogToSlides } from '@/utils/dto/blog' +import { fetchHighlights } from '@/fetch/highlights' +import { EventRow } from '@/components/EventRow/EventRow' +import { highlightLink } from '@/utils/dto/highlight' const sortWorship = (worship: Worship[]) => { const map = new Map(); @@ -62,6 +65,7 @@ export default async function Home() { ) const blog = await fetchBlog(); + const highlights = await fetchHighlights() return ( @@ -93,7 +97,17 @@ export default async function Home() { '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 vielen 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} />} - + <Title title={"Akutelle Highlights"} size={"md"} fontStyle={"sans-serif"} color={"white"} /> + {highlights?.docs.map(highlight => ( + <EventRow + color={"white"} + key={highlight.id} + date={highlight.date} + title={highlight.text} + href={highlightLink(highlight)} + cancelled={false} + /> + ))} </>}> <Container position={"right"}> <Section> diff --git a/src/collections/Highlight.ts b/src/collections/Highlight.ts new file mode 100644 index 0000000..85207be --- /dev/null +++ b/src/collections/Highlight.ts @@ -0,0 +1,68 @@ +import { CollectionConfig } from 'payload' +import { isAdminOrEmployee } from '@/collections/access/admin' + +export const Highlight: CollectionConfig = { + slug: 'highlight', + labels: { + singular: { + de: "Highlight" + }, + plural: { + de: "Highlights" + } + }, + fields: [ + { + name: 'from', + type: 'date', + required: true, + label: { + de: "Anzeigen von" + } + }, + { + name: 'until', + type: 'date', + required: true, + label: { + de: "Anzeigen bis" + } + }, + { + name: 'date', + type: 'date', + required: true, + label: { + de: 'Datum' + } + }, + { + name: 'link', + type: 'relationship', + relationTo: ['event', 'blog', 'worship'], + admin: { + allowCreate: false, + allowEdit: false + }, + required: false + }, + { + name: 'text', + type: 'textarea', + required: true, + maxLength: 144, + label: { + de: "Nachricht" + } + } + ], + admin: { + useAsTitle: 'text' + }, + access: { + read: () => true, + create: isAdminOrEmployee(), + update: isAdminOrEmployee(), + delete: isAdminOrEmployee(), + } +} \ No newline at end of file diff --git a/src/collections/Tweets.ts b/src/collections/Tweets.ts deleted file mode 100644 index 50e9bf2..0000000 --- a/src/collections/Tweets.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' - -export const Tweets: CollectionConfig = { - slug: 'tweet', - labels: { - singular: { - de: "Kurznachricht" - }, - plural: { - de: "Kurznachrichten" - } - }, - fields: [ - { - name: 'parish', - type: 'relationship', - relationTo: 'parish', - hasMany: true, - label: { - de: "Gemeinde" - } - }, - { - name: 'text', - type: 'textarea', - required: true, - label: { - de: "Nachricht" - } - } - ], - access: { - read: () => true, - create: isAdminOrEmployee(), - update: isAdminOrEmployee(), - delete: isAdminOrEmployee(), - } -} \ No newline at end of file diff --git a/src/components/EventRow/EventRow.stories.tsx b/src/components/EventRow/EventRow.stories.tsx index 39f62c5..036d900 100644 --- a/src/components/EventRow/EventRow.stories.tsx +++ b/src/components/EventRow/EventRow.stories.tsx @@ -36,4 +36,20 @@ export const CancelledEvent: Story = { location: "St. Richard", cancelled: true }, +} + +export const DarkBackground: Story = { + args: { + date: '2024-01-06T15:00:00+01:00', + title: 'Herz Jesu Feier', + href: 'https://www.herzJesuFeier.com', + location: "St. Clara", + cancelled: false, + color: "white" + }, + parameters: { + backgrounds: { + default: 'dark' + } + } } \ No newline at end of file diff --git a/src/components/EventRow/EventRow.tsx b/src/components/EventRow/EventRow.tsx index 32f2bb2..056433e 100644 --- a/src/components/EventRow/EventRow.tsx +++ b/src/components/EventRow/EventRow.tsx @@ -7,9 +7,10 @@ export type EventRowProps = { /** datetime 8601 format */ date: string, title: string, - href: string, + href?: string, location?: string, cancelled: boolean + color?: "base" | "white" } /** @@ -38,20 +39,27 @@ const shortMonth = (date: string) => { -export const EventRow = ({date, title, location, cancelled, href}: EventRowProps) => { +export const EventRow = ({date, title, location, cancelled, href, color = "base"}: EventRowProps) => { const day = useMemo(() => date.substring(8, 10), [date]); const dateObj = useMemo(() => new Date(date), [date]); const month = useMemo(() => shortMonth(date), [date]); return ( - <Link href={href} className={styles.link}> + <Link href={href || "https://"} className={styles.link}> <div className={styles.container}> - <div className={styles.day}> + <div className={classNames({ + [styles.day]: true, + [styles.dayBase]: color === "base", + [styles.dayWhite]: color === "white" + })}> {day} <br /> {month} </div> - <div className={styles.line}></div> + <div className={classNames({ + [styles.line]: true, + [styles.lineWhite]: color === "white", + })}></div> <div className={classNames({ [styles.details]: true, diff --git a/src/components/EventRow/styles.module.scss b/src/components/EventRow/styles.module.scss index c373760..3ae821c 100644 --- a/src/components/EventRow/styles.module.scss +++ b/src/components/EventRow/styles.module.scss @@ -1,7 +1,6 @@ @import "template.scss"; .day { - color: $base-color; line-height: 105%; font-size: 25px; font-weight: bold; @@ -10,13 +9,26 @@ transition: color 0.2s ease-in; } +.dayBase { + color: $base-color; +} + +.dayWhite{ + color: $shade3; +} + .line { width: 0.7px; - background: $base-color; + background: $shade1; height: 96px; margin: 0 30px; } +.lineWhite { + background: $shade3; + opacity: 0.5; +} + .details { line-height: 147%; } @@ -38,10 +50,14 @@ text-decoration: line-through; } -.container:hover .day { +.container:hover .dayBase { color: $contrast-color; } +.container:hover .dayWhite { + color: $shade2; +} + @media screen and (max-width: 576px) { .day { margin-left: 15px; diff --git a/src/components/SideSlider/SideSlider.tsx b/src/components/SideSlider/SideSlider.tsx index 386a210..b1fffa0 100644 --- a/src/components/SideSlider/SideSlider.tsx +++ b/src/components/SideSlider/SideSlider.tsx @@ -1,15 +1,68 @@ +"use client" + import styles from "./styles.module.scss" +import { useCallback, useEffect, useMemo, useState } from 'react' +import classNames from 'classnames' type SideSliderProps = { children: React.ReactNode; } export const SideSlider = ({ children }: SideSliderProps) => { + const [isVisible, setIsVisible] = useState(false) + const [startX, setStartX] = useState<number|undefined>(undefined) + const [endX, setEndX] = useState<number|undefined>(undefined) + + const [isMobile, setIsMobile] = useState(false) + useEffect(() => { + if(window.innerWidth < 700) { + setIsMobile(true) + } + + window.addEventListener("resize", () => { + if(window.innerWidth < 700) { + setIsMobile(true) + } else { + setIsMobile(false) + } + }) + }, [setIsMobile]) + + // translateX the container when startX or endX changed + const style = useMemo(() => { + if (typeof endX === "number" && typeof startX === "number") + return { + transform: `translateX(${endX-startX}px)` + } + + return undefined + }, [endX, startX]) + + const className = classNames({ + [styles.container]: true, + [styles.isVisible]: isVisible, + }); + return ( <div className={styles.wrapper}> - <div className={styles.container}> - <div className={styles.icon}> - + <div + className={className} + onMouseEnter={!isMobile ? () => setIsVisible(true): undefined} + onMouseLeave={!isMobile ? () => setIsVisible(false): undefined} + style={style} + > + <div + className={styles.icon} + onTouchStart={e => { + if(typeof startX === "undefined" && typeof endX === "undefined") { + setStartX(e.touches[0].clientX) + setEndX(e.touches[0].clientX) + } + }} + onTouchMove={e => { + setEndX(e.changedTouches[0].clientX) + }} + > </div> <div className={styles.content}> <div className={styles.padding}> diff --git a/src/components/SideSlider/styles.module.scss b/src/components/SideSlider/styles.module.scss index 16ee302..1597b1e 100644 --- a/src/components/SideSlider/styles.module.scss +++ b/src/components/SideSlider/styles.module.scss @@ -12,13 +12,13 @@ $iconSize: 150px; .container { width: $width; min-height: 450px; - transform: translate(calc($width - $iconSize), 0); - transition: 300ms all ease-in; + transition: 200ms transform ease-out; position: relative; + right: -550px; } -.container:hover { - transform: translate(0, 0); +.isVisible { + transform: translateX(-550px); } .icon { @@ -39,6 +39,7 @@ $iconSize: 150px; padding-left: $iconSize/2; min-height: 450px; width: $width; + color: #ffffff; } .padding { @@ -51,7 +52,7 @@ $iconSize: 150px; } .container { - transform: none; + right: 0; width: 100%; } @@ -60,6 +61,6 @@ $iconSize: 150px; width: 100%; position: relative; left: 0; - + padding: 0 20px; } } \ No newline at end of file diff --git a/src/components/Title/styles.module.scss b/src/components/Title/styles.module.scss index 7c8bd5a..05da9ff 100644 --- a/src/components/Title/styles.module.scss +++ b/src/components/Title/styles.module.scss @@ -20,7 +20,7 @@ } .white { - color: #ffffff; + color: $shade3; } .extraLarge { diff --git a/src/fetch/highlights.ts b/src/fetch/highlights.ts new file mode 100644 index 0000000..3bf04c8 --- /dev/null +++ b/src/fetch/highlights.ts @@ -0,0 +1,34 @@ +import { stringify } from 'qs-esm' +import { PaginatedDocs } from 'payload' +import { Highlight } from '@/payload-types' + +export const fetchHighlights = async (): Promise<PaginatedDocs<Highlight> | undefined> => { + const date = new Date(); + date.setHours(0, 0, 0, 0); + + const query: any = { + and: [ + { + from: { + less_than_equal: date.toISOString(), + }, + until: { + greater_than_equal: date.toISOString(), + } + } + ], + } + + const stringifiedQuery = stringify( + { + sort: "date", + where: query, + limit: 3 + }, + { addQueryPrefix: true }, + ) + + const response = await fetch(`http://localhost:3000/api/highlight${stringifiedQuery}`) + if (!response.ok) return undefined + return response.json() +} \ No newline at end of file diff --git a/src/payload-types.ts b/src/payload-types.ts index 5e3472a..a869a3c 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -16,7 +16,7 @@ export interface Config { worship: Worship; vermeldungen: Vermeldungen; blog: Blog; - tweet: Tweet; + highlight: Highlight; event: Event; group: Group; employees: Employee; @@ -36,7 +36,7 @@ export interface Config { worship: WorshipSelect<false> | WorshipSelect<true>; vermeldungen: VermeldungenSelect<false> | VermeldungenSelect<true>; blog: BlogSelect<false> | BlogSelect<true>; - tweet: TweetSelect<false> | TweetSelect<true>; + highlight: HighlightSelect<false> | HighlightSelect<true>; event: EventSelect<false> | EventSelect<true>; group: GroupSelect<false> | GroupSelect<true>; employees: EmployeesSelect<false> | EmployeesSelect<true>; @@ -293,11 +293,26 @@ export interface Document { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "tweet". + * via the `definition` "highlight". */ -export interface Tweet { +export interface Highlight { id: string; - parish?: (string | Parish)[] | null; + from: string; + until: string; + date: string; + link?: + | ({ + relationTo: 'event'; + value: string | Event; + } | null) + | ({ + relationTo: 'blog'; + value: string | Blog; + } | null) + | ({ + relationTo: 'worship'; + value: string | Worship; + } | null); text: string; updatedAt: string; createdAt: string; @@ -452,8 +467,8 @@ export interface PayloadLockedDocument { value: string | Blog; } | null) | ({ - relationTo: 'tweet'; - value: string | Tweet; + relationTo: 'highlight'; + value: string | Highlight; } | null) | ({ relationTo: 'event'; @@ -644,10 +659,13 @@ export interface BlogSelect<T extends boolean = true> { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "tweet_select". + * via the `definition` "highlight_select". */ -export interface TweetSelect<T extends boolean = true> { - parish?: T; +export interface HighlightSelect<T extends boolean = true> { + from?: T; + until?: T; + date?: T; + link?: T; text?: T; updatedAt?: T; createdAt?: T; diff --git a/src/payload.config.ts b/src/payload.config.ts index 0c10cc6..cbbf33a 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -30,7 +30,7 @@ import { Groups } from '@/collections/Groups' import { Events } from '@/collections/Events' import { Announcements } from '@/collections/Announcements' import { Blog } from '@/collections/Blog' -import { Tweets } from '@/collections/Tweets' +import { Highlight } from '@/collections/Highlight' import { Pages } from '@/collections/Pages' import { Documents } from '@/collections/Documents' import { Underdog } from 'next/dist/compiled/@next/font/dist/google' @@ -48,7 +48,7 @@ export default buildConfig({ Worship, Announcements, Blog, - Tweets, + Highlight, Events, Groups, Employees, diff --git a/src/utils/dto/highlight.ts b/src/utils/dto/highlight.ts new file mode 100644 index 0000000..7cd2914 --- /dev/null +++ b/src/utils/dto/highlight.ts @@ -0,0 +1,23 @@ +import { Highlight } from '@/payload-types' + + +export const highlightLink = (highlight: Highlight) => { + if(!highlight.link) { + return undefined + } + + if (typeof highlight.link.value !== 'object') { + return undefined + } + + switch (highlight.link.relationTo) { + case 'worship': + return `/gottesdienst/${highlight.link.value.id}`; + case 'event': + return `/event/${highlight.link.value.id}`; + case 'blog': + return `/blog/${highlight.link.value.id}`; + default: + return undefined + } +} \ No newline at end of file