feature: dynamic menu

This commit is contained in:
Benno Tielen 2025-08-20 15:26:45 +02:00
parent 78f01ec6c3
commit 6a93806095
13 changed files with 7263 additions and 701 deletions

View file

@ -0,0 +1,6 @@
import { redirect } from 'next/navigation'
export default function RedirectPage() {
const date = new Date();
redirect(`/gebetsanliegen-des-papstes/${date.getFullYear()}/${(date.getUTCMonth() + 1).toString(10).padStart(2, '0')}`)
}

View file

@ -1,10 +1,11 @@
import type { Metadata } from 'next'
import { defaultFont } from '@/assets/fonts'
import './globals.css'
import { Menu } from '@/components/Menu/Menu'
import { DynamicMenu, Menu } from '@/components/Menu/Menu'
import { Footer } from '@/compositions/Footer/Footer'
import { comment } from '@/app/(home)/layout-comment'
export const metadata: Metadata = {
title: 'Katholische Pfarrei Heilige drei Könige Berlin',
}
@ -15,324 +16,12 @@ export default function RootLayout({
children: React.ReactNode
}>) {
const date = new Date()
return (
<html lang="de" className={defaultFont.className}>
<body>
<div dangerouslySetInnerHTML={{ __html: comment }}></div>
<Menu menu={{
leftItems: [
{
text: 'Home',
href: '/'
},
{
text: 'Aktuelles',
megaMenu: {
text: {
quote: 'Ich bin bei euch alle Tage bis an das Ende der Welt.',
source: 'Matt 28,20'
},
groups: [
{
title: "Pfarrei",
items: [
{
title: "Veranstaltungen",
description: "Mehr informationen",
href: "/veranstaltungen"
},
{
title: "Immobilienprozess",
description: "Planen, strukturieren, vernetzen, Visionen fördern",
href: "/pfarrei/immobilienentwicklung"
},
]
},
]
}
},
{
text: 'Gemeinschaft finden',
megaMenu: {
text: {
quote: '„Denn wo zwei oder drei in meinem Namen versammelt sind, da bin ich mitten unter ihnen.“',
source: 'Matt 18,20'
},
groups: [
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Brunchgruppe für junge Erwachsene",
href: "/gruppe/kathocchino"
},
{
title: "Bibel & Agape",
description: "Bibelgesprächskreis für Erwachsene",
href: "/gruppe/bibel-agape"
},
{
title: "Gemeinsam Kultur erleben",
description: "Monatliche Entdeckungstouren",
href: "/gruppe/gemeinsam-kultur-erleben"
},
{
title: "Reisen",
description: "Gemeinsam Zeit verbringen",
href: "/gruppe/reisen"
}
]
},
{
title: "Kinder & Jugend",
items: [
{
title: "Mädelsgruppe",
description: "Zusammen die Welt entdecken",
href: "/gruppe/maedchen"
},
{
title: 'Ministranten',
description: "Am Altar Gottes dienen",
href: "/gruppe/ministranten"
},
{
title: "Unsere Kitas",
description: "Geborgenheit und Glaube",
href: "/gruppe/kitas"
},
{
title: "Religiöse Kinderwoche",
description: "In Zinnowitz!",
href: "/gruppe/rkw"
}
]
},
{
title: "Musik",
items: [
{
title: "Kirchenchor",
description: "Singen und mitgestalten",
href: "/gruppe/kirchenchor"
},
{
title: "Little Richards",
description: "Band von St. Richard",
href: "/gruppe/little-richards"
},
{
title: "Kindergruppe",
description: "Jeden Freitag singen und spielen",
href: "/gruppe/kinder-musik"
}
]
}
]
}
},
{
text: 'Glauben feiern',
megaMenu: {
text: {
quote: '"Freut euch im Herrn zu jeder Zeit! Noch einmal sage ich: Freut euch!"',
source: 'Phil 4,4'
},
groups: [
{
title: "Sakramente",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "/sakramente/taufe"
},
{
title: "Kommunion",
description: "Gemeinschaft durch Brot und Wein",
href: "/sakramente/kommunion"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "/sakramente/firmung"
},
{
title: "Ehe",
description: "Bund in Liebe und Treue",
href: "/sakramente/ehe"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "/sakramente/beichte"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "/sakramente/krankensalbung"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "/gottesdienst"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "/gruppe/rosenkranz"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "/gruppe/eucharistische-anbetung"
},
{
title: "Gebetsanliegen des Papstes",
description: "Vereint im Gebet mit dem Papst",
href: `/gebetsanliegen-des-papstes/${date.getFullYear()}/${(date.getUTCMonth() + 1).toString(10).padStart(2, '0')}`
},
{
title: "Holy Hour",
description: "Dank, Ehre und Freude",
href: "/gruppe/lobpreis"
},
]
},
{
title: "Glaubenswachstum",
items: [
{
title: "Erstkommunion",
description: "Ein Fest der Gemeinschaft mit Jesus",
href: "/gruppe/erstkommunion"
},
{
title: "Taufkurs",
description: "Katholischer Glaubenskurs in Berlin",
href: "/gruppe/erwachsenentaufe"
},
{
title: "Alphakurs",
description: "Freude am Glauben entdecken",
href: "/gruppe/alphakurs"
},
{
title: "Jüngerschaftsschule",
description: "Wachsen in der Gottesbeziehung",
href: "/gruppe/juengerschaftsschule"
},
{
title: "Bibel & Exerzitien",
description: "Zeit für Gott",
href: "/gruppe/exerzitien"
},
{
title: "Wallfahrt",
description: "Gott näher kommen",
href: "/gruppe/wallfahrt"
}
]
}
]
}
},
{
text: 'Nächstenliebe leben',
megaMenu: {
text: {
quote: '"Die Liebe ist langmütig, die Liebe ist gütig."',
source: '1. Kor 13-4'
},
groups: [
{
title: "Soziales Engagement",
items: [
{
href: '/gruppe/waermestube',
title: 'Wärmestube',
description: 'Kälteschutz für Bedürftige',
},
{
href: '/gruppe/essen-ist-fertig',
title: 'Essen ist fertig',
description: 'Essensausgabe Neukölln',
},
{
title: "Dicke Linda",
description: "Marktstand auf dem Kranoldplatz",
href: "/gruppe/dicke-linda"
}
]
},
{
title: "Mitmachen",
items: [
{
title: 'Liturgische Dienste',
description: "Deine Talente für die Gemeinde",
href: "/gruppe/liturgische-dienste"
},
{
href: '/gruppe/die-unterstuetzer',
title: 'Anpacken & Gutes tun',
description: 'Hilfe bei Kirchenreinigung und anderem',
},
{
href: '/mithelfen/kleinanzeigen',
title: 'Kleinanzeigen',
description: 'Gemeinsam erreichen wir mehr'
}
]
}
]
}
},
{
text: 'Kontakt',
href: '/kontakt'
}
],
rightItems: [
{
text: 'Mithelfen',
href: '/mithelfen',
},
{
text: 'Neu hier?',
href: '/neu-in-der-gemeinde',
display: "button"
}
]
}} />
<DynamicMenu />
<main className={"mainContent"}>
{children}
</main>

