fix lag question
This commit is contained in:
parent
efb687b742
commit
869e857f2c
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -11,6 +11,7 @@
|
||||||
"handelnavigate",
|
"handelnavigate",
|
||||||
"Karim",
|
"Karim",
|
||||||
"katex",
|
"katex",
|
||||||
|
"Latext",
|
||||||
"Popconfirm",
|
"Popconfirm",
|
||||||
"queryqlent",
|
"queryqlent",
|
||||||
"registraion",
|
"registraion",
|
||||||
|
|
|
||||||
71
package-lock.json
generated
71
package-lock.json
generated
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
105
src/Components/CustomFields/ImageBoxField/ImageBoxFieldMemo.tsx
Normal file
105
src/Components/CustomFields/ImageBoxField/ImageBoxFieldMemo.tsx
Normal 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;
|
||||||
106
src/Components/LatextInput/LaTeXInputMemo.tsx
Normal file
106
src/Components/LatextInput/LaTeXInputMemo.tsx
Normal 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;
|
||||||
23
src/Components/LatextInput/areFieldPropsEqual.ts
Normal file
23
src/Components/LatextInput/areFieldPropsEqual.ts
Normal 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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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")}
|
||||||
|
|
|
||||||
|
|
@ -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()}}>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const QuestionList = () => {
|
||||||
|
return (
|
||||||
|
<div>QuestionList</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QuestionList
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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;
|
||||||
126
src/Pages/Admin/question/Model/Malty/components/Questions.tsx
Normal file
126
src/Pages/Admin/question/Model/Malty/components/Questions.tsx
Normal 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;
|
||||||
|
|
@ -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 />
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
23
src/utils/areFieldPropsEqual.ts
Normal file
23
src/utils/areFieldPropsEqual.ts
Normal 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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -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('$$')) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user