w-funnel/docs/REGISTRATION_FIELD_KEY.md
2025-10-07 23:38:50 +02:00

11 KiB
Raw Permalink Blame History

Registration Field Key для List Single Selection

Описание

Функциональность registrationFieldKey позволяет автоматически передавать выбранные значения из list single selection экранов в payload регистрации пользователя при авторизации через email экран.

Как это работает

1. Настройка в админке

Для list экранов с selectionType: "single" в админке появляется дополнительное поле "Ключ поля для регистрации".

В это поле можно указать путь к полю в объекте регистрации, используя точечную нотацию для вложенных объектов.

Примеры:

  • profile.gender{ profile: { gender: "selected-id" } }
  • profile.relationship_status{ profile: { relationship_status: "selected-id" } }
  • partner.gender{ partner: { gender: "selected-id" } }

2. Пример JSON конфигурации

{
  "id": "gender-screen",
  "template": "list",
  "title": {
    "text": "What is your gender?"
  },
  "list": {
    "selectionType": "single",
    "registrationFieldKey": "profile.gender",
    "options": [
      {
        "id": "male",
        "label": "Male",
        "emoji": "👨"
      },
      {
        "id": "female",
        "label": "Female",
        "emoji": "👩"
      },
      {
        "id": "other",
        "label": "Other",
        "emoji": "🧑"
      }
    ]
  }
}

3. Как данные попадают в регистрацию и сессию

Передача в сессию (на каждом экране):

  1. Пользователь выбирает вариант на экране (например, "Male" с id "male")
  2. Ответ сохраняется в FunnelAnswers под ключом экрана: { "gender-screen": ["male"] }
  3. При переходе вперед (нажатие Continue или автопереход):
    • Вызывается buildSessionDataFromScreen() для текущего экрана
    • Создается объект с вложенной структурой: { profile: { gender: "male" } }
    • Вызывается updateSession() с данными:
      {
        answers: { "gender-screen": ["male"] },  // Старая логика
        profile: { gender: "male" }              // Новая логика с registrationFieldKey
      }
      
  4. Данные отправляются в API и сохраняются в сессии пользователя

Передача в регистрацию (при авторизации):

  1. При переходе на email экран вызывается функция buildRegistrationDataFromAnswers()
  2. Функция обрабатывает все list single selection экраны с registrationFieldKey
  3. Создается объект с вложенной структурой из всех экранов: { profile: { gender: "male", relationship_status: "single" } }
  4. При авторизации этот объект объединяется с базовым payload
  5. Отправляется на сервер в составе ICreateAuthorizeRequest

4. Структура payload регистрации

Базовый payload (без registrationFieldKey):

{
  email: "user@example.com",
  timezone: "Europe/Moscow",
  locale: "en",
  source: "funnel-id",
  sign: true,
  signDate: "2024-01-01T00:00:00.000Z",
  feature: "stripe"
}

С registrationFieldKey (profile.gender = "male"):

{
  email: "user@example.com",
  timezone: "Europe/Moscow",
  locale: "en",
  source: "funnel-id",
  sign: true,
  signDate: "2024-01-01T00:00:00.000Z",
  feature: "stripe",
  profile: {
    gender: "male"
  }
}

С несколькими registrationFieldKey:

{
  email: "user@example.com",
  timezone: "Europe/Moscow",
  locale: "en",
  source: "funnel-id",
  sign: true,
  signDate: "2024-01-01T00:00:00.000Z",
  feature: "stripe",
  profile: {
    gender: "male",
    relationship_status: "single"
  },
  partner: {
    gender: "female"
  }
}

Полный пример воронки

{
  "meta": {
    "id": "dating-funnel",
    "title": "Dating Profile",
    "firstScreenId": "gender"
  },
  "screens": [
    {
      "id": "gender",
      "template": "list",
      "title": { "text": "What is your gender?" },
      "list": {
        "selectionType": "single",
        "registrationFieldKey": "profile.gender",
        "options": [
          { "id": "male", "label": "Male", "emoji": "👨" },
          { "id": "female", "label": "Female", "emoji": "👩" }
        ]
      },
      "navigation": {
        "defaultNextScreenId": "relationship-status"
      }
    },
    {
      "id": "relationship-status",
      "template": "list",
      "title": { "text": "What is your relationship status?" },
      "list": {
        "selectionType": "single",
        "registrationFieldKey": "profile.relationship_status",
        "options": [
          { "id": "single", "label": "Single" },
          { "id": "relationship", "label": "In a relationship" },
          { "id": "married", "label": "Married" }
        ]
      },
      "navigation": {
        "defaultNextScreenId": "partner-gender"
      }
    },
    {
      "id": "partner-gender",
      "template": "list",
      "title": { "text": "What is your partner's gender?" },
      "list": {
        "selectionType": "single",
        "registrationFieldKey": "partner.gender",
        "options": [
          { "id": "male", "label": "Male", "emoji": "👨" },
          { "id": "female", "label": "Female", "emoji": "👩" }
        ]
      },
      "navigation": {
        "defaultNextScreenId": "email"
      }
    },
    {
      "id": "email",
      "template": "email",
      "title": { "text": "Enter your email" },
      "emailInput": {
        "label": "Email",
        "placeholder": "your@email.com"
      }
    }
  ]
}

