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

@@ -10,7 +10,8 @@ import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { ArrowRight, Camera, QrCode, ShieldCheck, Sparkles, Smartphone } from 'lucide-react';
import { ArrowRight, Camera, QrCode, ShieldCheck, Sparkles, Smartphone, Loader2, CheckCircle2 } from 'lucide-react';
import { usePage } from '@inertiajs/react';
interface Package {
id: number;
@@ -34,10 +35,12 @@ const Home: React.FC<Props> = ({ packages }) => {
variant: heroCtaVariant,
trackClick: trackHeroCtaClick,
} = useCtaExperiment('home_hero_cta');
const { flash } = usePage<{ flash?: { success?: string } }>().props;
const { data, setData, post, processing, errors, reset } = useForm({
name: '',
email: '',
message: '',
nickname: '',
});
const heroBulletsRaw = t('home.hero_bullets', { returnObjects: true });
@@ -447,14 +450,54 @@ const Home: React.FC<Props> = ({ packages }) => {
<section id="contact" className="bg-gray-50 py-20 px-4 dark:bg-gray-950/80">
<div className="container mx-auto max-w-4xl">
<Card className="border-gray-200/70 bg-white/95 shadow-lg shadow-rose-200/40 dark:border-gray-800/60 dark:bg-gray-900/80">
<CardHeader className="text-center">
<Badge className="mx-auto mb-3 w-fit bg-rose-100 px-3 py-1 text-xs font-semibold uppercase text-rose-600 dark:bg-rose-500/20 dark:text-rose-200">
{t('home.contact_title')}
</Badge>
<CardTitle className="text-2xl md:text-3xl">{t('home.contact_lead')}</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
<CardHeader className="text-center">
<Badge className="mx-auto mb-3 w-fit bg-rose-100 px-3 py-1 text-xs font-semibold uppercase text-rose-600 dark:bg-rose-500/20 dark:text-rose-200">
{t('home.contact_title')}
</Badge>
<CardTitle className="text-2xl md:text-3xl">{t('home.contact_lead')}</CardTitle>
</CardHeader>
<CardContent>
{flash?.success ? (
<div className="mb-4 flex items-center gap-2 rounded-xl border border-emerald-200/70 bg-emerald-50 px-3 py-2 text-sm text-emerald-800 dark:border-emerald-500/40 dark:bg-emerald-500/10 dark:text-emerald-100">
<CheckCircle2 className="h-4 w-4" />
<span>{flash.success}</span>
</div>
) : null}
<div className="grid gap-6 lg:grid-cols-[1fr,1.1fr]">
<div className="space-y-3 rounded-2xl border border-rose-100/70 bg-rose-50/50 p-5 text-left text-sm text-slate-700 shadow-inner dark:border-rose-500/20 dark:bg-rose-500/5 dark:text-slate-200">
<p className="text-xs font-semibold uppercase tracking-wide text-rose-500 dark:text-rose-200">
{t('home.contact_title')}
</p>
<p className="text-base font-semibold text-slate-900 dark:text-white">{t('home.contact_lead')}</p>
<ul className="space-y-2">
<li className="flex items-start gap-3">
<div className="mt-0.5 h-2.5 w-2.5 rounded-full bg-rose-500" />
<span>{t('home.hero_bullets.0', 'Antwort in <24h')}</span>
</li>
<li className="flex items-start gap-3">
<div className="mt-0.5 h-2.5 w-2.5 rounded-full bg-rose-500" />
<span>{t('home.hero_bullets.1', 'Keine Weitergabe, kein Tracking')}</span>
</li>
<li className="flex items-start gap-3">
<div className="mt-0.5 h-2.5 w-2.5 rounded-full bg-rose-500" />
<span>{t('home.contact_privacy')}</span>
</li>
</ul>
<div className="rounded-xl border border-white/60 bg-white/80 p-3 text-xs font-medium text-slate-700 shadow-sm dark:border-white/10 dark:bg-white/10 dark:text-slate-100">
{t('home.contact_privacy')}
</div>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<input
type="text"
name="nickname"
value={data.nickname}
onChange={(event) => setData('nickname', event.target.value)}
className="hidden"
tabIndex={-1}
autoComplete="off"
aria-hidden
/>
<div className="grid gap-4 md:grid-cols-2">
<div className="flex flex-col gap-2">
<label htmlFor="name" className="text-sm font-semibold text-gray-600 dark:text-gray-200">
@@ -467,9 +510,11 @@ const Home: React.FC<Props> = ({ packages }) => {
className="h-12 rounded-xl border-gray-200/70 bg-white/90 shadow-inner shadow-gray-200/40 focus-visible:ring-rose-300/60 dark:border-gray-700 dark:bg-gray-900/70"
autoComplete="name"
required
aria-invalid={Boolean(errors.name)}
aria-describedby={errors.name ? 'contact-name-error' : undefined}
/>
{errors.name && (
<p className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.name}</p>
<p id="contact-name-error" className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.name}</p>
)}
</div>
<div className="flex flex-col gap-2">
@@ -484,9 +529,11 @@ const Home: React.FC<Props> = ({ packages }) => {
className="h-12 rounded-xl border-gray-200/70 bg-white/90 shadow-inner shadow-gray-200/40 focus-visible:ring-rose-300/60 dark:border-gray-700 dark:bg-gray-900/70"
autoComplete="email"
required
aria-invalid={Boolean(errors.email)}
aria-describedby={errors.email ? 'contact-email-error' : undefined}
/>
{errors.email && (
<p className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.email}</p>
<p id="contact-email-error" className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.email}</p>
)}
</div>
</div>
@@ -494,31 +541,41 @@ const Home: React.FC<Props> = ({ packages }) => {
<label htmlFor="message" className="text-sm font-semibold text-gray-600 dark:text-gray-200">
{t('home.message_label')} *
</label>
<Textarea
id="message"
rows={5}
value={data.message}
onChange={(event) => setData('message', event.target.value)}
className="rounded-xl border-gray-200/70 bg-white/90 shadow-inner shadow-gray-200/40 focus-visible:ring-rose-300/60 dark:border-gray-700 dark:bg-gray-900/70"
required
/>
{errors.message && (
<p className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.message}</p>
)}
<Textarea
id="message"
rows={5}
value={data.message}
onChange={(event) => setData('message', event.target.value)}
className="rounded-xl border-gray-200/70 bg-white/90 shadow-inner shadow-gray-200/40 focus-visible:ring-rose-300/60 dark:border-gray-700 dark:bg-gray-900/70"
required
aria-invalid={Boolean(errors.message)}
aria-describedby={errors.message ? 'contact-message-error' : undefined}
/>
{errors.message && (
<p id="contact-message-error" className="text-sm font-medium text-rose-600 dark:text-rose-300">{errors.message}</p>
)}
</div>
<div className="space-y-3 text-sm text-muted-foreground">
<p>{t('home.contact_privacy')}</p>
<Button
type="submit"
disabled={processing}
className="h-12 w-full rounded-full bg-gradient-to-r from-[#ff5f87] via-[#ec4899] to-[#6366f1] text-base font-semibold text-white shadow-[0_18px_35px_-18px_rgba(236,72,153,0.7)] transition hover:from-[#ff4470] hover:via-[#ec4899] hover:to-[#4f46e5] disabled:cursor-not-allowed disabled:opacity-60"
>
{processing ? (
<span className="flex items-center justify-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t('home.sending')}
</span>
) : (
t('home.send')
)}
</Button>
</div>
</form>
</div>
<div className="space-y-3 text-sm text-muted-foreground">
<p>{t('home.contact_privacy')}</p>
<Button
type="submit"
disabled={processing}
className="h-12 w-full rounded-full bg-gradient-to-r from-[#ff5f87] via-[#ec4899] to-[#6366f1] text-base font-semibold text-white shadow-[0_18px_35px_-18px_rgba(236,72,153,0.7)] transition hover:from-[#ff4470] hover:via-[#ec4899] hover:to-[#4f46e5] disabled:cursor-not-allowed disabled:opacity-60"
>
{processing ? t('home.sending') : t('home.send')}
</Button>
</div>
</form>
</CardContent>
</Card>
</CardContent>
</Card>
</div>
</section>