From e8519615082369f3f90ecb7ef51ec7401dbd396e Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Thu, 9 Oct 2025 20:29:40 +0200 Subject: [PATCH 1/3] fix date select and link redirect --- .../domains/TrialPayment/Legal/Legal.tsx | 2 +- .../templates/DateTemplate/DateTemplate.tsx | 33 ++++++++++++------- .../TrialPaymentTemplate.tsx | 4 --- .../QuestionDateAnswers.stories.tsx | 6 ---- .../widgets/DateInput/DateInput.stories.tsx | 8 +---- .../widgets/DateInput/DateInput.tsx | 3 -- .../PrivacyTermsConsent.tsx | 4 +-- src/hooks/useDateInput.ts | 22 ++++++------- 8 files changed, 36 insertions(+), 46 deletions(-) diff --git a/src/components/domains/TrialPayment/Legal/Legal.tsx b/src/components/domains/TrialPayment/Legal/Legal.tsx index d21ba58..3247bca 100644 --- a/src/components/domains/TrialPayment/Legal/Legal.tsx +++ b/src/components/domains/TrialPayment/Legal/Legal.tsx @@ -45,7 +45,7 @@ export default function Legal({ link.className )} > - {link.children} + {link.children} ))} diff --git a/src/components/funnel/templates/DateTemplate/DateTemplate.tsx b/src/components/funnel/templates/DateTemplate/DateTemplate.tsx index a567150..5356e3a 100644 --- a/src/components/funnel/templates/DateTemplate/DateTemplate.tsx +++ b/src/components/funnel/templates/DateTemplate/DateTemplate.tsx @@ -9,18 +9,19 @@ import { TemplateLayout } from "../layouts/TemplateLayout"; import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers"; // Утилита для форматирования даты на основе паттерна -function formatDateByPattern(date: Date, pattern: string): string { +// Принимает год, месяц, день как числа (без зависимости от часовых зон) +function formatDateByPattern(year: number, month: number, day: number, pattern: string): string { const monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; return pattern - .replace("MMMM", monthNames[date.getMonth()]) - .replace("MMM", monthNames[date.getMonth()].substring(0, 3)) - .replace("yyyy", date.getFullYear().toString()) - .replace("dd", date.getDate().toString().padStart(2, '0')) - .replace("d", date.getDate().toString()); + .replace("MMMM", monthNames[month - 1]) + .replace("MMM", monthNames[month - 1].substring(0, 3)) + .replace("yyyy", year.toString()) + .replace("dd", day.toString().padStart(2, '0')) + .replace("d", day.toString()); } interface DateTemplateProps { @@ -83,12 +84,21 @@ export function DateTemplate({ // Форматированная дата для отображения const formattedDate = useMemo(() => { - if (!isoDate) return null; + const { month, day, year } = selectedDate; + if (!month || !day || !year) return null; - const date = new Date(isoDate); - const pattern = screen.dateInput?.selectedDateFormat || "MMMM d, yyyy"; - return formatDateByPattern(date, pattern); - }, [isoDate, screen.dateInput?.selectedDateFormat]); + const monthNum = parseInt(month); + const dayNum = parseInt(day); + const yearNum = parseInt(year); + + // Валидация даты + if (monthNum >= 1 && monthNum <= 12 && dayNum >= 1 && dayNum <= 31 && yearNum > 1900) { + const pattern = screen.dateInput?.selectedDateFormat || "MMMM d, yyyy"; + return formatDateByPattern(yearNum, monthNum, dayNum, pattern); + } + + return null; + }, [selectedDate, screen.dateInput?.selectedDateFormat]); // Компонент отображения выбранной даты над кнопкой const selectedDateDisplay = formattedDate && screen.dateInput?.showSelectedDate !== false ? ( @@ -138,7 +148,6 @@ export function DateTemplate({ onChange={handleDateChange} maxYear={new Date().getFullYear() - 11} yearsRange={100} - locale="en" /> {defaultTexts?.privacyBanner && ( diff --git a/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.tsx b/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.tsx index b074cb6..f387f1c 100644 --- a/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.tsx +++ b/src/components/funnel/templates/TrialPaymentTemplate/TrialPaymentTemplate.tsx @@ -689,8 +689,6 @@ export function TrialPaymentTemplate({ By clicking Continue, you agree to our{" "} Terms of Use & Service @@ -698,8 +696,6 @@ export function TrialPaymentTemplate({ and{" "} Privacy Policy diff --git a/src/components/templates/QuestionDateAnswers/QuestionDateAnswers.stories.tsx b/src/components/templates/QuestionDateAnswers/QuestionDateAnswers.stories.tsx index ae2ac58..b6ba595 100644 --- a/src/components/templates/QuestionDateAnswers/QuestionDateAnswers.stories.tsx +++ b/src/components/templates/QuestionDateAnswers/QuestionDateAnswers.stories.tsx @@ -34,7 +34,6 @@ const meta: Meta = { onChange: fn(), maxYear: new Date().getFullYear() - 11, yearsRange: 100, - locale: "en", }, bottomActionButtonProps: { actionButtonProps: { @@ -72,7 +71,6 @@ export const WithInitialValue = { onChange: fn(), maxYear: new Date().getFullYear() - 11, yearsRange: 100, - locale: "en", }, }, } satisfies Story; @@ -84,7 +82,6 @@ export const WithError = { onChange: fn(), maxYear: new Date().getFullYear() - 11, yearsRange: 100, - locale: "en", }, }, } satisfies Story; @@ -96,7 +93,6 @@ export const WithCustomLocale = { onChange: fn(), maxYear: new Date().getFullYear() - 11, yearsRange: 100, - locale: "ru", }, }, } satisfies Story; @@ -108,7 +104,6 @@ export const WithCustomYearRange = { onChange: fn(), maxYear: 2000, yearsRange: 50, - locale: "en", }, }, } satisfies Story; @@ -120,7 +115,6 @@ export const WithoutBottomButton = { onChange: fn(), maxYear: new Date().getFullYear() - 11, yearsRange: 100, - locale: "en", }, bottomActionButtonProps: undefined, }, diff --git a/src/components/widgets/DateInput/DateInput.stories.tsx b/src/components/widgets/DateInput/DateInput.stories.tsx index bc84af3..e09aef9 100644 --- a/src/components/widgets/DateInput/DateInput.stories.tsx +++ b/src/components/widgets/DateInput/DateInput.stories.tsx @@ -27,11 +27,6 @@ const meta: Meta = { control: { type: "number" }, description: "Диапазон лет для выбора", }, - locale: { - control: { type: "select" }, - options: ["en", "ru", "de", "fr"], - description: "Локаль для отображения месяцев", - }, error: { control: { type: "text" }, description: "Сообщение об ошибке", @@ -74,10 +69,9 @@ export const WithError = { }, } satisfies Story; -export const RussianLocale = { +export const WithEmptyValue = { args: { value: null, - locale: "ru", }, } satisfies Story; diff --git a/src/components/widgets/DateInput/DateInput.tsx b/src/components/widgets/DateInput/DateInput.tsx index 4ee6fee..16366d9 100644 --- a/src/components/widgets/DateInput/DateInput.tsx +++ b/src/components/widgets/DateInput/DateInput.tsx @@ -17,7 +17,6 @@ export interface DateInputProps { maxYear?: number; yearsRange?: number; // onBlur?: () => void; - locale?: string; daySelectProps?: LocalSelectInputProps; monthSelectProps?: LocalSelectInputProps; yearSelectProps?: LocalSelectInputProps; @@ -30,7 +29,6 @@ export default function DateInput({ maxYear = new Date().getFullYear() - 11, yearsRange = 100, // onBlur, - locale = "en", daySelectProps, monthSelectProps, yearSelectProps, @@ -51,7 +49,6 @@ export default function DateInput({ onChange, maxYear, yearsRange, - locale, }); const inputs = { diff --git a/src/components/widgets/PrivacyTermsConsent/PrivacyTermsConsent.tsx b/src/components/widgets/PrivacyTermsConsent/PrivacyTermsConsent.tsx index 7e4b9cb..b8ccc7e 100644 --- a/src/components/widgets/PrivacyTermsConsent/PrivacyTermsConsent.tsx +++ b/src/components/widgets/PrivacyTermsConsent/PrivacyTermsConsent.tsx @@ -27,13 +27,13 @@ export default function PrivacyTermsConsent({ I agree to the{" "} {privacyPolicy && ( )} {", "} {termsOfUse && ( )}{" "} and to the use of cookies and tracking technologies, that require your diff --git a/src/hooks/useDateInput.ts b/src/hooks/useDateInput.ts index 119eff2..7816955 100644 --- a/src/hooks/useDateInput.ts +++ b/src/hooks/useDateInput.ts @@ -1,8 +1,12 @@ import { SelectInputProps } from "@/components/ui/SelectInput/SelectInput"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +// Валидация даты без зависимости от часовых зон +// Проверяет что дата существует (например, 31 февраля - невалидна) const isValidDate = (year: number, month: number, day: number) => { if (!year || !month || !day) return false; + // Используем Date в локальной зоне только для валидации + // Это безопасно, т.к. мы не сохраняем результат, только проверяем корректность const date = new Date(year, month - 1, day); return ( date.getFullYear() === year && @@ -25,13 +29,11 @@ const parseDateValue = ( return { year, month, day }; }; -// Упрощенное определение порядка полей даты на основе локали. -// В реальном приложении здесь лучше использовать данные из next-intl. -const getDateInputLocaleFormat = (locale: string): ("d" | "m" | "y")[] => { - const format = new Intl.DateTimeFormat(locale).format(new Date(2001, 1, 3)); // Используем 3/Feb/2001 - if (/^3.*2/.test(format)) return ["d", "m", "y"]; // 3/2/2001 -> d/m/y - if (/^2.*3/.test(format)) return ["m", "d", "y"]; // 2/3/2001 -> m/d/y - return ["y", "m", "d"]; // 2001/2/3 -> y/m/d +// Порядок полей даты: месяц-день-год (американский формат) +// Этот формат используется для всех локалей чтобы обеспечить единообразие +const getDateInputLocaleFormat = (): ("d" | "m" | "y")[] => { + // Всегда используем американский формат: месяц, день, год + return ["m", "d", "y"]; }; interface UseDateInputProps { @@ -39,7 +41,6 @@ interface UseDateInputProps { onChange?: (value: string | null) => void; maxYear?: number; yearsRange?: number; - locale?: string; } export const useDateInput = ({ @@ -47,7 +48,6 @@ export const useDateInput = ({ onChange, maxYear = new Date().getFullYear() - 11, yearsRange = 100, - locale = "en", }: UseDateInputProps) => { const [year, setYear] = useState(""); const [month, setMonth] = useState(""); @@ -125,8 +125,8 @@ export const useDateInput = ({ }, [year, month]); const localeFormat = useMemo( - () => getDateInputLocaleFormat(locale), - [locale] + () => getDateInputLocaleFormat(), + [] ); const handleYearChange: SelectInputProps["onValueChange"] = useCallback( From e7b9f9e3a49ec858e8ab75f365b7559a369b44ef Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Thu, 9 Oct 2025 20:36:54 +0200 Subject: [PATCH 2/3] fix date select and link redirect --- public/funnels/soulmate.json | 4 ++-- src/lib/funnel/bakedFunnels.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/funnels/soulmate.json b/public/funnels/soulmate.json index 9046fcd..6b46418 100644 --- a/public/funnels/soulmate.json +++ b/public/funnels/soulmate.json @@ -2360,7 +2360,7 @@ }, "trustedByOver": { "text": { - "text": "Trusted by over **355,000 people." + "text": "Trusted by over **355,000** people." } }, "findingOneGuide": { @@ -2373,7 +2373,7 @@ } }, "text": { - "text": "You’re special — you have imagination and energy that not everyone can understand. You deserve more than to be “half-understood” — you deserve to meet someone who sees the world as deeply and vividly as you do. This guide will help you recognize the signs of destiny, trust your path, and find the one whose heart is already connected to yours." + "text": "You’re special — you have imagination and energy that not everyone can understand. You deserve more than to be “half-understood” — you deserve to meet someone who sees the world as deeply and vividly as you do. This guide will help you recognize the signs of destiny, trust your path, and find the one whose heart is already connected to yours.\nThey may already be closer than you think — drawn to your light, searching for the same rare connection. Stay open, follow your intuition, and notice the quiet moments that feel like fate. Every step you take brings you nearer to the soul that was always meant to walk beside you." }, "blur": { "text": { diff --git a/src/lib/funnel/bakedFunnels.ts b/src/lib/funnel/bakedFunnels.ts index 2b7ed25..66ed7a4 100644 --- a/src/lib/funnel/bakedFunnels.ts +++ b/src/lib/funnel/bakedFunnels.ts @@ -2368,7 +2368,7 @@ export const BAKED_FUNNELS: Record = { }, "trustedByOver": { "text": { - "text": "Trusted by over **355,000 people." + "text": "Trusted by over **355,000** people." } }, "findingOneGuide": { @@ -2381,7 +2381,7 @@ export const BAKED_FUNNELS: Record = { } }, "text": { - "text": "You’re special — you have imagination and energy that not everyone can understand. You deserve more than to be “half-understood” — you deserve to meet someone who sees the world as deeply and vividly as you do. This guide will help you recognize the signs of destiny, trust your path, and find the one whose heart is already connected to yours." + "text": "You’re special — you have imagination and energy that not everyone can understand. You deserve more than to be “half-understood” — you deserve to meet someone who sees the world as deeply and vividly as you do. This guide will help you recognize the signs of destiny, trust your path, and find the one whose heart is already connected to yours.\nThey may already be closer than you think — drawn to your light, searching for the same rare connection. Stay open, follow your intuition, and notice the quiet moments that feel like fate. Every step you take brings you nearer to the soul that was always meant to walk beside you." }, "blur": { "text": { From d8689c385db074d05d05a0b60788a58d5cbb73af Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Thu, 9 Oct 2025 20:39:35 +0200 Subject: [PATCH 3/3] fix date select and link redirect --- src/components/ui/ActionButton/ActionButton.tsx | 2 +- src/components/ui/MainButton/MainButton.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ui/ActionButton/ActionButton.tsx b/src/components/ui/ActionButton/ActionButton.tsx index 2a111bc..7f4d30c 100644 --- a/src/components/ui/ActionButton/ActionButton.tsx +++ b/src/components/ui/ActionButton/ActionButton.tsx @@ -12,7 +12,7 @@ const buttonVariants = cva( "font-inter text-xl/[24px] font-bold text-primary-foreground", "px-[27px] py-5", "transition-all", - "disabled:opacity-50", + "disabled:opacity-30", "shadow-blue-glow" ), { diff --git a/src/components/ui/MainButton/MainButton.tsx b/src/components/ui/MainButton/MainButton.tsx index a4e6308..a82d121 100644 --- a/src/components/ui/MainButton/MainButton.tsx +++ b/src/components/ui/MainButton/MainButton.tsx @@ -16,7 +16,7 @@ const buttonVariants = cva( "pl-[26px] pr-[18px] py-[18px]", "transition-[background-color,border-color,color]", "duration-200", - "disabled:opacity-50", + "disabled:opacity-30", "border-2", "[-webkit-tap-highlight-color:transparent]", "[transform:translateZ(0)]" @@ -78,7 +78,7 @@ function MainButton({