funnel done

This commit is contained in:
dev.daminik00 2025-10-09 02:14:36 +02:00
parent 2c5c525c99
commit 4a7f327f36
4 changed files with 387 additions and 1002 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,300 +0,0 @@
{
"meta": {
"id": "soulmate_test",
"title": "Новая воронка",
"description": "Описание новой воронки",
"firstScreenId": "onboarding",
"yandexMetrikaId": "104471567"
},
"defaultTexts": {
"nextButton": "Continue"
},
"screens": [
{
"id": "onboarding",
"template": "info",
"title": {
"text": "Добро пожаловать!",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "md",
"align": "center",
"color": "default"
},
"subtitle": {
"text": "Это ваша новая воронка. Начните редактирование.",
"show": true,
"font": "manrope",
"weight": "regular",
"size": "md",
"align": "center",
"color": "muted"
},
"navigation": {
"rules": [],
"defaultNextScreenId": "gender",
"isEndScreen": false
},
"icon": {
"type": "emoji",
"value": "🎯",
"size": "lg"
},
"variables": [],
"variants": []
},
{
"id": "gender",
"template": "list",
"header": {
"showBackButton": true,
"show": true
},
"title": {
"text": "Новый экран",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "2xl",
"align": "left",
"color": "default"
},
"subtitle": {
"text": "Добавьте детали справа",
"show": true,
"font": "manrope",
"weight": "medium",
"size": "lg",
"align": "left",
"color": "default"
},
"bottomActionButton": {
"show": false,
"cornerRadius": "3xl",
"showPrivacyTermsConsent": false
},
"navigation": {
"rules": [],
"defaultNextScreenId": "example",
"isEndScreen": false
},
"list": {
"selectionType": "single",
"options": [
{
"id": "male",
"label": "male",
"disabled": false
},
{
"id": "female",
"label": "female",
"disabled": false
}
],
"registrationFieldKey": "profile.gender"
},
"variants": []
},
{
"id": "example",
"template": "list",
"header": {
"showBackButton": true,
"show": true
},
"title": {
"text": "Новый экран",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "2xl",
"align": "left",
"color": "default"
},
"subtitle": {
"text": "Добавьте детали справа",
"show": true,
"font": "manrope",
"weight": "medium",
"size": "lg",
"align": "left",
"color": "default"
},
"bottomActionButton": {
"show": true,
"cornerRadius": "3xl",
"showPrivacyTermsConsent": false
},
"navigation": {
"rules": [],
"defaultNextScreenId": "birthdate",
"isEndScreen": false
},
"list": {
"selectionType": "single",
"options": [
{
"id": "option-1",
"label": "Вариант 1",
"disabled": false
},
{
"id": "option-2",
"label": "Вариант 2",
"disabled": false
}
]
},
"variants": []
},
{
"id": "birthdate",
"template": "date",
"header": {
"showBackButton": true,
"show": true
},
"title": {
"text": "Новый экран",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "2xl",
"align": "left",
"color": "default"
},
"subtitle": {
"text": "Добавьте детали справа",
"show": true,
"font": "manrope",
"weight": "medium",
"size": "lg",
"align": "left",
"color": "default"
},
"bottomActionButton": {
"show": true,
"cornerRadius": "3xl",
"showPrivacyTermsConsent": false
},
"navigation": {
"rules": [],
"defaultNextScreenId": "email",
"isEndScreen": false
},
"dateInput": {
"monthLabel": "Месяц",
"dayLabel": "День",
"yearLabel": "Год",
"monthPlaceholder": "ММ",
"dayPlaceholder": "ДД",
"yearPlaceholder": "ГГГГ",
"showSelectedDate": true,
"selectedDateFormat": "dd MMMM yyyy",
"selectedDateLabel": "Выбранная дата:",
"zodiac": {
"enabled": true,
"storageKey": "userZodiac"
},
"registrationFieldKey": "profile.birthdate"
},
"variants": []
},
{
"id": "email",
"template": "email",
"header": {
"showBackButton": true,
"show": true
},
"title": {
"text": "Портрет твоей второй половинки готов! Куда нам его отправить?",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "2xl",
"align": "center",
"color": "default"
},
"bottomActionButton": {
"show": true,
"cornerRadius": "3xl",
"showPrivacyTermsConsent": true
},
"navigation": {
"rules": [],
"defaultNextScreenId": "final",
"isEndScreen": false
},
"emailInput": {
"label": "Email адрес",
"placeholder": "example@email.com"
},
"image": {
"src": "/female-portrait.jpg"
},
"variants": [
{
"conditions": [
{
"screenId": "gender",
"operator": "includesAny",
"optionIds": [
"male"
]
}
],
"overrides": {
"image": {
"src": "/male-portrait.jpg"
}
}
}
]
},
{
"id": "final",
"template": "info",
"header": {
"showBackButton": true,
"show": true
},
"title": {
"text": "Спасибо за регистрацию",
"show": true,
"font": "manrope",
"weight": "bold",
"size": "2xl",
"align": "center",
"color": "default"
},
"subtitle": {
"text": "Добавьте подзаголовок для информационного экрана",
"show": true,
"font": "manrope",
"weight": "medium",
"size": "lg",
"align": "center",
"color": "default"
},
"bottomActionButton": {
"show": false,
"cornerRadius": "3xl",
"showPrivacyTermsConsent": false
},
"navigation": {
"rules": [],
"isEndScreen": true
},
"icon": {
"type": "emoji",
"value": "",
"size": "xl"
},
"variables": [],
"variants": []
}
]
}

View File

@ -5,7 +5,7 @@ import {
MainButton,
MainButtonProps,
} from "@/components/ui/MainButton/MainButton";
import { useEffect, useState, useRef } from "react";
import { useState } from "react";
export interface SelectAnswersListProps extends React.ComponentProps<"div"> {
answers: MainButtonProps[];
@ -22,37 +22,21 @@ function SelectAnswersList({
onAnswerClick,
...props
}: SelectAnswersListProps) {
// Инициализируем состояние только один раз из activeAnswers
const [selectedAnswers, setSelectedAnswers] = useState<
MainButtonProps[] | null
>(activeAnswers);
const isInitialMount = useRef(true);
useEffect(() => {
setSelectedAnswers(activeAnswers ?? null);
}, [activeAnswers]);
>(() => activeAnswers ?? null);
const handleAnswerClick = (answer: MainButtonProps) => {
if (selectedAnswers?.some((a) => a.id === answer.id)) {
setSelectedAnswers(
(prev) => prev?.filter((a) => a.id !== answer.id) || null
);
} else {
setSelectedAnswers((prev) => [...(prev || []), answer]);
}
const newSelectedAnswers = selectedAnswers?.some((a) => a.id === answer.id)
? selectedAnswers.filter((a) => a.id !== answer.id) || null
: [...(selectedAnswers || []), answer];
setSelectedAnswers(newSelectedAnswers);
onChangeSelectedAnswers?.(newSelectedAnswers);
onAnswerClick?.(answer);
};
useEffect(() => {
// НЕ вызываем callback при первоначальной загрузке компонента
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
onChangeSelectedAnswers?.(selectedAnswers);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedAnswers]);
return (
<div className={cn("flex flex-col gap-3 w-full", className)} {...props}>
{answers.map((answer) => (

File diff suppressed because it is too large Load Diff