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
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>
|
|
);
|
|
}
|