diff --git a/public/funnels/soulmate.json b/public/funnels/soulmate.json index 9ebc35b..cfe4eef 100644 --- a/public/funnels/soulmate.json +++ b/public/funnels/soulmate.json @@ -2325,10 +2325,28 @@ "navigation": { "rules": [], "defaultNextScreenId": "specialoffer", - "isEndScreen": true, - "onBackScreenId": "specialoffer" + "isEndScreen": false }, - "variants": [], + "variants": [ + { + "conditions": [ + { + "screenId": "partner-gender", + "operator": "includesAny", + "optionIds": [ + "male" + ] + } + ], + "overrides": { + "unlockYourSketch": { + "image": { + "src": "/trial-payment/portrait-male.jpg" + } + } + } + } + ], "headerBlock": { "text": { "text": "⚠️ Your sketch expires soon!" @@ -2776,10 +2794,6 @@ "cornerRadius": "3xl", "showPrivacyTermsConsent": false }, - "navigation": { - "rules": [], - "isEndScreen": true - }, "variants": [], "text": { "title": { diff --git a/public/trial-payment/portrait-male.jpg b/public/trial-payment/portrait-male.jpg new file mode 100644 index 0000000..ae26cb6 Binary files /dev/null and b/public/trial-payment/portrait-male.jpg differ diff --git a/src/components/admin/builder/templates/TrialPaymentScreenConfig.tsx b/src/components/admin/builder/templates/TrialPaymentScreenConfig.tsx index 0d3309f..c4b4d9f 100644 --- a/src/components/admin/builder/templates/TrialPaymentScreenConfig.tsx +++ b/src/components/admin/builder/templates/TrialPaymentScreenConfig.tsx @@ -1,18 +1,64 @@ "use client"; -import React from "react"; +import React, { useState } from "react"; import type { BuilderScreen } from "@/lib/admin/builder/types"; import type { TrialPaymentScreenDefinition } from "@/lib/funnel/types"; import { TextInput } from "@/components/ui/TextInput/TextInput"; import { TextAreaInput } from "@/components/ui/TextAreaInput/TextAreaInput"; import { Button } from "@/components/ui/button"; -import { Trash } from "lucide-react"; +import { Trash, ChevronDown, ChevronRight } from "lucide-react"; interface TrialPaymentScreenConfigProps { screen: BuilderScreen & { template: "trialPayment" }; onUpdate: (updates: Partial) => void; } +function CollapsibleSection({ + title, + children, + defaultExpanded = false, +}: { + title: string; + children: React.ReactNode; + defaultExpanded?: boolean; +}) { + const storageKey = `trial-payment-section-${title.toLowerCase().replace(/\s+/g, '-')}`; + + const [isExpanded, setIsExpanded] = useState(() => { + if (typeof window === 'undefined') return defaultExpanded; + + const stored = sessionStorage.getItem(storageKey); + return stored !== null ? JSON.parse(stored) : defaultExpanded; + }); + + const handleToggle = () => { + const newExpanded = !isExpanded; + setIsExpanded(newExpanded); + + if (typeof window !== 'undefined') { + sessionStorage.setItem(storageKey, JSON.stringify(newExpanded)); + } + }; + + return ( +
+ + {isExpanded &&
{children}
} +
+ ); +} + export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScreenConfigProps) { const updateHeaderBlock = (updates: Partial>) => { onUpdate({ headerBlock: { ...screen.headerBlock, ...updates } }); @@ -55,9 +101,9 @@ export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScree }; return ( -
-
-

Header Block

+
+ {/* Header Block */} +
updateHeaderBlock({ timerSeconds: Number(e.target.value) })} />
-
+ -
-

Reviews

+ {/* Unlock Your Sketch */} + +
+ updateUnlock({ title: { text: e.target.value } })} /> + updateUnlock({ subtitle: { text: e.target.value } })} /> + updateUnlock({ image: { src: e.target.value } })} /> + updateUnlock({ blur: { ...(screen.unlockYourSketch?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } as NonNullable["blur"] })} /> + updateUnlock({ buttonText: e.target.value })} /> +
+
+ + {/* Social Proof */} + +
+ onUpdate({ joinedToday: { ...screen.joinedToday, count: { text: e.target.value } } })} + /> + onUpdate({ joinedToday: { ...screen.joinedToday, text: { text: e.target.value } } })} + /> + onUpdate({ trustedByOver: { text: { text: e.target.value } } })} + /> +
+
+ + {/* Finding The One Guide */} + +
+ onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), emoji: { text: e.target.value } } } })} + /> + onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), title: { text: e.target.value } } } })} + /> + onUpdate({ findingOneGuide: { ...screen.findingOneGuide, text: { text: e.target.value } } })} + /> + onUpdate({ findingOneGuide: { ...screen.findingOneGuide, blur: { ...(screen.findingOneGuide?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } } })} + /> +
+
+ + {/* Try For Days */} + +
+ onUpdate({ tryForDays: { ...screen.tryForDays, title: { text: e.target.value } } })} + /> +
+
+ Items + +
+ {(screen.tryForDays?.textList?.items ?? []).map((it, idx) => ( +
+ { + const current = screen.tryForDays?.textList?.items ?? []; + const items = current.map((v, i) => (i === idx ? { ...v, text: e.target.value } : v)); + onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } }); + }} + /> +
+ +
+
+ ))} +
+
+
+ + {/* Pricing & Payment */} + +
+ onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), title: { text: e.target.value } }, priceContainer: screen.totalPrice?.priceContainer } })} + /> + onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), buttonText: e.target.value }, priceContainer: screen.totalPrice?.priceContainer } })} + /> + onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), title: { text: e.target.value } } } })} + /> + onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), price: { text: e.target.value } } } })} + /> + onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), oldPrice: { text: e.target.value } } } })} + /> + onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), discount: { text: e.target.value } } } })} + /> + +
+ Payment Buttons + {(screen.paymentButtons?.buttons ?? []).map((b, i) => ( +
+ updatePaymentButtons(i, "text", e.target.value)} /> + updatePaymentButtons(i, "icon", e.target.value)} /> + +
+ ))} +
+
+
+ + {/* Trust & Guarantees */} + +
+ onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, title: { text: e.target.value } } })} + /> + onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, text: { text: e.target.value } } })} + /> + onUpdate({ policy: { text: { text: e.target.value } } })} + /> +
+
+ + {/* Reviews */} +
{(screen.reviews?.items ?? []).map((r, idx) => ( -
+
-
+
-
-

Common Questions

+ {/* Common Questions */} +
{(screen.commonQuestions?.items ?? []).map((q, idx) => ( -
+
-
+
-
-

Progress To See Soulmate

+ {/* Progress & Steps */} +
onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, rightText: { text: e.target.value } } })} />
-
+ -
-

Steps To See Soulmate

+ {/* Steps To See Soulmate */} +
Steps @@ -303,7 +534,7 @@ export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScree
{(screen.stepsToSeeSoulmate?.steps ?? []).map((step, idx) => ( -
+
))}
-
+ -
-

Money Back Guarantee

-
- onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, title: { text: e.target.value } } })} - /> - onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, text: { text: e.target.value } } })} - /> -
-
- -
-

