+
+
Header Block
+
+ updateHeaderBlock({ text: { ...(screen.headerBlock?.text ?? {}), text: e.target.value } })}
+ />
+ updateHeaderBlock({ timerSeconds: Number(e.target.value) })}
+ />
+
+
+
+
+
Reviews
+
+
onUpdate({ reviews: { title: { text: e.target.value }, items: screen.reviews?.items ?? [] } })}
+ />
+
+
+ Items
+
+
+ {(screen.reviews?.items ?? []).map((r, idx) => (
+
+
{
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, name: { text: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+ {
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, date: { text: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+ {
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, text: { text: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+ {
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, avatar: { src: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+ {
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, portrait: { src: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+ {
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, photo: { src: e.target.value } } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+
+
{
+ const current = screen.reviews?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, rating: Number(e.target.value) } : v));
+ onUpdate({ reviews: { ...screen.reviews, items } });
+ }}
+ />
+
+
+
+
+
+ ))}
+
+
+
+
+
+
Common Questions
+
+
onUpdate({ commonQuestions: { title: { text: e.target.value }, items: screen.commonQuestions?.items ?? [] } })}
+ />
+
+
+ Items
+
+
+ {(screen.commonQuestions?.items ?? []).map((q, idx) => (
+
+
{
+ const current = screen.commonQuestions?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, question: e.target.value } : v));
+ onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
+ }}
+ />
+ {
+ const current = screen.commonQuestions?.items ?? [];
+ const items = current.map((v, i) => (i === idx ? { ...v, answer: e.target.value } : v));
+ onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
+ }}
+ />
+
+
+
+
+ ))}
+
+
+
+
+
+
Progress To See Soulmate
+
+ onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, title: { text: e.target.value } } })}
+ />
+ onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, progress: { value: Number(e.target.value) } } })}
+ />
+ onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, leftText: { text: e.target.value } } })}
+ />
+ onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, rightText: { text: e.target.value } } })}
+ />
+
+
+
+
+
Steps To See Soulmate
+
+
+ Steps
+
+
+
+ {(screen.stepsToSeeSoulmate?.steps ?? []).map((step, idx) => (
+
+
+ {
+ const current = screen.stepsToSeeSoulmate?.steps ?? [];
+ const steps = current.map((s, i) => (i === idx ? { ...s, title: { text: e.target.value } } : s));
+ onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
+ }}
+ />
+ {
+ const icon = e.target.value as "questions" | "profile" | "sketch" | "astro" | "chat";
+ const current = screen.stepsToSeeSoulmate?.steps ?? [];
+ const steps = current.map((s, i) => (i === idx ? { ...s, icon } : s));
+ onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
+ }}
+ />
+
+
{
+ const current = screen.stepsToSeeSoulmate?.steps ?? [];
+ const steps = current.map((s, i) => (i === idx ? { ...s, description: { text: e.target.value } } : s));
+ onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
+ }}
+ />
+
+
+
+
+
+ ))}
+
+
+
+
+
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
+
+ onUpdate({ usersPortraits: { ...screen.usersPortraits, title: { text: e.target.value } } })}
+ />
+ onUpdate({ usersPortraits: { ...screen.usersPortraits, buttonText: e.target.value } })}
+ />
+
+
+
+ Images
+
+
+ {(screen.usersPortraits?.images ?? []).map((img, idx) => (
+
+
{
+ const current = screen.usersPortraits?.images ?? [];
+ const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v));
+ onUpdate({ usersPortraits: { ...screen.usersPortraits, images } });
+ }}
+ />
+
+
+
+
+ ))}
+
+
+
+
+
Joined Today With Avatars
+
+ onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, count: { text: e.target.value } } })}
+ />
+ 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
+
+
+ {(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 } } });
+ }}
+ />
+
+
+
+
+ ))}
+
+
+
+
+
+
Total Price
+
+ 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 } })}
+ />
+
+
+
+ );
+}
+
+
diff --git a/src/components/admin/builder/templates/index.ts b/src/components/admin/builder/templates/index.ts
index 3070441..f31d3b8 100644
--- a/src/components/admin/builder/templates/index.ts
+++ b/src/components/admin/builder/templates/index.ts
@@ -4,3 +4,4 @@ export { CouponScreenConfig } from "./CouponScreenConfig";
export { FormScreenConfig } from "./FormScreenConfig";
export { ListScreenConfig } from "./ListScreenConfig";
export { TemplateConfig } from "./TemplateConfig";
+export { TrialPaymentScreenConfig } from "./TrialPaymentScreenConfig";
diff --git a/src/components/funnel/templates/EmailTemplate/EmailTemplate.tsx b/src/components/funnel/templates/EmailTemplate/EmailTemplate.tsx
index 9f92dfc..416c6e6 100644
--- a/src/components/funnel/templates/EmailTemplate/EmailTemplate.tsx
+++ b/src/components/funnel/templates/EmailTemplate/EmailTemplate.tsx
@@ -47,7 +47,7 @@ export function EmailTemplate({
defaultTexts,
}: EmailTemplateProps) {
const { authorization, isLoading, error } = useAuth({
- funnelId: funnel.meta.id,
+ funnelId: funnel?.meta?.id ?? "preview",
});
const [isTouched, setIsTouched] = useState(false);
diff --git a/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.stories.tsx b/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.stories.tsx
new file mode 100644
index 0000000..c7a04d7
--- /dev/null
+++ b/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.stories.tsx
@@ -0,0 +1,266 @@
+import { Meta, StoryObj } from "@storybook/nextjs-vite";
+import { TrialPaymentTemplate } from "./TrialPaymentTemplate";
+import { fn } from "storybook/test";
+import type { TrialPaymentScreenDefinition } from "@/lib/funnel/types";
+
+const defaultScreen: TrialPaymentScreenDefinition = {
+ id: "trial-payment-screen-story",
+ template: "trialPayment",
+ title: { text: "" },
+ subtitle: { text: "" },
+ bottomActionButton: { show: false, showPrivacyTermsConsent: false },
+ headerBlock: {
+ timerSeconds: 600,
+ text: { text: "⚠️ Your sketch expires soon!" },
+ timer: { text: "" },
+ },
+ unlockYourSketch: {
+ title: { text: "Unlock Your Sketch" },
+ subtitle: { text: "Just One Click to Reveal Your Match!" },
+ image: { src: "/trial-payment/portrait-female.jpg" },
+ blur: { text: { text: "Unlock to reveal your personalized portrait" }, icon: "lock" },
+ buttonText: "Get Me Soulmate Sketch",
+ },
+ joinedToday: {
+ count: { text: "954" },
+ text: { text: "Joined today" },
+ },
+ trustedByOver: {
+ text: { text: "Trusted by over 355,000 people." },
+ },
+ findingOneGuide: {
+ header: {
+ emoji: { text: "❤️" },
+ title: { text: "Finding the One Guide" },
+ },
+ text: {
+ text:
+ "You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're. You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're",
+ },
+ blur: { text: { text: "Чтобы открыть весь отчёт, нужен полный доступ." }, icon: "lock" },
+ },
+ tryForDays: {
+ title: { text: "Попробуйте в течение 7 дней!" },
+ textList: {
+ items: [
+ { text: "Receive a hand-drawn sketch of your soulmate, crafted by a trained AI-model." },
+ { text: "Reveal the path to your soulmate with the Finding the One guide." },
+ { text: "Talk to live experts and get guidance on finding your soulmate." },
+ { text: "Start your 7-day trial for just $1.00 — then only $14.50/week for full access." },
+ { text: "Cancel anytime—just 24 hours before renewal." },
+ ],
+ },
+ },
+ totalPrice: {
+ couponContainer: {
+ title: { text: "Coupon\nCode" },
+ buttonText: "SOULMATE94",
+ },
+ priceContainer: {
+ title: { text: "Total" },
+ price: { text: "$1.00" },
+ oldPrice: { text: "$14.99" },
+ discount: { text: "94% discount applied" },
+ },
+ },
+ paymentButtons: {
+ buttons: [
+ { text: "Pay", icon: "pay" },
+ { text: "Pay", icon: "google" },
+ { text: "Credit or debit card", icon: "card", primary: true },
+ ],
+ },
+ moneyBackGuarantee: {
+ title: { text: "30-DAY MONEY-BACK GUARANTEE" },
+ text: { text: "If you don't receive your soulmate sketch, we'll refund your money!" },
+ },
+ policy: {
+ text: { text: "By clicking Continue, you agree to our Terms of Use & Service and Privacy Policy. You also acknowledge that your 1 week introductory plan to Respontika, billed at $1.00, will automatically renew at $14.50 every 1 week unless canceled before the end of the trial period." },
+ },
+ usersPortraits: {
+ title: { text: "Our Users' Soulmate Portraits" },
+ images: [
+ { src: "/trial-payment/users-portraits/1.jpg" },
+ { src: "/trial-payment/users-portraits/2.jpg" },
+ { src: "/trial-payment/users-portraits/3.jpg" },
+ ],
+ buttonText: "Get me soulmate sketch",
+ },
+ joinedTodayWithAvatars: {
+ count: { text: "954" },
+ text: { text: "people joined today" },
+ avatars: {
+ images: [
+ { src: "/trial-payment/avatars/1.jpg" },
+ { src: "/trial-payment/avatars/2.jpg" },
+ { src: "/trial-payment/avatars/3.jpg" },
+ { src: "/trial-payment/avatars/4.jpg" },
+ { src: "/trial-payment/avatars/5.jpg" },
+ ],
+ },
+ },
+ progressToSeeSoulmate: {
+ title: { text: "See Your Soulmate – Just One Step Away" },
+ progress: { value: 92 },
+ leftText: { text: "Step 2 of 5" },
+ rightText: { text: "99% Complete" },
+ },
+ stepsToSeeSoulmate: {
+ steps: [
+ {
+ title: { text: "Questions Answered" },
+ description: { text: "You've provided all the necessary information about your preferences and personality." },
+ icon: "questions",
+ isActive: true,
+ },
+ {
+ title: { text: "Profile Analysis" },
+ description: { text: "Our advanced system is creating your perfect soulmate profile." },
+ icon: "profile",
+ isActive: true,
+ },
+ {
+ title: { text: "Sketch Creation" },
+ description: { text: "Your personalized soulmate sketch will be created." },
+ icon: "sketch",
+ isActive: false,
+ },
+ {
+ title: { text: "Астрологические Идеи" },
+ description: { text: "Уникальные астрологические рекомендации, усиливающие совместимость." },
+ icon: "astro",
+ isActive: false,
+ },
+ {
+ title: { text: "Персонализированный чат с экспертом" },
+ description: { text: "Персональные советы от экспертов по отношениям." },
+ icon: "chat",
+ isActive: false,
+ },
+ ],
+ buttonText: "Show Me My Soulmate",
+ },
+ reviews: {
+ title: { text: "Loved and Trusted Worldwide" },
+ items: [
+ {
+ name: { text: "Jennifer Wilson 🇺🇸" },
+ text: { text: "**“Я увидела свои ошибки… и нашла мужа”**\nПортрет сразу зацепил — было чувство, что я уже где-то его видела. Но настоящий перелом произошёл после гайда: я поняла, почему снова и снова выбирала «не тех». И самое удивительное — вскоре я познакомилась с мужчиной, который оказался точной копией того самого портрета. Сейчас он мой муж, и когда мы сравнили рисунок с его фото, сходство было просто вау." },
+ avatar: { src: "/trial-payment/reviews/avatars/1.jpg" },
+ portrait: { src: "/trial-payment/reviews/portraits/1.jpg" },
+ photo: { src: "/trial-payment/reviews/photos/1.jpg" },
+ rating: 5,
+ date: { text: "1 day ago" },
+ },
+ {
+ name: { text: "Amanda Davis 🇨🇦" },
+ text: { text: "**“Я поняла своего партнёра лучше за один вечер, чем за несколько лет”**\nПрошла тест ради интереса — портрет нас удивил. Но настоящий прорыв случился, когда я прочитала гайд о второй половинке. Там были точные подсказки о том, как мы можем поддерживать друг друга. Цена смешная, а ценность огромная: теперь у нас меньше недопониманий и больше тепла." },
+ avatar: { src: "/trial-payment/reviews/avatars/2.jpg" },
+ portrait: { src: "/trial-payment/reviews/portraits/2.jpg" },
+ photo: { src: "/trial-payment/reviews/photos/2.jpg" },
+ rating: 5,
+ date: { text: "4 days ago" },
+ },
+ {
+ name: { text: "Michael Johnson 🇬🇧" },
+ text: { text: "**“Увидел её лицо — и мурашки по коже”**\nКогда пришёл результат теста и показали портрет, я реально замер. Это была та самая девушка, с которой я начал встречаться пару недель назад. И гайд прямо описал, почему мы тянемся друг к другу. Честно, я не ожидал такого совпадения." },
+ avatar: { src: "/trial-payment/reviews/avatars/3.jpg" },
+ portrait: { src: "/trial-payment/reviews/portraits/3.jpg" },
+ photo: { src: "/trial-payment/reviews/photos/3.jpg" },
+ rating: 5,
+ date: { text: "1 week ago" },
+ },
+ ],
+ },
+ commonQuestions: {
+ title: { text: "Common Questions" },
+ items: [
+ {
+ question: "When will I receive my sketch?",
+ answer:
+ "Your personalized soulmate sketch will be delivered within 24-48 hours after completing your order. You'll receive an email notification when it's ready for viewing in your account.",
+ },
+ {
+ question: "How do I cancel my subscription?",
+ answer:
+ "You can cancel anytime from your account settings. Make sure to cancel at least 24 hours before the renewal date to avoid being charged.",
+ },
+ {
+ question: "How accurate are the readings?",
+ answer:
+ "Our readings are based on a combination of your answers and advanced pattern analysis. While they provide valuable insights, they are intended for guidance and entertainment purposes.",
+ },
+ {
+ question: "Is my data secure and private?",
+ answer:
+ "Yes. We follow strict data protection standards. Your data is encrypted and never shared with third parties without your consent.",
+ },
+ ],
+ },
+ stillHaveQuestions: {
+ title: { text: "Still have questions? We're here to help!" },
+ actionButtonText: "Get me Soulmate Sketch",
+ contactButtonText: "Contact Support",
+ },
+ footer: {
+ title: { text: "WIT LAB ©" },
+ contacts: {
+ title: { text: "CONTACTS" },
+ email: { href: "support@witlab.com", text: "support@witlab.com" },
+ address: { text: "Wit Lab 2108 N ST STE N SACRAMENTO, CA95816, US" },
+ },
+ legal: {
+ title: { text: "LEGAL" },
+ links: [
+ { href: "https://witlab.com/terms", text: "Terms of Service" },
+ { href: "https://witlab.com/privacy", text: "Privacy Policy" },
+ { href: "https://witlab.com/refund", text: "Refund Policy" },
+ ],
+ copyright: {
+ text:
+ "Copyright © 2025 Wit Lab™. All rights reserved. All trademarks referenced herein are the properties of their respective owners.",
+ },
+ },
+ paymentMethods: {
+ title: { text: "PAYMENT METHODS" },
+ methods: [
+ { src: "/trial-payment/payment-methods/visa.svg", alt: "visa" },
+ { src: "/trial-payment/payment-methods/mastercard.svg", alt: "mastercard" },
+ { src: "/trial-payment/payment-methods/discover.svg", alt: "discover" },
+ { src: "/trial-payment/payment-methods/apple.svg", alt: "apple" },
+ { src: "/trial-payment/payment-methods/google.svg", alt: "google" },
+ { src: "/trial-payment/payment-methods/paypal.svg", alt: "paypal" },
+ ],
+ },
+ },
+};
+
+const meta: Meta