import React, { useEffect, useMemo, useState } from 'react'; import { Head, Link, router, usePage } from '@inertiajs/react'; import { useTranslation } from 'react-i18next'; import MatomoTracker, { MatomoConfig } from '@/components/analytics/MatomoTracker'; import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes'; import Footer from '@/layouts/app/Footer'; import { useAppearance } from '@/hooks/use-appearance'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { MoreHorizontal, Sun, Moon, Languages, LayoutDashboard, LogOut, LogIn } from 'lucide-react'; import toast from 'react-hot-toast'; interface MarketingLayoutProps { children: React.ReactNode; title?: string; } type PageProps = { translations?: Record>; locale?: string; analytics?: { matomo?: MatomoConfig }; supportedLocales?: string[]; appUrl?: string; auth?: { user?: { name?: string; email?: string } }; }; const MarketingLayout: React.FC = ({ children, title }) => { const page = usePage(); const { url } = page; const { t, i18n, ready } = useTranslation(['marketing', 'common', 'legal', 'auth']); const { locale, analytics, supportedLocales = ['de', 'en'], appUrl, auth } = page.props; const user = auth?.user ?? null; const { localizedPath } = useLocalizedRoutes(); const { appearance, updateAppearance } = useAppearance(); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const occasionLinks = useMemo(() => ([ { key: 'wedding', label: t('nav.occasions_types.weddings', 'Hochzeiten'), href: localizedPath('/anlaesse/hochzeit'), }, { key: 'birthday', label: t('nav.occasions_types.birthdays', 'Geburtstage'), href: localizedPath('/anlaesse/geburtstag'), }, { key: 'corporate', label: t('nav.occasions_types.corporate', 'Firmenevents'), href: localizedPath('/anlaesse/firmenevent'), }, { key: 'confirmation', label: t('nav.occasions_types.confirmation', 'Konfirmation'), href: localizedPath('/anlaesse/konfirmation'), }, ]), [localizedPath, t]); const navLinks = useMemo(() => ([ { key: 'how', label: t('nav.how_it_works', "So funktioniert's"), href: localizedPath('/so-funktionierts'), }, { key: 'packages', label: t('nav.packages', 'Pakete'), href: localizedPath('/packages'), }, { key: 'occasions', label: t('nav.occasions', 'Anlässe'), children: occasionLinks, }, { key: 'blog', label: t('nav.blog', 'Blog'), href: localizedPath('/blog'), }, { key: 'contact', label: t('nav.contact', 'Kontakt'), href: localizedPath('/kontakt'), }, ]), [localizedPath, occasionLinks, t]); const ctaHref = localizedPath('/demo'); const themeIsDark = appearance === 'dark'; const themeLabel = themeIsDark ? t('nav.theme_light', 'Helles Design') : t('nav.theme_dark', 'Dunkles Design'); const toggleTheme = () => updateAppearance(themeIsDark ? 'light' : 'dark'); const handleLogout = () => { router.post('/logout'); }; useEffect(() => { if (locale && i18n.language !== locale) { i18n.changeLanguage(locale); } }, [locale, i18n]); if (!ready) { return (
Lade Inhalte …
); } const marketing = page.props.translations?.marketing ?? {}; const getString = (key: string, fallback: string) => { const value = marketing[key]; return typeof value === 'string' ? value : fallback; }; const activeLocale = locale || supportedLocales[0] || 'de'; const baseUrl = (typeof appUrl === 'string' && appUrl.length > 0) ? appUrl.replace(/\/+$/, '') : 'https://fotospiel.app'; const [rawPath, rawQuery = ''] = url.split('?'); const localePattern = supportedLocales.length > 0 ? supportedLocales.join('|') : 'de|en'; const localeRegex = new RegExp(`^/(${localePattern})(?=/|$)`, 'i'); const relativePath = rawPath.replace(localeRegex, '') || '/'; const canonicalPath = localizedPath(relativePath, activeLocale); const canonicalUrl = `${baseUrl}${canonicalPath}${rawQuery ? `?${rawQuery}` : ''}`; const buildAlternateUrl = (targetLocale: string) => { const alternatePath = localizedPath(relativePath, targetLocale); return `${baseUrl}${alternatePath}${rawQuery ? `?${rawQuery}` : ''}`; }; const alternates = supportedLocales.reduce>((acc, currentLocale) => { acc[currentLocale] = buildAlternateUrl(currentLocale); return acc; }, {}); const orderedLocales = useMemo(() => { const preferredOrder = ['de', 'en']; return [...supportedLocales].sort((a, b) => { const aIndex = preferredOrder.indexOf(a); const bIndex = preferredOrder.indexOf(b); if (aIndex === -1 && bIndex === -1) return a.localeCompare(b); if (aIndex === -1) return 1; if (bIndex === -1) return -1; return aIndex - bIndex; }); }, [supportedLocales]); const resolveLanguageLabel = (code: string) => { if (code === 'de') { return t('nav.language_de', 'Deutsch'); } if (code === 'en') { return t('nav.language_en', 'English'); } return code.toUpperCase(); }; const languageOptions = useMemo(() => orderedLocales.map((code) => ({ code, label: resolveLanguageLabel(code), })), [orderedLocales, t]); const useInlineLocaleToggle = supportedLocales.length === 2; const handleLocaleChange = (nextLocale: string) => { if (nextLocale === activeLocale) { setMobileMenuOpen(false); return; } const targetPath = localizedPath(relativePath, nextLocale); const targetUrl = `${targetPath}${rawQuery ? `?${rawQuery}` : ''}`; const nextLabel = resolveLanguageLabel(nextLocale); setMobileMenuOpen(false); router.visit(targetUrl, { replace: true, preserveState: false, onSuccess: () => { i18n.changeLanguage(nextLocale); toast.success(t('nav.language_changed', '{{language}} selected', { language: nextLabel })); }, }); }; const FlagDe = () => ( ); const FlagEn = () => ( ); const resolveFlag = (code: string) => { if (code === 'de') { return ; } if (code === 'en') { return ; } return null; }; return ( <> {title || t('meta.title', getString('title', 'Fotospiel'))} {supportedLocales .filter((code) => code !== activeLocale) .map((code) => ( ))} {Object.entries(alternates).map(([code, href]) => ( ))}
setMobileMenuOpen(false)} > Logo der Fotospiel App Die Fotospiel App
{useInlineLocaleToggle && (
{languageOptions.map((option) => { const isActive = option.code === activeLocale; return ( ); })}
)} {t('nav.preferences', 'Einstellungen')} { event.preventDefault(); toggleTheme(); }} className="flex items-center gap-2 font-sans-marketing dark:text-gray-100" > {themeIsDark ? : } {themeLabel} {!useInlineLocaleToggle && ( <> {t('nav.language', 'Sprache')} {supportedLocales.map((code) => ( {resolveLanguageLabel(code)} ))} )} {user ? ( <> {user.name ?? user.email} { event.preventDefault(); router.visit(localizedPath('/voucher-status')); }} className="flex items-center gap-2 font-sans-marketing dark:text-gray-100" > {t('gift.lookup_title', 'Gutscheinstatus')} { event.preventDefault(); router.visit('/event-admin'); }} className="flex items-center gap-2 font-sans-marketing dark:text-gray-100" > {t('nav.dashboard', 'Zum Admin-Bereich')} { event.preventDefault(); handleLogout(); }} className="flex items-center gap-2 font-sans-marketing dark:text-gray-100" > {t('nav.logout', 'Abmelden')} ) : ( <> { event.preventDefault(); router.visit(localizedPath('/login')); }} className="flex items-center gap-2 font-sans-marketing dark:text-gray-100" > {t('nav.login', 'Anmelden')} )}
{mobileMenuOpen && (
{navLinks.map((item) => ( item.children ? (

{item.label}

{item.children.map((child) => ( setMobileMenuOpen(false)} > {child.label} ))}
) : ( setMobileMenuOpen(false)} > {item.label} ) ))}
{useInlineLocaleToggle ? (

{t('nav.language', 'Sprache')}

{languageOptions.map((option) => { const isActive = option.code === activeLocale; return ( ); })}
) : ( <> )}
)}
{children}
); }; export default MarketingLayout;