fix lag question

This commit is contained in:
karimaldeen 2024-09-21 12:52:06 +03:00
parent efb687b742
commit 869e857f2c
23 changed files with 890 additions and 310 deletions

View File

@ -11,6 +11,7 @@
"handelnavigate", "handelnavigate",
"Karim", "Karim",
"katex", "katex",
"Latext",
"Popconfirm", "Popconfirm",
"queryqlent", "queryqlent",
"registraion", "registraion",

71
package-lock.json generated
View File

@ -36,6 +36,8 @@
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"react-window": "^1.8.10",
"react-window-dynamic": "^1.8.0-alpha.2",
"reactstrap": "^9.2.2", "reactstrap": "^9.2.2",
"sass": "^1.77.4", "sass": "^1.77.4",
"yup": "^1.4.0", "yup": "^1.4.0",
@ -52,6 +54,7 @@
"@types/react-helmet": "^6.1.11", "@types/react-helmet": "^6.1.11",
"@types/react-katex": "^3.0.4", "@types/react-katex": "^3.0.4",
"@types/react-latex": "^2.0.3", "@types/react-latex": "^2.0.3",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-legacy": "^5.4.1", "@vitejs/plugin-legacy": "^5.4.1",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.0",
"jest": "^29.7.0", "jest": "^29.7.0",
@ -3660,6 +3663,15 @@
"redux": "^4.0.0" "redux": "^4.0.0"
} }
}, },
"node_modules/@types/react-window": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
"integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/stack-utils": { "node_modules/@types/stack-utils": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@ -9112,6 +9124,38 @@
"react-dom": ">=16.6.0" "react-dom": ">=16.6.0"
} }
}, },
"node_modules/react-window": {
"version": "1.8.10",
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
},
"engines": {
"node": ">8.0.0"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-window-dynamic": {
"version": "1.8.0-alpha.2",
"resolved": "https://registry.npmjs.org/react-window-dynamic/-/react-window-dynamic-1.8.0-alpha.2.tgz",
"integrity": "sha512-PYR1nu5kzEr+PDg9/+19uTqO1OqUCckYE5dDpjFpEGBtVTcq4smxE3jXhk+zLi4AOZlLVr9pJIjwPL68zwq5Ww==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
},
"engines": {
"node": ">8.0.0"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
}
},
"node_modules/reactstrap": { "node_modules/reactstrap": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz", "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz",
@ -13436,6 +13480,15 @@
"redux": "^4.0.0" "redux": "^4.0.0"
} }
}, },
"@types/react-window": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
"integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/stack-utils": { "@types/stack-utils": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@ -17409,6 +17462,24 @@
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"react-window": {
"version": "1.8.10",
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
"requires": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
}
},
"react-window-dynamic": {
"version": "1.8.0-alpha.2",
"resolved": "https://registry.npmjs.org/react-window-dynamic/-/react-window-dynamic-1.8.0-alpha.2.tgz",
"integrity": "sha512-PYR1nu5kzEr+PDg9/+19uTqO1OqUCckYE5dDpjFpEGBtVTcq4smxE3jXhk+zLi4AOZlLVr9pJIjwPL68zwq5Ww==",
"requires": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
}
},
"reactstrap": { "reactstrap": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz", "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz",

View File

@ -31,6 +31,8 @@
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"react-window": "^1.8.10",
"react-window-dynamic": "^1.8.0-alpha.2",
"reactstrap": "^9.2.2", "reactstrap": "^9.2.2",
"sass": "^1.77.4", "sass": "^1.77.4",
"yup": "^1.4.0", "yup": "^1.4.0",
@ -73,6 +75,7 @@
"@types/react-helmet": "^6.1.11", "@types/react-helmet": "^6.1.11",
"@types/react-katex": "^3.0.4", "@types/react-katex": "^3.0.4",
"@types/react-latex": "^2.0.3", "@types/react-latex": "^2.0.3",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-legacy": "^5.4.1", "@vitejs/plugin-legacy": "^5.4.1",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.0",
"jest": "^29.7.0", "jest": "^29.7.0",

View File

@ -62,7 +62,8 @@ const ImageBoxField = ({ name }: any) => {
fileInputRef.current.value = ""; fileInputRef.current.value = "";
} }
}; };
console.log(name);
return ( return (
<div className="ImageBoxField"> <div className="ImageBoxField">
<div className="ImageHeader"> <div className="ImageHeader">

View File

@ -0,0 +1,105 @@
import "./ImageBoxField.scss";
import { useState, useRef, useEffect,memo } from "react";
import ImageIcon from "./ImageIcon";
import ImageCancelIcon from "./ImageCancelIcon";
import { getNestedValue } from "../../../utils/getNestedValue";
import { generateImagePreview } from "./generateImagePreview";
import { useTranslation } from "react-i18next";
import { areFieldPropsEqual } from "../../../utils/areFieldPropsEqual";
// Helper function to generate image preview from a File
const ImageBoxFieldMemo = memo(({ form,field }: any) => {
const {values,setFieldValue} = form
const {name} = field;
const value = getNestedValue(values, name);
const [imagePreview, setImagePreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [t] = useTranslation();
useEffect(() => {
if (value instanceof File) {
generateImagePreview(value, setImagePreview);
} else if (typeof value === "string") {
setImagePreview(value);
} else {
setImagePreview(null);
}
}, [value]);
const handleFileChange = (event: any) => {
const file = event.target.files[0];
if (file) {
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
alert(t('validation.File_size_exceeds_2_MB_limit.'));
event.target.value = '';
return;
}
// Process the file
}
if (file) {
generateImagePreview(file, setImagePreview);
setFieldValue(name, file);
}
};
const handleButtonClick = () => {
const fileInput = fileInputRef.current;
if (fileInput) {
fileInput.click();
}
};
const handleCancel = () => {
setImagePreview("");
setFieldValue(name, null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
console.log(name);
return (
<div className="ImageBoxField">
<div className="ImageHeader">
{imagePreview ? (
<>
<ImageCancelIcon
onClick={handleCancel}
className="ImageCancelIcon"
/>
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
</>
) : (
<div className="VisibleHidden">hidden</div>
)}
</div>
<div className="ImageBox" onClick={handleButtonClick}>
{imagePreview ? (
<img src={imagePreview} alt="Preview" className="imagePreview" />
) : (
<ImageIcon className="ImageBoxIcon" />
)}
</div>
<input
id={`file-input-${name}`}
type="file"
accept="image/png, image/jpeg, image/webp"
style={{ display: "none" }}
onChange={handleFileChange}
ref={fileInputRef}
/>
</div>
);
}, (prevProps, nextProps) => {
return areFieldPropsEqual(prevProps, nextProps)
});
export default ImageBoxFieldMemo;

View File

@ -0,0 +1,106 @@
import TextArea from 'antd/es/input/TextArea';
import { useFormikContext } from 'formik';
import React, { Suspense, useState } from 'react';
import { parseTextAndLatex } from '../../utils/parseTextAndLatex';
import LatexPreview from '../CustomFields/MathComponent';
import { Checkbox } from 'antd';
import { CheckboxProps } from 'antd/lib';
import { useTranslation } from 'react-i18next';
import { FaPlus } from 'react-icons/fa';
import { useObjectToEdit } from '../../zustand/ObjectToEditState';
import SpinContainer from '../Layout/SpinContainer';
import { areFieldPropsEqual } from './areFieldPropsEqual';
const AddLazyModal = React.lazy(() => import("./AddLaTexModal"));
const EditLazyModal = React.lazy(() => import("./EditLaTexModal"));
const LaTeXInputMemo: React.FC<any> = React.memo(({ field ,form, label, ...props }) => {
const { name ,value} = field;
const { setFieldValue } = form;
const { ShowLatexOption } = useObjectToEdit();
const [showPreview, setShowPreview] = useState(false);
const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFieldValue(name, e.target.value);
};
console.log(name,"name");
const Preview = parseTextAndLatex(value ?? "");
const onPreviewChange: CheckboxProps['onChange'] = (e) => {
setShowPreview(e.target.checked);
};
const [t] = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [Latex, setLatex] = useState<string>("");
const showModal = () => {
setIsModalOpen(true);
};
const handleEditModal = (item: any) => {
console.log(item);
setLatex(item);
setIsEditModalOpen(true);
};
return (
<div className='LaTeXInput'>
<label htmlFor={name} className="text">
{t(label || name)}
</label>
<div className='LaTeXInputArea'>
<TextArea
size="large"
showCount
maxLength={1000}
autoSize={{ minRows: 6, maxRows: 10 }}
style={{ height: "400px" }}
onChange={handleChangeInput}
value={value}
{...props}
/>
{ShowLatexOption && (
<div className='LaTeXInputOptions'>
<Checkbox onChange={onPreviewChange}>{t("header.show_preview")}</Checkbox>
<button type='button' className='addMML' onClick={showModal}>
<FaPlus/> {t("MML")}
</button>
</div>
)}
{showPreview && (
<div className='showPreviewInput'>
{Preview?.map((item: any, index: number) => {
if (item?.isLatex) {
return (
<div key={index} onClick={() => handleEditModal(item)} className='LatexPreview'>
<LatexPreview Latex={item?.text} />
</div>
);
}
return <div key={index}>{item?.text}</div>;
})}
</div>
)}
</div>
<Suspense fallback={<SpinContainer />}>
<AddLazyModal name={name} Latex={Latex} isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} setLatex={setLatex} />
<EditLazyModal name={name} Latex={Latex} isModalOpen={isEditModalOpen} setIsModalOpen={setIsEditModalOpen} setLatex={setLatex} />
</Suspense>
</div>
);
}, (prevProps, nextProps) => {
return areFieldPropsEqual(prevProps, nextProps)
});
export default LaTeXInputMemo;

View File

@ -0,0 +1,23 @@
// utilityFunctions.ts
import { FieldProps } from 'formik';
export const areFieldPropsEqual = (
prevProps: any,
nextProps: any
): boolean => {
const prevError = prevProps.form.errors[prevProps.field.name];
const nextError = nextProps.form.errors[nextProps.field.name];
const prevTouched = prevProps.form.touched[prevProps.field.name];
const nextTouched = nextProps.form.touched[nextProps.field.name];
const prevValue = prevProps.field.value;
const nextValue = nextProps.field.value;
return (
prevValue === nextValue
&&
prevError === nextError &&
prevTouched === nextTouched
);
};

View File

@ -1,16 +1,14 @@
import React from "react"; import React from "react";
import { Choice } from "../../../../../types/Item"; import { Choice } from "../../../../../types/Item";
import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from "formik"; import { Field, useFormikContext } from "formik";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
import CheckboxField from "./CheckboxField"; import CheckboxField from "./CheckboxField";
import TextField from "./TextField";
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go"; import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd"; import { Popconfirm } from "antd";
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import LaTeXInput from "../../../../../Components/LatextInput/LaTeXInput"; import LaTeXInputMemo from "../../../../../Components/LatextInput/LaTeXInputMemo";
import ImageBoxFieldMemo from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxFieldMemo";
const ChoiceFields = ({ index }: { index: number; data: Choice }) => { const ChoiceFields = ({ index }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
@ -44,8 +42,14 @@ const ChoiceFields = ({ index }: { index: number; data: Choice }) => {
return ( return (
<> <>
<div id={`ChoiceField_${index}`} className="ChoiceFields"> <div id={`ChoiceField_${index}`} className="ChoiceFields">
<LaTeXInput id={`choice_${index + 1}`} name={`answers.${index}.content`} label={t(`input.choice`) + ` ` +`( ${index + 1} )`}/> <Field
<ImageBoxField name={`answers.${index}.content_image`} /> component={LaTeXInputMemo}
id={`choice_${index + 1}`} name={`answers.${index}.content`} label={t(`input.choice`) + ` ` +`( ${index + 1} )`}
/>
<Field
component={ImageBoxFieldMemo}
name={`answers.${index}.content_image`}
/>
<div className="answer_status"> <div className="answer_status">
<CheckboxField <CheckboxField

View File

@ -1,72 +0,0 @@
import { Form, Input } from "antd";
import React from "react";
import useFormField from "../../../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
const { TextArea } = Input;
const TextField = ({
name,
label,
label2,
placeholder,
isDisabled,
onChange,
props,
no_label,
label_icon,
id,
className,
}: any) => {
const newName = `answers[${name}].content`;
const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
// console.log('Change:', e.target.value);
formik.setFieldValue(newName, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<Field
as={TextArea}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
name={newName}
disabled={isDisabled}
size="large"
showCount
maxLength={1000}
onChange={onChange || TextFilehandleChange}
autoSize={{ minRows: 4, maxRows: 10 }}
id={id}
/>
</Form.Item>
</div>
);
};
export default React.memo(TextField);

View File

@ -1,143 +1,128 @@
import React from "react"; import React from "react";
import { Choice } from "../../../../../../types/Item";
import ValidationField from "../../../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from "formik"; import { Field } from "formik";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from "../../../../../../utils/getCharFromNumber";
import CheckboxField from "./CheckboxField"; import CheckboxField from "./CheckboxField";
import TextField from "./TextField";
import { toast } from "react-toastify";
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go"; import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd"; import { Popconfirm } from "antd";
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
import LaTeXInput from "../../../../../../Components/LatextInput/LaTeXInput"; import LaTeXInputMemo from "../../../../../../Components/LatextInput/LaTeXInputMemo";
import ImageBoxFieldMemo from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxFieldMemo";
const ChoiceFields = ({ const ChoiceFields = React.memo(
index, ({
parent_index, index,
data, parent_index,
}: { setFieldValue,
index: number; values
parent_index: number; }: {
data: Choice; index: number;
}) => { parent_index: number;
const formik = useFormikContext<any>(); setFieldValue:any;
values:any
const [t] = useTranslation(); }) => {
const { ShowHint } = useObjectToEdit(); console.log(567)
const handleDeleteChoice = () => { const [t] = useTranslation();
document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.add("exit") const { ShowHint } = useObjectToEdit();
const handleDeleteChoice = () => {
const updatedAnswers = formik.values.Questions?.[ document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.add("exit")
parent_index
].answers.filter((_: any, i: any) => i !== index);
setTimeout(() => {
formik.setFieldValue(`Questions[${parent_index}].answers`, updatedAnswers);
document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.remove("exit")
}, 500);
};
const values = formik.values.Questions?.[parent_index]?.answers?.[index] ;
const handelCanDeleteAnswers = ()=>{ const updatedAnswers = values.Questions?.[
const content = values?.content ; parent_index
const content_image = values?.content_image ; ].answers.filter((_: any, i: any) => i !== index);
if(!content && !content_image ){ setTimeout(() => {
return true setFieldValue(`Questions[${parent_index}].answers`, updatedAnswers);
document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.remove("exit")
}, 500);
};
const value = values.Questions?.[parent_index]?.answers?.[index] ;
const handelCanDeleteAnswers = ()=>{
const content = value?.content ;
const content_image = value?.content_image ;
if(!content && !content_image ){
return true
}
return false
} }
return false
}
return (
<>
return ( <div id={`ChoiceField_${parent_index}_${index}`} className="ChoiceFields">
<>
<div id={`ChoiceField_${parent_index}_${index}`} className="ChoiceFields"> <Field
{/* <TextField component={LaTeXInputMemo}
className="textarea_exercise" name={`Questions[${parent_index}].answers[${index}].content`} label={
placeholder={"choice"}
label2={
t(`input.choice`) +
` ` +
`( ${t(`alphabet.${getCharFromNumber(index)}`)} )`
}
name={index}
parent_index={parent_index}
type="TextArea"
/> */}
<LaTeXInput name={`Questions[${parent_index}].answers[${index}].content`} label={
t(`input.choice`) + t(`input.choice`) +
` ` + ` ` +
`( ${index + 1} )` `( ${index + 1} )`
}/> }
<ImageBoxField />
<Field
component={ImageBoxFieldMemo}
name={`Questions.${parent_index}.answers.${index}.content_image`} name={`Questions.${parent_index}.answers.${index}.content_image`}
/> />
<div className="answer_status">
<CheckboxField <div className="answer_status">
className="" <CheckboxField
label="The_correct_answer" className=""
name={index} label="The_correct_answer"
type="Checkbox" name={index}
parent_index={parent_index} type="Checkbox"
/> parent_index={parent_index}
{handelCanDeleteAnswers() ?
<p className="delete_question_options" onClick={()=>{handleDeleteChoice()}} >
{t("header.delete_choice")}
<GoTrash
className="trash_icon"
size={17}
/>
</p>
:
<Popconfirm
title={t("header.this_will_un_do_all_your_changes")}
okText={t("practical.yes")}
cancelText={t("practical.no")}
onConfirm={()=>{handleDeleteChoice()}}
defaultOpen={false}
>
<p className="delete_question_options" >
{t("header.delete_choice")}
<GoTrash
className="trash_icon"
size={17}
/> />
</p>
</Popconfirm> {handelCanDeleteAnswers() ?
<p className="delete_question_options" onClick={()=>{handleDeleteChoice()}} >
} {t("header.delete_choice")}
<GoTrash
className="trash_icon"
size={17}
/>
</p>
:
<Popconfirm
title={t("header.this_will_un_do_all_your_changes")}
okText={t("practical.yes")}
cancelText={t("practical.no")}
onConfirm={()=>{handleDeleteChoice()}}
defaultOpen={false}
>
<p className="delete_question_options" >
{t("header.delete_choice")}
<GoTrash
className="trash_icon"
size={17}
/>
</p>
</Popconfirm>
}
</div>
</div> </div>
</div>
<div className="exercise_form_width"> </>
{ShowHint && );
<ValidationField }
className="hint" , (prevProps, nextProps) => {
placeholder="_" console.log(prevProps.values?.Questions?.[prevProps?.parent_index]?.answers?.[prevProps?.index] === nextProps.values?.Questions?.[nextProps?.parent_index]?.answers?.[prevProps?.index]);
name={`Questions.${parent_index}.answers.${index}.hint`} console.log(2255);
label="hint"
type="TextArea" return prevProps.values?.Questions?.[prevProps?.parent_index]?.answers?.[prevProps?.index] === nextProps.values?.Questions?.[nextProps?.parent_index]?.answers?.[prevProps?.index];
style={{ width: "100%" , height: 60,resize:"none" }} });
showCount={false}
autoSize={{ minRows: 2, maxRows: 10 }}
/>
}
</div>
</>
);
};
export default ChoiceFields; export default ChoiceFields;

View File

@ -1,12 +1,11 @@
import React from "react"; import React from "react";
import ChoiceFields from "./ChoiceFields"; import ChoiceFields from "./ChoiceFields";
import { Choice } from "../../../../../../types/Item"; import { Choice } from "../../../../../../types/Item";
import { useFormikContext } from "formik"; // import { useFormikContext } from "formik";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; // import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import { HolderOutlined } from "@ant-design/icons"; // import { HolderOutlined } from "@ant-design/icons";
const Choices = ({ parent_index }: { parent_index: number }) => { const Choices = React.memo( ({setFieldValue ,values,parent_index }:any) => {
const formik = useFormikContext<any>();
const handleDragEnd = (result: any) => { const handleDragEnd = (result: any) => {
// Check if the item was dropped outside the list // Check if the item was dropped outside the list
@ -15,11 +14,11 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
if (!result.destination) return; if (!result.destination) return;
console.log(formik?.values?.Questions?.[parent_index]?.answers); console.log(values?.Questions?.[parent_index]?.answers);
// Create a new array from the current answers // Create a new array from the current answers
const items = Array.from( const items = Array.from(
formik?.values?.Questions?.[parent_index]?.answers, values?.Questions?.[parent_index]?.answers,
); );
console.log(items); console.log(items);
// Remove the item from the original position // Remove the item from the original position
@ -36,18 +35,21 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
order: index + 1, // Update order to be 1-based index order: index + 1, // Update order to be 1-based index
})); }));
// Update the formik state with the new order // Update the state with the new order
console.log(updatedItems, "updatedItems"); console.log(updatedItems, "updatedItems");
formik.setFieldValue(`Questions.${parent_index}.answers`, updatedItems); setFieldValue(`Questions.${parent_index}.answers`, updatedItems);
}; };
console.log(123);
console.log(setFieldValue);
console.log(values?.Questions?.[parent_index]);
return ( return (
<> <>
<div> <div>
{( {(
(formik?.values as any)?.Questions?.[parent_index]?.answers || values?.Questions?.[parent_index]?.answers ||
[] []
).map((item: Choice, index: number) => { ).map((item: Choice, index: number) => {
return ( return (
@ -59,7 +61,8 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
key={index} key={index}
parent_index={parent_index} parent_index={parent_index}
index={index} index={index}
data={item} setFieldValue={setFieldValue}
values={values}
/> />
</div> </div>
); );
@ -112,6 +115,11 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
</DragDropContext> */} </DragDropContext> */}
</> </>
); );
}; }, (prevProps, nextProps) => {
console.log(prevProps.values?.Questions?.[prevProps?.parent_index]?.answers === nextProps.values?.Questions?.[nextProps?.parent_index]?.answers);
console.log(22);
return prevProps.values?.Questions?.[prevProps?.parent_index]?.answers === nextProps.values?.Questions?.[nextProps?.parent_index]?.answers;
});
export default Choices; export default Choices;

View File

@ -1,29 +1,20 @@
import { Col, Row } from "reactstrap"; import { Row } from "reactstrap";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import { useFormikContext } from "formik";
import { useFormikContext } from "formik";
import { FaCirclePlus } from "react-icons/fa6"; import { FaCirclePlus } from "react-icons/fa6";
import { Choice } from "../../../../../types/Item"; import { Choice } from "../../../../../types/Item";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import Choices from "./ChoiceField/Choices";
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import MaltySelectTag from "./Tags/MaltySelectTag";
import useKeyCombination from "../../../../../Hooks/useKeyCombination"; import useKeyCombination from "../../../../../Hooks/useKeyCombination";
import { CombinationKeyEnum } from "../../../../../enums/CombinationKeyEnum"; import { CombinationKeyEnum } from "../../../../../enums/CombinationKeyEnum";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import LaTeXInput from "../../../../../Components/LatextInput/LaTeXInput"; import MainInputs from "./components/MainInputs";
import Questions from "./components/Questions";
const Form = () => { const Form = () => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const { setSuccess, Success, setSavedQuestionData ,ShowHint} = useObjectToEdit(); const { setSuccess, Success ,ShowHint} = useObjectToEdit();
console.log(formik.errors);
useEffect(() => {
setSavedQuestionData(formik.values);
}, [formik?.values]);
const handleAddChoice = ( const handleAddChoice = (
parent_index: number, parent_index: number,
fromKeyCombination: boolean = false, fromKeyCombination: boolean = false,
@ -71,7 +62,8 @@ const Form = () => {
}; };
const [t] = useTranslation(); const [t] = useTranslation();
const lastQuestions = formik?.values?.Questions?.length - 1;
const lastQuestions = formik?.values?.Questions?.length - 1;
useKeyCombination( useKeyCombination(
{ ctrlKey: true, shiftKey: true, code: CombinationKeyEnum.CHOICE }, { ctrlKey: true, shiftKey: true, code: CombinationKeyEnum.CHOICE },
() => { () => {
@ -101,60 +93,9 @@ const Form = () => {
return ( return (
<Row className="w-100 exercise_form_container"> <Row className="w-100 exercise_form_container">
<div className="exercise_form"> <MainInputs/>
<LaTeXInput name={"content"} label={t("input.main_question")}/>
<ImageBoxField name="content_image" />
<div></div>
</div>
<div className="flex"></div> <div className="flex"></div>
<Questions setFieldValue={formik.setFieldValue} values={formik.values} />
{((formik?.values as any)?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
return (
<div key={parent_index}>
<div className="exercise_form QuestionFIeld">
<QuestionFIeld
key={parent_index}
index={parent_index}
data={item}
/>
</div>
<Choices parent_index={parent_index} />
{formik?.values?.Questions?.[parent_index]?.answers?.length <
5 && (
<p className="add_new_button">
<FaCirclePlus
onClick={() => handleAddChoice(parent_index)}
size={23}
/>{" "}
{t("header.add_new_choice")}
</p>
)}
<div className="exercise_form_width">
{ShowHint &&
<ValidationField
className=" "
placeholder="_"
name={`Questions.${parent_index}.hint`}
label="hint_question"
type="TextArea"
style={{ width: "100%" , height: 60,resize:"none" }}
autoSize={{ minRows: 2, maxRows: 10 }}
showCount={false}
/>
}
<MaltySelectTag parent_index={parent_index} />
</div>
</div>
);
},
)}
<p className="add_new_button"> <p className="add_new_button">
<FaCirclePlus onClick={() => handleAddQuestion()} size={23} />{" "} <FaCirclePlus onClick={() => handleAddQuestion()} size={23} />{" "}
{t("header.add_new_question")} {t("header.add_new_question")}

View File

@ -1,6 +1,6 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Choice } from "../../../../../../types/Item"; import { Choice } from "../../../../../../types/Item";
import { useFormikContext } from "formik"; import { Field, useFormikContext } from "formik";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from "../../../../../../utils/getCharFromNumber"; import { getCharFromNumber } from "../../../../../../utils/getCharFromNumber";
import TextField from "./TextField"; import TextField from "./TextField";
@ -9,8 +9,10 @@ import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxFie
import { GoTrash } from "react-icons/go"; import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd"; import { Popconfirm } from "antd";
import LaTeXInput from "../../../../../../Components/LatextInput/LaTeXInput"; import LaTeXInput from "../../../../../../Components/LatextInput/LaTeXInput";
import LaTeXInputMemo from "../../../../../../Components/LatextInput/LaTeXInputMemo";
import ImageBoxFieldMemo from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxFieldMemo";
const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => { const QuestionFIeld = ({ index , setFieldValue,values }: { index: number , setFieldValue:any,values:any }) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit(); const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
@ -32,11 +34,11 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
const values = formik.values.Questions?.[index] ; const value = formik.values.Questions?.[index] ;
const handelCanDeleteAnswers = ()=>{ const handelCanDeleteAnswers = ()=>{
const content = values?.content ; const content = value?.content ;
const content_image = values?.content_image ; const content_image = value?.content_image ;
if(!content && !content_image ){ if(!content && !content_image ){
return true return true
} }
@ -49,13 +51,18 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
<> <>
<div className="exercise_forms"> <div className="exercise_forms">
<div className="ChoiceFields"> <div className="ChoiceFields">
<LaTeXInput id={`question_${index + 1}`} name={`Questions[${index}].content`} label={ <Field
t(`input.question`) + component={LaTeXInputMemo}
` ` + id={`question_${index + 1}`} name={`Questions[${index}].content`} label={
`( ${index + 1} )` t(`input.question`) +
}/> ` ` +
<ImageBoxField name={`Questions.${index}.content_image`} /> `( ${index + 1} )`
}
/>
<Field
component={ImageBoxFieldMemo}
name={`Questions.${index}.content_image`}
/>
{handelCanDeleteAnswers() ? {handelCanDeleteAnswers() ?
<div className="answer_status" > <div className="answer_status" >
<p className="delete_question_options" onClick={()=>{handleDeleteQuestion()}}> <p className="delete_question_options" onClick={()=>{handleDeleteQuestion()}}>

View File

@ -0,0 +1,9 @@
import React from 'react'
const QuestionList = () => {
return (
<div>QuestionList</div>
)
}
export default QuestionList

View File

@ -0,0 +1,27 @@
import { Field } from 'formik'
import React from 'react'
import LaTeXInputMemo from '../../../../../../Components/LatextInput/LaTeXInputMemo'
import ImageBoxFieldMemo from '../../../../../../Components/CustomFields/ImageBoxField/ImageBoxFieldMemo'
import { useTranslation } from 'react-i18next'
const MainInputs = () => {
const [t] = useTranslation()
return (
<div className="exercise_form">
<Field
name="content"
component={LaTeXInputMemo}
label={t("input.answer_content")}
/>
<Field
component={ImageBoxFieldMemo}
name="content_image"
/>
<div></div>
</div>
)
}
export default MainInputs

View File

@ -0,0 +1,115 @@
import React, { useCallback, useMemo } from 'react';
import { FixedSizeList as List, areEqual } from 'react-window';
import { Choice } from '../../../../../../types/Item';
import QuestionFIeld from '../QuestionFIeld/QuestionFIeld';
import Choices from '../ChoiceField/Choices';
import { FaCirclePlus } from 'react-icons/fa6';
import ValidationField from '../../../../../../Components/ValidationField/ValidationField';
import MaltySelectTag from '../Tags/MaltySelectTag';
import { useTranslation } from 'react-i18next';
import { useObjectToEdit } from '../../../../../../zustand/ObjectToEditState';
import { toast } from 'react-toastify';
interface QuestionsProps {
setFieldValue: (field: string, value: any) => void;
values: {
Questions: Choice[];
};
}
const Row: React.FC<any> = React.memo(({ index, data }) => {
const { values, setFieldValue } = data;
const { ShowHint } = useObjectToEdit();
const [t] = useTranslation();
const handleAddChoice = useCallback((
parent_index: number,
fromKeyCombination: boolean = false,
) => {
setFieldValue(`Questions.[${parent_index}].answers`, [
...(values?.Questions?.[parent_index]?.answers as Choice[]),
{
answer: null,
content_image: null,
content: null,
isCorrect: 0,
},
]);
if (fromKeyCombination) {
toast.success(t("header.new_choice_have_been_added"));
}
}, [setFieldValue, values, t]);
return (
<div key={index}>
<div className="exercise_form QuestionFIeld">
<QuestionFIeld setFieldValue={setFieldValue} values={values} key={index} index={index} />
</div>
<Choices setFieldValue={setFieldValue} values={values} parent_index={index} />
{values?.Questions?.[index]?.answers?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus
onClick={() => handleAddChoice(index)}
size={23}
/>{" "}
{t("header.add_new_choice")}
</p>
)}
<div className="exercise_form_width">
{ShowHint && (
<ValidationField
className=" "
placeholder="_"
name={`Questions.${index}.hint`}
label="hint_question"
type="TextArea"
style={{ width: "100%", height: 60, resize: "none" }}
autoSize={{ minRows: 2, maxRows: 10 }}
showCount={false}
/>
)}
<MaltySelectTag parent_index={index} />
</div>
</div>
);
}, areEqual);
const Questions: React.FC<QuestionsProps> = React.memo(({ setFieldValue, values }) => {
const questions = values.Questions || [];
const itemData = useMemo(() => ({ values, setFieldValue }), [values, setFieldValue]);
const itemSize = 1100;
const listHeight = useMemo(() => {
return Math.min(1300, questions.length * itemSize); // Adjust 400 to your desired max height
}, [questions.length]);
return (
<>
<List
height={listHeight}
itemCount={questions.length}
itemSize={itemSize}
width="100%"
itemData={itemData}
direction='rtl'
>
{Row}
</List>
{/* {(values?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
return (
<Row key={parent_index} index={parent_index} data={{values, setFieldValue}} />
);
}
)} */}
</>
);
}, (prevProps, nextProps) => prevProps.values.Questions === nextProps.values.Questions);
export default Questions;

View File

@ -0,0 +1,85 @@
import React from 'react'
import QuestionFIeld from '../QuestionFIeld/QuestionFIeld';
import { Choice } from '../../../../../../types/Item';
import Choices from '../ChoiceField/Choices';
import { FaCirclePlus } from 'react-icons/fa6';
import ValidationField from '../../../../../../Components/ValidationField/ValidationField';
import MaltySelectTag from '../Tags/MaltySelectTag';
import { useObjectToEdit } from '../../../../../../zustand/ObjectToEditState';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
// Memoized Questions Component
const Questions = React.memo(({setFieldValue,values}:any) => {
const { ShowHint } = useObjectToEdit();
const [t] = useTranslation();
const handleAddChoice = (
parent_index: number,
fromKeyCombination: boolean = false,
) => {
setFieldValue(`Questions.[${parent_index}].answers`, [
...((values as any)?.Questions?.[parent_index]
?.answers as Choice[]),
{
answer: null,
content_image: null,
content: null,
isCorrect: 0,
},
]);
if (fromKeyCombination) {
toast.success(t("header.new_choice_have_been_added"));
}
};
return (
<>
{(values?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
return (
<div key={parent_index}>
<div className="exercise_form QuestionFIeld">
<QuestionFIeld setFieldValue={setFieldValue} values={values} key={parent_index} index={parent_index} />
</div>
<Choices setFieldValue={setFieldValue} values={values} parent_index={parent_index} />
{values?.Questions?.[parent_index]?.answers?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus
onClick={() => handleAddChoice(parent_index)}
size={23}
/>{" "}
{t("header.add_new_choice")}
</p>
)}
<div className="exercise_form_width">
{ShowHint && (
<ValidationField
className=" "
placeholder="_"
name={`Questions.${parent_index}.hint`}
label="hint_question"
type="TextArea"
style={{ width: "100%", height: 60, resize: "none" }}
autoSize={{ minRows: 2, maxRows: 10 }}
showCount={false}
/>
)}
<MaltySelectTag parent_index={parent_index} />
</div>
</div>
);
}
)}
</>
);
}, (prevProps, nextProps) => {
return prevProps.values.Questions === nextProps.values.Questions;
});
export default Questions;

View File

@ -0,0 +1,126 @@
import React, { useCallback, useMemo, useRef, useEffect } from 'react';
import { VariableSizeList as List } from 'react-window';
import { Choice } from '../../../../../../types/Item';
import QuestionFIeld from '../QuestionFIeld/QuestionFIeld';
import Choices from '../ChoiceField/Choices';
import { FaCirclePlus } from 'react-icons/fa6';
import ValidationField from '../../../../../../Components/ValidationField/ValidationField';
import MaltySelectTag from '../Tags/MaltySelectTag';
import { useTranslation } from 'react-i18next';
import { useObjectToEdit } from '../../../../../../zustand/ObjectToEditState';
import { toast } from 'react-toastify';
interface QuestionsProps {
setFieldValue: (field: string, value: any) => void;
values: {
Questions: any[];
};
}
const Row: React.FC<any> = React.memo(({ index, style, data }) => {
const { values, setFieldValue, ShowHint, t } = data;
const handleAddChoice = useCallback((
parent_index: number,
fromKeyCombination: boolean = false,
) => {
setFieldValue(`Questions.[${parent_index}].answers`, [
...(values?.Questions?.[parent_index]?.answers as Choice[]),
{
answer: null,
content_image: null,
content: null,
isCorrect: 0,
},
]);
if (fromKeyCombination) {
toast.success(t("header.new_choice_have_been_added"));
}
}, [setFieldValue, values, t]);
return (
<div style={style}>
<div className="exercise_form QuestionFIeld">
<QuestionFIeld setFieldValue={setFieldValue} values={values} key={index} index={index} />
</div>
<Choices setFieldValue={setFieldValue} values={values} parent_index={index} />
{values?.Questions?.[index]?.answers?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus
onClick={() => handleAddChoice(index)}
size={23}
/>{" "}
{t("header.add_new_choice")}
</p>
)}
<div className="exercise_form_width">
{ShowHint && (
<ValidationField
className=" "
placeholder="_"
name={`Questions.${index}.hint`}
label="hint_question"
type="TextArea"
style={{ width: "100%", height: 60, resize: "none" }}
autoSize={{ minRows: 2, maxRows: 10 }}
showCount={false}
/>
)}
<MaltySelectTag parent_index={index} />
</div>
</div>
);
});
const Questions: React.FC<QuestionsProps> = React.memo(({ setFieldValue, values }) => {
const questions = values.Questions || [];
const { ShowHint } = useObjectToEdit();
const [t] = useTranslation();
const listRef = useRef<List>(null);
const getItemSize = useCallback((index: number) => {
const question = questions[index];
let height = 300; // Base height for QuestionField
height += (question.answers?.length || 0) * 212; // Height for each answer
if (question.answers?.length < 5) height += 40; // "Add new choice" button
if (ShowHint) height += 80; // Hint field
height += 50; // MaltySelectTag
return height;
}, [questions, ShowHint]);
const itemData = useMemo(() => ({
values,
setFieldValue,
ShowHint,
t
}), [values, setFieldValue, ShowHint, t]);
useEffect(() => {
if (listRef.current) {
listRef.current.resetAfterIndex(0);
}
}, [questions, ShowHint]);
return (
<List
ref={listRef}
height={window.innerHeight - 20} // Adjust based on your layout
itemCount={questions.length}
itemSize={getItemSize}
width="100%"
itemData={itemData}
direction='rtl'
className='ListQuestions'
>
{Row}
</List>
);
}, (prevProps, nextProps) => prevProps.values.Questions === nextProps.values.Questions);
export default Questions;

View File

@ -1,17 +1,18 @@
import { Row } from "reactstrap"; import { Row } from "reactstrap";
import ValidationField from "../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from "formik"; import { Field, useFormikContext } from "formik";
import { FaCirclePlus } from "react-icons/fa6"; import { FaCirclePlus } from "react-icons/fa6";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useObjectToEdit } from "../../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
import { useEffect } from "react"; import { useEffect } from "react";
import Choices from "./Field/Choices"; import Choices from "./Field/Choices";
import ImageBoxField from "../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import SelectTag from "../../../../Components/CustomFields/SelectTag"; import SelectTag from "../../../../Components/CustomFields/SelectTag";
import useKeyCombination from "../../../../Hooks/useKeyCombination"; import useKeyCombination from "../../../../Hooks/useKeyCombination";
import { CombinationKeyEnum } from "../../../../enums/CombinationKeyEnum"; import { CombinationKeyEnum } from "../../../../enums/CombinationKeyEnum";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import LaTeXInput from "../../../../Components/LatextInput/LaTeXInput"; import LaTeXInput from "../../../../Components/LatextInput/LaTeXInput";
import LaTeXInputMemo from "../../../../Components/LatextInput/LaTeXInputMemo";
import ImageBoxFieldMemo from "../../../../Components/CustomFields/ImageBoxField/ImageBoxFieldMemo";
const Form = () => { const Form = () => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -51,9 +52,16 @@ const Form = () => {
return ( return (
<Row className="w-100 exercise_form_container"> <Row className="w-100 exercise_form_container">
<div className="exercise_form"> <div className="exercise_form">
<Field
<LaTeXInput name={"content"} label={t("input.answer_content")}/> name="content"
<ImageBoxField name="content_image" /> component={LaTeXInputMemo}
label={t("input.answer_content")}
/>
<Field
component={ImageBoxFieldMemo}
name="content_image"
/>
</div> </div>
<Choices /> <Choices />

View File

@ -46,11 +46,8 @@ export const getValidationSchema = () => {
}).test( }).test(
"content-or-image-required", "content-or-image-required",
"At least one of content or content_image must be provided", "At least one of content or content_image must be provided",
(obj:any) => { (obj:any) => {
console.log(obj);
const isValid = !!obj.content || !!obj.content_image; const isValid = !!obj.content || !!obj.content_image;
console.log(isValid,"isValid");
return isValid; return isValid;
} }
@ -82,7 +79,6 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
}); });
const questions = newQuestions ?? [{answers:[]}]; const questions = newQuestions ?? [{answers:[]}];
console.log(objectToEdit,"objectToEdit");
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,

View File

@ -8,6 +8,9 @@
margin-bottom: 30px; margin-bottom: 30px;
// background: #000 !important; // background: #000 !important;
} }
.exercise_form{
margin-bottom: 20px;
}
.exercise_form, .exercise_form,
.ChoiceFields { .ChoiceFields {
.upload_image_button { .upload_image_button {
@ -42,7 +45,7 @@
.exercise_add_main { .exercise_add_main {
background: var(--bg); background: var(--bg);
padding: 10px 2vw; padding: 10px 2vw;
max-height: 84vh; // max-height: 84vh;
overflow-y: scroll; overflow-y: scroll;
@include Scrollbar(); @include Scrollbar();
} }
@ -350,4 +353,9 @@
transform: translateY(-40px); transform: translateY(-40px);
opacity: 0; opacity: 0;
} }
}
.ListQuestions{
@include Scrollbar()
} }

View File

@ -0,0 +1,23 @@
// utilityFunctions.ts
import { FieldProps } from 'formik';
export const areFieldPropsEqual = (
prevProps: any,
nextProps: any
): boolean => {
const prevError = prevProps.form.errors[prevProps.field.name];
const nextError = nextProps.form.errors[nextProps.field.name];
const prevTouched = prevProps.form.touched[prevProps.field.name];
const nextTouched = nextProps.form.touched[nextProps.field.name];
const prevValue = prevProps.field.value;
const nextValue = nextProps.field.value;
return (
prevValue === nextValue
&&
prevError === nextError &&
prevTouched === nextTouched
);
};

View File

@ -6,8 +6,8 @@ interface TextLatexPart {
export const parseTextAndLatex = (input: string): TextLatexPart[] => { export const parseTextAndLatex = (input: string): TextLatexPart[] => {
const result: TextLatexPart[] = []; const result: TextLatexPart[] = [];
const parts = input.split(/(\$\$[^$]+\$\$)/g); const parts = input?.split(/(\$\$[^$]+\$\$)/g);
parts.forEach((part,index) => { parts.forEach((part,index) => {
if (part.startsWith('$$') && part.endsWith('$$')) { if (part.startsWith('$$') && part.endsWith('$$')) {