494 lines
25 KiB
TypeScript
494 lines
25 KiB
TypeScript
import React from 'react';
|
||
import { Navigate } from 'react-router-dom';
|
||
import {
|
||
ArrowRight,
|
||
CheckCircle2,
|
||
Layers,
|
||
ListChecks,
|
||
Menu,
|
||
Moon,
|
||
Palette,
|
||
QrCode,
|
||
ShieldCheck,
|
||
Smartphone,
|
||
Sparkles,
|
||
Sun,
|
||
Wand2,
|
||
} from 'lucide-react';
|
||
import { useTranslation } from 'react-i18next';
|
||
|
||
import { Button } from '@/components/ui/button';
|
||
import { Badge } from '@/components/ui/badge';
|
||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||
import { cn } from '@/lib/utils';
|
||
import { useAppearance } from '@/hooks/use-appearance';
|
||
|
||
import { ADMIN_DEFAULT_AFTER_LOGIN_PATH, ADMIN_LOGIN_PATH } from '../constants';
|
||
import { useAuth } from '../auth/context';
|
||
import { LanguageSwitcher } from '../components/LanguageSwitcher';
|
||
import { navigateToHref } from '../lib/navigation';
|
||
import { getCurrentLocale } from '../lib/locale';
|
||
|
||
type Feature = {
|
||
key: string;
|
||
title: string;
|
||
description: string;
|
||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
||
};
|
||
|
||
type Step = {
|
||
key: string;
|
||
title: string;
|
||
description: string;
|
||
accent: string;
|
||
};
|
||
|
||
type Plan = {
|
||
key: string;
|
||
title: string;
|
||
badge?: string;
|
||
highlight?: string;
|
||
points: string[];
|
||
};
|
||
|
||
export default function WelcomeTeaserPage() {
|
||
const { t } = useTranslation('common');
|
||
const { status } = useAuth();
|
||
const { appearance, updateAppearance } = useAppearance();
|
||
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
|
||
|
||
React.useEffect(() => {
|
||
document.body.classList.add('tenant-admin-theme', 'tenant-admin-welcome-theme');
|
||
|
||
return () => {
|
||
document.body.classList.remove('tenant-admin-theme', 'tenant-admin-welcome-theme');
|
||
};
|
||
}, []);
|
||
|
||
if (status === 'authenticated') {
|
||
return <Navigate to={ADMIN_DEFAULT_AFTER_LOGIN_PATH} replace />;
|
||
}
|
||
|
||
const locale = getCurrentLocale();
|
||
const packagesHref = `/${locale}/packages`;
|
||
const howItWorksHref = locale === 'de' ? `/${locale}/so-funktionierts` : `/${locale}/how-it-works`;
|
||
|
||
const features: Feature[] = [
|
||
{
|
||
key: 'branding',
|
||
title: t('welcome.features.branding.title', 'Branding & Layout'),
|
||
description: t('welcome.features.branding.description', 'Farben, Schriften, QR-Layouts und Einladungen in einem Fluss.'),
|
||
icon: Palette,
|
||
},
|
||
{
|
||
key: 'tasks',
|
||
title: t('welcome.features.tasks.title', 'Aufgaben & Emotion-Sets'),
|
||
description: t('welcome.features.tasks.description', 'Sammlungen importieren oder eigene Aufgaben erstellen – mobil abhakbar.'),
|
||
icon: ListChecks,
|
||
},
|
||
{
|
||
key: 'moderation',
|
||
title: t('welcome.features.moderation.title', 'Foto-Moderation'),
|
||
description: t('welcome.features.moderation.description', 'Uploads sofort prüfen, Highlights markieren und Galerie-Link teilen.'),
|
||
icon: ShieldCheck,
|
||
},
|
||
{
|
||
key: 'invites',
|
||
title: t('welcome.features.invites.title', 'Einladungen & QR'),
|
||
description: t('welcome.features.invites.description', 'Links und Druckvorlagen generieren – mit Paketlimits im Blick.'),
|
||
icon: QrCode,
|
||
},
|
||
];
|
||
|
||
const steps: Step[] = [
|
||
{
|
||
key: 'prepare',
|
||
title: t('welcome.steps.prepare.title', 'Vorbereiten'),
|
||
description: t('welcome.steps.prepare.description', 'Event anlegen, Branding setzen, Aufgaben aktivieren.'),
|
||
accent: t('welcome.steps.prepare.accent', 'Setup'),
|
||
},
|
||
{
|
||
key: 'share',
|
||
title: t('welcome.steps.share.title', 'Teilen & Einladen'),
|
||
description: t('welcome.steps.share.description', 'QRs/Links verteilen, Missionen auswählen, Team onboarden.'),
|
||
accent: t('welcome.steps.share.accent', 'Share'),
|
||
},
|
||
{
|
||
key: 'run',
|
||
title: t('welcome.steps.run.title', 'Live moderieren'),
|
||
description: t('welcome.steps.run.description', 'Uploads prüfen, Highlights pushen und nach dem Event die Galerie teilen.'),
|
||
accent: t('welcome.steps.run.accent', 'Live'),
|
||
},
|
||
];
|
||
|
||
const plans: Plan[] = [
|
||
{
|
||
key: 'starter',
|
||
title: t('welcome.plans.starter.title', 'Starter'),
|
||
badge: t('welcome.plans.starter.badge', 'Für ein Event'),
|
||
points: [
|
||
t('welcome.plans.starter.p1', '1 Event, Basis-Branding'),
|
||
t('welcome.plans.starter.p2', 'Aufgaben & Einladungen inklusive'),
|
||
t('welcome.plans.starter.p3', 'Moderation & Galerie-Link'),
|
||
],
|
||
},
|
||
{
|
||
key: 'standard',
|
||
title: t('welcome.plans.standard.title', 'Standard'),
|
||
badge: t('welcome.plans.standard.badge', 'Beliebt'),
|
||
highlight: t('welcome.plans.standard.highlight', 'Mehr Kontingent & Branding'),
|
||
points: [
|
||
t('welcome.plans.standard.p1', 'Mehr Events pro Jahr'),
|
||
t('welcome.plans.standard.p2', 'Erweitertes Branding & Layouts'),
|
||
t('welcome.plans.standard.p3', 'Support bei Live-Events'),
|
||
],
|
||
},
|
||
{
|
||
key: 'reseller',
|
||
title: t('welcome.plans.reseller.title', 'Reseller S'),
|
||
badge: t('welcome.plans.reseller.badge', 'Für Dienstleister'),
|
||
highlight: t('welcome.plans.reseller.highlight', 'Mehrere Events parallel verwalten'),
|
||
points: [
|
||
t('welcome.plans.reseller.p1', 'Bis zu 5 Events pro Paket'),
|
||
t('welcome.plans.reseller.p2', 'Aufgaben-Sammlungen und Vorlagen'),
|
||
t('welcome.plans.reseller.p3', 'Teamrollen & Rechteverwaltung'),
|
||
],
|
||
},
|
||
];
|
||
|
||
const audienceCards = [
|
||
{
|
||
key: 'endcustomers',
|
||
title: t('welcome.audience.endcustomers.title', 'Endkund:innen'),
|
||
description: t('welcome.audience.endcustomers.description', 'Schnell einrichten, mobil moderieren und nach dem Event die Galerie teilen.'),
|
||
icon: Smartphone,
|
||
},
|
||
{
|
||
key: 'resellers',
|
||
title: t('welcome.audience.resellers.title', 'Reseller & Agenturen'),
|
||
description: t('welcome.audience.resellers.description', 'Mehrere Events im Blick behalten, Kontingente überwachen und Vorlagen nutzen.'),
|
||
icon: Layers,
|
||
},
|
||
];
|
||
|
||
const previewRaw = t('welcome.preview.items', {
|
||
defaultValue: [
|
||
'Moderation, Aufgaben und Einladungen als Schnellzugriff',
|
||
'Sticky Actions auf Mobile für den Eventtag',
|
||
'Paket-Status & Limits jederzeit sichtbar',
|
||
],
|
||
returnObjects: true,
|
||
});
|
||
|
||
const previewBullets = Array.isArray(previewRaw) ? previewRaw : [String(previewRaw)];
|
||
|
||
const themeLabel = appearance === 'dark' ? t('welcome.theme.dark', 'Dunkel') : t('welcome.theme.light', 'Hell');
|
||
|
||
const handleLogin = React.useCallback(() => {
|
||
navigateToHref(ADMIN_LOGIN_PATH);
|
||
}, []);
|
||
|
||
const handlePackages = React.useCallback(() => navigateToHref(packagesHref), [packagesHref]);
|
||
const handleHow = React.useCallback(() => navigateToHref(howItWorksHref), [howItWorksHref]);
|
||
const handleThemeToggle = React.useCallback(() => {
|
||
updateAppearance(appearance === 'dark' ? 'light' : 'dark');
|
||
}, [appearance, updateAppearance]);
|
||
|
||
const renderMenuActions = () => (
|
||
<div className="flex flex-col gap-3">
|
||
<Button size="sm" onClick={handleLogin} className="justify-between">
|
||
<span>{t('welcome.cta.login', 'Login')}</span>
|
||
<ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="outline" size="sm" onClick={handlePackages} className="justify-between">
|
||
<span>{t('welcome.cta.packages', 'Pakete ansehen')}</span>
|
||
<ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="ghost" size="sm" onClick={handleHow} className="justify-between">
|
||
<span>{t('welcome.cta.how', "So funktioniert's")}</span>
|
||
<ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
<div className="flex items-center justify-between rounded-xl border border-slate-200/70 px-4 py-3 text-sm dark:border-white/10">
|
||
<div>
|
||
<p className="text-xs text-slate-500 dark:text-slate-400">{t('welcome.theme.label', 'Darstellung')}</p>
|
||
<p className="text-sm font-semibold text-slate-900 dark:text-white">{themeLabel}</p>
|
||
</div>
|
||
<Button variant="outline" size="icon" aria-label={t('welcome.theme.aria', 'Darstellung umschalten')} onClick={handleThemeToggle}>
|
||
{appearance === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||
</Button>
|
||
</div>
|
||
<div className="flex items-center justify-between rounded-xl border border-slate-200/70 px-4 py-3 text-sm dark:border-white/10">
|
||
<span className="text-slate-700 dark:text-slate-200">{t('app.languageSwitch')}</span>
|
||
<LanguageSwitcher />
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
return (
|
||
<div className="relative min-h-svh overflow-hidden bg-gradient-to-b from-rose-50 via-white to-slate-50 text-slate-900 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950 dark:text-white">
|
||
<div
|
||
aria-hidden
|
||
className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_10%_20%,rgba(255,137,170,0.35),transparent_45%),radial-gradient(circle_at_90%_0%,rgba(96,165,250,0.25),transparent_45%),radial-gradient(circle_at_50%_90%,rgba(16,185,129,0.15),transparent_45%)] opacity-70 dark:opacity-30"
|
||
/>
|
||
<div aria-hidden className="absolute inset-0 bg-gradient-to-b from-white/70 via-white/40 to-transparent dark:from-slate-950/90 dark:via-slate-950/70 dark:to-slate-950/80" />
|
||
|
||
<div className="relative z-10 mx-auto flex w-full max-w-6xl flex-col gap-10 px-4 pb-16 pt-8 sm:px-6 lg:px-10 lg:pt-12">
|
||
<header className="flex items-center justify-between gap-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-rose-500 text-lg font-semibold text-white shadow-lg shadow-rose-200/60 dark:bg-rose-400 dark:text-slate-900">
|
||
FS
|
||
</div>
|
||
<div className="space-y-1">
|
||
<p className="text-[11px] font-semibold uppercase tracking-[0.35em] text-rose-600 dark:text-rose-200">
|
||
{t('welcome.eyebrow', 'Event Admin')}
|
||
</p>
|
||
<p className="text-sm font-semibold text-slate-900 dark:text-white">Fotospiel.app</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="hidden items-center gap-2 sm:flex">
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={handleThemeToggle}
|
||
aria-label={t('welcome.theme.aria', 'Darstellung umschalten')}
|
||
className="rounded-full border border-slate-200/80 bg-white/80 text-slate-700 hover:border-rose-200 hover:text-rose-700 dark:border-white/10 dark:bg-white/10 dark:text-white"
|
||
>
|
||
{appearance === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||
<span className="hidden sm:inline">{themeLabel}</span>
|
||
</Button>
|
||
<LanguageSwitcher />
|
||
<Button variant="outline" size="sm" onClick={handlePackages} className="border-rose-200 text-rose-700 hover:bg-rose-50">
|
||
{t('welcome.cta.packages', 'Pakete ansehen')}
|
||
</Button>
|
||
<Button size="sm" onClick={handleLogin} className="bg-rose-500 text-white hover:bg-rose-600">
|
||
{t('welcome.cta.login', 'Login')}
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="sm:hidden">
|
||
<Sheet open={isMenuOpen} onOpenChange={setIsMenuOpen}>
|
||
<SheetTrigger asChild>
|
||
<Button
|
||
variant="outline"
|
||
size="icon"
|
||
aria-label={isMenuOpen ? t('welcome.menu.close', 'Menü schließen') : t('welcome.menu.open', 'Menü öffnen')}
|
||
className="rounded-full border-rose-200 bg-white/80 text-slate-700 shadow-sm dark:border-white/10 dark:bg-white/10 dark:text-white"
|
||
>
|
||
{isMenuOpen ? <Sparkles className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
|
||
</Button>
|
||
</SheetTrigger>
|
||
<SheetContent side="right" className="bg-white/95 text-slate-900 dark:bg-slate-950 dark:text-white">
|
||
<SheetHeader>
|
||
<SheetTitle className="text-lg font-semibold">{t('welcome.menu.title', 'Navigation')}</SheetTitle>
|
||
</SheetHeader>
|
||
{renderMenuActions()}
|
||
</SheetContent>
|
||
</Sheet>
|
||
</div>
|
||
</header>
|
||
|
||
<main className="flex flex-col gap-12">
|
||
<section className="grid gap-8 lg:grid-cols-[1.1fr,0.9fr]">
|
||
<div className="space-y-6">
|
||
<div className="flex flex-wrap items-center gap-3 text-sm text-slate-600 dark:text-slate-300">
|
||
<Badge variant="secondary" className="bg-rose-100/80 text-rose-700 hover:bg-rose-100 dark:bg-rose-400/20 dark:text-rose-100">
|
||
<Sparkles className="h-3.5 w-3.5" />
|
||
{t('welcome.badge', 'Fotos, Aufgaben & Einladungen an einem Ort')}
|
||
</Badge>
|
||
<div className="flex items-center gap-2 rounded-full bg-white/70 px-3 py-1 text-xs font-medium text-slate-700 shadow-sm shadow-rose-200/50 ring-1 ring-slate-200/70 dark:bg-white/5 dark:text-white dark:ring-white/10">
|
||
<ShieldCheck className="h-3.5 w-3.5 text-emerald-500" />
|
||
{t('welcome.loginPrompt', 'Bereits Kunde? Login oben rechts.')}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
<h1 className="font-display text-3xl font-semibold leading-tight tracking-tight text-slate-900 dark:text-white sm:text-4xl">
|
||
{t('welcome.title', 'Event-Branding, Aufgaben & Foto-Moderation in einer App.')}
|
||
</h1>
|
||
<p className="text-lg text-slate-700 dark:text-slate-200">
|
||
{t('welcome.subtitle', 'Bereite dein Event vor, teile Einladungen, moderiere Uploads live und gib die Galerie danach frei.')}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex flex-wrap items-center gap-3">
|
||
<Button size="lg" onClick={handleLogin} className="bg-rose-500 text-white hover:bg-rose-600">
|
||
{t('welcome.cta.open', 'Event Admin öffnen')}
|
||
<ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="outline" size="lg" onClick={handleHow} className="border-rose-200 text-rose-700 hover:bg-rose-50">
|
||
{t('welcome.cta.how', "So funktioniert's")}
|
||
</Button>
|
||
<Button variant="ghost" size="lg" onClick={handlePackages} className="text-slate-700 hover:text-rose-700 dark:text-white">
|
||
{t('welcome.cta.packages', 'Pakete ansehen')}
|
||
</Button>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div className="relative">
|
||
<div className="absolute inset-0 rounded-3xl bg-gradient-to-br from-rose-100/80 via-white/70 to-sky-100/60 blur-3xl dark:from-rose-400/10 dark:via-slate-900 dark:to-sky-400/10" aria-hidden />
|
||
<div className="relative flex h-full flex-col gap-4 rounded-3xl border border-slate-200/60 bg-white/80 p-6 shadow-lg shadow-rose-100/40 backdrop-blur dark:border-white/10 dark:bg-white/5 dark:shadow-none">
|
||
<div className="flex items-center gap-2 text-sm font-semibold text-slate-800 dark:text-white">
|
||
<Wand2 className="h-4 w-4 text-rose-500" />
|
||
{t('welcome.preview.title', 'Was dich erwartet')}
|
||
</div>
|
||
<div className="space-y-3 text-sm text-slate-700 dark:text-slate-200">
|
||
{previewBullets.map((item) => (
|
||
<div key={item} className="flex items-start gap-2 rounded-2xl border border-slate-200/70 bg-white/70 px-3 py-2 text-left dark:border-white/10 dark:bg-white/5">
|
||
<CheckCircle2 className="mt-0.5 h-4 w-4 text-emerald-500" />
|
||
<p>{item}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="mt-auto grid grid-cols-2 gap-3 text-xs text-slate-600 dark:text-slate-300">
|
||
<div className="rounded-2xl border border-dashed border-rose-200/70 bg-rose-50/70 p-4 dark:border-rose-200/30 dark:bg-rose-200/10">
|
||
<p className="text-[11px] uppercase tracking-[0.3em] text-rose-500">{t('welcome.highlight.moderation', 'Live-Moderation')}</p>
|
||
<p className="mt-2 font-semibold text-slate-900 dark:text-white">{t('welcome.highlight.moderationHint', 'Approve/Hide, Highlights, Galerie-Link')}</p>
|
||
</div>
|
||
<div className="rounded-2xl border border-dashed border-sky-200/80 bg-sky-50/80 p-4 dark:border-sky-200/40 dark:bg-sky-200/10">
|
||
<p className="text-[11px] uppercase tracking-[0.3em] text-sky-600">{t('welcome.highlight.tasks', 'Aufgaben & Emotion-Sets')}</p>
|
||
<p className="mt-2 font-semibold text-slate-900 dark:text-white">{t('welcome.highlight.tasksHint', 'Sammlungen importieren oder eigene erstellen')}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="space-y-4">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-xs uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">{t('welcome.features.title', 'Was du steuern kannst')}</p>
|
||
<h2 className="text-2xl font-semibold text-slate-900 dark:text-white sm:text-3xl">{t('welcome.features.subtitle', 'Alles an einem Ort')}</h2>
|
||
</div>
|
||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||
{features.map((feature) => (
|
||
<div
|
||
key={feature.key}
|
||
className="group flex h-full flex-col gap-3 rounded-2xl border border-slate-200 bg-white/90 p-4 text-left shadow-sm transition hover:-translate-y-0.5 hover:border-rose-200 hover:shadow-md dark:border-white/10 dark:bg-white/5"
|
||
>
|
||
<div className="flex items-center gap-2 text-sm font-semibold text-slate-900 dark:text-white">
|
||
<feature.icon className="h-4 w-4 text-rose-500" />
|
||
{feature.title}
|
||
</div>
|
||
<p className="text-sm text-slate-600 dark:text-slate-300">{feature.description}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="space-y-4">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-xs uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">{t('welcome.steps.title', "So funktioniert's")}</p>
|
||
<h2 className="text-2xl font-semibold text-slate-900 dark:text-white sm:text-3xl">{t('welcome.steps.subtitle', 'In drei Schritten bereit')}</h2>
|
||
</div>
|
||
<div className="grid gap-4 md:grid-cols-3">
|
||
{steps.map((step) => (
|
||
<div
|
||
key={step.key}
|
||
className="flex h-full flex-col gap-3 rounded-2xl border border-slate-200/70 bg-white/90 p-4 shadow-sm dark:border-white/10 dark:bg-white/5"
|
||
>
|
||
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">
|
||
{step.accent}
|
||
</div>
|
||
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">{step.title}</h3>
|
||
<p className="text-sm text-slate-600 dark:text-slate-300">{step.description}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="space-y-4">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-xs uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">{t('welcome.plans.title', 'Pakete im Überblick')}</p>
|
||
<h2 className="text-2xl font-semibold text-slate-900 dark:text-white sm:text-3xl">{t('welcome.plans.subtitle', 'Wähle das passende Kontingent')}</h2>
|
||
<p className="text-sm text-slate-600 dark:text-slate-300">{t('welcome.plans.hint', 'Starter, Standard oder Reseller – alles mit Moderation & Einladungen.')}</p>
|
||
</div>
|
||
<div className="grid gap-4 md:grid-cols-3">
|
||
{plans.map((plan) => (
|
||
<div
|
||
key={plan.key}
|
||
className={cn(
|
||
'flex h-full flex-col gap-3 rounded-2xl border border-slate-200 bg-white/90 p-5 text-left shadow-sm dark:border-white/10 dark:bg-white/5',
|
||
plan.highlight ? 'ring-1 ring-rose-200 dark:ring-rose-300/30' : ''
|
||
)}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
<Badge variant="secondary" className="bg-rose-100/80 text-rose-700 dark:bg-rose-300/20 dark:text-rose-100">
|
||
{plan.badge ?? t('welcome.plans.badge', 'Paket')}
|
||
</Badge>
|
||
{plan.highlight ? (
|
||
<Badge className="bg-emerald-500/15 text-emerald-700 dark:bg-emerald-400/20 dark:text-emerald-200">
|
||
{plan.highlight}
|
||
</Badge>
|
||
) : null}
|
||
</div>
|
||
<div className="flex items-center gap-2 text-lg font-semibold text-slate-900 dark:text-white">
|
||
<Sparkles className="h-4 w-4 text-rose-500" />
|
||
{plan.title}
|
||
</div>
|
||
<ul className="space-y-2 text-sm text-slate-600 dark:text-slate-300">
|
||
{plan.points.map((point) => (
|
||
<li key={point} className="flex items-start gap-2">
|
||
<CheckCircle2 className="mt-0.5 h-4 w-4 text-emerald-500" />
|
||
<span>{point}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
<div className="mt-auto">
|
||
<Button variant="outline" size="sm" className="border-rose-200 text-rose-700 hover:bg-rose-50" onClick={handlePackages}>
|
||
{t('welcome.cta.packages', 'Pakete ansehen')} <ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="space-y-4">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-xs uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">{t('welcome.audience.title', 'Für wen?')}</p>
|
||
<h2 className="text-2xl font-semibold text-slate-900 dark:text-white sm:text-3xl">{t('welcome.audience.subtitle', 'Endkunden & Reseller im Blick')}</h2>
|
||
</div>
|
||
<div className="grid gap-4 md:grid-cols-2">
|
||
{audienceCards.map((audience) => (
|
||
<div
|
||
key={audience.key}
|
||
className="flex h-full flex-col gap-3 rounded-2xl border border-slate-200 bg-white/90 p-4 shadow-sm dark:border-white/10 dark:bg-white/5"
|
||
>
|
||
<div className="flex items-center gap-2 text-lg font-semibold text-slate-900 dark:text-white">
|
||
<audience.icon className="h-5 w-5 text-rose-500" />
|
||
{audience.title}
|
||
</div>
|
||
<p className="text-sm text-slate-600 dark:text-slate-300">{audience.description}</p>
|
||
<div className="flex items-center gap-2 text-xs uppercase tracking-[0.3em] text-slate-500 dark:text-slate-400">
|
||
<ArrowRight className="h-3.5 w-3.5" />
|
||
{t('welcome.audience.cta', 'Wenige Klicks bis zum Start')}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="rounded-3xl border border-slate-200 bg-white/85 p-6 shadow-lg shadow-rose-100/40 backdrop-blur dark:border-white/10 dark:bg-white/5">
|
||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||
<div className="space-y-1">
|
||
<p className="text-xs uppercase tracking-[0.35em] text-rose-500 dark:text-rose-200">{t('welcome.footer.eyebrow', 'Bereit?')}</p>
|
||
<h3 className="text-xl font-semibold text-slate-900 dark:text-white">{t('welcome.footer.title', 'Melde dich an oder prüfe die Pakete')}</h3>
|
||
<p className="text-sm text-slate-600 dark:text-slate-300">{t('welcome.footer.subtitle', 'Login für bestehende Kunden, Pakete für neue Teams.')}</p>
|
||
</div>
|
||
<div className="flex flex-wrap items-center gap-3">
|
||
<Button size="lg" onClick={handleLogin} className="bg-rose-500 text-white hover:bg-rose-600">
|
||
{t('welcome.cta.login', 'Login')} <ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="outline" size="lg" onClick={handlePackages} className="border-rose-200 text-rose-700 hover:bg-rose-50">
|
||
{t('welcome.cta.packages', 'Pakete ansehen')}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|