fix date select and link redirect
This commit is contained in:
parent
fdb98c0322
commit
e851961508
@ -45,7 +45,7 @@ export default function Legal({
|
|||||||
link.className
|
link.className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Link href={link.href} target="_blank" rel="noopener noreferrer">{link.children}</Link>
|
<Link href={link.href}>{link.children}</Link>
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,18 +9,19 @@ import { TemplateLayout } from "../layouts/TemplateLayout";
|
|||||||
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
|
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 = [
|
const monthNames = [
|
||||||
"January", "February", "March", "April", "May", "June",
|
"January", "February", "March", "April", "May", "June",
|
||||||
"July", "August", "September", "October", "November", "December"
|
"July", "August", "September", "October", "November", "December"
|
||||||
];
|
];
|
||||||
|
|
||||||
return pattern
|
return pattern
|
||||||
.replace("MMMM", monthNames[date.getMonth()])
|
.replace("MMMM", monthNames[month - 1])
|
||||||
.replace("MMM", monthNames[date.getMonth()].substring(0, 3))
|
.replace("MMM", monthNames[month - 1].substring(0, 3))
|
||||||
.replace("yyyy", date.getFullYear().toString())
|
.replace("yyyy", year.toString())
|
||||||
.replace("dd", date.getDate().toString().padStart(2, '0'))
|
.replace("dd", day.toString().padStart(2, '0'))
|
||||||
.replace("d", date.getDate().toString());
|
.replace("d", day.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DateTemplateProps {
|
interface DateTemplateProps {
|
||||||
@ -83,12 +84,21 @@ export function DateTemplate({
|
|||||||
|
|
||||||
// Форматированная дата для отображения
|
// Форматированная дата для отображения
|
||||||
const formattedDate = useMemo(() => {
|
const formattedDate = useMemo(() => {
|
||||||
if (!isoDate) return null;
|
const { month, day, year } = selectedDate;
|
||||||
|
if (!month || !day || !year) return null;
|
||||||
|
|
||||||
const date = new Date(isoDate);
|
const monthNum = parseInt(month);
|
||||||
const pattern = screen.dateInput?.selectedDateFormat || "MMMM d, yyyy";
|
const dayNum = parseInt(day);
|
||||||
return formatDateByPattern(date, pattern);
|
const yearNum = parseInt(year);
|
||||||
}, [isoDate, screen.dateInput?.selectedDateFormat]);
|
|
||||||
|
// Валидация даты
|
||||||
|
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 ? (
|
const selectedDateDisplay = formattedDate && screen.dateInput?.showSelectedDate !== false ? (
|
||||||
@ -138,7 +148,6 @@ export function DateTemplate({
|
|||||||
onChange={handleDateChange}
|
onChange={handleDateChange}
|
||||||
maxYear={new Date().getFullYear() - 11}
|
maxYear={new Date().getFullYear() - 11}
|
||||||
yearsRange={100}
|
yearsRange={100}
|
||||||
locale="en"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{defaultTexts?.privacyBanner && (
|
{defaultTexts?.privacyBanner && (
|
||||||
|
|||||||
@ -689,8 +689,6 @@ export function TrialPaymentTemplate({
|
|||||||
By clicking Continue, you agree to our{" "}
|
By clicking Continue, you agree to our{" "}
|
||||||
<a
|
<a
|
||||||
href="https://witlab.us/terms"
|
href="https://witlab.us/terms"
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline hover:text-[#4B5563]"
|
className="underline hover:text-[#4B5563]"
|
||||||
>
|
>
|
||||||
Terms of Use & Service
|
Terms of Use & Service
|
||||||
@ -698,8 +696,6 @@ export function TrialPaymentTemplate({
|
|||||||
and{" "}
|
and{" "}
|
||||||
<a
|
<a
|
||||||
href="https://witlab.us/privacy"
|
href="https://witlab.us/privacy"
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline hover:text-[#4B5563]"
|
className="underline hover:text-[#4B5563]"
|
||||||
>
|
>
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
|
|||||||
@ -34,7 +34,6 @@ const meta: Meta<typeof QuestionDateAnswers> = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: new Date().getFullYear() - 11,
|
maxYear: new Date().getFullYear() - 11,
|
||||||
yearsRange: 100,
|
yearsRange: 100,
|
||||||
locale: "en",
|
|
||||||
},
|
},
|
||||||
bottomActionButtonProps: {
|
bottomActionButtonProps: {
|
||||||
actionButtonProps: {
|
actionButtonProps: {
|
||||||
@ -72,7 +71,6 @@ export const WithInitialValue = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: new Date().getFullYear() - 11,
|
maxYear: new Date().getFullYear() - 11,
|
||||||
yearsRange: 100,
|
yearsRange: 100,
|
||||||
locale: "en",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
@ -84,7 +82,6 @@ export const WithError = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: new Date().getFullYear() - 11,
|
maxYear: new Date().getFullYear() - 11,
|
||||||
yearsRange: 100,
|
yearsRange: 100,
|
||||||
locale: "en",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
@ -96,7 +93,6 @@ export const WithCustomLocale = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: new Date().getFullYear() - 11,
|
maxYear: new Date().getFullYear() - 11,
|
||||||
yearsRange: 100,
|
yearsRange: 100,
|
||||||
locale: "ru",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
@ -108,7 +104,6 @@ export const WithCustomYearRange = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: 2000,
|
maxYear: 2000,
|
||||||
yearsRange: 50,
|
yearsRange: 50,
|
||||||
locale: "en",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
@ -120,7 +115,6 @@ export const WithoutBottomButton = {
|
|||||||
onChange: fn(),
|
onChange: fn(),
|
||||||
maxYear: new Date().getFullYear() - 11,
|
maxYear: new Date().getFullYear() - 11,
|
||||||
yearsRange: 100,
|
yearsRange: 100,
|
||||||
locale: "en",
|
|
||||||
},
|
},
|
||||||
bottomActionButtonProps: undefined,
|
bottomActionButtonProps: undefined,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -27,11 +27,6 @@ const meta: Meta<typeof DateInput> = {
|
|||||||
control: { type: "number" },
|
control: { type: "number" },
|
||||||
description: "Диапазон лет для выбора",
|
description: "Диапазон лет для выбора",
|
||||||
},
|
},
|
||||||
locale: {
|
|
||||||
control: { type: "select" },
|
|
||||||
options: ["en", "ru", "de", "fr"],
|
|
||||||
description: "Локаль для отображения месяцев",
|
|
||||||
},
|
|
||||||
error: {
|
error: {
|
||||||
control: { type: "text" },
|
control: { type: "text" },
|
||||||
description: "Сообщение об ошибке",
|
description: "Сообщение об ошибке",
|
||||||
@ -74,10 +69,9 @@ export const WithError = {
|
|||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
|
|
||||||
export const RussianLocale = {
|
export const WithEmptyValue = {
|
||||||
args: {
|
args: {
|
||||||
value: null,
|
value: null,
|
||||||
locale: "ru",
|
|
||||||
},
|
},
|
||||||
} satisfies Story;
|
} satisfies Story;
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,6 @@ export interface DateInputProps {
|
|||||||
maxYear?: number;
|
maxYear?: number;
|
||||||
yearsRange?: number;
|
yearsRange?: number;
|
||||||
// onBlur?: () => void;
|
// onBlur?: () => void;
|
||||||
locale?: string;
|
|
||||||
daySelectProps?: LocalSelectInputProps;
|
daySelectProps?: LocalSelectInputProps;
|
||||||
monthSelectProps?: LocalSelectInputProps;
|
monthSelectProps?: LocalSelectInputProps;
|
||||||
yearSelectProps?: LocalSelectInputProps;
|
yearSelectProps?: LocalSelectInputProps;
|
||||||
@ -30,7 +29,6 @@ export default function DateInput({
|
|||||||
maxYear = new Date().getFullYear() - 11,
|
maxYear = new Date().getFullYear() - 11,
|
||||||
yearsRange = 100,
|
yearsRange = 100,
|
||||||
// onBlur,
|
// onBlur,
|
||||||
locale = "en",
|
|
||||||
daySelectProps,
|
daySelectProps,
|
||||||
monthSelectProps,
|
monthSelectProps,
|
||||||
yearSelectProps,
|
yearSelectProps,
|
||||||
@ -51,7 +49,6 @@ export default function DateInput({
|
|||||||
onChange,
|
onChange,
|
||||||
maxYear,
|
maxYear,
|
||||||
yearsRange,
|
yearsRange,
|
||||||
locale,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
|
|||||||
@ -27,13 +27,13 @@ export default function PrivacyTermsConsent({
|
|||||||
I agree to the{" "}
|
I agree to the{" "}
|
||||||
{privacyPolicy && (
|
{privacyPolicy && (
|
||||||
<Button variant="link" asChild {...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>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{", "}
|
{", "}
|
||||||
{termsOfUse && (
|
{termsOfUse && (
|
||||||
<Button variant="link" asChild {...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>
|
</Button>
|
||||||
)}{" "}
|
)}{" "}
|
||||||
and to the use of cookies and tracking technologies, that require your
|
and to the use of cookies and tracking technologies, that require your
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
import { SelectInputProps } from "@/components/ui/SelectInput/SelectInput";
|
import { SelectInputProps } from "@/components/ui/SelectInput/SelectInput";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
|
// Валидация даты без зависимости от часовых зон
|
||||||
|
// Проверяет что дата существует (например, 31 февраля - невалидна)
|
||||||
const isValidDate = (year: number, month: number, day: number) => {
|
const isValidDate = (year: number, month: number, day: number) => {
|
||||||
if (!year || !month || !day) return false;
|
if (!year || !month || !day) return false;
|
||||||
|
// Используем Date в локальной зоне только для валидации
|
||||||
|
// Это безопасно, т.к. мы не сохраняем результат, только проверяем корректность
|
||||||
const date = new Date(year, month - 1, day);
|
const date = new Date(year, month - 1, day);
|
||||||
return (
|
return (
|
||||||
date.getFullYear() === year &&
|
date.getFullYear() === year &&
|
||||||
@ -25,13 +29,11 @@ const parseDateValue = (
|
|||||||
return { year, month, day };
|
return { year, month, day };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Упрощенное определение порядка полей даты на основе локали.
|
// Порядок полей даты: месяц-день-год (американский формат)
|
||||||
// В реальном приложении здесь лучше использовать данные из next-intl.
|
// Этот формат используется для всех локалей чтобы обеспечить единообразие
|
||||||
const getDateInputLocaleFormat = (locale: string): ("d" | "m" | "y")[] => {
|
const getDateInputLocaleFormat = (): ("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
|
return ["m", "d", "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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface UseDateInputProps {
|
interface UseDateInputProps {
|
||||||
@ -39,7 +41,6 @@ interface UseDateInputProps {
|
|||||||
onChange?: (value: string | null) => void;
|
onChange?: (value: string | null) => void;
|
||||||
maxYear?: number;
|
maxYear?: number;
|
||||||
yearsRange?: number;
|
yearsRange?: number;
|
||||||
locale?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDateInput = ({
|
export const useDateInput = ({
|
||||||
@ -47,7 +48,6 @@ export const useDateInput = ({
|
|||||||
onChange,
|
onChange,
|
||||||
maxYear = new Date().getFullYear() - 11,
|
maxYear = new Date().getFullYear() - 11,
|
||||||
yearsRange = 100,
|
yearsRange = 100,
|
||||||
locale = "en",
|
|
||||||
}: UseDateInputProps) => {
|
}: UseDateInputProps) => {
|
||||||
const [year, setYear] = useState("");
|
const [year, setYear] = useState("");
|
||||||
const [month, setMonth] = useState("");
|
const [month, setMonth] = useState("");
|
||||||
@ -125,8 +125,8 @@ export const useDateInput = ({
|
|||||||
}, [year, month]);
|
}, [year, month]);
|
||||||
|
|
||||||
const localeFormat = useMemo(
|
const localeFormat = useMemo(
|
||||||
() => getDateInputLocaleFormat(locale),
|
() => getDateInputLocaleFormat(),
|
||||||
[locale]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleYearChange: SelectInputProps["onValueChange"] = useCallback(
|
const handleYearChange: SelectInputProps["onValueChange"] = useCallback(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user