fix date select and link redirect

This commit is contained in:
dev.daminik00 2025-10-09 20:29:40 +02:00
parent fdb98c0322
commit e851961508
8 changed files with 36 additions and 46 deletions

View File

@ -45,7 +45,7 @@ export default function Legal({
link.className
)}
>
<Link href={link.href} target="_blank" rel="noopener noreferrer">{link.children}</Link>
<Link href={link.href}>{link.children}</Link>
</Button>
))}
</div>

View File

@ -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 && (

View File

@ -689,8 +689,6 @@ export function TrialPaymentTemplate({
By clicking Continue, you agree to our{" "}
<a
href="https://witlab.us/terms"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-[#4B5563]"
>
Terms of Use & Service
@ -698,8 +696,6 @@ export function TrialPaymentTemplate({
and{" "}
<a
href="https://witlab.us/privacy"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-[#4B5563]"
>
Privacy Policy

View File

@ -34,7 +34,6 @@ const meta: Meta<typeof QuestionDateAnswers> = {
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,
},

View File

@ -27,11 +27,6 @@ const meta: Meta<typeof DateInput> = {
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;

View File

@ -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 = {

View File

@ -27,13 +27,13 @@ export default function PrivacyTermsConsent({
I agree to the{" "}
{privacyPolicy && (
<Button variant="link" asChild {...privacyPolicy}>
<Link href={privacyPolicy.href} target="_blank" rel="noopener noreferrer">{privacyPolicy.children}</Link>
<Link href={privacyPolicy.href}>{privacyPolicy.children}</Link>
</Button>
)}
{", "}
{termsOfUse && (
<Button variant="link" asChild {...termsOfUse}>
<Link href={termsOfUse.href} target="_blank" rel="noopener noreferrer">{termsOfUse.children}</Link>
<Link href={termsOfUse.href}>{termsOfUse.children}</Link>
</Button>
)}{" "}
and to the use of cookies and tracking technologies, that require your

View File

@ -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(