Результат после прохождения воронки:

Если пользователь выбрал:

  • Gender: Male
  • Relationship Status: Single
  • Partner Gender: Female
  • Email: user@example.com

Payload регистрации будет:

{
  email: "user@example.com",
  timezone: "Europe/Moscow",
  locale: "en",
  source: "dating-funnel",
  sign: true,
  signDate: "2024-01-01T00:00:00.000Z",
  feature: "stripe",
  profile: {
    gender: "male",
    relationship_status: "single"
  },
  partner: {
    gender: "female"
  }
}

Ограничения

  1. Только для single selection - работает только с selectionType: "single"
  2. Только ID опции - передается именно id выбранной опции, а не label или value
  3. Перезапись значений - если несколько экранов используют один и тот же ключ, последний перезапишет предыдущий
  4. Обязательный email экран - данные передаются только при авторизации через email экран

Техническая реализация

Файлы

  • types.ts - добавлено поле registrationFieldKey в ListScreenDefinition
  • ListScreenConfig.tsx - UI для настройки ключа в админке
  • registrationHelpers.ts - утилиты buildRegistrationDataFromAnswers() и buildSessionDataFromScreen()
  • FunnelRuntime.tsx - вызывает buildSessionDataFromScreen() при переходе вперед и передает в updateSession()
  • useAuth.ts - принимает registrationData и объединяет с базовым payload
  • EmailTemplate.tsx - вызывает buildRegistrationDataFromAnswers() и передает в useAuth
  • screenRenderer.tsx - передает answers в EmailTemplate

Функция buildRegistrationDataFromAnswers

Используется при авторизации для сбора данных со всех экранов воронки:

export function buildRegistrationDataFromAnswers(
  funnel: FunnelDefinition,
  answers: FunnelAnswers
): RegistrationDataObject {
  const registrationData: RegistrationDataObject = {};

  for (const screen of funnel.screens) {
    if (screen.template === "list") {
      const listScreen = screen as ListScreenDefinition;
      
      if (
        listScreen.list.selectionType === "single" &&
        listScreen.list.registrationFieldKey &&
        answers[screen.id] &&
        answers[screen.id].length > 0
      ) {
        const selectedId = answers[screen.id][0];
        const fieldKey = listScreen.list.registrationFieldKey;
        
        // Устанавливаем значение по многоуровневому ключу
        setNestedValue(registrationData, fieldKey, selectedId);
      }
    }
  }

  return registrationData;
}

Функция buildSessionDataFromScreen

Используется при переходе вперед для сбора данных с текущего экрана:

export function buildSessionDataFromScreen(
  screen: { template: string; id: string; list?: { selectionType?: string; registrationFieldKey?: string } },
  selectedIds: string[]
): RegistrationDataObject {
  const sessionData: RegistrationDataObject = {};

  if (screen.template === "list" && screen.list) {
    const { selectionType, registrationFieldKey } = screen.list;
    
    if (
      selectionType === "single" &&
      registrationFieldKey &&
      selectedIds.length > 0
    ) {
      const selectedId = selectedIds[0];
      setNestedValue(sessionData, registrationFieldKey, selectedId);
    }
  }

  return sessionData;
}

Best Practices

  1. Используйте понятные ID - ID опций должны соответствовать ожидаемым значениям на сервере
  2. Документируйте ключи - ведите список используемых registrationFieldKey для избежания конфликтов
  3. Проверяйте типы - убедитесь что ID опций соответствуют типам полей в ICreateAuthorizeRequest
  4. Тестируйте payload - проверяйте что данные корректно попадают в регистрацию

Примеры использования

Простой профиль

{
  "list": {
    "selectionType": "single",
    "registrationFieldKey": "profile.gender",
    "options": [...]
  }
}

Вложенная структура

{
  "list": {
    "selectionType": "single",
    "registrationFieldKey": "partner.birthplace.country",
    "options": [
      { "id": "US", "label": "United States" },
      { "id": "UK", "label": "United Kingdom" }
    ]
  }
}

Без регистрации (обычный list)

{
  "list": {
    "selectionType": "single",
    // registrationFieldKey не указан - данные не попадут в регистрацию
    "options": [...]
  }
}