# 🧪 Конкретный анализ AB-тестов в воронке Soulmate ## 📋 Два AB-теста в воронке 1. **`soulmate-onboarding-image`** - тест картинки/видео на первом экране 2. **`soulmate-trial-grid`** - тест навигации после email экрана --- ## 🎯 AB-тест #1: `soulmate-onboarding-image` ### 📍 Где находится в JSON **Файл:** `public/funnels/soulmate.json` **Экран:** `"onboarding"` (первый экран воронки) **Строки:** 51-106 ```json { "id": "onboarding", "template": "soulmate", "variants": [ { "conditions": [{ "conditionType": "unleash", "unleashFlag": "soulmate-onboarding-image", "unleashVariants": ["v0"] }], "overrides": {} }, { "conditions": [{ "conditionType": "unleash", "unleashFlag": "soulmate-onboarding-image", "unleashVariants": ["v1"] }], "overrides": { "soulmatePortraitsDelivered": { "mediaUrl": "/images/90b8c77f-c0cd-475d-a4de-bcabb3708c59.png", "mediaType": "image" } } }, { "conditions": [{ "conditionType": "unleash", "unleashFlag": "soulmate-onboarding-image", "unleashVariants": ["v2"] }], "overrides": { "soulmatePortraitsDelivered": { "mediaUrl": "/images/275472b0-30e0-47d7-a1ab-8090bc9fb236.mp4", "mediaType": "video" } } } ] } ``` ### 🎬 Что тестируется Три варианта картинки/видео на первом экране: - **v0** - дефолтное изображение `/soulmate-portrait-delivered-male.jpg` - **v1** - альтернативное изображение (PNG) - **v2** - видео (MP4) ### ⏱️ КОГДА ОТПРАВЛЯЕТСЯ impression событие ``` ┌─────────────────────────────────────────────────────────┐ │ T+0ms: Пользователь открывает /soulmate/onboarding │ │ Это ПЕРВЫЙ экран воронки │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+100ms: GoogleAnalytics загружается (gtag.js) │ │ window.gtag становится доступен │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+200ms: FunnelUnleashWrapper сканирует ВСЮ воронку │ │ Находит флаги: │ │ • soulmate-onboarding-image (из onboarding) │ │ • soulmate-trial-grid (из email) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+300ms: Unleash SDK возвращает варианты │ │ • soulmate-onboarding-image → "v1" (например) │ │ • soulmate-trial-grid → "grid" (например) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+400ms: FunnelRuntime монтируется │ │ currentScreen = onboarding экран │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+410ms: currentScreenFlags вычисляется │ │ Анализирует onboarding экран: │ │ • onboarding.variants → soulmate-onboarding-image│ │ • onboarding.navigation → нет флагов │ │ Результат: ["soulmate-onboarding-image"] │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+420ms: useEffect срабатывает │ │ (FunnelRuntime.tsx, строки 136-150) │ │ │ │ ✅ sendUnleashImpression( │ │ "soulmate-onboarding-image", │ │ "v1" │ │ ) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+421ms: ✅ ОТПРАВКА В GOOGLE ANALYTICS │ │ │ │ window.gtag("event", "experiment_impression", { │ │ app_name: "witlab-funnel", │ │ feature: "soulmate-onboarding-image", │ │ treatment: "v1" │ │ }); │ │ │ │ sessionStorage.setItem( │ │ "unleash_impression_soulmate-onboarding-image_v1", │ │ "true" │ │ ); │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ Console (development): │ │ [Unleash Impression] ✅ Sent successfully: │ │ { │ │ feature: "soulmate-onboarding-image", │ │ variant: "v1" │ │ } │ └─────────────────────────────────────────────────────────┘ ``` ### 📊 Когда КОНКРЕТНО **✅ Impression отправляется:** Сразу при входе пользователя в воронку на ПЕРВЫЙ экран "onboarding" **⏰ Timing:** ~420ms после загрузки страницы `/soulmate/onboarding` **🔧 Код:** - **Файл:** `src/components/funnel/FunnelRuntime.tsx` (строки 136-150) - **Функция:** `sendUnleashImpression()` из `src/lib/funnel/unleash/sendImpression.ts` **📍 Условие отправки:** ```typescript // В FunnelRuntime.tsx const currentScreenFlags = useMemo(() => { const flags = new Set(); // Сканирует onboarding.variants currentScreen.variants?.forEach((variant) => { variant.conditions.forEach((condition) => { if (condition.conditionType === "unleash" && condition.unleashFlag) { flags.add(condition.unleashFlag); // ← "soulmate-onboarding-image" } }); }); return Array.from(flags); // ["soulmate-onboarding-image"] }, [currentScreen]); useEffect(() => { if (currentScreenFlags.length === 0) return; currentScreenFlags.forEach((flag) => { const variant = activeVariants[flag]; // "v1" sendUnleashImpression(flag, variant); // ← ОТПРАВКА }); }, [currentScreenFlags, activeVariants]); ``` ### 🔍 Как проверить в браузере ```javascript // 1. Открыть /soulmate/onboarding // 2. Открыть DevTools → Console // 3. Увидеть: [Unleash Impression] ✅ Sent successfully: { feature: "soulmate-onboarding-image", variant: "v1" // или "v0", "v2" } // 4. DevTools → Network → Фильтр "collect" POST /g/collect?...&en=experiment_impression &ep.feature=soulmate-onboarding-image &ep.treatment=v1 // 5. DevTools → Application → Session Storage unleash_impression_soulmate-onboarding-image_v1: "true" ``` --- ## 🎯 AB-тест #2: `soulmate-trial-grid` ### 📍 Где находится в JSON **Файл:** `public/funnels/soulmate.json` **Экран:** `"email"` (экран ввода email) **Строки:** 2277-2309 ```json { "id": "email", "template": "email", "navigation": { "rules": [ { "conditions": [{ "conditionType": "unleash", "unleashFlag": "soulmate-trial-grid", "unleashVariants": ["coupon"] }], "nextScreenId": "coupon" }, { "conditions": [{ "conditionType": "unleash", "unleashFlag": "soulmate-trial-grid", "unleashVariants": ["grid"] }], "nextScreenId": "trial-choice" } ], "defaultNextScreenId": "coupon" } } ``` ### 🎬 Что тестируется Два варианта навигации после email экрана: - **coupon** - переход на экран с купоном (`coupon`) - **grid** - переход на экран с выбором тарифов (`trial-choice`) ### ⏱️ КОГДА ОТПРАВЛЯЕТСЯ impression событие ``` ┌─────────────────────────────────────────────────────────┐ │ Пользователь прошел всю воронку: │ │ onboarding → gender → partner-gender → ... → email │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+0ms: Пользователь попадает на экран /soulmate/email │ │ Это 19-й экран воронки (почти в конце) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ ВАЖНО: Unleash уже загружен! │ │ activeVariants уже содержит: │ │ { │ │ "soulmate-onboarding-image": "v1", ← уже отправлено │ │ "soulmate-trial-grid": "grid" ← еще НЕ отправлено│ │ } │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+100ms: FunnelRuntime обновляется │ │ currentScreen = email экран │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+110ms: currentScreenFlags пересчитывается │ │ Анализирует email экран: │ │ • email.variants → нет unleash флагов │ │ • email.navigation.rules → soulmate-trial-grid │ │ Результат: ["soulmate-trial-grid"] │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+120ms: useEffect срабатывает │ │ (FunnelRuntime.tsx, строки 136-150) │ │ │ │ ✅ sendUnleashImpression( │ │ "soulmate-trial-grid", │ │ "grid" │ │ ) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+121ms: Проверка sessionStorage: │ │ "unleash_impression_soulmate-trial-grid_grid" │ │ НЕ найдено ✅ (первый раз на этом экране) │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ T+122ms: ✅ ОТПРАВКА В GOOGLE ANALYTICS │ │ │ │ window.gtag("event", "experiment_impression", { │ │ app_name: "witlab-funnel", │ │ feature: "soulmate-trial-grid", │ │ treatment: "grid" │ │ }); │ │ │ │ sessionStorage.setItem( │ │ "unleash_impression_soulmate-trial-grid_grid", │ │ "true" │ │ ); │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ Console (development): │ │ [Unleash Impression] ✅ Sent successfully: │ │ { │ │ feature: "soulmate-trial-grid", │ │ variant: "grid" │ │ } │ └─────────────────────────────────────────────────────────┘ ``` ### 📊 Когда КОНКРЕТНО **✅ Impression отправляется:** Когда пользователь доходит до экрана "email" (19-й экран воронки) **⏰ Timing:** ~120ms после перехода на `/soulmate/email` **🔧 Код:** - **Файл:** `src/components/funnel/FunnelRuntime.tsx` (строки 136-150) - **Функция:** `sendUnleashImpression()` из `src/lib/funnel/unleash/sendImpression.ts` **📍 Условие отправки:** ```typescript // В FunnelRuntime.tsx const currentScreenFlags = useMemo(() => { const flags = new Set(); // Сканирует email.variants (пусто) // ... // Сканирует email.navigation.rules currentScreen.navigation?.rules?.forEach((rule) => { rule.conditions.forEach((condition) => { if (condition.conditionType === "unleash" && condition.unleashFlag) { flags.add(condition.unleashFlag); // ← "soulmate-trial-grid" } }); }); return Array.from(flags); // ["soulmate-trial-grid"] }, [currentScreen]); useEffect(() => { if (currentScreenFlags.length === 0) return; currentScreenFlags.forEach((flag) => { const variant = activeVariants[flag]; // "grid" sendUnleashImpression(flag, variant); // ← ОТПРАВКА }); }, [currentScreenFlags, activeVariants]); ``` ### 🔍 Как проверить в браузере ```javascript // 1. Пройти всю воронку до экрана email: /soulmate/email // 2. Открыть DevTools → Console // 3. Увидеть: [Unleash Impression] ✅ Sent successfully: { feature: "soulmate-trial-grid", variant: "grid" // или "coupon" } // 4. DevTools → Network → Фильтр "collect" POST /g/collect?...&en=experiment_impression &ep.feature=soulmate-trial-grid &ep.treatment=grid // 5. DevTools → Application → Session Storage // Должно быть 2 ключа: unleash_impression_soulmate-onboarding-image_v1: "true" unleash_impression_soulmate-trial-grid_grid: "true" ``` --- ## 📊 Сравнительная таблица | Аспект | soulmate-onboarding-image | soulmate-trial-grid | |--------|---------------------------|---------------------| | **Экран** | `onboarding` (1-й экран) | `email` (19-й экран) | | **Позиция в JSON** | `variants` | `navigation.rules` | | **Что тестирует** | Картинка/видео на первом экране | Навигация после email | | **Варианты** | v0, v1, v2 | coupon, grid | | **Когда отправляется** | Сразу при входе в воронку | При достижении email экрана | | **Timing** | T+420ms после загрузки | T+120ms после перехода на email | | **URL** | `/soulmate/onboarding` | `/soulmate/email` | --- ## 🎯 Финальная timeline для полной воронки ``` T+0ms Пользователь открывает /soulmate/onboarding │ T+420ms ✅ IMPRESSION #1: soulmate-onboarding-image → v1 │ T+5min Пользователь проходит всю воронку │ (gender, partner-gender, analysis-target, partner-age, │ partner-ethnicity, partner-eye-color, partner-hair-length, │ burnout-support, burnout-result, birthdate, │ nature-archetype, love-priority, love-priority-result, │ relationship-block, qualities, qualities-result, │ progress, progress-result) │ T+5min Пользователь доходит до /soulmate/email │ T+5min ✅ IMPRESSION #2: soulmate-trial-grid → grid +120ms │ │ T+5min Пользователь вводит email и нажимает Continue +1min │ │ └─ Переход на trial-choice (если variant = "grid") ИЛИ Переход на coupon (если variant = "coupon") ``` --- ## 📝 Ключевые моменты ### 1. Оба флага загружаются ЗАРАНЕЕ ``` FunnelUnleashWrapper сканирует ВСЮ воронку при загрузке → Находит оба флага сразу: • soulmate-onboarding-image • soulmate-trial-grid → Unleash SDK загружает варианты для ОБОИХ флагов → Варианты сохраняются в activeVariants ``` **Результат:** Быстрый переход между экранами (флаги уже загружены). ### 2. Impression отправляются ПОСЛЕДОВАТЕЛЬНО ``` Экран onboarding → impression для soulmate-onboarding-image Экран email → impression для soulmate-trial-grid ``` **Результат:** Точная аналитика - события отправляются только когда пользователь РЕАЛЬНО видит экран. ### 3. Защита от дубликатов работает ``` sessionStorage хранит: { "unleash_impression_soulmate-onboarding-image_v1": "true", "unleash_impression_soulmate-trial-grid_grid": "true" } ``` **Результат:** Даже если пользователь вернется назад или перезагрузит страницу, события НЕ отправятся повторно. --- ## 🐛 Отладка ### Проверка отправки первого теста ```bash # 1. Открыть /soulmate/onboarding # 2. В консоли должно появиться: [Unleash Impression] ✅ Sent successfully: { feature: "soulmate-onboarding-image", variant: "v1" } # 3. В Network tab: POST /g/collect?... &ep.feature=soulmate-onboarding-image &ep.treatment=v1 ``` ### Проверка отправки второго теста ```bash # 1. Пройти до /soulmate/email # 2. В консоли должно появиться: [Unleash Impression] ✅ Sent successfully: { feature: "soulmate-trial-grid", variant: "grid" } # 3. В Network tab: POST /g/collect?... &ep.feature=soulmate-trial-grid &ep.treatment=grid ``` ### Проверка что оба события отправлены ```javascript // В консоли браузера: Object.keys(sessionStorage) .filter(key => key.startsWith("unleash_impression_")) .forEach(key => console.log(key, sessionStorage.getItem(key))); // Вывод (если пользователь дошел до email): // unleash_impression_soulmate-onboarding-image_v1 "true" // unleash_impression_soulmate-trial-grid_grid "true" ``` --- ## ✅ Итоговый ответ ### Когда отправляется `soulmate-onboarding-image`? **🎯 Сразу при входе в воронку на первый экран "onboarding"** - URL: `/soulmate/onboarding` - Timing: ~420ms после загрузки страницы - Экран: 1-й из ~20 экранов воронки ### Когда отправляется `soulmate-trial-grid`? **🎯 Когда пользователь доходит до экрана "email" (почти в конце воронки)** - URL: `/soulmate/email` - Timing: ~120ms после перехода на экран - Экран: 19-й из ~20 экранов воронки ### Код отправки (для обоих) **Файл:** `src/components/funnel/FunnelRuntime.tsx` **Строки:** 136-150 ```typescript useEffect(() => { if (currentScreenFlags.length === 0) return; currentScreenFlags.forEach((flag) => { const variant = activeVariants[flag]; sendUnleashImpression(flag, variant); // ← ЗДЕСЬ }); }, [currentScreenFlags, activeVariants]); ``` **Функция:** `src/lib/funnel/unleash/sendImpression.ts` ```typescript export function sendUnleashImpression(flag: string, variant: string | undefined) { // Проверки... window.gtag("event", "experiment_impression", { app_name: "witlab-funnel", feature: flag, treatment: variant, }); sessionStorage.setItem(`unleash_impression_${flag}_${variant}`, "true"); } ```