View file

@ -14,174 +14,66 @@ export const Default: Story = {
leftItems: [
{
text: 'Home',
href: 'https://disney.com'
href: '/home',
type: 'default',
blockType: 'simple-item',
},
],
rightItems: [
{
text: 'Need help?',
href: '/help',
type: 'button',
blockType: 'simple-item',
},
{
text: 'Gemeinschaft finden',
megaMenu: {
text: {
quote: '',
source: ''
text: 'Contact',
href: '/contact',
type: 'default',
blockType: 'simple-item',
}
]
}
},
}
export const WithMegaMenu: Story = {
args: {
menu: {
leftItems: [
{
text: 'Home',
href: '/home',
type: 'default',
blockType: 'simple-item',
},
{
text: 'Products',
blockType: 'mega-menu',
groups: [
{
title: "Gemeinden",
title: 'Catogory 1',
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
title: 'Product 1',
description: 'Some description',
href: '/products',
}
]
}
},
{
text: 'Glauben lauben',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Kontakt',
href: '/kontakt'
],
quote: 'Some bible verse',
source: 'John 12-1'
}
],
rightItems: [
{
text: 'Mithelfen',
href: '/mithelfen',
},
{
text: 'Neu hier?',
href: '/ich-bin-neu',
display: "button"
text: 'Contact',
href: '/contact',
type: 'default',
blockType: 'simple-item',
}
]
}

View file

