You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.8 KiB
197 lines
5.8 KiB
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
import type { QuestionField } from "@/lib/schema-adapter";
|
|
import { useI18n } from "@/translations/provider";
|
|
import { useQuestionAnswers } from "./question-answer-storage";
|
|
import QuestionDateSheet from "./question-date-sheet";
|
|
import QuestionTitle from "./question-title";
|
|
|
|
type QuestionDateProps = {
|
|
question: QuestionField;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
const MIN_AGE = 18;
|
|
|
|
function parseDateParts(dateValue: string): {
|
|
year: string;
|
|
month: string;
|
|
day: string;
|
|
} {
|
|
const parts = dateValue ? dateValue.split("-") : [];
|
|
return {
|
|
year: parts[0] || "",
|
|
month: parts[1] || "",
|
|
day: parts[2] || "",
|
|
};
|
|
}
|
|
|
|
export function QuestionDate({ question, disabled }: QuestionDateProps) {
|
|
const { dictionary: t, locale } = useI18n();
|
|
const { getAnswerValue, setAnswerValue } = useQuestionAnswers();
|
|
const value = getAnswerValue(question);
|
|
const dateValue = typeof value === "string" ? value : "";
|
|
const { year, month, day } = parseDateParts(dateValue);
|
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const calculatedAge = useMemo(() => {
|
|
if (!year || !month || !day) return null;
|
|
const y = Number.parseInt(year, 10);
|
|
const m = Number.parseInt(month, 10);
|
|
const d = Number.parseInt(day, 10);
|
|
|
|
if (
|
|
!y ||
|
|
!m ||
|
|
!d ||
|
|
Number.isNaN(y) ||
|
|
Number.isNaN(m) ||
|
|
Number.isNaN(d)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const today = new Date();
|
|
const cYear = today.getFullYear();
|
|
const cMonth = today.getMonth() + 1;
|
|
const cDay = today.getDate();
|
|
|
|
let age = cYear - y;
|
|
if (cMonth < m || (cMonth === m && cDay < d)) {
|
|
age -= 1;
|
|
}
|
|
|
|
return age >= 0 ? age : null;
|
|
}, [year, month, day]);
|
|
|
|
const isUnder18 = calculatedAge !== null && calculatedAge < MIN_AGE;
|
|
|
|
const displayDate = useMemo(() => {
|
|
if (!year || !month || !day) return "";
|
|
const date = new Date(
|
|
Number.parseInt(year, 10),
|
|
Number.parseInt(month, 10) - 1,
|
|
Number.parseInt(day, 10),
|
|
);
|
|
return new Intl.DateTimeFormat(`${locale}-u-ca-gregory`, {
|
|
day: "numeric",
|
|
month: "long",
|
|
year: "numeric",
|
|
}).format(date);
|
|
}, [year, month, day, locale]);
|
|
|
|
const openSheet = () => {
|
|
if (disabled) return;
|
|
setIsOpen(true);
|
|
};
|
|
|
|
const handleApply = (formattedDate: string) => {
|
|
setAnswerValue(question, formattedDate);
|
|
};
|
|
|
|
const placeholder =
|
|
t["Select date"] || (locale === "fa" ? "انتخاب تاریخ" : "Select date");
|
|
|
|
return (
|
|
<div
|
|
className={[
|
|
"flex w-full flex-col gap-2.5 transition-opacity duration-200",
|
|
disabled ? "pointer-events-none opacity-30" : "",
|
|
].join(" ")}
|
|
>
|
|
<QuestionTitle question={question} />
|
|
|
|
{/* Date Trigger Field (single input, like the Flutter app's DOB field) */}
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={openSheet}
|
|
aria-haspopup="dialog"
|
|
className={[
|
|
"flex h-[54px] w-full cursor-pointer items-center gap-2.5 rounded-[16px] border bg-white px-4 text-start transition-all outline-none",
|
|
isOpen
|
|
? "border-[#6F6F6F] ring-1 ring-[#6F6F6F]"
|
|
: "border-[#D0D5DD] hover:border-[#98A2B3]",
|
|
].join(" ")}
|
|
>
|
|
<svg
|
|
aria-hidden="true"
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 20 20"
|
|
fill="none"
|
|
className="shrink-0 text-[#667085]"
|
|
>
|
|
<path
|
|
d="M15.5 3H14V2.25C14 1.836 13.664 1.5 13.25 1.5C12.836 1.5 12.5 1.836 12.5 2.25V3H7.5V2.25C7.5 1.836 7.166 1.5 6.75 1.5C6.336 1.5 6 1.836 6 2.25V3H4.5C3.671 3 3 3.671 3 4.5V15.5C3 16.329 3.671 17 4.5 17H15.5C16.329 17 17 16.329 17 15.5V4.5C17 3.671 16.329 3 15.5 3ZM15.5 15.5H4.5V7H15.5V15.5Z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
<span
|
|
className={[
|
|
"flex-1 truncate text-[15px] font-medium",
|
|
displayDate ? "text-[#181818]" : "text-[#667085]",
|
|
].join(" ")}
|
|
>
|
|
{displayDate || placeholder}
|
|
</span>
|
|
<svg
|
|
aria-hidden="true"
|
|
width="16"
|
|
height="10"
|
|
viewBox="0 0 16 10"
|
|
fill="none"
|
|
className="shrink-0 transition-transform duration-200"
|
|
>
|
|
<path
|
|
d="M14.75 1.25L7.75 8.25L0.75 1.25"
|
|
stroke="#344054"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
{calculatedAge !== null ? (
|
|
<div className="mt-2.5 flex w-full flex-col gap-2">
|
|
<span className="group-14 block leading-tight font-semibold text-[#181818]">
|
|
{t.Age || (locale === "fa" ? "سن" : "Age")}
|
|
</span>
|
|
<input
|
|
type="text"
|
|
disabled
|
|
readOnly
|
|
value={calculatedAge}
|
|
className={[
|
|
"h-[54px] w-full cursor-not-allowed rounded-[16px] border px-4.5 text-[15px] font-medium outline-none disabled:bg-[#F5F2F1] disabled:text-[#7C7472]",
|
|
isUnder18
|
|
? "border-[#F2465F] text-[#F2465F] ring-1 ring-[#F2465F]"
|
|
: "border-[#D0D5DD] text-[#7C7472]",
|
|
].join(" ")}
|
|
/>
|
|
{isUnder18 ? (
|
|
<span className="block text-[11px] font-semibold text-[#F2465F]">
|
|
{locale === "fa"
|
|
? "حداقل سن برای ثبتنام ۱۸ سال میباشد."
|
|
: "Minimum age required for registration is 18 years."}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
{isOpen && (
|
|
<QuestionDateSheet
|
|
question={question}
|
|
value={dateValue}
|
|
onApply={handleApply}
|
|
onClose={() => setIsOpen(false)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default QuestionDate;
|