feat: Complete checkout overhaul with Stripe PaymentIntent integration and abandoned cart recovery

This commit is contained in:
Codex Agent
2025-10-07 22:25:03 +02:00
parent dd5545605c
commit aa8c6c67c5
38 changed files with 1848 additions and 878 deletions

View File

@@ -0,0 +1,66 @@
import React from "react";
import { Head, usePage } from "@inertiajs/react";
import MarketingLayout from "@/layouts/marketing/MarketingLayout";
import type { CheckoutPackage } from "./checkout/types";
import { CheckoutWizard } from "./checkout/CheckoutWizard";
import { Button } from "@/components/ui/button";
import { X } from "lucide-react";
interface CheckoutWizardPageProps {
package: CheckoutPackage;
packageOptions: CheckoutPackage[];
stripePublishableKey: string;
privacyHtml: string;
}
export default function CheckoutWizardPage({
package: initialPackage,
packageOptions,
stripePublishableKey,
privacyHtml,
}: CheckoutWizardPageProps) {
const page = usePage<{ auth?: { user?: { id: number; email: string; name?: string } | null } }>();
const currentUser = page.props.auth?.user ?? null;
const dedupedOptions = React.useMemo(() => {
const ids = new Set<number>();
const list = [initialPackage, ...packageOptions];
return list.filter((pkg) => {
if (ids.has(pkg.id)) {
return false;
}
ids.add(pkg.id);
return true;
});
}, [initialPackage, packageOptions]);
return (
<MarketingLayout title="Checkout Wizard">
<Head title="Checkout Wizard" />
<div className="min-h-screen bg-muted/20 py-12">
<div className="mx-auto w-full max-w-4xl px-4">
{/* Abbruch-Button oben rechts */}
<div className="flex justify-end mb-4">
<Button
variant="ghost"
size="sm"
onClick={() => window.location.href = '/packages'}
className="text-muted-foreground hover:text-foreground"
>
<X className="h-4 w-4 mr-2" />
Abbrechen
</Button>
</div>
<CheckoutWizard
initialPackage={initialPackage}
packageOptions={dedupedOptions}
stripePublishableKey={stripePublishableKey}
privacyHtml={privacyHtml}
initialAuthUser={currentUser ? { id: currentUser.id, email: currentUser.email ?? '', name: currentUser.name ?? undefined } : null}
/>
</div>
</div>
</MarketingLayout>
);
}