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.
 
 
 
 
 

196 lines
6.7 KiB

"use client";
import { useEffect, useRef, useState } from "react";
import type { QuestionField } from "@/data/question-data";
import { useI18n } from "@/translations/provider";
import { useQuestionAnswers } from "./question-answer-storage";
import QuestionTitle from "./question-title";
type QuestionTextProps = {
question: QuestionField;
questionIndex: number;
description?: string;
disabled?: boolean;
heightClassName?: string;
};
function toEnglishDigits(str: string): string {
return str
.replace(/[٠-٩]/g, (d) => String(d.charCodeAt(0) - 1632))
.replace(/[۰-۹]/g, (d) => String(d.charCodeAt(0) - 1776));
}
export default function QuestionText({
question,
questionIndex,
description,
disabled,
heightClassName: _heightClassName,
}: QuestionTextProps) {
const { dictionary: t } = useI18n();
const { getAnswerValue, setAnswerValue } = useQuestionAnswers();
const value = getAnswerValue(question, questionIndex);
const isMuted = value === "-";
const [localValue, setLocalValue] = useState(
isMuted ? "" : String(value ?? ""),
);
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
setLocalValue(isMuted ? "" : String(value ?? ""));
}, [value, isMuted]);
const enTitleLower = (question.englishTitle || question.title).toLowerCase();
const isNumericQuestion =
question.type === "number" || enTitleLower.includes("duration");
const handleChange = (val: string) => {
let nextVal = val;
if (isNumericQuestion) {
nextVal = toEnglishDigits(nextVal).replace(/[^\d]/g, "");
}
setLocalValue(nextVal);
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
debounceTimerRef.current = setTimeout(() => {
setAnswerValue(question, questionIndex, nextVal);
}, 300);
};
const handleBlur = () => {
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
setAnswerValue(question, questionIndex, localValue);
};
const stringValue = localValue.trim();
const isEmailQuestion = (question.englishTitle || question.title)
.toLowerCase()
.includes("email");
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValidEmail = !isEmailQuestion || emailRegex.test(stringValue);
const showInvalidState =
isEmailQuestion && stringValue.length > 0 && !isValidEmail;
const isAnswered =
question.required === false
? isValidEmail || stringValue.length === 0
: isValidEmail && stringValue.length > 0;
const isMarjaQuestion =
(question.englishTitle || question.title) ===
"Marja' al-Taqlid (Religious Authority)";
if (isEmailQuestion) {
return (
<div
data-question-answered={isAnswered ? "true" : "false"}
data-question-type="email"
className={[
"flex w-full flex-col gap-2 transition-opacity duration-200",
disabled ? "pointer-events-none opacity-30" : "",
].join(" ")}
>
<QuestionTitle question={question} />
<div
dir="ltr"
className={[
"flex h-[54px] w-full items-center rounded-[16px] border bg-white text-[#181818] focus-within:border-[#6F6F6F] focus-within:ring-1 focus-within:ring-[#6F6F6F] transition-all",
showInvalidState
? "border-[#F2465F] ring-1 ring-[#F2465F]"
: "border-[#D0D5DD] hover:border-[#98A2B3]",
].join(" ")}
>
<input
type="text"
value={localValue}
onChange={(e) => handleChange(e.target.value)}
onBlur={handleBlur}
placeholder={question.extras.placeHolder}
disabled={disabled || isMuted}
className="h-full w-full border-0 bg-transparent px-4.5 text-[15px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3] disabled:opacity-50 disabled:cursor-not-allowed"
/>
</div>
{showInvalidState ? (
<span className="block group-10 font-semibold text-[#F2465F]">
{/[\u0600-\u06FF]/.test(question.title)
? "یک آدرس ایمیل معتبر وارد کنید."
: "Enter a valid email address."}
</span>
) : null}
{description ? (
<span className="block group-10 font-semibold text-[#747474]">
{description}
</span>
) : null}
</div>
);
}
return (
<div
data-question-answered={isAnswered ? "true" : "false"}
data-question-type={question.type}
className={[
"flex w-full flex-col gap-2 transition-opacity duration-200",
disabled ? "pointer-events-none opacity-30" : "",
].join(" ")}
>
<QuestionTitle question={question} />
<input
type={isNumericQuestion ? "tel" : "text"}
inputMode={isNumericQuestion ? "numeric" : undefined}
pattern={isNumericQuestion ? "[0-9]*" : undefined}
value={localValue}
onChange={(e) => handleChange(e.target.value)}
onBlur={handleBlur}
placeholder={question.extras.placeHolder}
disabled={disabled || isMuted}
className={[
"h-[54px] w-full rounded-[16px] border border-[#D0D5DD] bg-white px-4.5 text-[15px] font-medium text-[#181818] outline-none transition-all placeholder:text-[#98A2B3] hover:border-[#98A2B3] focus:border-[#6F6F6F] focus:ring-1 focus:ring-[#6F6F6F] disabled:bg-[#F5F2F1] disabled:text-[#7C7472]",
isMuted
? "opacity-50 cursor-not-allowed bg-[#F5F5F5] border-[#E2E8F0]"
: "",
]
.filter(Boolean)
.join(" ")}
/>
{isMarjaQuestion ? (
<div className="mt-2.5 flex items-center">
<label className="flex cursor-pointer items-center gap-2 select-none active:scale-[0.99] transition-all">
<input
type="checkbox"
checked={isMuted}
onChange={(e) => {
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
if (e.target.checked) {
setAnswerValue(question, questionIndex, "-");
} else {
setAnswerValue(question, questionIndex, "");
}
}}
className="h-[18px] w-[18px] shrink-0 rounded border-[#D0D5DD] text-[#F2465F] focus:ring-[#F2465F] accent-[#F2465F] cursor-pointer"
/>
<span className="text-[13.5px] font-semibold text-[#4F4F4F]">
{t["This is not a priority for me"] ??
"This is not a priority for me"}
</span>
</label>
</div>
) : null}
{description ? (
<span className="block group-10 font-semibold text-[#747474]">
{description}
</span>
) : null}
</div>
);
}