# Trial Choice & Payment Integration ## Проблемы которые решены ### 1. Связь выбора Trial Choice → Payment **Проблема**: Пользователь выбирает вариант триала на экране TrialChoice, но на экране Payment всегда отображался первый вариант. **Решение**: - Создан `TrialVariantSelectionContext` для хранения выбранного `variantId` - `TrialChoiceTemplate` сохраняет выбор пользователя в контекст - `TrialPaymentTemplate` читает выбранный вариант из контекста и использует его вместо первого ### 2. Общая логика загрузки и кеширование **Проблема**: - Каждый экран (TrialChoice, Payment) делал отдельный запрос к API - TrialChoice показывал неправильные данные до завершения загрузки - Payment уже имел loader, но TrialChoice — нет **Решение**: - Создан `PaymentPlacementProvider` для кеширования загруженных placement данных - `usePaymentPlacement` hook теперь использует кеш из контекста - Повторные запросы к API не выполняются если данные уже загружены - `TrialChoiceTemplate` показывает loader (как Payment) до загрузки данных ## Архитектура ### Контексты #### PaymentPlacementProvider - **Путь**: `src/entities/session/payment/PaymentPlacementProvider.tsx` - **Назначение**: Кеширует результаты `loadFunnelPaymentById` чтобы избежать повторных запросов - **Ключ кеша**: `${funnelKey}:${paymentId}` - **Состояние**: `{ placement, isLoading, error }` #### TrialVariantSelectionProvider - **Путь**: `src/entities/session/payment/TrialVariantSelectionContext.tsx` - **Назначение**: Хранит выбранный пользователем `variantId` для передачи между экранами - **Состояние**: `{ selectedVariantId, setSelectedVariantId }` ### Обновленные компоненты #### TrialChoiceTemplate - **Изменения**: - Добавлен loader (Spinner) до загрузки placement - Сохраняет выбор в `TrialVariantSelectionContext` - Использует `usePaymentPlacement` с кешированием - Инициализирует локальный `selectedId` из `selectedVariantId` контекста #### TrialPaymentTemplate - **Изменения**: - Читает `selectedVariantId` из `TrialVariantSelectionContext` - Использует выбранный вариант, если доступен: `placement?.variants?.find((v) => v.id === selectedVariantId)` - Fallback на первый вариант если выбор отсутствует - Использует `usePaymentPlacement` с кешированием #### SpecialOfferTemplate - **Изменения**: Нет (специально) - **Поведение**: Продолжает использовать первый вариант `placement?.variants?.[0]` - **Причина**: Использует другой `paymentId` (`"main_secret_discount"`) и не должен зависеть от выбора Trial ### usePaymentPlacement Hook - **Изменения**: - Теперь использует `PaymentPlacementContext` для получения/загрузки данных - Упрощена логика: нет локального state, все в контексте - Автоматически триггерит загрузку если данных нет ### Layout Integration - **Файл**: `src/app/[funnelId]/layout.tsx` - **Изменения**: Обернут в два новых провайдера: ```tsx {children} ``` ## Потоки данных ### Поток 1: Воронка С экраном Trial Choice ``` 1. User открывает Trial Choice → PaymentPlacementProvider загружает "main" placement (если еще нет в кеше) → TrialChoiceTemplate показывает loader → После загрузки отображаются варианты 2. User выбирает вариант (например, id="plan-123") → setSelectedVariantId("plan-123") в TrialVariantSelectionContext 3. User переходит на Payment → PaymentPlacementProvider возвращает "main" placement из кеша (без запроса) → TrialPaymentTemplate читает selectedVariantId="plan-123" → Использует вариант с id="plan-123" вместо первого ``` ### Поток 2: Воронка БЕЗ экрана Trial Choice ``` 1. User сразу открывает Payment → PaymentPlacementProvider загружает "main" placement → TrialPaymentTemplate показывает loader → selectedVariantId === null → Использует первый вариант (дефолт) ``` ### Поток 3: Special Offer ``` 1. User открывает Special Offer → PaymentPlacementProvider загружает "main_secret_discount" placement → SpecialOfferTemplate показывает loader → Всегда использует первый вариант (не зависит от Trial Choice) ``` ## Тестирование ### Сценарий 1: Trial Choice → Payment 1. Открыть воронку с Trial Choice 2. Дождаться загрузки вариантов 3. Выбрать второй вариант (не первый) 4. Нажать Continue 5. **Ожидается**: На Payment отображается выбранный (второй) вариант ### Сценарий 2: Прямой переход на Payment 1. Открыть воронку без Trial Choice или перейти прямо на Payment 2. **Ожидается**: На Payment отображается первый вариант (дефолт) ### Сценарий 3: Кеширование 1. Открыть Trial Choice → загрузка placement 2. Перейти на Payment 3. **Ожидается**: Payment загружается мгновенно (из кеша), без повторного API запроса ### Сценарий 4: Special Offer 1. Открыть Special Offer 2. **Ожидается**: Всегда показывается первый вариант, независимо от выбора в Trial Choice ## API изменения (Backend) ### session.controller.ts - **Изменения**: Добавлены `title` и `accent` поля в варианты - **Поведение**: Возвращает все планы (не ограничивает до 4) - **Дефолты**: `title` использует `["Basic", "Standard", "Popular", "Premium"]` с fallback `"Premium"` ## Файлы созданы/изменены ### Новые файлы - `src/entities/session/payment/PaymentPlacementProvider.tsx` - `src/entities/session/payment/TrialVariantSelectionContext.tsx` - `src/entities/session/payment/index.ts` ### Измененные файлы - `src/hooks/payment/usePaymentPlacement.ts` - использует контекст вместо локального state - `src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.tsx` - loader + сохранение выбора - `src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.tsx` - использует выбранный вариант - `src/app/[funnelId]/layout.tsx` - обернут в провайдеры ## Будущие улучшения 1. **Персистентность**: Сохранять выбор в localStorage/sessionStorage для сохранения при перезагрузке 2. **Аналитика**: Трекинг выбора вариантов для A/B тестирования 3. **Валидация**: Проверять что выбранный variant все еще существует в placement 4. **Типизация**: Усилить типы для garantie что только валидные paymentId используются