vor marketing zu website umbenennung. stripe ist lauffähig

This commit is contained in:
Codex Agent
2025-10-08 11:20:00 +02:00
parent aa8c6c67c5
commit b3e6b6b597
11 changed files with 597 additions and 197 deletions

View File

@@ -13,11 +13,13 @@ const currencyFormatter = new Intl.NumberFormat("de-DE", {
});
function PackageSummary({ pkg }: { pkg: CheckoutPackage }) {
const isFree = pkg.price === 0;
return (
<Card className="shadow-sm">
<Card className={`shadow-sm ${isFree ? "opacity-75" : ""}`}>
<CardHeader className="space-y-1">
<CardTitle className="flex items-center gap-3 text-2xl">
<PackageIcon className="h-6 w-6 text-primary" />
<CardTitle className={`flex items-center gap-3 text-2xl ${isFree ? "text-muted-foreground" : ""}`}>
<PackageIcon className={`h-6 w-6 ${isFree ? "text-muted-foreground" : "text-primary"}`} />
{pkg.name}
</CardTitle>
<CardDescription className="text-base text-muted-foreground">
@@ -26,10 +28,10 @@ function PackageSummary({ pkg }: { pkg: CheckoutPackage }) {
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-baseline gap-2">
<span className="text-3xl font-semibold">
<span className={`text-3xl font-semibold ${isFree ? "text-muted-foreground" : ""}`}>
{pkg.price === 0 ? "Kostenlos" : currencyFormatter.format(pkg.price)}
</span>
<Badge variant="secondary" className="uppercase tracking-wider text-xs">
<Badge variant={isFree ? "outline" : "secondary"} className="uppercase tracking-wider text-xs">
{pkg.type === "reseller" ? "Reseller" : "Endkunde"}
</Badge>
</div>
@@ -37,7 +39,7 @@ function PackageSummary({ pkg }: { pkg: CheckoutPackage }) {
<ul className="space-y-3">
{pkg.features.map((feature, index) => (
<li key={index} className="flex items-start gap-3 text-sm text-muted-foreground">
<Check className="mt-0.5 h-4 w-4 text-primary" />
<Check className={`mt-0.5 h-4 w-4 ${isFree ? "text-muted-foreground" : "text-primary"}`} />
<span>{feature}</span>
</li>
))}
@@ -49,17 +51,23 @@ function PackageSummary({ pkg }: { pkg: CheckoutPackage }) {
}
function PackageOption({ pkg, isActive, onSelect }: { pkg: CheckoutPackage; isActive: boolean; onSelect: () => void }) {
const isFree = pkg.price === 0;
return (
<button
type="button"
onClick={onSelect}
className={`w-full rounded-md border bg-background p-4 text-left transition focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 ${
isActive ? "border-primary shadow-sm" : "border-border hover:border-primary/40"
isActive
? "border-primary shadow-sm"
: isFree
? "border-border hover:border-primary/40 opacity-75 hover:opacity-100"
: "border-border hover:border-primary/40"
}`}
>
<div className="flex items-center justify-between text-sm font-medium">
<span>{pkg.name}</span>
<span className="text-muted-foreground">
<span className={isFree ? "text-muted-foreground" : ""}>{pkg.name}</span>
<span className={isFree ? "text-muted-foreground font-normal" : "text-muted-foreground"}>
{pkg.price === 0 ? "Kostenlos" : currencyFormatter.format(pkg.price)}
</span>
</div>
@@ -72,9 +80,28 @@ export const PackageStep: React.FC = () => {
const { selectedPackage, packageOptions, setSelectedPackage, resetPaymentState, nextStep } = useCheckoutWizard();
const [isLoading, setIsLoading] = useState(false);
// Early return if no package is selected
if (!selectedPackage) {
return (
<div className="text-center py-8">
<p className="text-muted-foreground">Kein Paket ausgewählt. Bitte wähle ein Paket aus der Paketübersicht.</p>
</div>
);
}
const comparablePackages = useMemo(() => {
return packageOptions.filter((pkg) => pkg.type === selectedPackage.type);
}, [packageOptions, selectedPackage.type]);
// Filter by type and sort: free packages first, then by price ascending
return packageOptions
.filter((pkg: CheckoutPackage) => pkg.type === selectedPackage.type)
.sort((a: CheckoutPackage, b: CheckoutPackage) => {
// Free packages first
if (a.price === 0 && b.price > 0) return -1;
if (a.price > 0 && b.price === 0) return 1;
// Then sort by price ascending
return a.price - b.price;
});
}, [packageOptions, selectedPackage]);
const handlePackageChange = (pkg: CheckoutPackage) => {
if (pkg.id === selectedPackage.id) {