photobooth funktionen im event admin verlinkt, gäste pwa zeigt photobooth nur noch an, wenn diese aktiviert ist. kontaktformular optimiert. teilen-link mit iMessage und whatsapp erweitert.

This commit is contained in:
Codex Agent
2025-11-23 22:22:06 +01:00
parent 3d9eaa1194
commit df414a31cd
32 changed files with 809 additions and 280 deletions

View File

@@ -20,7 +20,7 @@ const Footer: React.FC = () => {
const currentYear = new Date().getFullYear();
return (
<footer className="mt-auto border-t border-gray-200 bg-white">
<footer className="mt-auto border-t border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
<div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
<div>
@@ -30,7 +30,7 @@ const Footer: React.FC = () => {
<Link href={links.home} className="font-display text-2xl font-bold text-pink-500">
Die Fotospiel App
</Link>
<p className="mt-2 font-sans-marketing text-gray-600">
<p className="mt-2 font-sans-marketing text-gray-600 dark:text-gray-300">
{t('marketing:footer.company', 'S.E.B. Fotografie')}
</p>
</div>
@@ -38,27 +38,27 @@ const Footer: React.FC = () => {
</div>
<div>
<h3 className="font-display mb-4 font-semibold text-gray-900">
<h3 className="font-display mb-4 font-semibold text-gray-900 dark:text-gray-50">
{t('legal:headline', 'Rechtliches')}
</h3>
<ul className="font-sans-marketing space-y-2 text-sm text-gray-600">
<ul className="font-sans-marketing space-y-2 text-sm text-gray-600 dark:text-gray-300">
<li>
<Link href={links.impressum} className="transition-colors hover:text-pink-500">
<Link href={links.impressum} className="transition-colors hover:text-pink-500 dark:hover:text-pink-300">
{t('legal:impressum')}
</Link>
</li>
<li>
<Link href={links.datenschutz} className="transition-colors hover:text-pink-500">
<Link href={links.datenschutz} className="transition-colors hover:text-pink-500 dark:hover:text-pink-300">
{t('legal:datenschutz')}
</Link>
</li>
<li>
<Link href={links.agb} className="transition-colors hover:text-pink-500">
<Link href={links.agb} className="transition-colors hover:text-pink-500 dark:hover:text-pink-300">
{t('legal:agb')}
</Link>
</li>
<li>
<Link href={links.kontakt} className="transition-colors hover:text-pink-500">
<Link href={links.kontakt} className="transition-colors hover:text-pink-500 dark:hover:text-pink-300">
{t('marketing:nav.contact')}
</Link>
</li>
@@ -66,7 +66,7 @@ const Footer: React.FC = () => {
<button
type="button"
onClick={openPreferences}
className="transition-colors hover:text-pink-500"
className="transition-colors hover:text-pink-500 dark:hover:text-pink-300"
>
{t('common:consent.footer.manage_link')}
</button>
@@ -75,18 +75,18 @@ const Footer: React.FC = () => {
</div>
<div>
<h3 className="font-display mb-4 font-semibold text-gray-900">
<h3 className="font-display mb-4 font-semibold text-gray-900 dark:text-gray-50">
{t('marketing:footer.social', 'Social')}
</h3>
<ul className="font-sans-marketing space-y-2 text-sm text-gray-600">
<li><a href="#" className="hover:text-pink-500">Instagram</a></li>
<li><a href="#" className="hover:text-pink-500">Facebook</a></li>
<li><a href="#" className="hover:text-pink-500">YouTube</a></li>
<ul className="font-sans-marketing space-y-2 text-sm text-gray-600 dark:text-gray-300">
<li><a href="#" className="hover:text-pink-500 dark:hover:text-pink-300">Instagram</a></li>
<li><a href="#" className="hover:text-pink-500 dark:hover:text-pink-300">Facebook</a></li>
<li><a href="#" className="hover:text-pink-500 dark:hover:text-pink-300">YouTube</a></li>
</ul>
</div>
</div>
<div className="font-sans-marketing mt-8 border-t border-gray-200 pt-8 text-center text-sm text-gray-500">
<div className="font-sans-marketing mt-8 border-t border-gray-200 pt-8 text-center text-sm text-gray-500 dark:border-gray-800 dark:text-gray-400">
&copy; {currentYear} Die Fotospiel App {t('marketing:footer.rights_reserved', 'Alle Rechte vorbehalten')}.
</div>
</div>

View File

@@ -167,12 +167,12 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
))}
</Head>
<MatomoTracker config={analytics?.matomo} />
<div className="min-h-screen bg-white">
<header className="sticky top-0 z-40 border-b border-gray-200/60 bg-white/95 backdrop-blur">
<div className="min-h-screen bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-50 transition-colors">
<header className="sticky top-0 z-40 border-b border-gray-200/60 bg-white/95 backdrop-blur dark:border-gray-800/60 dark:bg-gray-900/80">
<div className="container mx-auto flex items-center justify-between px-4 py-4">
<Link
href={localizedPath('/')}
className="flex items-center gap-3 text-gray-900"
className="flex items-center gap-3 text-gray-900 dark:text-gray-50"
onClick={() => setMobileMenuOpen(false)}
>
<img src="/logo-transparent-md.png" alt="Fotospiel App Logo" className="h-10 w-auto" />
@@ -184,10 +184,10 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
{navLinks.map((item) => (
item.children ? (
<div key={item.key} className="relative group">
<span className="inline-flex cursor-default items-center gap-1 text-md font-md text-gray-700 transition-colors group-hover:text-pink-600 font-sans-marketing">
<span className="inline-flex cursor-default items-center gap-1 text-md font-md text-gray-700 transition-colors group-hover:text-pink-600 dark:text-gray-200 dark:group-hover:text-pink-300 font-sans-marketing">
{item.label}
<svg
className="h-4 w-4 text-gray-400 transition group-hover:text-pink-500"
className="h-4 w-4 text-gray-400 transition group-hover:text-pink-500 dark:text-gray-500"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -196,12 +196,12 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<path d="M6 8L10 12L14 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
</span>
<div className="absolute left-0 top-full hidden min-w-[220px] flex-col gap-1 rounded-xl border border-gray-100 bg-white p-3 text-sm shadow-xl shadow-rose-200/50 transition group-hover:flex group-focus-within:flex">
<div className="absolute left-0 top-full hidden min-w-[220px] flex-col gap-1 rounded-xl border border-gray-100 bg-white p-3 text-sm shadow-xl shadow-rose-200/50 transition group-hover:flex group-focus-within:flex dark:border-gray-800 dark:bg-gray-900">
{item.children.map((child) => (
<Link
key={child.key}
href={child.href}
className="rounded-lg px-3 py-2 font-medium text-gray-600 transition hover:bg-rose-50 hover:text-pink-600 font-sans-marketing"
className="rounded-lg px-3 py-2 font-medium text-gray-600 transition hover:bg-rose-50 hover:text-pink-600 dark:text-gray-100 dark:hover:bg-gray-800 dark:hover:text-pink-300 font-sans-marketing"
onClick={() => setMobileMenuOpen(false)}
>
{child.label}
@@ -213,7 +213,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<Link
key={item.key}
href={item.href}
className="text-md font-md text-gray-700 transition hover:text-pink-600 font-sans-marketing"
className="text-md font-md text-gray-700 transition hover:text-pink-600 dark:text-gray-100 dark:hover:text-pink-300 font-sans-marketing"
onClick={() => setMobileMenuOpen(false)}
>
{item.label}
@@ -235,14 +235,14 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<Button
variant="outline"
size="icon"
className="rounded-full border-gray-200 text-gray-600 transition hover:border-pink-200 hover:text-pink-500"
className="rounded-full border-gray-200 text-gray-600 transition hover:border-pink-200 hover:text-pink-500 dark:border-gray-700 dark:text-gray-200 dark:hover:border-pink-400 dark:hover:text-pink-300"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">{t('nav.preferences', 'Einstellungen')}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 space-y-1 p-2">
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400">
<DropdownMenuContent align="end" className="w-56 space-y-1 p-2 dark:border-gray-800 dark:bg-gray-900">
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400 dark:text-gray-500">
{t('nav.preferences', 'Einstellungen')}
</DropdownMenuLabel>
<DropdownMenuItem
@@ -250,13 +250,13 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
event.preventDefault();
toggleTheme();
}}
className="flex items-center gap-2 font-sans-marketing"
className="flex items-center gap-2 font-sans-marketing dark:text-gray-100"
>
{themeIsDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
<span>{themeLabel}</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400">
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400 dark:text-gray-500">
{t('nav.language', 'Sprache')}
</DropdownMenuLabel>
<DropdownMenuRadioGroup value={activeLocale} onValueChange={handleLocaleChange}>
@@ -264,7 +264,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<DropdownMenuRadioItem
key={code}
value={code}
className="flex items-center gap-2 font-sans-marketing"
className="flex items-center gap-2 font-sans-marketing dark:text-gray-100"
>
<Languages className="h-4 w-4" />
<span>{code.toUpperCase()}</span>
@@ -274,7 +274,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<DropdownMenuSeparator />
{user ? (
<>
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400">
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-gray-400 dark:text-gray-500">
{user.name ?? user.email}
</DropdownMenuLabel>
<DropdownMenuItem
@@ -282,7 +282,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
event.preventDefault();
router.visit('/event-admin');
}}
className="flex items-center gap-2 font-sans-marketing"
className="flex items-center gap-2 font-sans-marketing dark:text-gray-100"
>
<LayoutDashboard className="h-4 w-4" />
<span>{t('nav.dashboard', 'Zum Admin-Bereich')}</span>
@@ -292,7 +292,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
event.preventDefault();
handleLogout();
}}
className="flex items-center gap-2 font-sans-marketing"
className="flex items-center gap-2 font-sans-marketing dark:text-gray-100"
>
<LogOut className="h-4 w-4" />
<span>{t('nav.logout', 'Abmelden')}</span>
@@ -305,7 +305,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
event.preventDefault();
router.visit(localizedPath('/login'));
}}
className="flex items-center gap-2 font-sans-marketing"
className="flex items-center gap-2 font-sans-marketing dark:text-gray-100"
>
<LogIn className="h-4 w-4" />
<span>{t('nav.login', 'Anmelden')}</span>
@@ -316,7 +316,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
</DropdownMenu>
<button
type="button"
className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-gray-200 bg-white text-gray-700 shadow-sm md:hidden"
className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-gray-200 bg-white text-gray-700 shadow-sm transition md:hidden dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
onClick={() => setMobileMenuOpen((open) => !open)}
aria-label={mobileMenuOpen ? t('nav.close_menu', 'Menü schließen') : t('nav.open_menu', 'Menü öffnen')}
>
@@ -333,18 +333,18 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
</div>
</div>
{mobileMenuOpen && (
<div className="border-t border-gray-200 bg-white md:hidden">
<div className="border-t border-gray-200 bg-white md:hidden dark:border-gray-800 dark:bg-gray-900">
<div className="container mx-auto space-y-4 px-4 py-4">
{navLinks.map((item) => (
item.children ? (
<div key={item.key} className="space-y-2">
<p className="text-sm font-semibold text-gray-500">{item.label}</p>
<p className="text-sm font-semibold text-gray-500 dark:text-gray-300">{item.label}</p>
<div className="flex flex-col gap-2">
{item.children.map((child) => (
<Link
key={child.key}
href={child.href}
className="rounded-lg border border-gray-100 px-3 py-2 text-sm font-medium text-gray-700 transition hover:border-pink-200 hover:bg-rose-50 hover:text-pink-600"
className="rounded-lg border border-gray-100 px-3 py-2 text-sm font-medium text-gray-700 transition hover:border-pink-200 hover:bg-rose-50 hover:text-pink-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:hover:border-pink-400 dark:hover:bg-gray-800 dark:hover:text-pink-300"
onClick={() => setMobileMenuOpen(false)}
>
{child.label}
@@ -356,7 +356,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
<Link
key={item.key}
href={item.href}
className="block rounded-lg border border-gray-100 px-3 py-2 text-sm font-semibold text-gray-700 transition hover:border-pink-200 hover:bg-rose-50 hover:text-pink-600 font-sans-marketing"
className="block rounded-lg border border-gray-100 px-3 py-2 text-sm font-semibold text-gray-700 transition hover:border-pink-200 hover:bg-rose-50 hover:text-pink-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:hover:border-pink-400 dark:hover:bg-gray-800 dark:hover:text-pink-300 font-sans-marketing"
onClick={() => setMobileMenuOpen(false)}
>
{item.label}
@@ -381,7 +381,7 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
id="marketing-language-select-mobile"
value={activeLocale}
onChange={(event) => handleLocaleChange(event.target.value)}
className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm focus:border-pink-400 focus:outline-none focus:ring focus:ring-pink-200"
className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm focus:border-pink-400 focus:outline-none focus:ring focus:ring-pink-200 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:focus:border-pink-400 dark:focus:ring-pink-300"
>
{supportedLocales.map((code) => (
<option key={code} value={code}>