login-seiten neu designt, homepage neu designt. "so funktioniert's" ergänzt und Demo-Seite hinzugefügt. Paketansicht in mobile verbessert.
This commit is contained in:
@@ -8,11 +8,11 @@ import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
import { Sheet, SheetClose, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
|
||||
import { Sun, Moon, Menu, X, ChevronRight } from 'lucide-react';
|
||||
import { Sun, Moon, Menu, X, Languages, UserRound } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
NavigationMenu,
|
||||
@@ -31,10 +31,15 @@ const Header: React.FC = () => {
|
||||
const { localizedPath } = useLocalizedRoutes();
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
const toggleTheme = () => {
|
||||
const newAppearance = appearance === 'dark' ? 'light' : 'dark';
|
||||
updateAppearance(newAppearance);
|
||||
const setTheme = useCallback((mode: 'light' | 'dark') => {
|
||||
if (appearance !== mode) {
|
||||
updateAppearance(mode);
|
||||
}
|
||||
setMobileMenuOpen(false);
|
||||
}, [appearance, updateAppearance]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(appearance === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const handleLanguageChange = useCallback(async (value: string) => {
|
||||
@@ -68,49 +73,61 @@ const Header: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const navItems = useMemo(() => ([
|
||||
{
|
||||
key: 'home',
|
||||
label: t('header.home', 'Home'),
|
||||
href: localizedPath('/'),
|
||||
},
|
||||
{
|
||||
key: 'packages',
|
||||
label: t('header.packages', 'Pakete'),
|
||||
href: localizedPath('/packages'),
|
||||
},
|
||||
{
|
||||
key: 'blog',
|
||||
label: t('header.blog', 'Blog'),
|
||||
href: localizedPath('/blog'),
|
||||
},
|
||||
{
|
||||
key: 'occasions',
|
||||
label: t('header.occasions.label', 'Anlässe'),
|
||||
children: [
|
||||
{
|
||||
key: 'wedding',
|
||||
label: t('header.occasions.wedding', 'Hochzeit'),
|
||||
href: localizedPath('/anlaesse/hochzeit'),
|
||||
},
|
||||
{
|
||||
key: 'birthday',
|
||||
label: t('header.occasions.birthday', 'Geburtstag'),
|
||||
href: localizedPath('/anlaesse/geburtstag'),
|
||||
},
|
||||
{
|
||||
key: 'corporate',
|
||||
label: t('header.occasions.corporate', 'Firmenevent'),
|
||||
href: localizedPath('/anlaesse/firmenevent'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
label: t('header.contact', 'Kontakt'),
|
||||
href: localizedPath('/kontakt'),
|
||||
},
|
||||
]), [localizedPath, t]);
|
||||
const ctaHref = localizedPath('/demo');
|
||||
|
||||
const navItems = useMemo(() => {
|
||||
const homeHref = localizedPath('/');
|
||||
const howItWorksHref = localizedPath('/so-funktionierts');
|
||||
|
||||
return [
|
||||
{
|
||||
key: 'howItWorks',
|
||||
label: t('header.how_it_works', "So geht's"),
|
||||
href: howItWorksHref,
|
||||
},
|
||||
{
|
||||
key: 'packages',
|
||||
label: t('header.packages', 'Pakete'),
|
||||
href: localizedPath('/packages'),
|
||||
},
|
||||
{
|
||||
key: 'occasions',
|
||||
label: t('header.occasions.label', 'Anlässe'),
|
||||
children: [
|
||||
{
|
||||
key: 'wedding',
|
||||
label: t('header.occasions.wedding', 'Hochzeiten'),
|
||||
href: localizedPath('/anlaesse/hochzeit'),
|
||||
},
|
||||
{
|
||||
key: 'birthday',
|
||||
label: t('header.occasions.birthday', 'Geburtstage'),
|
||||
href: localizedPath('/anlaesse/geburtstag'),
|
||||
},
|
||||
{
|
||||
key: 'corporate',
|
||||
label: t('header.occasions.corporate', 'Firmenfeiern'),
|
||||
href: localizedPath('/anlaesse/firmenevent'),
|
||||
},
|
||||
{
|
||||
key: 'confirmation',
|
||||
label: t('header.occasions.confirmation', 'Konfirmation/Jugendweihe'),
|
||||
href: localizedPath('/anlaesse/konfirmation'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'blog',
|
||||
label: t('header.blog', 'Blog'),
|
||||
href: localizedPath('/blog'),
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
label: t('header.contact', 'Kontakt'),
|
||||
href: localizedPath('/kontakt'),
|
||||
},
|
||||
];
|
||||
}, [localizedPath, t]);
|
||||
|
||||
const handleNavSelect = useCallback(() => setMobileMenuOpen(false), []);
|
||||
|
||||
@@ -125,25 +142,27 @@ const Header: React.FC = () => {
|
||||
</span>
|
||||
</Link>
|
||||
<NavigationMenu className="hidden lg:flex flex-1 justify-center" viewport={false}>
|
||||
<NavigationMenuList className="gap-2">
|
||||
<NavigationMenuList className="gap-1.5">
|
||||
{navItems.map((item) => (
|
||||
<NavigationMenuItem key={item.key}>
|
||||
{item.children ? (
|
||||
<>
|
||||
<NavigationMenuTrigger className="bg-transparent text-gray-700 hover:text-pink-600 hover:bg-pink-50 dark:text-gray-300 dark:hover:text-pink-400 dark:hover:bg-pink-950/20 font-sans-marketing !text-lg font-medium">
|
||||
<NavigationMenuTrigger className="bg-transparent px-3 py-1.5 !text-lg font-medium text-gray-700 hover:text-pink-600 hover:bg-pink-50 dark:text-gray-300 dark:hover:text-pink-400 dark:hover:bg-pink-950/20 font-sans-marketing">
|
||||
{item.label}
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent className="min-w-[220px] rounded-md border bg-popover p-3 shadow-lg">
|
||||
<NavigationMenuContent className="min-w-[260px] rounded-md border bg-popover p-3 shadow-lg">
|
||||
<ul className="flex flex-col gap-1">
|
||||
{item.children.map((child) => (
|
||||
<li key={child.key}>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
href={child.href}
|
||||
className="flex items-center justify-between rounded-md px-3 py-2 !text-lg font-medium text-gray-700 transition hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-900/60 font-sans-marketing"
|
||||
className="flex w-full items-center rounded-md px-3 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-900/60 font-sans-marketing"
|
||||
>
|
||||
{child.label}
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<span aria-hidden className="mr-2 flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground">
|
||||
›
|
||||
</span>
|
||||
<span className="whitespace-nowrap">{child.label}</span>
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
@@ -157,7 +176,7 @@ const Header: React.FC = () => {
|
||||
href={item.href}
|
||||
className={cn(
|
||||
navigationMenuTriggerStyle(),
|
||||
"bg-transparent !text-lg font-medium text-gray-700 hover:bg-pink-50 hover:text-pink-600 dark:text-gray-300 dark:hover:bg-pink-950/20 dark:hover:text-pink-400 font-sans-marketing"
|
||||
"bg-transparent px-3 py-1.5 !text-lg font-medium text-gray-700 hover:bg-pink-50 hover:text-pink-600 dark:text-gray-300 dark:hover:bg-pink-950/20 dark:hover:text-pink-400 font-sans-marketing"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
@@ -168,26 +187,49 @@ const Header: React.FC = () => {
|
||||
))}
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
<div className="hidden lg:flex items-center space-x-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={toggleTheme}
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<Sun className={cn("h-4 w-4", appearance === "dark" && "hidden")} />
|
||||
<Moon className={cn("h-4 w-4", appearance !== "dark" && "hidden")} />
|
||||
<span className="sr-only">Theme Toggle</span>
|
||||
<div className="hidden lg:flex items-center space-x-2">
|
||||
<Button asChild size="sm" className="bg-[#FF5F87] hover:bg-[#ff4674] text-white shadow-md shadow-rose-500/20">
|
||||
<Link href={ctaHref} className="font-sans-marketing font-semibold px-3">
|
||||
{t('header.cta', 'Jetzt ausprobieren')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Select value={i18n.language || 'de'} onValueChange={handleLanguageChange}>
|
||||
<SelectTrigger className="w-[70px] h-8">
|
||||
<SelectValue placeholder={t('common.ui.language_select')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="de">DE</SelectItem>
|
||||
<SelectItem value="en">EN</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
aria-label={t('header.utility', 'Darstellung und Sprache öffnen')}
|
||||
>
|
||||
<Languages className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-52" align="end" forceMount>
|
||||
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-muted-foreground">
|
||||
{t('header.appearance', 'Darstellung')}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => setTheme('light')} className="font-sans-marketing">
|
||||
<Sun className="mr-2 h-4 w-4" />
|
||||
{t('header.appearance_light', 'Hell')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('dark')} className="font-sans-marketing">
|
||||
<Moon className="mr-2 h-4 w-4" />
|
||||
{t('header.appearance_dark', 'Dunkel')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="font-sans-marketing text-xs uppercase tracking-wide text-muted-foreground">
|
||||
{t('header.language', 'Sprache')}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuRadioGroup value={i18n.language || 'de'} onValueChange={handleLanguageChange}>
|
||||
<DropdownMenuRadioItem value="de" className="font-sans-marketing">
|
||||
Deutsch
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="en" className="font-sans-marketing">
|
||||
English
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{auth.user ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -223,14 +265,16 @@ const Header: React.FC = () => {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<>
|
||||
<Button asChild variant="ghost" size="icon" className="h-8 w-8">
|
||||
<Link
|
||||
href={localizedPath('/login')}
|
||||
className="text-gray-700 hover:text-pink-600 dark:text-gray-300 dark:hover:text-pink-400 font-medium transition-colors duration-200 font-sans-marketing"
|
||||
className="flex items-center justify-center text-gray-700 hover:text-pink-600 dark:text-gray-300 dark:hover:text-pink-400"
|
||||
aria-label={t('header.login')}
|
||||
>
|
||||
{t('header.login')}
|
||||
<UserRound className="h-4 w-4" />
|
||||
<span className="sr-only">{t('header.login')}</span>
|
||||
</Link>
|
||||
</>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center lg:hidden">
|
||||
@@ -279,11 +323,11 @@ const Header: React.FC = () => {
|
||||
<SheetClose asChild key={child.key}>
|
||||
<Link
|
||||
href={child.href}
|
||||
className="flex items-center justify-between rounded-md border border-transparent px-3 py-2 text-base font-medium text-gray-700 transition hover:border-gray-200 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-900/60 font-sans-marketing"
|
||||
className="flex w-full items-center rounded-md border border-transparent px-3 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-200 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-900/60 font-sans-marketing"
|
||||
onClick={handleNavSelect}
|
||||
>
|
||||
<span aria-hidden className="mr-2 text-muted-foreground">›</span>
|
||||
<span>{child.label}</span>
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
</Link>
|
||||
</SheetClose>
|
||||
))}
|
||||
@@ -305,6 +349,15 @@ const Header: React.FC = () => {
|
||||
</nav>
|
||||
<Separator />
|
||||
<div className="flex flex-col gap-4">
|
||||
<SheetClose asChild>
|
||||
<Link
|
||||
href={ctaHref}
|
||||
className="rounded-full bg-[#FF5F87] px-4 py-3 text-center text-base font-semibold text-white shadow-md shadow-rose-500/20 transition hover:bg-[#ff4674] font-sans-marketing"
|
||||
onClick={handleNavSelect}
|
||||
>
|
||||
{t('header.cta', 'Jetzt ausprobieren')}
|
||||
</Link>
|
||||
</SheetClose>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-muted-foreground">Darstellung</span>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user