feat: Complete checkout overhaul with Stripe PaymentIntent integration and abandoned cart recovery
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import { Link, router } from '@inertiajs/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -13,7 +13,7 @@ import { Sun, Moon } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const { auth, locale } = usePage().props as any;
|
||||
const { auth } = usePage().props as any;
|
||||
const { t } = useTranslation('auth');
|
||||
const { appearance, updateAppearance } = useAppearance();
|
||||
const { localizedPath } = useLocalizedRoutes();
|
||||
@@ -23,15 +23,29 @@ const Header: React.FC = () => {
|
||||
updateAppearance(newAppearance);
|
||||
};
|
||||
|
||||
const handleLanguageChange = (value: string) => {
|
||||
router.post('/set-locale', { locale: value }, {
|
||||
preserveState: true,
|
||||
replace: true,
|
||||
onSuccess: () => {
|
||||
const handleLanguageChange = useCallback(async (value: string) => {
|
||||
console.log('handleLanguageChange called with:', value);
|
||||
try {
|
||||
const response = await fetch('/set-locale', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
|
||||
},
|
||||
body: JSON.stringify({ locale: value }),
|
||||
});
|
||||
|
||||
console.log('fetch response:', response.status);
|
||||
if (response.ok) {
|
||||
console.log('calling i18n.changeLanguage with:', value);
|
||||
i18n.changeLanguage(value);
|
||||
},
|
||||
});
|
||||
};
|
||||
// Reload only the locale prop to update the page props
|
||||
router.reload({ only: ['locale'] });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to change locale:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleLogout = () => {
|
||||
router.post('/logout');
|
||||
@@ -45,18 +59,24 @@ const Header: React.FC = () => {
|
||||
Fotospiel
|
||||
</Link>
|
||||
<nav className="flex space-x-8">
|
||||
<Link href={localizedPath('/')} className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white font-sans-marketing text-lg">
|
||||
{t('header.home', 'Home')}
|
||||
</Link>
|
||||
<Link href={localizedPath('/packages')} className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white font-sans-marketing text-lg">
|
||||
{t('header.packages', 'Pakete')}
|
||||
</Link>
|
||||
<Link href={localizedPath('/blog')} className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white font-sans-marketing text-lg">
|
||||
{t('header.blog', 'Blog')}
|
||||
</Link>
|
||||
<Button asChild variant="ghost" className="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 transition-all duration-200">
|
||||
<Link href={localizedPath('/')}>
|
||||
{t('header.home', 'Home')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="ghost" className="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 transition-all duration-200">
|
||||
<Link href={localizedPath('/packages')}>
|
||||
{t('header.packages', 'Pakete')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="ghost" className="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 transition-all duration-200">
|
||||
<Link href={localizedPath('/blog')}>
|
||||
{t('header.blog', 'Blog')}
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white font-sans-marketing text-lg">
|
||||
<Button variant="ghost" className="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 transition-all duration-200">
|
||||
Anlässe
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
@@ -78,9 +98,11 @@ const Header: React.FC = () => {
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Link href={localizedPath('/kontakt')} className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white font-sans-marketing text-lg">
|
||||
{t('header.contact', 'Kontakt')}
|
||||
</Link>
|
||||
<Button asChild variant="ghost" className="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 transition-all duration-200">
|
||||
<Link href={localizedPath('/kontakt')}>
|
||||
{t('header.contact', 'Kontakt')}
|
||||
</Link>
|
||||
</Button>
|
||||
</nav>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Button
|
||||
@@ -93,7 +115,7 @@ const Header: React.FC = () => {
|
||||
<Moon className={cn("h-4 w-4", appearance !== "dark" && "hidden")} />
|
||||
<span className="sr-only">Theme Toggle</span>
|
||||
</Button>
|
||||
<Select value={locale} onValueChange={handleLanguageChange}>
|
||||
<Select value={i18n.language || 'de'} onValueChange={handleLanguageChange}>
|
||||
<SelectTrigger className="w-[70px] h-8">
|
||||
<SelectValue placeholder="DE" />
|
||||
</SelectTrigger>
|
||||
@@ -140,7 +162,7 @@ const Header: React.FC = () => {
|
||||
<>
|
||||
<Link
|
||||
href={localizedPath('/login')}
|
||||
className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white"
|
||||
className="text-gray-700 hover:text-pink-600 dark:text-gray-300 dark:hover:text-pink-400 font-medium transition-colors duration-200"
|
||||
>
|
||||
{t('header.login')}
|
||||
</Link>
|
||||
|
||||
@@ -8,6 +8,7 @@ interface Step {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
details?: string
|
||||
}
|
||||
|
||||
interface StepsProps {
|
||||
@@ -33,10 +34,18 @@ const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
||||
{index + 1}
|
||||
</div>
|
||||
<div className="mt-2 text-xs font-medium text-center">
|
||||
<p className={cn(index === currentStep ? "text-blue-600" : "text-gray-500")}>
|
||||
<p className={cn(
|
||||
"font-semibold",
|
||||
index === currentStep ? "text-blue-600 dark:text-blue-400" : "text-gray-500 dark:text-gray-400"
|
||||
)}>
|
||||
{step.title}
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">{step.description}</p>
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">{step.description}</p>
|
||||
{step.details && index === currentStep && (
|
||||
<p className="text-xs text-blue-500 dark:text-blue-300 mt-1 font-medium">
|
||||
{step.details}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{index < steps.length - 1 && (
|
||||
<div className="flex-1 h-px bg-gray-200 dark:bg-gray-700 mx-2">
|
||||
|
||||
Reference in New Issue
Block a user