commit
fc003e6ada
@ -142,7 +142,9 @@ export function FunnelRuntime({ funnel, initialScreenId }: FunnelRuntimeProps) {
|
||||
currentScreen.dateInput?.registrationFieldKey;
|
||||
|
||||
updateSession({
|
||||
...(shouldSkipAnswers ? {} : {
|
||||
...(shouldSkipAnswers
|
||||
? {}
|
||||
: {
|
||||
answers: {
|
||||
[currentScreen.id]: answers[currentScreen.id],
|
||||
},
|
||||
@ -159,9 +161,10 @@ export function FunnelRuntime({ funnel, initialScreenId }: FunnelRuntimeProps) {
|
||||
goToScreen(nextScreenId);
|
||||
};
|
||||
|
||||
const handleSelectionChange = (ids: string[]) => {
|
||||
const handleSelectionChange = (ids: string[], skipCheckChanges = false) => {
|
||||
const prevSelectedIds = selectedOptionIds;
|
||||
const hasChanged =
|
||||
skipCheckChanges ||
|
||||
prevSelectedIds.length !== ids.length ||
|
||||
prevSelectedIds.some((value, index) => value !== ids[index]);
|
||||
|
||||
|
||||
@ -12,10 +12,13 @@ import type { ListScreenDefinition } from "@/lib/funnel/types";
|
||||
import { TemplateLayout } from "../layouts/TemplateLayout";
|
||||
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
|
||||
|
||||
interface ListTemplateProps {
|
||||
export interface ListTemplateProps {
|
||||
screen: ListScreenDefinition;
|
||||
selectedOptionIds: string[];
|
||||
onSelectionChange: (selectedIds: string[]) => void;
|
||||
onSelectionChange: (
|
||||
selectedIds: string[],
|
||||
skipCheckChanges?: boolean
|
||||
) => void;
|
||||
actionButtonProps?: ActionButtonProps;
|
||||
canGoBack: boolean;
|
||||
onBack: () => void;
|
||||
@ -39,7 +42,8 @@ export function ListTemplate({
|
||||
screenProgress,
|
||||
}: ListTemplateProps) {
|
||||
const buttons = useMemo(
|
||||
() => mapListOptionsToButtons(screen.list.options, screen.list.selectionType),
|
||||
() =>
|
||||
mapListOptionsToButtons(screen.list.options, screen.list.selectionType),
|
||||
[screen.list.options, screen.list.selectionType]
|
||||
);
|
||||
|
||||
@ -70,9 +74,15 @@ export function ListTemplate({
|
||||
onSelectionChange(id ? [id] : []);
|
||||
};
|
||||
|
||||
const handleSelectChange: SelectAnswersListProps["onChangeSelectedAnswers"] = (
|
||||
answers
|
||||
const handleRadioAnswerClick: RadioAnswersListProps["onAnswerClick"] = (
|
||||
answer
|
||||
) => {
|
||||
const id = stringId(answer?.id);
|
||||
onSelectionChange(id ? [id] : [], true);
|
||||
};
|
||||
|
||||
const handleSelectChange: SelectAnswersListProps["onChangeSelectedAnswers"] =
|
||||
(answers) => {
|
||||
const ids = answers
|
||||
?.map((answer) => stringId(answer.id))
|
||||
.filter((value): value is string => Boolean(value));
|
||||
@ -84,6 +94,7 @@ export function ListTemplate({
|
||||
answers: buttons,
|
||||
activeAnswer,
|
||||
onChangeSelectedAnswer: handleRadioChange,
|
||||
onAnswerClick: handleRadioAnswerClick,
|
||||
};
|
||||
|
||||
const selectContent: SelectAnswersListProps = {
|
||||
@ -92,16 +103,20 @@ export function ListTemplate({
|
||||
onChangeSelectedAnswers: handleSelectChange,
|
||||
};
|
||||
|
||||
const actionButtonOptions = actionButtonProps ? {
|
||||
defaultText: actionButtonProps.children as string || "Next",
|
||||
const actionButtonOptions = actionButtonProps
|
||||
? {
|
||||
defaultText: (actionButtonProps.children as string) || "Next",
|
||||
// Кнопка неактивна если: 1) disabled из props ИЛИ 2) ничего не выбрано
|
||||
disabled: actionButtonProps.disabled || selectedOptionIds.length === 0,
|
||||
onClick: () => {
|
||||
if (actionButtonProps.onClick) {
|
||||
actionButtonProps.onClick({} as React.MouseEvent<HTMLButtonElement>);
|
||||
actionButtonProps.onClick(
|
||||
{} as React.MouseEvent<HTMLButtonElement>
|
||||
);
|
||||
}
|
||||
},
|
||||
} : undefined;
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const layoutProps = createTemplateLayoutProps(
|
||||
screen,
|
||||
|
||||
@ -8,7 +8,7 @@ import type {
|
||||
import { TemplateLayout } from "../layouts/TemplateLayout";
|
||||
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useRef } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import {
|
||||
Header,
|
||||
JoinedToday,
|
||||
@ -109,12 +109,16 @@ export function TrialPaymentTemplate({
|
||||
const currency = placement?.currency || Currency.USD;
|
||||
const paymentUrl = placement?.paymentUrl || "";
|
||||
|
||||
const handlePayClick = () => {
|
||||
const [loadingButtonIndex, setLoadingButtonIndex] = useState<number>();
|
||||
|
||||
const handlePayClick = (buttonIndex: number) => {
|
||||
if (!!loadingButtonIndex || loadingButtonIndex === 0) {
|
||||
return;
|
||||
}
|
||||
setLoadingButtonIndex(buttonIndex);
|
||||
const redirectUrl = `${paymentUrl}?paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
|
||||
(trialPrice || 100) / 100
|
||||
).toFixed(2)}¤cy=${currency}&${getTrackingCookiesForRedirect()}`;
|
||||
console.log("redirectUrl", redirectUrl);
|
||||
|
||||
return window.location.replace(redirectUrl);
|
||||
};
|
||||
|
||||
@ -574,7 +578,7 @@ export function TrialPaymentTemplate({
|
||||
<div ref={paymentSectionRef} className="w-full">
|
||||
<PaymentButtons
|
||||
className="mt-[46px]"
|
||||
buttons={screen.paymentButtons.buttons.map((b) => {
|
||||
buttons={screen.paymentButtons.buttons.map((b, index) => {
|
||||
const icon =
|
||||
b.icon === "pay" ? (
|
||||
<svg
|
||||
@ -650,10 +654,15 @@ export function TrialPaymentTemplate({
|
||||
const className = b.primary ? "bg-primary" : undefined;
|
||||
|
||||
return {
|
||||
children: b.text,
|
||||
icon,
|
||||
children:
|
||||
index === loadingButtonIndex ? (
|
||||
<Spinner className="size-6" />
|
||||
) : (
|
||||
b.text
|
||||
),
|
||||
icon: loadingButtonIndex === index ? undefined : icon,
|
||||
className,
|
||||
onClick: handlePayClick,
|
||||
onClick: () => handlePayClick(index),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
|
||||
@ -89,7 +89,8 @@ export function FunnelProvider({ children }: FunnelProviderProps) {
|
||||
}
|
||||
}, [state, isHydrated]);
|
||||
|
||||
const registerScreenVisit = useCallback((funnelId: string, screenId: string) => {
|
||||
const registerScreenVisit = useCallback(
|
||||
(funnelId: string, screenId: string) => {
|
||||
setState((prev) => {
|
||||
// Пытаемся загрузить сохраненное состояние, если его еще нет в памяти
|
||||
let previousState = prev[funnelId];
|
||||
@ -123,7 +124,9 @@ export function FunnelProvider({ children }: FunnelProviderProps) {
|
||||
},
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const updateScreenAnswers = useCallback(
|
||||
(funnelId: string, screenId: string, answers: string[]) => {
|
||||
@ -205,7 +208,9 @@ export function FunnelProvider({ children }: FunnelProviderProps) {
|
||||
[state, registerScreenVisit, updateScreenAnswers, resetFunnel]
|
||||
);
|
||||
|
||||
return <FunnelContext.Provider value={value}>{children}</FunnelContext.Provider>;
|
||||
return (
|
||||
<FunnelContext.Provider value={value}>{children}</FunnelContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function useFunnelContext() {
|
||||
@ -220,7 +225,19 @@ export function useFunnelRuntime(funnelId: string) {
|
||||
const { state, registerScreenVisit, updateScreenAnswers, resetFunnel } =
|
||||
useFunnelContext();
|
||||
|
||||
const runtime = state[funnelId] ?? DEFAULT_RUNTIME_STATE;
|
||||
const [runtime, setRuntime] = useState<FunnelRuntimeState>(
|
||||
DEFAULT_RUNTIME_STATE
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const inMemory = state[funnelId];
|
||||
if (inMemory) {
|
||||
setRuntime(inMemory);
|
||||
return;
|
||||
}
|
||||
const loaded = loadFunnelState(funnelId);
|
||||
setRuntime(loaded ?? DEFAULT_RUNTIME_STATE);
|
||||
}, [state, funnelId]);
|
||||
|
||||
const setAnswers = useCallback(
|
||||
(screenId: string, answers: string[]) => {
|
||||
|
||||
@ -33,7 +33,7 @@ export interface ScreenRenderProps {
|
||||
funnel: FunnelDefinition;
|
||||
screen: ScreenDefinition;
|
||||
selectedOptionIds: string[];
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
onSelectionChange: (ids: string[], skipCheckChanges?: boolean) => void;
|
||||
onContinue: () => void;
|
||||
canGoBack: boolean;
|
||||
onBack: () => void;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user