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

355 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

# 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 конфигурации
```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()` с данными:
```typescript
{
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):**
```typescript
{
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"):**
```typescript
{
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:**
```typescript
{
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"
}
}
```
## Полный пример воронки
```json
{
"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 регистрации будет:
```typescript
{
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
Используется при авторизации для сбора данных со всех экранов воронки:
```typescript
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
Используется при переходе вперед для сбора данных с текущего экрана:
```typescript
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** - проверяйте что данные корректно попадают в регистрацию
## Примеры использования
### Простой профиль
```json
{
"list": {
"selectionType": "single",
"registrationFieldKey": "profile.gender",
"options": [...]
}
}
```
### Вложенная структура
```json
{
"list": {
"selectionType": "single",
"registrationFieldKey": "partner.birthplace.country",
"options": [
{ "id": "US", "label": "United States" },
{ "id": "UK", "label": "United Kingdom" }
]
}
}
```
### Без регистрации (обычный list)
```json
{
"list": {
"selectionType": "single",
// registrationFieldKey не указан - данные не попадут в регистрацию
"options": [...]
}
}
```