import React from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Receipt, ShieldCheck, ArrowRight, ArrowLeft, CreditCard, AlertTriangle, Info, Loader2, } from "lucide-react"; import { TenantWelcomeLayout, WelcomeStepCard, OnboardingCTAList, useOnboardingProgress, } from ".."; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { FrostedSurface } from "../../components/tenant"; import { ADMIN_BILLING_PATH, ADMIN_WELCOME_EVENT_PATH, ADMIN_WELCOME_PACKAGES_PATH } from "../../constants"; import { useTenantPackages } from "../hooks/useTenantPackages"; import { assignFreeTenantPackage, createTenantPaddleCheckout, } from "../../api"; import { cn } from "@/lib/utils"; type PaddleCheckoutProps = { packageId: number; onSuccess: () => void; t: ReturnType["t"]; className?: string; }; function useLocaleFormats(locale: string) { const currencyFormatter = React.useMemo( () => new Intl.NumberFormat(locale, { style: "currency", currency: "EUR", minimumFractionDigits: 0, }), [locale] ); const dateFormatter = React.useMemo( () => new Intl.DateTimeFormat(locale, { year: "numeric", month: "2-digit", day: "2-digit", }), [locale] ); return { currencyFormatter, dateFormatter }; } function humanizeFeature(t: ReturnType["t"], key: string): string { const translationKey = `summary.details.features.${key}`; const translated = t(translationKey); if (translated !== translationKey) { return translated; } return key .split("_") .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(" "); } function PaddleCheckout({ packageId, onSuccess, t, className }: PaddleCheckoutProps) { const [status, setStatus] = React.useState<'idle' | 'processing' | 'success' | 'error'>('idle'); const [error, setError] = React.useState(null); const handleCheckout = React.useCallback(async () => { try { setStatus('processing'); setError(null); const { checkout_url } = await createTenantPaddleCheckout(packageId); window.open(checkout_url, '_blank', 'noopener'); setStatus('success'); onSuccess(); } catch (err) { console.error('[Onboarding] Paddle checkout failed', err); setStatus('error'); setError(err instanceof Error ? err.message : t('summary.paddle.genericError')); } }, [packageId, onSuccess, t]); return (

{t('summary.paddle.sectionTitle')}

{t('summary.paddle.heading')}

{error ? (

{t('summary.paddle.errorTitle')}

{error}

) : null}

{t('summary.paddle.hint')}

); } export default function WelcomeOrderSummaryPage() { const navigate = useNavigate(); const location = useLocation(); const { progress, markStep } = useOnboardingProgress(); const packagesState = useTenantPackages(); const { t, i18n } = useTranslation("onboarding"); const locale = i18n.language?.startsWith("en") ? "en-GB" : "de-DE"; const { currencyFormatter } = useLocaleFormats(locale); const packageIdFromState = typeof location.state === "object" && location.state !== null && "packageId" in location.state ? (location.state as { packageId?: number | string | null }).packageId : undefined; const selectedPackageId = progress.selectedPackage?.id ?? packageIdFromState ?? null; React.useEffect(() => { if (!selectedPackageId && packagesState.status !== "loading") { navigate(ADMIN_WELCOME_PACKAGES_PATH, { replace: true }); } }, [selectedPackageId, packagesState.status, navigate]); React.useEffect(() => { markStep({ lastStep: "summary" }); }, [markStep]); const packageDetails = packagesState.status === "success" && selectedPackageId ? packagesState.catalog.find((pkg) => pkg.id === selectedPackageId) : null; const activePackage = packagesState.status === "success" ? packagesState.purchasedPackages.find((pkg) => pkg.package_id === selectedPackageId) : null; const isSubscription = Boolean(packageDetails?.features?.subscription); const requiresPayment = Boolean(packageDetails && packageDetails.price > 0); const [freeAssignStatus, setFreeAssignStatus] = React.useState<"idle" | "loading" | "success" | "error">("idle"); const [freeAssignError, setFreeAssignError] = React.useState(null); const priceText = progress.selectedPackage?.priceText ?? (packageDetails && typeof packageDetails.price === "number" ? currencyFormatter.format(packageDetails.price) : null); const detailBadges = React.useMemo(() => { if (!packageDetails) { return [] as string[]; } const badges: string[] = []; if (packageDetails.max_photos) { badges.push(t("summary.details.photos", { count: packageDetails.max_photos })); } if (packageDetails.gallery_days) { badges.push(t("summary.details.galleryDays", { count: packageDetails.gallery_days })); } if (packageDetails.max_guests) { badges.push(t("summary.details.guests", { count: packageDetails.max_guests })); } return badges; }, [packageDetails, t]); const featuresList = React.useMemo(() => { if (!packageDetails) { return [] as string[]; } return Object.entries(packageDetails.features ?? {}) .filter(([, enabled]) => Boolean(enabled)) .map(([feature]) => humanizeFeature(t, feature)); }, [packageDetails, t]); const nextSteps = t("summary.nextSteps", { returnObjects: true }) as string[]; return ( navigate(ADMIN_WELCOME_PACKAGES_PATH)} > {t("summary.footer.back")} } > {packagesState.status === "loading" && ( {t("summary.state.loading")} )} {packagesState.status === "error" && (

{t("summary.state.errorTitle")}

{packagesState.message ?? t("summary.state.errorDescription")}

)} {packagesState.status === "success" && !packageDetails && (

{t("summary.state.missingTitle")}

{t("summary.state.missingDescription")}

)} {packagesState.status === "success" && packageDetails && (
{t(isSubscription ? "summary.details.subscription" : "summary.details.creditPack")}

{packageDetails.name}

{priceText ? ( {priceText} ) : null} {activePackage ? t("summary.details.section.statusActive") : t("summary.details.section.statusInactive")}
{t("summary.details.section.photosTitle")}
{packageDetails.max_photos ? t("summary.details.section.photosValue", { count: packageDetails.max_photos, days: packageDetails.gallery_days ?? t("summary.details.infinity"), }) : t("summary.details.section.photosUnlimited")}
{t("summary.details.section.guestsTitle")}
{packageDetails.max_guests ? t("summary.details.section.guestsValue", { count: packageDetails.max_guests }) : t("summary.details.section.guestsUnlimited")}
{t("summary.details.section.featuresTitle")}
{featuresList.length ? (
{featuresList.map((feature) => ( {feature} ))}
) : ( {t("summary.details.section.featuresNone")} )}
{t("summary.details.section.statusTitle")}
{activePackage ? t("summary.details.section.statusActive") : t("summary.details.section.statusInactive")}
{detailBadges.length > 0 ? (
{detailBadges.map((badge) => ( {badge} ))}
) : null}
{!activePackage && (

{t("summary.status.pendingTitle")}

{t("summary.status.pendingDescription")}

)} {packageDetails.price === 0 && (

{t("summary.free.description")}

{freeAssignStatus === "success" ? (

{t("summary.free.successTitle")}

{t("summary.free.successDescription")}

) : ( )} {freeAssignStatus === "error" && freeAssignError ? (

{t("summary.free.failureTitle")}

{freeAssignError}

) : null}
)} {requiresPayment && ( { markStep({ packageSelected: true }); navigate(ADMIN_WELCOME_EVENT_PATH, { replace: true }); }} t={t} /> )}

{t("summary.nextStepsTitle")}

    {nextSteps.map((step, index) => (
  1. {step}
  2. ))}
)} { markStep({ lastStep: "event-setup" }); navigate(ADMIN_WELCOME_EVENT_PATH); }, icon: ArrowRight, }, ]} /> ); } export { PaddleCheckout };