diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e413e5..8b8c795 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@c2s/payload-contracts': specifier: github:complexcaresolutions/payload-contracts - version: git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7(react@19.2.1) + version: git+https://git@github.com:complexcaresolutions/payload-contracts.git#a0eea9649d35ec2a4554632554d53799a36b7f4b(react@19.2.1) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -125,8 +125,8 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7': - resolution: {commit: 64847594b2150bfdce09a7bd7f54ad2f52d6f2b7, repo: git@github.com:complexcaresolutions/payload-contracts.git, type: git} + '@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#a0eea9649d35ec2a4554632554d53799a36b7f4b': + resolution: {commit: a0eea9649d35ec2a4554632554d53799a36b7f4b, repo: git@github.com:complexcaresolutions/payload-contracts.git, type: git} version: 1.0.0 peerDependencies: react: ^19.0.0 @@ -2048,7 +2048,7 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7(react@19.2.1)': + '@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#a0eea9649d35ec2a4554632554d53799a36b7f4b(react@19.2.1)': optionalDependencies: react: 19.2.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..8e4c181 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - "@c2s/payload-contracts" diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 63ed651..0bf6475 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -71,10 +71,9 @@ export default async function RootLayout({ }: Readonly<{ children: React.ReactNode }>) { - // Fetch navigation and settings in parallel - const [headerNav, footerNav, settings] = await Promise.all([ - getNavigation('header'), - getNavigation('footer'), + // Fetch navigation (one doc per tenant) and settings in parallel + const [navigation, settings] = await Promise.all([ + getNavigation(), getSiteSettings(), ]) @@ -89,9 +88,9 @@ export default async function RootLayout({ Zum Hauptinhalt springen
-
+
{children}
-
diff --git a/src/components/blocks/index.tsx b/src/components/blocks/index.tsx index 8d345ef..e1e956c 100644 --- a/src/components/blocks/index.tsx +++ b/src/components/blocks/index.tsx @@ -59,7 +59,6 @@ export function BlockRenderer({ blocks }: BlockRendererProps) { return null } - // Extract the block data, excluding blockType for the component props const { blockType, ...blockProps } = block return ( diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 4311634..ac9c6ac 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,29 +1,21 @@ import Link from 'next/link' import Image from 'next/image' -import type { Navigation, SiteSettings, Address } from '@/lib/types' +import type { Navigation as NavigationType } from '@c2s/payload-contracts/types' +import type { SiteSettings } from '@/lib/types' -// Helper to format address -function formatAddress(address: Address | string | undefined): string | null { - if (!address) return null - if (typeof address === 'string') return address - - const parts: string[] = [] - if (address.street) parts.push(address.street) - if (address.additionalLine) parts.push(address.additionalLine) - if (address.zip || address.city) { - parts.push([address.zip, address.city].filter(Boolean).join(' ')) - } - if (address.country) parts.push(address.country) - - return parts.length > 0 ? parts.join('\n') : null -} +type FooterMenuItem = NonNullable[number] interface FooterProps { - navigation: Navigation | null + footerMenu: FooterMenuItem[] | null settings: SiteSettings | null } -export function Footer({ navigation, settings }: FooterProps) { +function getPageSlug(page: FooterMenuItem['page']): string | undefined { + if (typeof page === 'object' && page !== null) return (page as { slug: string }).slug + return undefined +} + +export function Footer({ footerMenu, settings }: FooterProps) { const currentYear = new Date().getFullYear() return ( @@ -56,84 +48,38 @@ export function Footer({ navigation, settings }: FooterProps) { {settings?.socialLinks && (
{settings.socialLinks.instagram && ( - - - + )} {settings.socialLinks.youtube && ( - - - + )} {settings.socialLinks.pinterest && ( - - - + )} {settings.socialLinks.tiktok && ( - - - + )} {settings.socialLinks.facebook && ( - - - + )}
)} - {/* Navigation Columns */} - {navigation?.items && navigation.items.length > 0 && ( - <> - {groupNavigationItems(navigation.items).map((group, index) => ( -
- {group.title && ( -

- {group.title} -

- )} -
    - {group.items.map((item) => ( -
  • - -
  • - ))} -
-
- ))} - + {/* Footer Navigation */} + {footerMenu && footerMenu.length > 0 && ( +
+

+ Links +

+
    + {footerMenu.map((item) => ( +
  • + +
  • + ))} +
+
)} {/* Contact Column */} @@ -163,11 +109,6 @@ export function Footer({ navigation, settings }: FooterProps) {

)} - {formatAddress(settings.address) && ( -

- {formatAddress(settings.address)} -

- )} )} @@ -185,24 +126,20 @@ export function Footer({ navigation, settings }: FooterProps) { ) } -function FooterLink({ item }: { item: Navigation['items'][0] }) { +function FooterLink({ item }: { item: FooterMenuItem }) { const className = 'text-sm text-soft-white hover:text-sand transition-colors' + const slug = getPageSlug(item.page) + const href = item.linkType === 'page' && slug ? `/${slug}` : item.url || '/' + const isExternal = item.linkType === 'custom' && item.url?.startsWith('http') - if (item.type === 'external' && item.url) { + if (isExternal) { return ( - + {item.label} ) } - const href = item.page?.slug ? `/${item.page.slug}` : item.url || '/' - return ( {item.label} @@ -210,32 +147,20 @@ function FooterLink({ item }: { item: Navigation['items'][0] }) { ) } -// Helper to group navigation items for footer columns -function groupNavigationItems(items: Navigation['items']) { - // Simple grouping - you can customize based on your needs - const groups: { title?: string; items: Navigation['items'] }[] = [] - - items.forEach((item) => { - if (item.type === 'submenu' && item.children?.length) { - groups.push({ - title: item.label, - items: item.children, - }) - } - }) - - // Add remaining items as a group - const topLevelItems = items.filter( - (item) => item.type !== 'submenu' || !item.children?.length +function SocialAnchor({ href, label, platform }: { href: string; label: string; platform: string }) { + return ( + + + ) - if (topLevelItems.length > 0) { - groups.unshift({ items: topLevelItems }) - } - - return groups } -// Social Media Icons function SocialIcon({ platform }: { platform: string }) { const iconClass = 'w-5 h-5' @@ -258,12 +183,6 @@ function SocialIcon({ platform }: { platform: string }) { ) - case 'linkedin': - return ( - - - - ) case 'pinterest': return ( @@ -279,12 +198,7 @@ function SocialIcon({ platform }: { platform: string }) { default: return ( - + ) } diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 91cf28f..3d0740b 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -5,14 +5,17 @@ import Link from 'next/link' import Image from 'next/image' import { Navigation } from './Navigation' import { MobileMenu } from './MobileMenu' -import type { Navigation as NavigationType, SiteSettings } from '@/lib/types' +import type { Navigation as NavigationType } from '@c2s/payload-contracts/types' +import type { SiteSettings } from '@/lib/types' + +type MainMenuItem = NonNullable[number] interface HeaderProps { - navigation: NavigationType | null + mainMenu: MainMenuItem[] | null settings: SiteSettings | null } -export function Header({ navigation, settings }: HeaderProps) { +export function Header({ mainMenu, settings }: HeaderProps) { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) return ( @@ -39,7 +42,7 @@ export function Header({ navigation, settings }: HeaderProps) { {/* Desktop Navigation */}
- +
{/* Mobile Menu Toggle */} @@ -69,7 +72,7 @@ export function Header({ navigation, settings }: HeaderProps) { {/* Mobile Menu */} setIsMobileMenuOpen(false)} /> diff --git a/src/components/layout/MobileMenu.tsx b/src/components/layout/MobileMenu.tsx index ff277ee..2d1d748 100644 --- a/src/components/layout/MobileMenu.tsx +++ b/src/components/layout/MobileMenu.tsx @@ -3,10 +3,12 @@ import { useEffect, useRef, useCallback } from 'react' import Link from 'next/link' import { cn } from '@/lib/utils' -import type { NavigationItem } from '@/lib/types' +import type { Navigation as NavigationType, Page } from '@c2s/payload-contracts/types' + +type MainMenuItem = NonNullable[number] interface MobileMenuProps { - items: NavigationItem[] + items: MainMenuItem[] isOpen: boolean onClose: () => void } @@ -33,7 +35,6 @@ export function MobileMenu({ items, isOpen, onClose }: MobileMenuProps) { useEffect(() => { if (isOpen) { previousActiveElement.current = document.activeElement as HTMLElement - // Small delay to allow animation to start setTimeout(() => { closeButtonRef.current?.focus() }, 100) @@ -142,46 +143,39 @@ export function MobileMenu({ items, isOpen, onClose }: MobileMenuProps) { ) } -interface MobileNavItemProps { - item: NavigationItem - onClose: () => void - depth?: number -} - -function MobileNavItem({ item, onClose, depth = 0 }: MobileNavItemProps) { +function MobileNavItem({ item, onClose, depth = 0 }: { item: MainMenuItem; onClose: () => void; depth?: number }) { const linkClasses = cn( 'block py-3 text-base font-medium text-espresso', 'hover:text-brass transition-colors', depth > 0 && 'pl-4 text-sm' ) - // With children - if (item.type === 'submenu' && item.children?.length) { + // With submenu + if (item.type === 'submenu' && item.submenu?.length) { return (
  • {item.label}
      - {item.children.map((child) => ( - + {item.submenu.map((child) => ( + ))}
  • ) } - // External link - if (item.type === 'external' && item.url) { + // Link + const slug = typeof item.page === 'object' && item.page !== null ? (item.page as Page).slug : undefined + const href = item.type === 'page' && slug ? `/${slug}` : item.url || '/' + const isExternal = item.type === 'custom' && item.url?.startsWith('http') + + if (isExternal) { return (
  • + + {item.label} + +
  • + ) +} + +type SubMenuItem = NonNullable[number] + +function MobileSubItem({ item, onClose }: { item: SubMenuItem; onClose: () => void }) { + const linkClasses = 'block py-3 pl-4 text-sm font-medium text-espresso hover:text-brass transition-colors' + const slug = typeof item.page === 'object' && item.page !== null ? (item.page as Page).slug : undefined + const href = item.linkType === 'page' && slug ? `/${slug}` : item.url || '/' return (
  • diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx index e6ab288..bc6984f 100644 --- a/src/components/layout/Navigation.tsx +++ b/src/components/layout/Navigation.tsx @@ -3,13 +3,20 @@ import { useState } from 'react' import Link from 'next/link' import { cn } from '@/lib/utils' -import type { NavigationItem } from '@/lib/types' +import type { Navigation as NavigationType, Page } from '@c2s/payload-contracts/types' + +type MainMenuItem = NonNullable[number] interface NavigationProps { - items: NavigationItem[] + items: MainMenuItem[] className?: string } +function getPageSlug(page: MainMenuItem['page']): string | undefined { + if (typeof page === 'object' && page !== null) return (page as Page).slug + return undefined +} + export function Navigation({ items, className }: NavigationProps) { return (