Compare commits

..

6 Commits

Author SHA1 Message Date
karimaldeen
fc861e768f Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-14 15:10:54 +03:00
karimaldeen
5c41a13cb1 remove question drag and drop 2024-09-14 15:08:53 +03:00
karimaldeen
cd89ad405b add button for ux 2024-09-14 14:52:14 +03:00
karimaldeen
dbdaa04fde fix 2024-09-14 13:01:18 +03:00
karimaldeen
a8967b62f2 dix button delete 2024-09-14 11:59:18 +03:00
karimaldeen
0586a88cab add validation 2024-09-14 11:55:13 +03:00
25 changed files with 416 additions and 136 deletions

View File

@ -10,6 +10,7 @@
"Groupbutton", "Groupbutton",
"handelnavigate", "handelnavigate",
"Karim", "Karim",
"Popconfirm",
"queryqlent", "queryqlent",
"registraion", "registraion",
"SENDNOTIFICATION", "SENDNOTIFICATION",

View File

@ -65,7 +65,7 @@ const ImageBoxField = ({ name }: any) => {
</div> </div>
<div className="ImageBox"> <div className="ImageBox">
{imagePreview ? ( {imagePreview ? (
<img src={imagePreview} alt="Preview" className="imagePreview" /> <img src={imagePreview} onClick={handleButtonClick} alt="Preview" className="imagePreview" />
) : ( ) : (
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" /> <ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
)} )}

View File

@ -14,10 +14,10 @@ const TextField = ({
placeholder, placeholder,
isDisabled, isDisabled,
onChange, onChange,
props,
no_label, no_label,
label_icon, label_icon,
className, className,
...props
}: any) => { }: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props); const { formik, isError, errorMsg, t } = useFormField(name, props);
const TextFilehandleChange = ( const TextFilehandleChange = (
@ -48,6 +48,7 @@ const TextField = ({
onChange={onChange || TextFilehandleChange} onChange={onChange || TextFilehandleChange}
style={{ height: 120 }} style={{ height: 120 }}
id={name} id={name}
{...props}
/> />
</ValidationFieldContainer> </ValidationFieldContainer>
</div> </div>

View File

@ -4,14 +4,16 @@ import { useTranslation } from "react-i18next";
import { GoArrowSwitch } from "react-icons/go"; import { GoArrowSwitch } from "react-icons/go";
import { useObjectToEdit } from "../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import { QUESTION_OBJECT_KEY } from "../../config/AppKey"; import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
import { Popover } from "antd"; import { Popconfirm, Popover } from "antd";
import { CombinationKeyEnum } from "../../enums/CombinationKeyEnum"; import { CombinationKeyEnum } from "../../enums/CombinationKeyEnum";
import { PopconfirmProps } from "antd/lib";
const Header = () => { const Header = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const { values, setFieldValue, setValues } = useFormikContext<any>(); const { values, setFieldValue, setValues } = useFormikContext<any>();
const { isBseQuestion, setIsBseQuestion } = useObjectToEdit(); const { isBseQuestion, setIsBseQuestion } = useObjectToEdit();
const { setSavedQuestionData } = useObjectToEdit(); const { setSavedQuestionData } = useObjectToEdit();
const handleChange = () => { const handleChange = () => {
setSavedQuestionData(null); setSavedQuestionData(null);
localStorage.removeItem(QUESTION_OBJECT_KEY); localStorage.removeItem(QUESTION_OBJECT_KEY);
@ -26,6 +28,14 @@ const Header = () => {
} }
}; };
const confirm: PopconfirmProps['onConfirm'] = (e) => {
console.log(e);
setTimeout(() => {
handleChange()
}, 500);
};
const content = ( const content = (
<div> <div>
<p> <p>
@ -52,7 +62,18 @@ const Header = () => {
</div> </div>
</article> </article>
<div> <div>
<GoArrowSwitch onClick={handleChange} className="m-2" /> <Popconfirm
title={t("header.this_will_un_do_all_your_changes")}
okText={t("practical.yes")}
cancelText={t("practical.no")}
onConfirm={()=>{confirm()}}
defaultOpen={false}
>
<GoArrowSwitch className="m-2" />
</Popconfirm>
{isBseQuestion || values?.isBase === 1 {isBseQuestion || values?.isBase === 1
? t("header.malty_exercise") ? t("header.malty_exercise")
: t("header.exercise")} : t("header.exercise")}

View File

@ -7,6 +7,7 @@ import { useEffect } from "react";
const useFormField = (name: string, props?: any) => { const useFormField = (name: string, props?: any) => {
const [field, meta] = useField({ name, ...props }); const [field, meta] = useField({ name, ...props });
const { t } = useTranslation(); const { t } = useTranslation();
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();

View File

@ -29,6 +29,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{(formik) => { {(formik) => {
useEffect(() => { useEffect(() => {

View File

@ -10,7 +10,7 @@ import {
} from "./Model/formUtil"; } from "./Model/formUtil";
import { useAddQuestion, useAddQuestionAsync } from "../../../api/Question"; import { useAddQuestion, useAddQuestionAsync } from "../../../api/Question";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useLocation, useNavigate, useParams } from "react-router-dom";
import { ParamsEnum } from "../../../enums/params"; import { ParamsEnum } from "../../../enums/params";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
@ -19,9 +19,13 @@ import { Question } from "../../../types/Item";
import BaseForm from "./Model/Malty/Form"; import BaseForm from "./Model/Malty/Form";
import ModelForm from "./Model/ModelForm"; import ModelForm from "./Model/ModelForm";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { Form, Formik } from "formik";
import { MdOutlineArrowForwardIos } from "react-icons/md";
const AcceptModal = lazy(() => import("./Model/AcceptModal")); const AcceptModal = lazy(() => import("./Model/AcceptModal"));
const AddPage: React.FC = () => { const AddPage: React.FC = () => {
const location = useLocation();
const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync(); const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync();
const { mutate, isLoading, isSuccess } = useAddQuestion(); const { mutate, isLoading, isSuccess } = useAddQuestion();
const { isBseQuestion, setTagsSearch, objectToEdit, setSuccess } = const { isBseQuestion, setTagsSearch, objectToEdit, setSuccess } =
@ -30,12 +34,7 @@ const AddPage: React.FC = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const { subject_id, lesson_id } = useParams<ParamsEnum>(); const { subject_id, lesson_id } = useParams<ParamsEnum>();
console.log(objectToEdit, "objectToEdit"); const handleSubmit = ( values: any) => {
const handleSubmit = (
values: any,
{ resetForm }: { resetForm: () => void },
) => {
const DataToSend = structuredClone(values); const DataToSend = structuredClone(values);
setTagsSearch(null); setTagsSearch(null);
console.log(1); console.log(1);
@ -95,17 +94,6 @@ const AddPage: React.FC = () => {
}; };
}); });
if (answers?.length > 0) {
const isValidAnswers = answers?.some(
(answer: any) => answer?.isCorrect === 1,
);
console.log(!isValidAnswers);
if (!isValidAnswers) {
toast.error(t("validation.at_least_one_answer_should_be_correct"));
return;
}
}
const NewQuestion = { const NewQuestion = {
...values, ...values,
@ -122,14 +110,65 @@ const AddPage: React.FC = () => {
} }
}; };
const handleValidateSingleQuestion = (values:any)=>{
const haveAnswers = values?.answers?.length > 0 ;
const haveMoreThanOneAnswer = haveAnswers && values?.answers?.length > 1;
const haveOneAnswerRight = haveMoreThanOneAnswer && values?.answers?.some((item:any)=> item?.isCorrect === 1 || item.isCorrect === true )
if(!haveAnswers){
return false ;
}
if(!haveMoreThanOneAnswer){
toast.error(t("validation.it_should_have_more_than_one_answers")) ;
return false ;
}
if(!haveOneAnswerRight){
toast.error(t("validation.it_should_have_more_than_one_correct_answers")) ;
return false ;
}
}
const handleValidateBaseQuestion = (values: any) => {
const haveAnswers = values?.Questions?.every((Question: any, QuestionsIndex: number) => {
const answers = Question?.answers;
const haveAnswers = answers?.length > 0;
const haveMoreThanOneAnswer = haveAnswers && answers?.length > 1;
const haveOneAnswerRight =
haveMoreThanOneAnswer && answers?.some((item: any) => item?.isCorrect === 1 || item.isCorrect === true);
if (!haveAnswers) {
toast.error(t("validation.it_should_have_more_than_one_answers"));
return false;
}
if (!haveMoreThanOneAnswer) {
toast.error(t("validation.it_should_have_more_than_one_answers"));
return false;
}
if (!haveOneAnswerRight) {
toast.error(t("validation.it_should_have_more_than_one_correct_answers"));
return false;
}
return true;
});
console.log(haveAnswers, "haveAnswers");
};
const navigate = useNavigate(); const navigate = useNavigate();
const handleNavigateToPage = () => {
const cleanedUrl = location.pathname?.replace("/Question/add", "");
navigate(cleanedUrl);
};
const handleCancel = () => { const handleCancel = () => {
navigate(-1); navigate(-1);
}; };
const Loading = LoadingAsync || isLoading const Loading = LoadingAsync || isLoading
useEffect(() => { useEffect(() => {
console.log("all api success");
if (isSuccess) { if (isSuccess) {
setSuccess(true); setSuccess(true);
} }
@ -137,19 +176,32 @@ const AddPage: React.FC = () => {
if (isBseQuestion) { if (isBseQuestion) {
return ( return (
<div className="QuestionPractical">
<header>
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.add_new_question")}
</header>
<div className="exercise_add"> <div className="exercise_add">
<FormikForm <Formik
handleSubmit={handleSubmit} onSubmit={handleSubmit}
initialValues={getInitialValuesBase(objectToEdit)} initialValues={getInitialValuesBase({} as any)}
validationSchema={getValidationSchemaBase} validationSchema={getValidationSchemaBase}
enableReinitialize
> >
{({ values,handleSubmit }) => (
<Form className="w-100">
<main className="w-100 exercise_add_main"> <main className="w-100 exercise_add_main">
<Header /> <Header />
<BaseForm /> <BaseForm />
<div className="exercise_add_buttons"> <div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div> <div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={Loading} className="relative" type="submit"> <button disabled={Loading} className="relative" type="submit"
onClick={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values)}}
onSubmit={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values) }}
>
{t("practical.add")} {t("practical.add")}
{Loading && ( {Loading && (
@ -160,42 +212,67 @@ const AddPage: React.FC = () => {
</button> </button>
</div> </div>
</main> </main>
</FormikForm> </Form>
)}
</Formik>
<Suspense fallback={<Spin />}> <Suspense fallback={<Spin />}>
<AcceptModal /> <AcceptModal />
</Suspense> </Suspense>
</div> </div>
</div>
); );
} }
return ( return (
<div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValues(objectToEdit)}
validationSchema={getValidationSchema}
>
<main className="w-100 exercise_add_main">
<Header />
<ModelForm />
<div className="exercise_add_buttons"> <div className="QuestionPractical">
<div onClick={handleCancel}>{t("practical.back")}</div> <header>
<button disabled={Loading} className="relative" type="submit"> <MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.add_new_question")}
{t("practical.add")} </header>
<div className="exercise_add">
<Formik
enableReinitialize={true}
initialValues={getInitialValues({} as any)}
validationSchema={getValidationSchema}
onSubmit={(values) => {
handleSubmit(values);
}}
>
{({ values,handleSubmit }) => (
<Form className="w-100">
<main className="w-100 exercise_add_main">
<Header />
<ModelForm />
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
<button
disabled={Loading}
className="relative"
onClick={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values)}}
onSubmit={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values) }}
type="submit"
>
{t("practical.add")}
{Loading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</Form>
)}
</Formik>
{Loading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</FormikForm>
<Suspense fallback={<Spin />}> <Suspense fallback={<Spin />}>
<AcceptModal /> <AcceptModal />
</Suspense> </Suspense>
</div> </div>
</div>
); );
}; };

View File

@ -25,6 +25,8 @@ import BaseForm from "./Model/Malty/Form";
import { Question } from "../../../types/Item"; import { Question } from "../../../types/Item";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { deletePathSegments } from "../../../utils/deletePathSegments"; import { deletePathSegments } from "../../../utils/deletePathSegments";
import { Form, Formik } from "formik";
import { MdOutlineArrowForwardIos } from "react-icons/md";
const EditPage: React.FC = () => { const EditPage: React.FC = () => {
const { subject_id, lesson_id, question_id } = useParams<ParamsEnum>(); const { subject_id, lesson_id, question_id } = useParams<ParamsEnum>();
@ -187,6 +189,59 @@ const EditPage: React.FC = () => {
}; };
const handleValidateSingleQuestion = (values:any)=>{
const haveAnswers = values?.answers?.length > 0 ;
const haveMoreThanOneAnswer = haveAnswers && values?.answers?.length > 1;
const haveOneAnswerRight = haveMoreThanOneAnswer && values?.answers?.some((item:any)=> item?.isCorrect === 1 || item.isCorrect === true )
if(!haveAnswers){
return false ;
}
if(!haveMoreThanOneAnswer){
toast.error(t("validation.it_should_have_more_than_one_answers")) ;
return false ;
}
if(!haveOneAnswerRight){
toast.error(t("validation.it_should_have_more_than_one_correct_answers")) ;
return false ;
}
}
const handleValidateBaseQuestion = (values: any) => {
const haveAnswers = values?.Questions?.every((Question: any, QuestionsIndex: number) => {
const answers = Question?.answers;
const haveAnswers = answers?.length > 0;
const haveMoreThanOneAnswer = haveAnswers && answers?.length > 1;
const haveOneAnswerRight =
haveMoreThanOneAnswer && answers?.some((item: any) => item?.isCorrect === 1 || item.isCorrect === true);
if (!haveAnswers) {
toast.error(t("validation.it_should_have_more_than_one_answers"));
return false;
}
if (!haveMoreThanOneAnswer) {
toast.error(t("validation.it_should_have_more_than_one_answers"));
return false;
}
if (!haveOneAnswerRight) {
toast.error(t("validation.it_should_have_more_than_one_correct_answers"));
return false;
}
return true;
});
console.log(haveAnswers, "haveAnswers");
};
const handleNavigateToPage = () => {
const cleanedUrl = location.pathname.replace(/\/Question\/\d+$/, "");
navigate(cleanedUrl);
};
useEffect(() => { useEffect(() => {
if (isSuccess) { if (isSuccess) {
toast.success(t("validation.the_possess_done_successful")); toast.success(t("validation.the_possess_done_successful"));
@ -201,13 +256,24 @@ const EditPage: React.FC = () => {
} }
if (objectToEdit?.isBase) { if (objectToEdit?.isBase) {
return ( return (
<div className="QuestionPractical">
<header>
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.edit_question")}
</header>
<div className="exercise_add"> <div className="exercise_add">
<FormikForm <Formik
handleSubmit={handleSubmit} onSubmit={handleSubmit}
initialValues={getInitialValuesBase(objectToEdit)} initialValues={getInitialValuesBase(objectToEdit)}
validationSchema={getValidationSchemaBase} validationSchema={getValidationSchemaBase}
enableReinitialize
> >
<main className="w-100 exercise_add_main"> {({ values,handleSubmit }) => (
<Form className="w-100">
<main className="w-100 exercise_add_main">
{/* <Header/> */} {/* <Header/> */}
<header className="exercise_add_header mb-4"> <header className="exercise_add_header mb-4">
<div> <div>
@ -218,8 +284,10 @@ const EditPage: React.FC = () => {
<BaseForm /> <BaseForm />
<div className="exercise_add_buttons"> <div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div> <div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={Loading} className="relative" type="submit"> <button disabled={Loading} className="relative" type="submit"
{t("practical.edit")} onClick={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values)}}
onSubmit={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values) }}
> {t("practical.edit")}
{Loading && ( {Loading && (
<span className="Spinier_Div"> <span className="Spinier_Div">
@ -229,19 +297,35 @@ const EditPage: React.FC = () => {
</button> </button>
</div> </div>
</main> </main>
</FormikForm> </Form>
)}
</Formik>
</div>
</div> </div>
); );
} }
return ( return (
<div className="QuestionPractical">
<header>
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.edit_question")}
</header>
<div className="exercise_add"> <div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit} <Formik
initialValues={getInitialValues(objectToEdit)} enableReinitialize={true}
validationSchema={getValidationSchema} initialValues={getInitialValues(objectToEdit)}
> validationSchema={getValidationSchema}
<main className="w-100 exercise_add_main"> onSubmit={(values) => {
handleSubmit(values);
}}
>
{({ values,handleSubmit }) => (
<Form className="w-100">
<main className="w-100 exercise_add_main">
{/* <Header/> */} {/* <Header/> */}
<header className="exercise_add_header mb-4"> <header className="exercise_add_header mb-4">
<div> <div>
@ -252,8 +336,13 @@ const EditPage: React.FC = () => {
<ModelForm /> <ModelForm />
<div className="exercise_add_buttons"> <div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div> <div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={Loading} className="relative" type="submit"> <button
{t("practical.edit")} disabled={Loading}
className="relative"
onClick={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values)}}
onSubmit={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values) }}
type="submit"
> {t("practical.edit")}
{Loading && ( {Loading && (
<span className="Spinier_Div"> <span className="Spinier_Div">
@ -263,8 +352,14 @@ const EditPage: React.FC = () => {
</button> </button>
</div> </div>
</main> </main>
</FormikForm>
</Form>
)}
</Formik>
</div> </div>
</div>
); );
}; };

View File

@ -39,6 +39,7 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
name={index} name={index}
id={`choice_${index + 1}`} id={`choice_${index + 1}`}
type="TextArea" type="TextArea"
/> />
<ImageBoxField name={`answers.${index}.content_image`} /> <ImageBoxField name={`answers.${index}.content_image`} />
@ -50,11 +51,11 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
name={index} name={index}
type="Checkbox" type="Checkbox"
/> />
<p className="delete_question_options"> <p className="delete_question_options" onClick={handleDeleteChoice}>
{t("header.delete_choice")} {t("header.delete_choice")}
<GoTrash <GoTrash
className="trash_icon" className="trash_icon"
onClick={handleDeleteChoice}
size={17} size={17}
/> />
</p> </p>
@ -66,8 +67,9 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
placeholder="_" placeholder="_"
name={`answers.${index}.hint`} name={`answers.${index}.hint`}
label="hint" label="hint"
type="text" type="TextArea"
style={{ width: "100%" }} style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
/> />
</div> </div>
</> </>

View File

@ -36,12 +36,21 @@ const Choices = () => {
}; };
return ( return (
<DragDropContext onDragEnd={handleDragEnd}> <>
{formik?.values?.answers?.map((item: Choice, index: number) => {
return (
<div
>
<ChoiceFields index={index} data={item} />
</div>
);
})}
{/* <DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="choices"> <Droppable droppableId="choices">
{(provided) => ( {(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}> <div {...provided.droppableProps} ref={provided.innerRef}>
{formik?.values?.answers?.map((item: Choice, index: number) => { {formik?.values?.answers?.map((item: Choice, index: number) => {
// Use a unique identifier for draggableId
const draggableId = item.name const draggableId = item.name
? item.name.toString() ? item.name.toString()
: `item-${index}`; : `item-${index}`;
@ -67,11 +76,12 @@ const Choices = () => {
</Draggable> </Draggable>
); );
})} })}
{provided.placeholder} {/* Placeholder for spacing */} {provided.placeholder}
</div> </div>
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext> */}
</>
); );
}; };

View File

@ -72,11 +72,11 @@ const ChoiceFields = ({
type="Checkbox" type="Checkbox"
parent_index={parent_index} parent_index={parent_index}
/> />
<p className="delete_question_options"> <p className="delete_question_options" onClick={handleDeleteChoice}>
{t("header.delete_choice")} {t("header.delete_choice")}
<GoTrash <GoTrash
className="trash_icon" className="trash_icon"
onClick={handleDeleteChoice}
size={17} size={17}
/> />
</p> </p>
@ -89,8 +89,9 @@ const ChoiceFields = ({
placeholder="_" placeholder="_"
name={`Questions.${parent_index}.answers.${index}.hint`} name={`Questions.${parent_index}.answers.${index}.hint`}
label="hint" label="hint"
type="text" type="TextArea"
style={{ width: "100%" }} style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
/> />
</div> </div>
</> </>

View File

@ -44,7 +44,28 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
return ( return (
<> <>
<DragDropContext onDragEnd={handleDragEnd}>
<div>
{(
(formik?.values as any)?.Questions?.[parent_index]?.answers ||
[]
).map((item: Choice, index: number) => {
return (
<div
>
<ChoiceFields
key={index}
parent_index={parent_index}
index={index}
data={item}
/>
</div>
);
})}
</div>
{/* <DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="choices"> <Droppable droppableId="choices">
{(provided) => ( {(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}> <div {...provided.droppableProps} ref={provided.innerRef}>
@ -87,7 +108,7 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
</div> </div>
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext> */}
</> </>
); );
}; };

View File

@ -86,6 +86,8 @@ 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">
@ -99,7 +101,7 @@ const Form = () => {
<div></div> <div></div>
</div> </div>
<div className=" flex "></div> <div className="flex"></div>
{((formik?.values as any)?.Questions || [])?.map( {((formik?.values as any)?.Questions || [])?.map(
(item: Choice, parent_index: number) => { (item: Choice, parent_index: number) => {
@ -132,8 +134,9 @@ const Form = () => {
placeholder="_" placeholder="_"
name={`Questions.${parent_index}.hint`} name={`Questions.${parent_index}.hint`}
label="hint_question" label="hint_question"
type="text" type="TextArea"
style={{ width: "100%" }} style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
/> />
<MaltySelectTag parent_index={parent_index} /> <MaltySelectTag parent_index={parent_index} />
</div> </div>

View File

@ -53,12 +53,12 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
<ImageBoxField name={`Questions.${index}.content_image`} /> <ImageBoxField name={`Questions.${index}.content_image`} />
<div className="answer_status"> <div className="answer_status" onClick={handleDeleteQuestion}>
<p className="delete_question_options"> <p className="delete_question_options">
{t("header.delete_question")} {t("header.delete_question")}
<GoTrash <GoTrash
className="trash_icon" className="trash_icon"
onClick={handleDeleteQuestion}
size={17} size={17}
/> />
</p> </p>

View File

@ -41,14 +41,10 @@ const MaltySelectTag = ({ parent_index }: { parent_index: number }) => {
? [{ id: searchValue, name: searchValue }] ? [{ id: searchValue, name: searchValue }]
: []; : [];
console.log(options);
const value = const value =
formik?.values?.Questions[parent_index]?.tags?.map( formik?.values?.Questions[parent_index]?.tags?.map(
(item: any) => item?.id ?? item, (item: any) => item?.id ?? item,
) ?? []; ) ?? [];
console.log(formik?.values?.Questions[parent_index]);
console.log(value);
const AllOptions = [...options, ...additionalData]; const AllOptions = [...options, ...additionalData];

View File

@ -20,7 +20,7 @@ const Form = () => {
const handleAddChoice = (fromKeyCombination: boolean = false) => { const handleAddChoice = (fromKeyCombination: boolean = false) => {
formik.setFieldValue("answers", [ formik.setFieldValue("answers", [
...((formik?.values as any)?.answers as Choice[]), ...(formik?.values?.answers ?? []) ,
{ {
content: null, content: null,
content_image: null, content_image: null,
@ -42,13 +42,12 @@ const Form = () => {
useEffect(() => { useEffect(() => {
if (Success) { if (Success) {
formik?.setValues({}); formik.resetForm()
formik.setErrors({});
setSuccess(false); setSuccess(false);
console.log(formik.errors);
} }
}, [Success]); }, [Success]);
return ( return (
<Row className="w-100 exercise_form_container"> <Row className="w-100 exercise_form_container">
<div className="exercise_form"> <div className="exercise_form">
@ -62,7 +61,7 @@ const Form = () => {
</div> </div>
<Choices /> <Choices />
{formik?.values?.answers?.length < 5 && ( {(formik?.values?.answers === null || formik?.values?.answers === undefined || formik?.values?.answers?.length < 5) && (
<p className="add_new_button"> <p className="add_new_button">
<FaCirclePlus onClick={() => handleAddChoice()} size={23} />{" "} <FaCirclePlus onClick={() => handleAddChoice()} size={23} />{" "}
{t("header.add_new_choice")} {t("header.add_new_choice")}
@ -75,8 +74,10 @@ const Form = () => {
placeholder="_" placeholder="_"
name="hint" name="hint"
label="hint_question" label="hint_question"
type="text" type="TextArea"
style={{ width: "100%" }} style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
/> />
<SelectTag /> <SelectTag />
</div> </div>

View File

@ -16,7 +16,7 @@ export const getInitialValues = (objectToEdit: Question): any => {
hint: objectToEdit?.hint ?? null, hint: objectToEdit?.hint ?? null,
isBase: 0, isBase: 0,
parent_id: objectToEdit?.parent_id ?? "", parent_id: objectToEdit?.parent_id ?? "",
answers: objectToEdit?.answers ?? [], answers: objectToEdit?.answers ?? null,
tags: tags ?? [], tags: tags ?? [],
}; };
}; };
@ -32,6 +32,19 @@ export const getValidationSchema = () => {
content_image: Yup.string().nullable(), content_image: Yup.string().nullable(),
isCorrect: Yup.boolean(), isCorrect: Yup.boolean(),
}), }),
).min(2).test(
"at-least-one-correct",
"At least one answer must be correct",
(answers: any) => {
console.log(answers, "answers");
if(answers === null){
return true
}
return answers?.some(
(answer: any) =>
answer?.isCorrect === true || answer?.isCorrect === 1,
);
},
), ),
}); });
}; };
@ -42,10 +55,18 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
id: tag?.id, id: tag?.id,
name: tag?.name, name: tag?.name,
})); }));
console.log(item, "item"); const newAnswers = item?.answers?.map((item:any)=>{
return {
...item,
content : item?.content ?? null
}
})
console.log(newAnswers,"newAnswers");
return { return {
...item, ...item,
answer:newAnswers,
canAnswersBeShuffled: objectToEdit?.canAnswersBeShuffled ? 1 : 0, canAnswersBeShuffled: objectToEdit?.canAnswersBeShuffled ? 1 : 0,
hint: objectToEdit?.hint ?? "", hint: objectToEdit?.hint ?? "",
isBase: 0, isBase: 0,
@ -53,7 +74,7 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
}; };
}); });
const questions = newQuestions ?? []; const questions = newQuestions ?? [{answers:[]}];
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,
@ -84,12 +105,15 @@ export const getValidationSchemaBase = () => {
answer_image: Yup.string().nullable(), answer_image: Yup.string().nullable(),
isCorrect: Yup.boolean(), isCorrect: Yup.boolean(),
}), }),
) ).min(2)
.test( .test(
"at-least-one-correct", "at-least-one-correct",
"At least one answer must be correct", "At least one answer must be correct",
(answers: any) => { (answers: any) => {
console.log(answers, "answers");
if(answers === null){
return true
}
return answers.some( return answers.some(
(answer: any) => (answer: any) =>
@ -98,7 +122,7 @@ export const getValidationSchemaBase = () => {
}, },
), ),
}), }),
), ).min(1),
}); });
}; };

View File

@ -11,7 +11,6 @@ import DeleteModels from "../../../Layout/Dashboard/DeleteModels";
import { ModalEnum } from "../../../enums/Model"; import { ModalEnum } from "../../../enums/Model";
import { useGetAllSubject } from "../../../api/subject"; import { useGetAllSubject } from "../../../api/subject";
import { useGetAllGrade } from "../../../api/grade"; import { useGetAllGrade } from "../../../api/grade";
import { useGetAllCurriculum } from "../../../api/curriculum";
import PageHeader from "../../../Layout/Dashboard/PageHeader"; import PageHeader from "../../../Layout/Dashboard/PageHeader";
import { ABILITIES_ENUM } from "../../../enums/abilities"; import { ABILITIES_ENUM } from "../../../enums/abilities";
import { canAddQuestion } from "../../../utils/hasAbilityFn"; import { canAddQuestion } from "../../../utils/hasAbilityFn";

View File

@ -14,6 +14,7 @@ import {
} from "../../config/AppKey"; } from "../../config/AppKey";
import useFormatAuthDataToSelect from "../../utils/useFormatAuthDataToSelect"; import useFormatAuthDataToSelect from "../../utils/useFormatAuthDataToSelect";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ValidationField from "../../Components/ValidationField/ValidationField";
type FormFieldType = { type FormFieldType = {
isLoading: boolean; isLoading: boolean;
@ -25,22 +26,11 @@ const FormField = ({ isLoading }: FormFieldType) => {
<Form className="AuthForm"> <Form className="AuthForm">
{/* <Image style={{background:"#000"}} src="../App/Logo.png" /> */} {/* <Image style={{background:"#000"}} src="../App/Logo.png" /> */}
<h2>{t("تسجيل الدخول إلى حسابك")}</h2> <h2>{t("تسجيل الدخول إلى حسابك")}</h2>
<div className="AuthInput">
<label className="form-label" htmlFor="username">
{t("input.Username")}
</label>
<Field
placeholder={t("input.Username")}
as={Input}
type="text"
id="username"
name="username"
className="Input"
size="large"
/>
</div>
<div className="AuthInput"> <ValidationField name="username" label="username" />
<ValidationField name="password" label="password" />
{/* <div className="AuthInput">
<label className="form-label" htmlFor="password"> <label className="form-label" htmlFor="password">
{t("input.Password")} {t("input.Password")}
</label> </label>
@ -53,7 +43,7 @@ const FormField = ({ isLoading }: FormFieldType) => {
className="passwordInput" className="passwordInput"
size="large" size="large"
/> />
</div> </div> */}
<button disabled={isLoading} type="submit" className="auth_submit_button"> <button disabled={isLoading} type="submit" className="auth_submit_button">
{t("practical.login")} {t("practical.login")}

View File

@ -4,7 +4,7 @@ import useAuthState from "../../zustand/AuthState";
import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess"; import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess";
import { useLoginAdmin } from "../../api/auth"; import { useLoginAdmin } from "../../api/auth";
import FormField from "./FormField"; import FormField from "./FormField";
import { initialValues } from "./formutils"; import { initialValues, validationSchema } from "./formutils";
import { FormValues } from "../../types/Auth"; import { FormValues } from "../../types/Auth";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -39,7 +39,7 @@ const LoginForm = () => {
return ( return (
<div className="LoginForm"> <div className="LoginForm">
<Formik initialValues={initialValues} onSubmit={handelSubmit}> <Formik initialValues={initialValues} onSubmit={handelSubmit} validationSchema={validationSchema} >
{(formikProps) => <FormField isLoading={isLoading} />} {(formikProps) => <FormField isLoading={isLoading} />}
</Formik> </Formik>
</div> </div>

View File

@ -2,8 +2,8 @@ import * as Yup from "yup";
import { FormValues } from "../../types/Auth"; import { FormValues } from "../../types/Auth";
export const validationSchema = Yup.object().shape({ export const validationSchema = Yup.object().shape({
username: Yup.string().required("Username is required"), username: Yup.string().required("validation.required"),
password: Yup.string().required("Password is required"), password: Yup.string().required("validation.required").min(8,"validation.Password_must_be_at_least_8_characters_long"),
}); });
export const initialValues: FormValues = { export const initialValues: FormValues = {

View File

@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { ABILITIES_ENUM } from "../../enums/abilities"; import { ABILITIES_ENUM } from "../../enums/abilities";
import useSetPageTitle from "../../Hooks/useSetPageTitle"; import useSetPageTitle from "../../Hooks/useSetPageTitle";
import useFilter from "../../Components/FilterField/components/useFilter"; import useFilter from "../../Components/FilterField/components/useFilter";
import { Button, Popconfirm } from "antd";
const Dummy = () => { const Dummy = () => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -12,6 +13,7 @@ const Dummy = () => {
<div className="DummyHomePage"> <div className="DummyHomePage">
{/* <FilterButton /> {/* <FilterButton />
<FilterBody>karim</FilterBody> */} <FilterBody>karim</FilterBody> */}
</div> </div>
); );
}; };

View File

@ -10,12 +10,7 @@
} }
} }
} }
x
.Popover {
.ant-popover-inner {
padding: 0 !important;
}
}
.Color_type_checkbox.false { .Color_type_checkbox.false {
.ant-checkbox-checked .ant-checkbox-inner { .ant-checkbox-checked .ant-checkbox-inner {

View File

@ -41,7 +41,7 @@
// padding: 2vw; // padding: 2vw;
.exercise_add_main { .exercise_add_main {
background: var(--bg); background: var(--bg);
padding: 2vw; padding: 10px 2vw;
} }
.exercise_add_buttons { .exercise_add_buttons {
display: flex; display: flex;
@ -144,7 +144,8 @@
.delete_question_options { .delete_question_options {
margin-top: 25px; margin-top: 25px;
color: var(--warning); color: var(--warning);
z-index: 9999;
cursor: pointer;
padding-left: 10px; padding-left: 10px;
.trash_icon { .trash_icon {
margin-right: 10px !important; margin-right: 10px !important;
@ -211,3 +212,37 @@
transform: translateY(55px); transform: translateY(55px);
z-index: -1; z-index: -1;
} }
.ant-popconfirm .ant-popconfirm-buttons{
display: flex;
align-items: center;
justify-content: center;
>{
flex: 1;
min-height: auto;
min-width: 30px;
}
.ant-btn{
min-height: 30px !important;
max-height: 30px !important;
min-width: 50px;
padding: 5px !important;
display: flex;
align-items: center;
justify-content: center ;
}
}
.QuestionPractical{
display: flex;
flex-direction: column;
background: var(--bg);
>header{
padding: 30px 2vw 10px 2vw;
}
}

View File

@ -46,7 +46,9 @@
"grade_to_pass_must_be_less_than_max_grade": "يجب أن تكون درجة النجاح أقل من الحد الأقصى للدرجة", "grade_to_pass_must_be_less_than_max_grade": "يجب أن تكون درجة النجاح أقل من الحد الأقصى للدرجة",
"max_mark_must_be_greater_than_min_mark_to_pass": "يجب ان تكون اكبر من علامة النجاح", "max_mark_must_be_greater_than_min_mark_to_pass": "يجب ان تكون اكبر من علامة النجاح",
"Sorry, the question must have at least one option": "عذرًا، يجب أن يحتوي السؤال على خيار واحد على الأقل", "Sorry, the question must have at least one option": "عذرًا، يجب أن يحتوي السؤال على خيار واحد على الأقل",
"at_least_one_answer_should_be_correct": "يجب أن تكون إجابة واحدة صحيحة" "at_least_one_answer_should_be_correct": "يجب أن تكون إجابة واحدة صحيحة",
"it_should_have_more_than_one_answers":"يجب أن يحتوي على أكثر من إجابة",
"it_should_have_more_than_one_correct_answers":"يجب أن يحتوي على إجابة صحيحة"
}, },
"header": { "header": {
"register_students": "تسجيل الطلاب", "register_students": "تسجيل الطلاب",
@ -123,7 +125,9 @@
"personal_information": "المعلومات الشخصية", "personal_information": "المعلومات الشخصية",
"address": "العنوان", "address": "العنوان",
"attachment": "المرفق", "attachment": "المرفق",
"subject_of_class": "مواد الصف" "subject_of_class": "مواد الصف",
"this_will_un_do_all_your_changes":"سوف يؤدي هذا إلى إلغاء جميع تغييراتك",
"edit_question":"تعديل سؤال"
}, },
"columns": { "columns": {
"id": "الرقم التعريفي", "id": "الرقم التعريفي",