Policy

-
- onUpdate({ policy: { text: { text: e.target.value } } })} - /> -
-
- -
-

Users' Portraits

+ {/* Users' Portraits */} +
onUpdate({ usersPortraits: { ...screen.usersPortraits, buttonText: e.target.value } })} /> -
-
-
- Images - -
- {(screen.usersPortraits?.images ?? []).map((img, idx) => ( -
- { +
+
+ Images + +
+ {(screen.usersPortraits?.images ?? []).map((img, idx) => ( +
+ { const current = screen.usersPortraits?.images ?? []; - const images = current.filter((_, i) => i !== idx); + const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v)); onUpdate({ usersPortraits: { ...screen.usersPortraits, images } }); }} - aria-label="Удалить изображение" - > - - + /> +
+ +
-
- ))} + ))} +
-
+ -
-

Joined Today With Avatars

+ {/* Joined Today With Avatars */} +
onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, text: { text: e.target.value } } })} /> -
-
-
- Avatars - -
- {(screen.joinedTodayWithAvatars?.avatars?.images ?? []).map((img, idx) => ( -
- { - const current = screen.joinedTodayWithAvatars?.avatars?.images ?? []; - const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v)); - onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } }); - }} - /> -
- -
-
- ))} -
-
- -
-

Try For Days

