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 href={link.href} target="_blank" rel="noopener noreferrer">{link.children}</Link>
|
||||
<Link href={link.href}>{link.children}</Link>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user