w-funnel/docs/UNLEASH_ANALYTICS_FLOW.md
2025-10-22 22:42:01 +02:00

491 lines
16 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📊 Unleash AB Testing → Google Analytics - Подробная документация
## 🎯 Общая схема работы
```
1. Пользователь заходит в воронку
2. UnleashProvider инициализируется (layout.tsx)
3. FunnelUnleashWrapper собирает все флаги из воронки
4. Unleash SDK загружает варианты флагов
5. useUnleash подписывается на события "impression"
6. При активации флага → отправка в Google Analytics
```
---
## 1В какой момент отправляется событие
### Цепочка инициализации:
#### Шаг 1: Загрузка воронки
```tsx
// src/app/[funnelId]/layout.tsx
<UnleashProvider> {/* ← Инициализируется один раз */}
<PixelsProvider googleAnalyticsId={funnel.meta.googleAnalyticsId}>
{children}
</PixelsProvider>
</UnleashProvider>
```
#### Шаг 2: Сбор флагов из воронки
```tsx
// src/components/funnel/FunnelUnleashWrapper.tsx
export function FunnelUnleashWrapper({ funnel }) {
// Сканирует все экраны и собирает уникальные флаги
const allFlags = useMemo(() => {
const flags = new Set<string>();
funnel.screens.forEach((screen) => {
// Из вариантов экранов
screen.variants?.forEach(variant => {
variant.conditions.forEach(condition => {
if (condition.conditionType === "unleash") {
flags.add(condition.unleashFlag);
}
});
});
// Из правил навигации
screen.navigation?.rules?.forEach(rule => {
rule.conditions.forEach(condition => {
if (condition.conditionType === "unleash") {
flags.add(condition.unleashFlag);
}
});
});
});
return Array.from(flags);
}, [funnel.screens]);
// Получаем варианты для ВСЕХ флагов сразу
const flagVariants = allFlags.map(flag => {
const variant = useVariant(flag);
return { flag, variant: variant?.name };
});
}
```
#### Шаг 3: Подписка на события
```tsx
// src/lib/funnel/unleash/useUnleash.ts
useEffect(() => {
const handleImpression = (impressionEvent) => {
if (impressionEvent.enabled) {
// ✅ ОТПРАВКА В GA
window.gtag("event", "experiment_impression", {
app_name: "witlab-funnel",
feature: impressionEvent.featureName,
treatment: impressionEvent.variant,
});
}
};
unleashClient.on("impression", handleImpression);
return () => {
unleashClient.off("impression", handleImpression);
};
}, [unleashClient]);
```
### ⏰ Timing событий:
| Момент | Действие | Когда отправляется в GA |
|--------|----------|-------------------------|
| **T+0ms** | Пользователь открывает воронку | - |
| **T+100ms** | UnleashProvider подключается к Unleash | - |
| **T+300ms** | Unleash возвращает варианты флагов | ✅ **Здесь!** |
| **T+300ms** | Unleash SDK эмитит событие "impression" | ✅ **И здесь!** |
| **T+301ms** | useUnleash ловит событие → window.gtag() | ✅ **События отправлены** |
| **T+500ms** | Воронка рендерится с правильными вариантами | - |
### 🔄 Повторная отправка:
**НЕТ повторных отправок** для одного флага в рамках сессии:
- Unleash SDK внутри помнит какие impression уже отправлены
- События эмитятся только **один раз** при первой активации флага
- При переходе между экранами события **НЕ отправляются повторно**
---
## 2⃣ Какие данные отправляются
### Формат события в Google Analytics:
```javascript
window.gtag("event", "experiment_impression", {
app_name: "witlab-funnel", // Идентификатор приложения
feature: "trial-button-test", // Название флага из Unleash
treatment: "v1" // Вариант который получил пользователь
});
```
### Детали полей:
| Поле | Тип | Значение | Описание |
|------|-----|----------|----------|
| **event** | string | `"experiment_impression"` | Стандартное название для AB тестов в GA |
| **app_name** | string | `"witlab-funnel"` | Фиксированное имя приложения |
| **feature** | string | `impressionEvent.featureName` | Название флага из Unleash |
| **treatment** | string | `impressionEvent.variant` | Вариант: "v0", "v1", "v2", "disabled" и т.д. |
### Пример реальных данных:
```javascript
// AB тест кнопки оплаты
{
event: "experiment_impression",
app_name: "witlab-funnel",
feature: "trial-payment-button",
treatment: "v1"
}
// AB тест навигации
{
event: "experiment_impression",
app_name: "witlab-funnel",
feature: "onboarding-flow-test",
treatment: "short"
}
// Несколько флагов → несколько событий
{
event: "experiment_impression",
app_name: "witlab-funnel",
feature: "special-offer-test",
treatment: "v2"
}
```
### 📊 Как увидеть в Google Analytics:
1. **Realtime → Events**
- Событие: `experiment_impression`
- Параметры: `app_name`, `feature`, `treatment`
2. **Reports → Engagement → Events**
- Найти `experiment_impression`
- Посмотреть breakdown по `feature`
- Посмотреть distribution по `treatment`
3. **Создать кастомный отчет:**
```
Dimensions:
- Event name (experiment_impression)
- Event parameter: feature
- Event parameter: treatment
Metrics:
- Event count
- Users
```
---
## 3⃣ Совпадает ли логика с aura-webapp
### ✅ Сходства (95% идентичны):
#### 1. Точно такой же механизм отправки:
```typescript
// ✅ WITLAB-FUNNEL
unleashClient.on("impression", (impressionEvent) => {
window.gtag("event", "experiment_impression", {
app_name: "witlab-funnel",
feature: impressionEvent.featureName,
treatment: impressionEvent.variant,
});
});
// ✅ AURA-WEBAPP
unleashClient.on("impression", (impressionEvent: any) => {
window.gtag?.("event", "experiment_impression", {
app_name: impressionEvent.context.appName,
feature: impressionEvent.featureName,
treatment: impressionEvent.variant,
});
});
```
#### 2. Одинаковый формат события:
- Название: `"experiment_impression"`
- Параметры: `app_name`, `feature`, `treatment`
- Условие: только если `impressionEvent.enabled === true`
#### 3. Подписка в useEffect:
- В обоих случаях подписка через `unleashClient.on("impression")`
- В обоих случаях cleanup через `unleashClient.off("impression")`
### ⚠️ Различия (минимальные):
| Аспект | witlab-funnel | aura-webapp | Критично? |
|--------|---------------|-------------|-----------|
| **app_name** | Хардкод `"witlab-funnel"` | Из `context.appName` | ⚠️ Да* |
| **window.gtag** | `window.gtag` | `window.gtag?.` | ✅ Нет |
| **Debug логи** | ✅ Есть в dev | ❌ Нет | ✅ Нет |
| **Типизация** | Явная типизация | `any` | ✅ Нет |
**\* Рекомендация:** Можно улучшить чтобы брать из context:
```typescript
// Улучшенная версия (опционально)
window.gtag("event", "experiment_impression", {
app_name: impressionEvent.context?.appName || "witlab-funnel",
feature: impressionEvent.featureName,
treatment: impressionEvent.variant,
});
```
### 📋 Вывод по совместимости:
**Логика полностью совместима**
- События будут выглядеть одинаково в GA
- Можно создать единые отчеты для обоих приложений
- Единственная разница - в `app_name` (можно использовать для фильтрации)
---
## 4⃣ Что если Google Analytics не установлена
### Сценарий: GA не настроена
```tsx
// В воронке:
{
"meta": {
"googleAnalyticsId": undefined // ← Не указан
}
}
```
### ✅ Защита от ошибок:
```typescript
// src/lib/funnel/unleash/useUnleash.ts
if (typeof window !== "undefined" && window.gtag) {
// ^^^^^^^^^^^^
// Проверка что gtag существует
window.gtag("event", "experiment_impression", {...});
}
```
### Что произойдет:
1.**Unleash продолжит работать нормально**
- Флаги загружаются
- Варианты применяются
- AB тесты работают
2.**Событие "impression" будет эмититься**
- `unleashClient.on("impression")` сработает
- `handleImpression()` будет вызван
3. ⚠️ **Отправка в GA будет пропущена**
- Проверка `window.gtag` вернет `false`
- Блок с `window.gtag()` НЕ выполнится
- **Никакой ошибки не будет**
4. 🐛 **Debug логи все равно работают**
```javascript
// В консоли браузера (в dev):
[Unleash Analytics] {
feature: "trial-button-test",
variant: "v1"
}
```
### Поведение в разных средах:
| Среда | GA установлена? | window.gtag? | Что происходит |
|-------|----------------|--------------|----------------|
| **Production с GA** | ✅ Да | ✅ Да | ✅ События отправляются |
| **Production без GA** | ❌ Нет | ❌ Нет | ✅ AB тесты работают, GA молчит |
| **Development с GA** | ✅ Да | ✅ Да | ✅ События + debug логи |
| **Development без GA** | ❌ Нет | ❌ Нет | ✅ AB тесты + debug логи |
| **Preview/Staging** | Зависит | Зависит | Зависит от настройки |
### Рекомендации:
#### ✅ Текущая реализация безопасна:
```typescript
// Нет ошибок если GA отсутствует
if (typeof window !== "undefined" && window.gtag) {
window.gtag(...); // Выполнится только если GA загружен
}
```
#### ⚠️ Можно добавить предупреждение (опционально):
```typescript
if (process.env.NODE_ENV === "development") {
if (typeof window !== "undefined" && !window.gtag) {
console.warn("[Unleash Analytics] Google Analytics not loaded");
}
}
```
---
## 🔍 Debug и мониторинг
### В development режиме:
```javascript
// Консоль браузера автоматически показывает:
[Unleash Analytics] { feature: "trial-test", variant: "v1" }
[Unleash Analytics] { feature: "button-test", variant: "v2" }
```
### Проверка отправки в GA:
#### 1. Через Network tab:
```
1. Откройте DevTools → Network
2. Фильтр: "collect" или "analytics"
3. Ищите POST запросы к:
- https://www.google-analytics.com/g/collect
- https://www.google-analytics.com/j/collect
4. В payload должны быть:
- en=experiment_impression
- ep.feature=trial-test
- ep.treatment=v1
```
#### 2. Через Google Analytics DebugView:
```bash
# 1. Включите debug mode
localStorage.setItem('ga_debug', '1')
# 2. Перезагрузите страницу
# 3. Откройте GA → Admin → DebugView
# 4. Увидите события в реальном времени
```
#### 3. Через расширение Google Analytics Debugger:
```
1. Установите Chrome extension "Google Analytics Debugger"
2. Включите его
3. В консоли увидите все GA события с деталями
```
---
## 📊 Пример полного flow
### Сценарий: Воронка с 2 AB тестами
```json
{
"meta": {
"googleAnalyticsId": "G-XXXXXXXXXX"
},
"screens": [
{
"id": "payment",
"variants": [
{
"conditions": [
{
"conditionType": "unleash",
"unleashFlag": "trial-button-test",
"unleashVariants": ["v1"]
}
]
}
]
},
{
"id": "gender",
"navigation": {
"rules": [
{
"conditions": [
{
"conditionType": "unleash",
"unleashFlag": "onboarding-flow",
"unleashVariants": ["short"]
}
]
}
]
}
}
]
}
```
### Timeline событий:
```
T+0ms: Пользователь открывает /soulmate/payment
T+50ms: UnleashProvider инициализируется
T+100ms: FunnelUnleashWrapper собирает флаги:
["trial-button-test", "onboarding-flow"]
T+300ms: Unleash возвращает варианты:
trial-button-test → v1
onboarding-flow → short
T+300ms: Unleash эмитит 2 impression события
T+301ms: useUnleash ловит события
T+301ms: ✅ Отправка в GA #1:
{
event: "experiment_impression",
app_name: "witlab-funnel",
feature: "trial-button-test",
treatment: "v1"
}
T+302ms: ✅ Отправка в GA #2:
{
event: "experiment_impression",
app_name: "witlab-funnel",
feature: "onboarding-flow",
treatment: "short"
}
T+500ms: FunnelLoadingScreen исчезает
T+501ms: Экран рендерится с правильными вариантами
```
### В консоли разработчика:
```
[Unleash Analytics] { feature: "trial-button-test", variant: "v1" }
[Unleash Analytics] { feature: "onboarding-flow", variant: "short" }
```
---
## 🎯 Итоговые выводы
### 1. Момент отправки:
**При первой активации флага** (~300ms после загрузки воронки)
**Только один раз** за сессию для каждого флага
**Автоматически** без необходимости явного вызова
### 2. Данные:
✅ Событие: `"experiment_impression"`
✅ Параметры: `app_name`, `feature`, `treatment`
✅ Формат совместим с Google Analytics 4
### 3. Совместимость с aura-webapp:
**95% идентично**
✅ Можно использовать единые GA отчеты
⚠️ Единственная разница: `app_name` (легко фильтровать)
### 4. Без Google Analytics:
**AB тесты работают нормально**
**Никаких ошибок**
✅ Debug логи в dev режиме
⚠️ События просто не отправляются (graceful degradation)
---
## 📚 См. также
- `AB_TESTING_GUIDE.md` - Общее руководство по AB тестам
- `AB_TESTING_UPDATES.md` - Технические детали и оптимизации
- `UNLEASH_SETUP.md` - Настройка Unleash