-
- onUpdate({ tryForDays: { ...screen.tryForDays, title: { text: e.target.value } } })} - />
- Items + Avatars
- {(screen.tryForDays?.textList?.items ?? []).map((it, idx) => ( -
- ( +
+ { - const current = screen.tryForDays?.textList?.items ?? []; - const items = current.map((v, i) => (i === idx ? { ...v, text: e.target.value } : v)); - onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } }); + const current = screen.joinedTodayWithAvatars?.avatars?.images ?? []; + const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v)); + onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } }); }} />
@@ -556,11 +703,11 @@ export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScree variant="ghost" className="h-8 w-8 p-0 text-red-500 hover:text-red-600" onClick={() => { - const current = screen.tryForDays?.textList?.items ?? []; - const items = current.filter((_, i) => i !== idx); - onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } }); + const current = screen.joinedTodayWithAvatars?.avatars?.images ?? []; + const images = current.filter((_, i) => i !== idx); + onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } }); }} - aria-label="Удалить элемент" + aria-label="Удалить аватар" > @@ -569,149 +716,45 @@ export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScree ))}
-
+ -
-

Total Price

+ {/* FAQ & Support */} +
onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), title: { text: e.target.value } }, priceContainer: screen.totalPrice?.priceContainer } })} - /> - onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), buttonText: e.target.value }, priceContainer: screen.totalPrice?.priceContainer } })} - /> - onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), title: { text: e.target.value } } } })} - /> - onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), price: { text: e.target.value } } } })} - /> - onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), oldPrice: { text: e.target.value } } } })} - /> - onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), discount: { text: e.target.value } } } })} - /> -
-
- -
-

Joined Today

-
- onUpdate({ joinedToday: { ...screen.joinedToday, count: { text: e.target.value } } })} - /> - onUpdate({ joinedToday: { ...screen.joinedToday, text: { text: e.target.value } } })} - /> -
-
- -
-

Trusted By Over

-
- onUpdate({ trustedByOver: { text: { text: e.target.value } } })} - /> -
-
- -
-

Finding The One Guide

-
- onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), emoji: { text: e.target.value } } } })} - /> - onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), title: { text: e.target.value } } } })} - /> - onUpdate({ findingOneGuide: { ...screen.findingOneGuide, text: { text: e.target.value } } })} - /> - onUpdate({ findingOneGuide: { ...screen.findingOneGuide, blur: { ...(screen.findingOneGuide?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } } })} - /> -
-
- -
-

Unlock Your Sketch

-
- updateUnlock({ title: { text: e.target.value } })} /> - updateUnlock({ subtitle: { text: e.target.value } })} /> - updateUnlock({ image: { src: e.target.value } })} /> - updateUnlock({ blur: { ...(screen.unlockYourSketch?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } as NonNullable["blur"] })} /> - updateUnlock({ buttonText: e.target.value })} /> -
-
- -
-

Payment Buttons

- {(screen.paymentButtons?.buttons ?? []).map((b, i) => ( -
- updatePaymentButtons(i, "text", e.target.value)} /> - updatePaymentButtons(i, "icon", e.target.value)} /> - -
- ))} -
- -
-

Footer / Contacts

-
- updateFooterContacts("email", { href: e.target.value, text: e.target.value })} /> - updateFooterContacts("address", { text: e.target.value })} /> -
-
- -
-

Still Have Questions

-
- onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, title: { text: e.target.value } } })} /> onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, actionButtonText: e.target.value } })} /> onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, contactButtonText: e.target.value } })} />
-
+ + + {/* Footer */} + +
+ updateFooterContacts("email", { href: e.target.value, text: e.target.value })} + /> + updateFooterContacts("address", { text: e.target.value })} + /> +
+
); } - - diff --git a/src/lib/funnel/bakedFunnels.ts b/src/lib/funnel/bakedFunnels.ts index 2bcbf06..85c685c 100644 --- a/src/lib/funnel/bakedFunnels.ts +++ b/src/lib/funnel/bakedFunnels.ts @@ -2333,10 +2333,28 @@ export const BAKED_FUNNELS: Record = { "navigation": { "rules": [], "defaultNextScreenId": "specialoffer", - "isEndScreen": true, - "onBackScreenId": "specialoffer" + "isEndScreen": false }, - "variants": [], + "variants": [ + { + "conditions": [ + { + "screenId": "partner-gender", + "operator": "includesAny", + "optionIds": [ + "male" + ] + } + ], + "overrides": { + "unlockYourSketch": { + "image": { + "src": "/trial-payment/portrait-male.jpg" + } + } + } + } + ], "headerBlock": { "text": { "text": "⚠️ Your sketch expires soon!" @@ -2784,10 +2802,6 @@ export const BAKED_FUNNELS: Record = { "cornerRadius": "3xl", "showPrivacyTermsConsent": false }, - "navigation": { - "rules": [], - "isEndScreen": true - }, "variants": [], "text": { "title": {