355 lines
11 KiB
Markdown
355 lines
11 KiB
Markdown
# 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": [...]
|
||
}
|
||
}
|
||
```
|