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.
 
 
 
 
 

214 lines
7.3 KiB

"use client";
import { useEffect, useRef, useState } from "react";
import type { QuestionField } from "@/lib/schema-adapter";
import { useI18n } from "@/translations/provider";
import { useQuestionAnswers } from "./question-answer-storage";
import QuestionTitle from "./question-title";
type QuestionTextProps = {
question: QuestionField;
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,
description,
disabled,
heightClassName: _heightClassName,
}: QuestionTextProps) {
const { dictionary: t, locale } = useI18n();
const { getAnswerValue, setAnswerValue } = useQuestionAnswers();
const value = getAnswerValue(question);
const isMuted = value === "-";
const [localValue, setLocalValue] = useState(
isMuted ? "" : String(value ?? ""),
);
const [isReadOnly, setIsReadOnly] = useState(true);
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
setLocalValue(isMuted ? "" : String(value ?? ""));
}, [value, isMuted]);
const isNumericQuestion = question.type === "number" || question.validation?.format === "number";
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, nextVal);
}, 300);
};
const handleBlur = () => {
setIsReadOnly(true);
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
setAnswerValue(question, localValue);
};
const handleFocus = () => {
setIsReadOnly(false);
};
const stringValue = localValue.trim();
const isEmailQuestion = question.type === "email" || question.validation?.format === "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.ui_config?.showNotPriorityCheckbox === true;
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"
name="off_email"
readOnly={isReadOnly || disabled || isMuted}
onFocus={handleFocus}
onTouchStart={handleFocus}
autoComplete="one-time-code"
autoCorrect="off"
autoCapitalize="none"
spellCheck="false"
data-lpignore="true"
data-form-type="other"
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]">
{question.validation?.errorMessage ||
(locale === "fa"
? "یک آدرس ایمیل معتبر وارد کنید."
: "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}
readOnly={isReadOnly || disabled || isMuted}
onFocus={handleFocus}
onTouchStart={handleFocus}
autoComplete="one-time-code"
autoCorrect="off"
autoCapitalize="none"
spellCheck="false"
data-lpignore="true"
data-form-type="other"
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, "-");
} else {
setAnswerValue(question, "");
}
}}
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>
);
}