nicer package layout, also in checkout step 1, fixed missing registration language strings, registration error handling, email verification redirect, email verification error handling and messaging,
This commit is contained in:
@@ -4,6 +4,7 @@ import type { TFunction } from 'i18next';
|
||||
import { Check, Package as PackageIcon } from "lucide-react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useCheckoutWizard } from "../WizardContext";
|
||||
import type { CheckoutPackage } from "../types";
|
||||
|
||||
@@ -18,11 +19,45 @@ function translateFeature(feature: string, t: TFunction<'marketing'>) {
|
||||
return t(`packages.feature_${feature}`, { defaultValue: fallback });
|
||||
}
|
||||
|
||||
const DETAIL_LABEL_MAP: Record<string, string> = {
|
||||
fotos: 'photos',
|
||||
photos: 'photos',
|
||||
gaeste: 'guests',
|
||||
gäste: 'guests',
|
||||
guests: 'guests',
|
||||
aufgaben: 'tasks',
|
||||
challenges: 'tasks',
|
||||
galerie: 'gallery',
|
||||
gallery: 'gallery',
|
||||
branding: 'branding',
|
||||
'events_jahr': 'events_per_year',
|
||||
eventsjahr: 'events_per_year',
|
||||
};
|
||||
|
||||
function translateDetailLabel(label: string | undefined, t: TFunction<'marketing'>): string | undefined {
|
||||
if (!label) {
|
||||
return label;
|
||||
}
|
||||
|
||||
const normalised = label
|
||||
.normalize('NFD')
|
||||
.replace(/\p{Diacritic}/gu, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '_')
|
||||
.replace(/^_|_$/g, '') || label.toLowerCase();
|
||||
|
||||
const key = DETAIL_LABEL_MAP[normalised] ?? normalised;
|
||||
return t(`packages.detail_labels.${key}`, { defaultValue: label });
|
||||
}
|
||||
|
||||
function PackageSummary({ pkg, t }: { pkg: CheckoutPackage; t: TFunction<'marketing'> }) {
|
||||
const isFree = pkg.price === 0;
|
||||
const accentGradient = pkg.type === 'reseller'
|
||||
? 'border-amber-100 bg-gradient-to-br from-amber-50/80 via-white to-amber-100/70 shadow-lg shadow-amber-100/60'
|
||||
: 'border-rose-100 bg-gradient-to-br from-rose-50/80 via-white to-rose-100/70 shadow-lg shadow-rose-100/60';
|
||||
|
||||
return (
|
||||
<Card className={`shadow-sm ${isFree ? 'opacity-75' : ''}`}>
|
||||
<Card className={cn('shadow-sm transition', isFree ? 'opacity-75' : accentGradient)}>
|
||||
<CardHeader className="space-y-1">
|
||||
<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'}`} />
|
||||
@@ -55,7 +90,9 @@ function PackageSummary({ pkg, t }: { pkg: CheckoutPackage; t: TFunction<'market
|
||||
{pkg.description_breakdown.map((row, index) => (
|
||||
<div key={index} className="rounded-lg border border-muted/40 bg-muted/20 px-3 py-2">
|
||||
{row.title && (
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">{row.title}</p>
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||
{translateDetailLabel(row.title, t)}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-sm text-muted-foreground">{row.value}</p>
|
||||
</div>
|
||||
@@ -80,18 +117,22 @@ function PackageSummary({ pkg, t }: { pkg: CheckoutPackage; t: TFunction<'market
|
||||
|
||||
function PackageOption({ pkg, isActive, onSelect, t }: { pkg: CheckoutPackage; isActive: boolean; onSelect: () => void; t: TFunction<'marketing'> }) {
|
||||
const isFree = pkg.price === 0;
|
||||
const accentGradient = pkg.type === 'reseller'
|
||||
? 'border-amber-100 bg-gradient-to-r from-amber-50/70 via-white to-amber-100/60 shadow-md shadow-amber-100/60'
|
||||
: 'border-rose-100 bg-gradient-to-r from-rose-50/70 via-white to-rose-100/60 shadow-md shadow-rose-100/60';
|
||||
|
||||
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 ${
|
||||
className={cn(
|
||||
'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"
|
||||
? accentGradient
|
||||
: isFree
|
||||
? "border-border hover:border-primary/40 opacity-75 hover:opacity-100"
|
||||
: "border-border hover:border-primary/40"
|
||||
}`}
|
||||
? '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 className={isFree ? "text-muted-foreground" : ""}>{pkg.name}</span>
|
||||
|
||||
Reference in New Issue
Block a user