@ -4,22 +4,55 @@ import styles from './styles.module.scss'
import MenuIcon from './menu.svg'
import Image from 'next/image'
import classNames from 'classnames'
import {Menu as MenuType, MenuItem as MenuItemType} from "./menu.types"
import { MegaMenuItemProps, Menu as MenuType, MenuItem as MenuItemType, SimpleItemProps } from './menu.types'
import { Logo } from '@/components/Logo/Logo'
import { useCallback, useState } from 'react'
import { Fragment, useCallback, useEffect, useState } from 'react'
import { MegaMenu } from '@/components/MegaMenu/MegaMenu'
import { CollapsibleArrow } from '@/components/CollapsibleArrow/CollapsibleArrow'
import Link from 'next/link'
import { fetchConfig } from '@/fetch/config'
type MenuItemProps = MenuItemType & {
onItemClick?: () => void
/**
* Represents a simple item component.
*
* @param {Object} SimpleItemProps - The properties for the SimpleItem component.
* @param {string} SimpleItemProps.text - The text to display for the item.
* @param {string} SimpleItemProps.href - The URL to navigate to when the item is clicked.
* @param {string} SimpleItemProps.type - The type of the item ("default" or "button").
* @param {function} SimpleItemProps.onItemClick - The function to call when the item is clicked.
* @returns {JSX.Element} A Link component with the specified text, href, type, and onItemClick function.
*/
const SimpleItem = ({text, href, type, onItemClick}: SimpleItemProps) => {
const className = classNames({
[styles.menuLink]: type === "default",
[styles.button]: type === "button"
});
return (
<span>
<Link
className={className}
href={href}
onClick={onItemClick}
>
{text}
</Link>
</span>
)
}
const MenuItem = ({text, href, display = "normal", megaMenu, onItemClick}: MenuItemProps) => {
const className = classNames({
[styles.menuLink]: display === "normal",
[styles.button]: display === "button"
});
/**
* Represents a mega menu item component.
* @param {Object} MegaMenuItemProps - The props for the MegaMenuItem component.
* @param {string} MegaMenuItemProps.text - The text to display in the menu item.
* @param {string} MegaMenuItemProps.quote - The quote to display in the mega menu.
* @param {string} MegaMenuItemProps.source - The source of the quote.
* @param {Array<string>} MegaMenuItemProps.groups - The groups associated with the menu item.
* @param {Function} MegaMenuItemProps.onItemClick - The function to handle item click event.
* @returns {JSX.Element} A JSX element representing the MegaMenuItem component.
*/
const MegaMenuItem = ({text, quote, source, groups, onItemClick}: MegaMenuItemProps) => {
const [isActive, setIsActive] = useState<boolean>(false);
const itemClicked = useCallback(() => {
@ -35,14 +68,13 @@ const MenuItem = ({text, href, display = "normal", megaMenu, onItemClick}: MenuI
onMouseLeave={() => setIsActive(false)}
>
<Link
className={className}
href={href || ""}
onClick={() => href ? itemClicked() : setIsActive(!isActive)}
href={""}
onClick={onItemClick}
className={styles.menuLink}
>
{text} {megaMenu && <CollapsibleArrow direction={isActive ? "UP" : "DOWN"} stroke={1.5} /> }
{text} <CollapsibleArrow direction={isActive ? "UP" : "DOWN"} stroke={1.5} />
</Link>
{megaMenu &&
<div
className={classNames({
[styles.megaMenu]: true,
@ -50,34 +82,55 @@ const MenuItem = ({text, href, display = "normal", megaMenu, onItemClick}: MenuI
})}
>
<MegaMenu
bibleText={megaMenu.text.quote}
bibleBook={megaMenu.text.source}
groups={megaMenu.groups}
bibleText={quote}
bibleBook={source}
groups={groups}
onItemClick={itemClicked}
/>
</div>
}
</span>
)
}
type MenuItemsProps = {
items: MenuItemType[]
onItemClick?: () => void
onItemClick: () => void
}
/**
* Renders a list of menu items based on the provided props.
*
* @param {Object} items - An array of menu items to render.
* @param {Function} onItemClick - Callback function to handle item click events.
*/
const MenuItems = ({items, onItemClick}: MenuItemsProps) => {
return (
<>
{items.map(item =>
<MenuItem
key={item.text}
{items.map(item => (
<Fragment key={item.id}>
{ item.blockType === "simple-item" &&
<SimpleItem
text={item.text}
href={item.href}
display={item.display}
megaMenu={item.megaMenu}
type={item.type}
blockType={item.blockType}
onItemClick={onItemClick}
/>
}
{
item.blockType === "mega-menu" &&
<MegaMenuItem
text={item.text}
quote={item.quote}
source={item.source}
blockType={item.blockType}
groups={item.groups}
onItemClick={onItemClick}
/>
}
</Fragment>
)
)}
</>
)
@ -87,6 +140,11 @@ type MenuProps = {
menu: MenuType
}
/**
* Represents a menu component that displays navigation items on the screen.
* @param {Object} MenuProps - The props object containing menu items for left and right side of the menu.
* @returns The rendered menu component.
*/
export const Menu = ({menu}: MenuProps) => {
const [displayMenuMobile, setDisplayMenuMobile] = useState(false)
@ -131,3 +189,22 @@ export const Menu = ({menu}: MenuProps) => {
)
}
/**
* Represents a dynamic menu component.
* This component fetches menu data from a remote source and renders it.
* @returns {JSX.Element} The rendered dynamic menu component
*/
export const DynamicMenu = () => {
const [menu, setMenu] = useState<MenuType>({leftItems: [], rightItems: []})
useEffect(() => {
fetchConfig().then((data) => {
setMenu(data.menu);
});
}, [])
return <Menu menu={menu} />
}

View file

@ -3,31 +3,34 @@ export type Menu = {
rightItems: MenuItem[]
}
export type MenuItem = {
text: string,
href?: string
megaMenu?: MegaMenu
display?: "normal" | "button"
export type SimpleItemProps = {
text: string;
href: string;
type: 'default' | 'button';
id?: string | null;
blockName?: string | null;
blockType: 'simple-item';
onItemClick?: () => void;
}
type MegaMenu = {
groups: MegaMenuGroup[]
text: MegaMenuText
export type MegaMenuItemProps = {
text: string;
quote: string;
source: string;
groups: {
title: string;
items: {
title: string;
description: string;
href: string;
id?: string | null;
}[];
id?: string | null;
}[];
id?: string | null;
blockName?: string | null;
blockType: 'mega-menu';
onItemClick?: () => void;
}
type MegaMenuGroup = {
title: string
items: MegaMenuItem[]
}
type MegaMenuItem = {
title: string,
description: string
href: string
icon?: string
}
type MegaMenuText = {
quote: string
source: string
}
export type MenuItem = SimpleItemProps | MegaMenuItemProps;

21
src/fetch/config.ts Normal file
View file

@ -0,0 +1,21 @@
import { Menu } from '@/payload-types'
type Config = {
menu: Menu
}
export const fetchConfig = async (): Promise<Config> => {
const rep = await fetch(
"/api/globals/menu",
{ next: { tags: ['menu'] } } // cache fetch result
);
if (!rep.ok) {
throw new Error("Could not fetch menu")
}
const menu = await rep.json();
return {
menu
}
}

145
src/globals/Menu.ts Normal file
View file

@ -0,0 +1,145 @@
import { Block, GlobalConfig } from 'payload'
import { isAdmin } from '@/collections/access/admin'
import { revalidateTag } from 'next/cache'
const SimpleItem: Block = {
slug: 'simple-item',
labels: {
singular: 'Einfach',
plural: 'Einfach'
},
fields: [
{
name: 'text',
type: 'text',
required: true
},
{
name: 'href',
type: 'text',
label: 'Zieladresse',
required: true
},
{
name: 'type',
type: 'select',
options: [
{
label: 'Standard',
value: 'default'
},
{
label: 'Button',
value: 'button'
}
],
defaultValue: "default",
required: true
}
]
}
const MegaMenuItem: Block = {
slug: 'mega-menu',
fields: [
{
name: 'text',
type: 'text',
required: true,
},
{
name: 'quote',
label: "Bibelvers",
type: 'text',
required: true
},
{
name: 'source',
label: 'Bibelvers buch',
type: 'text',
required: true
},
{
name: 'groups',
label: 'Navigationsgruppen',
type: 'array',
fields: [
{
name: 'title',
label: 'Titel',
type: 'text',
required: true,
},
{
name: 'items',
label: 'Elemente',
type: 'array',
fields: [
{
name: 'title',
label: 'title',
type: 'text',
required: true,
},
{
name: 'description',
label: 'Beschreibung',
type: 'text',
maxLength: 100,
required: true,
},
{
name: 'href',
label: 'Zieladresse',
type: 'text',
required: true,
}
],
required: true,
}
],
minRows: 1,
maxRows: 5,
required: true,
}
]
}
export const MenuGlobal: GlobalConfig = {
slug: 'menu',
label: {
de: 'Navigationsmenü'
},
admin: {
description: "Hier können Sie die Einträge und die Reihenfolge der Hauptnavigation festlegen."
},
fields: [
{
name: 'leftItems',
label: 'Elemente links',
type: 'blocks',
required: true,
blocks: [
SimpleItem,
MegaMenuItem
]
},
{
name: 'rightItems',
label: 'Elemente Rechts',
type: 'blocks',
required: true,
blocks: [
SimpleItem,
MegaMenuItem
]
}
],
access: {
read: () => true,
update: isAdmin()
},
hooks: {
afterChange: [() => revalidateTag("menu")]
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_menu_blocks_simple_item_type" AS ENUM('default', 'button');
CREATE TABLE IF NOT EXISTS "menu_blocks_simple_item" (
"_order" integer NOT NULL,
"_parent_id" uuid NOT NULL,
"_path" text NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"text" varchar NOT NULL,
"href" varchar NOT NULL,
"type" "enum_menu_blocks_simple_item_type" DEFAULT 'default' NOT NULL,
"block_name" varchar
);
CREATE TABLE IF NOT EXISTS "menu_blocks_mega_menu_groups_items" (
"_order" integer NOT NULL,
"_parent_id" varchar NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"title" varchar NOT NULL,
"description" varchar NOT NULL,
"href" varchar NOT NULL
);
CREATE TABLE IF NOT EXISTS "menu_blocks_mega_menu_groups" (
"_order" integer NOT NULL,
"_parent_id" varchar NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"title" varchar NOT NULL
);
CREATE TABLE IF NOT EXISTS "menu_blocks_mega_menu" (
"_order" integer NOT NULL,
"_parent_id" uuid NOT NULL,
"_path" text NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"text" varchar NOT NULL,
"quote" varchar NOT NULL,
"source" varchar NOT NULL,
"block_name" varchar
);
CREATE TABLE IF NOT EXISTS "menu" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"updated_at" timestamp(3) with time zone,
"created_at" timestamp(3) with time zone
);
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2025-08-24T14:31:15.010Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2025-08-24T14:31:15.092Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2025-09-17T14:31:15.112Z';
DO $$ BEGIN
ALTER TABLE "menu_blocks_simple_item" ADD CONSTRAINT "menu_blocks_simple_item_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."menu"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_blocks_mega_menu_groups_items" ADD CONSTRAINT "menu_blocks_mega_menu_groups_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."menu_blocks_mega_menu_groups"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_blocks_mega_menu_groups" ADD CONSTRAINT "menu_blocks_mega_menu_groups_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."menu_blocks_mega_menu"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_blocks_mega_menu" ADD CONSTRAINT "menu_blocks_mega_menu_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."menu"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE INDEX IF NOT EXISTS "menu_blocks_simple_item_order_idx" ON "menu_blocks_simple_item" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "menu_blocks_simple_item_parent_id_idx" ON "menu_blocks_simple_item" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "menu_blocks_simple_item_path_idx" ON "menu_blocks_simple_item" USING btree ("_path");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_groups_items_order_idx" ON "menu_blocks_mega_menu_groups_items" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_groups_items_parent_id_idx" ON "menu_blocks_mega_menu_groups_items" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_groups_order_idx" ON "menu_blocks_mega_menu_groups" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_groups_parent_id_idx" ON "menu_blocks_mega_menu_groups" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_order_idx" ON "menu_blocks_mega_menu" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_parent_id_idx" ON "menu_blocks_mega_menu" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "menu_blocks_mega_menu_path_idx" ON "menu_blocks_mega_menu" USING btree ("_path");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "menu_blocks_simple_item" DISABLE ROW LEVEL SECURITY;
ALTER TABLE "menu_blocks_mega_menu_groups_items" DISABLE ROW LEVEL SECURITY;
ALTER TABLE "menu_blocks_mega_menu_groups" DISABLE ROW LEVEL SECURITY;
ALTER TABLE "menu_blocks_mega_menu" DISABLE ROW LEVEL SECURITY;
ALTER TABLE "menu" DISABLE ROW LEVEL SECURITY;
DROP TABLE "menu_blocks_simple_item" CASCADE;
DROP TABLE "menu_blocks_mega_menu_groups_items" CASCADE;
DROP TABLE "menu_blocks_mega_menu_groups" CASCADE;
DROP TABLE "menu_blocks_mega_menu" CASCADE;
DROP TABLE "menu" CASCADE;
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2025-06-15T10:51:24.116Z';
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2025-06-15T10:51:24.203Z';
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2025-07-08T10:51:24.219Z';
DROP TYPE "public"."enum_menu_blocks_simple_item_type";`)
}

View file

@ -7,6 +7,7 @@ import * as migration_20250224_083653_cleanup from './20250224_083653_cleanup';
import * as migration_20250319_101337_donationbox from './20250319_101337_donationbox';
import * as migration_20250322_134918_classifieds from './20250322_134918_classifieds';
import * as migration_20250608_105124_new_blocks from './20250608_105124_new_blocks';
import * as migration_20250818_143115_menu from './20250818_143115_menu';
export const migrations = [
{
@ -52,6 +53,11 @@ export const migrations = [
{
up: migration_20250608_105124_new_blocks.up,
down: migration_20250608_105124_new_blocks.down,
name: '20250608_105124_new_blocks'
name: '20250608_105124_new_blocks',
},
{
up: migration_20250818_143115_menu.up,
down: migration_20250818_143115_menu.down,
name: '20250818_143115_menu'
},
];

View file

@ -13,174 +13,17 @@ const meta: Meta<typeof Parish> = {
leftItems: [
{
text: 'Home',
href: '/'
href: '/',
type: 'default',
blockType: 'simple-item'
},
{
text: 'Gemeinschaft finden',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Glauben lauben',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "/sakramente/taufe"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "/sakramente/eucharistie"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "/sakramente/firmung"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "/sakramente/ehe"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "/sakramente/beichte"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "/sakramente/krankensalbung"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Kontakt',
href: '/kontakt'
}
],
rightItems: [
{
text: 'Mithelfen',
href: '/mithelfen',
},
{
text: 'Neu hier?',
href: '/ich-bin-neu',
display: "button"
type: 'default',
blockType: 'simple-item'
}
]
}}/>

View file

@ -56,8 +56,12 @@ export interface Config {
db: {
defaultIDType: string;
};
globals: {};
globalsSelect: {};
globals: {
menu: Menu;
};
globalsSelect: {
menu: MenuSelect<false> | MenuSelect<true>;
};
locale: null;
user: User & {
collection: 'users';
@ -1089,6 +1093,154 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* Hier können Sie die Einträge und die Reihenfolge der Hauptnavigation festlegen.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "menu".
*/
export interface Menu {
id: string;
leftItems: (
| {
text: string;
href: string;
type: 'default' | 'button';
id?: string | null;
blockName?: string | null;
blockType: 'simple-item';
}
| {
text: string;
quote: string;
source: string;
groups: {
title: string;
items: {
title: string;
description: string;
href: string;
id?: string | null;
}[];
id?: string | null;
}[];
id?: string | null;
blockName?: string | null;
blockType: 'mega-menu';
}
)[];
rightItems: (
| {
text: string;
href: string;
type: 'default' | 'button';
id?: string | null;
blockName?: string | null;
blockType: 'simple-item';
}
| {
text: string;
quote: string;
source: string;
groups: {
title: string;
items: {
title: string;
description: string;
href: string;
id?: string | null;
}[];
id?: string | null;
}[];
id?: string | null;
blockName?: string | null;
blockType: 'mega-menu';
}
)[];
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "menu_select".
*/
export interface MenuSelect<T extends boolean = true> {
leftItems?:
| T
| {
'simple-item'?:
| T
| {
text?: T;
href?: T;
type?: T;
id?: T;
blockName?: T;
};
'mega-menu'?:
| T
| {
text?: T;
quote?: T;
source?: T;
groups?:
| T
| {
title?: T;
items?:
| T
| {
title?: T;
description?: T;
href?: T;
id?: T;
};
id?: T;
};
id?: T;
blockName?: T;
};
};
rightItems?:
| T
| {
'simple-item'?:
| T
| {
text?: T;
href?: T;
type?: T;
id?: T;
blockName?: T;
};
'mega-menu'?:
| T
| {
text?: T;
quote?: T;
source?: T;
groups?:
| T
| {
title?: T;
items?:
| T
| {
title?: T;
description?: T;
href?: T;
id?: T;
};
id?: T;
};
id?: T;
blockName?: T;
};
};
updatedAt?: T;
createdAt?: T;
globalType?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".

View file

@ -36,6 +36,7 @@ import { gcsStorage } from '@payloadcms/storage-gcs'
import { PopesPrayerIntentions } from '@/collections/PopesPrayerIntentions'
import { LiturgicalCalendar } from '@/collections/LiturgicalCalendar'
import { Classifieds } from '@/collections/Classifieds'
import { MenuGlobal } from '@/globals/Menu'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@ -92,6 +93,9 @@ export default buildConfig({
Media,
Users,
],
globals: [
MenuGlobal
],
graphQL: {
disable: true
},