import { useState, useEffect, useMemo, useRef, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { useParams } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { fireAnalyticsEvent, getYandexCid } from '../hooks/useAnalyticsCounters'; import { motion, AnimatePresence } from 'framer-motion'; import DOMPurify from 'dompurify'; import { landingApi } from '../api/landings'; import type { LandingConfig, LandingTariff, LandingTariffPeriod, LandingPaymentMethod, PurchaseRequest, } from '../api/landings'; import { StaticBackgroundRenderer } from '../components/backgrounds/BackgroundRenderer'; import { CheckCircleIcon, CheckIcon, DevicesIcon, DownloadIcon } from '@/components/icons'; import LanguageSwitcher from '../components/LanguageSwitcher'; import { cn } from '../lib/utils'; import { getApiErrorMessage } from '../utils/api-error'; import { formatPrice } from '../utils/format'; import { useCurrency } from '../hooks/useCurrency'; function detectContactType(value: string): 'email' | 'telegram' { return value.startsWith('@') ? 'telegram' : 'email'; } function isValidContact(value: string): boolean { const trimmed = value.trim(); if (!trimmed) return false; if (trimmed.startsWith('@')) { return trimmed.length >= 4; } return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed); } function formatPeriodLabel( days: number, t: (key: string, options?: Record) => string, ): string { const key = `landing.periodLabels.d${days}`; const result = t(key); if (result !== key) return result; const months = Math.floor(days / 30); const remainder = days % 30; if (months > 0 && remainder === 0) { return t('landing.periodLabels.nMonths', { count: months }); } return t('landing.periodLabels.nDays', { count: days }); } function LoadingSkeleton() { return (
); } function ErrorState({ message }: { message: string }) { const { t } = useTranslation(); return (

{t('landing.error', 'Error')}

{message}

); } function PeriodTabs({ periods, selectedDays, onSelect, }: { periods: LandingTariffPeriod[]; selectedDays: number; onSelect: (days: number) => void; }) { const { t } = useTranslation(); return (
{periods.map((period) => ( ))}
); } function GiftToggle({ isGift, onToggle }: { isGift: boolean; onToggle: (v: boolean) => void }) { const { t } = useTranslation(); return (
); } function ContactForm({ contactValue, onContactChange, isGift, giftRecipient, onGiftRecipientChange, giftMessage, onGiftMessageChange, }: { contactValue: string; onContactChange: (v: string) => void; isGift: boolean; giftRecipient: string; onGiftRecipientChange: (v: string) => void; giftMessage: string; onGiftMessageChange: (v: string) => void; }) { const { t } = useTranslation(); return (
{/* Main contact */}
onContactChange(e.target.value)} placeholder={t('landing.contactPlaceholder', 'email@example.com or @telegram')} className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 px-4 py-3 text-sm text-dark-50 placeholder-dark-500 outline-none transition-colors focus:border-accent-500/50 focus:ring-1 focus:ring-accent-500/25" />

{t('landing.contactHint')}

{/* Gift fields */} {isGift && (
onGiftRecipientChange(e.target.value)} placeholder={t('landing.recipientPlaceholder', 'Recipient email or @telegram')} className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 px-4 py-3 text-sm text-dark-50 placeholder-dark-500 outline-none transition-colors focus:border-accent-500/50 focus:ring-1 focus:ring-accent-500/25" />