Compare commits

..

No commits in common. "251359e935443c2b2a3c4f20724e07be0e5f9575" and "82c1fb4944e3e3425e9989aa1e0d6c9ccfde6461" have entirely different histories.

20 changed files with 49 additions and 243 deletions

View File

@ -5,7 +5,6 @@ import ImageIcon from "./ImageIcon";
import ImageCancelIcon from "./ImageCancelIcon";
import { getNestedValue } from "../../../utils/getNestedValue";
import { generateImagePreview } from "./generateImagePreview";
import { useTranslation } from "react-i18next";
// Helper function to generate image preview from a File
@ -14,7 +13,7 @@ const ImageBoxField = ({ name }: any) => {
const value = getNestedValue(formik?.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);
@ -27,19 +26,6 @@ const ImageBoxField = ({ name }: any) => {
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
console.log('File selected:', file);
}
if (file) {
generateImagePreview(file, setImagePreview);
formik.setFieldValue(name, file);

View File

@ -1,62 +1,14 @@
import React, { useCallback, useState } from "react";
import '../styles/index.scss';
import CustomInput from "../design-system/CustomInput";
import { Button } from "antd";
import { useTranslation } from "react-i18next";
import React from "react";
interface IFilterBody {
children: React.ReactNode;
}
const useFilter = () => {
const [isBodyVisible, setIsBodyVisible] = useState(true);
const toggleBodyVisibility = () => {
setIsBodyVisible((prev) => !prev);
};
const FilterButton = () => {
return (
<button onClick={toggleBodyVisibility}>
{isBodyVisible ? "Hide Filter" : "Show Filter"}
</button>
);
return <div>FilterButton</div>;
};
const FilterBody = ({ children }: IFilterBody) => {
const [values, setValues] = useState({ name1: '', name2: '' });
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setValues((prev) => ({ ...prev, [name]: value }));
}, []);
const handleSubmit = (event:React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(values,"values");
};
const [t] = useTranslation()
return (
<div className={`filter_body ${isBodyVisible ? 'visible' : 'hidden'}`}>
<form onSubmit={handleSubmit} >
{children}
<CustomInput
name="name1"
value={values.name1}
onChange={handleChange}
/>
<CustomInput
name="name2"
value={values.name2}
onChange={handleChange}
/>
<Button block htmlType="submit" type="primary" > {t("practical.submit")} </Button>
</form>
</div>
);
return <div>FilterBody</div>;
};
return {

View File

@ -1,22 +0,0 @@
import { Input } from 'antd';
import React from 'react';
interface CustomInputProps {
name: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const CustomInput: React.FC<CustomInputProps> = React.memo(({ name, value, onChange }) => {
console.log(`Rendering ${name}`); // For debugging purposes
return (
<Input
type="text"
name={name}
value={value}
onChange={onChange}
/>
);
});
export default CustomInput;

View File

@ -1,33 +0,0 @@
.filter_body {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out, opacity 0.3s ease-out, transform 0.3s ease-out;
opacity: 0;
transform: translateY(-20px);
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.filter_body.visible {
max-height: 200px;
opacity: 1;
transform: translateY(0);
}
.filter_body.hidden {
max-height: 0;
opacity: 0;
transform: translateY(-20px);
}
.DummyHomePage {
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
width: 70%;
padding: 50px;
}

View File

@ -37,14 +37,19 @@ interface FormikFormProps extends Omit<FormikConfig<any>, OmitFormikProps> {
setIsOpen: any;
}
interface SubmitButtonProps extends Omit<ButtonProps, "loading"> {}
const useFilter = () => {
const { setIsOpen, isOpen } = useModalState((state) => state);
const { filterState, setFilterState, clearFilterState } = useFilterState();
const [t] = useTranslation();
const [formValues, setFormValues] = useState({});
const formik = useFormikContext();
// Define the type for the callback
type SubmitCallback = () => void;
// console.log(formik?.values);
// console.log(InitialValue);
const FilterButton = () => {
const handleState = () => {
if (isOpen === ModalEnum?.FILTER) {

View File

@ -48,18 +48,6 @@ const File = ({
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
onSuccess();
};
const beforeUpload = (file: File) => {
const maxSize = 2 * 1024 * 1024; // 2 MB in bytes
if (file.size > maxSize) {
alert(t('validation.File_size_exceeds_2_MB_limit.'));
return Upload.LIST_IGNORE; // Prevent the file from being uploaded
}
return true; // Allow the file to be uploaded
};
return (
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
<label htmlFor={name} className="text">
@ -67,7 +55,6 @@ const File = ({
</label>
<Upload
beforeUpload={beforeUpload} // Set the beforeUpload function
disabled={isDisabled}
listType="picture"
maxCount={1}
@ -76,7 +63,6 @@ const File = ({
customRequest={customRequest}
className={` w-100`}
id={name}
>
<Button
className={isError ? "isError w-100 " : " w-100"}

View File

@ -24,6 +24,8 @@ const DeleteModels: React.FC<ModalFormProps> = ({
const { mutate, isLoading, isSuccess } = deleteMutation;
const { objectToEdit, setObjectToEdit } = useObjectToEdit();
console.log(objectToEdit?.key);
console.log(inputValue);
const iaDisabled = idVerify
? Number(objectToEdit?.id) !== Number(inputValue) || isLoading

View File

@ -15,20 +15,16 @@ import {
import ActionButtons from "../../../Components/Table/ActionButtons";
import ColumnsImage from "../../../Components/Columns/ColumnsImage";
import { Grade } from "../../../types/Grade";
import { useFilterState } from "../../../Components/Utils/Filter/FilterState";
import { useFilterStateState } from "../../../zustand/Filter";
import { CiImageOff } from "react-icons/ci";
import { isValidImage } from "../../../utils/isValidImage";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const { setFilter } = useFilterStateState();
const handelShow = (record: Grade) => {
setFilter({})
navigate(`${record?.id}`);
};

View File

@ -11,13 +11,11 @@ import {
canEditQuestion,
} from "../../../utils/hasAbilityFn";
import ActionButtons from "../../../Components/Table/ActionButtons";
import { useFilterStateState } from "../../../zustand/Filter";
export const useColumns = () => {
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const { setIsOpen } = useModalState((state) => state);
const { setFilter } = useFilterStateState();
const handelDelete = (data: any) => {
setObjectToEdit(data);
@ -30,8 +28,6 @@ export const useColumns = () => {
const unit = lesson?.unit;
const subject = unit?.subject;
const grade = subject?.grade;
setFilter({})
navigate(`/${ABILITIES_ENUM?.GRADE}/${grade?.id}/${ABILITIES_ENUM?.SUBJECT}/${subject?.id}/${ABILITIES_ENUM?.UNIT}/${unit?.id}/${ABILITIES_ENUM?.LESSON}/${lesson?.id}/${ABILITIES_ENUM?.QUESTION}/${record?.id}`);
};
const [t] = useTranslation();

View File

@ -19,18 +19,14 @@ import ActionButtons from "../../../Components/Table/ActionButtons";
import { Unit } from "../../../types/Unit";
import { ConvertEnumToTranslate } from "../../../utils/ConvertEnumToTranslate";
import { DragHandleUnit } from "./DrapableTable";
import { useFilterState } from "../../../Components/Utils/Filter/FilterState";
import { useFilterStateState } from "../../../zustand/Filter";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setFilter } = useFilterStateState();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const handelShow = (record: Unit) => {
setFilter({})
navigate(`${ABILITIES_ENUM?.UNIT}/${record?.id}`);
};

View File

@ -15,18 +15,14 @@ import {
} from "../../../utils/hasAbilityFn";
import ActionButtons from "../../../Components/Table/ActionButtons";
import { DragHandleLesson } from "./DrapableTable";
import { useFilterState } from "../../../Components/Utils/Filter/FilterState";
import { useFilterStateState } from "../../../zustand/Filter";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const { setFilter } = useFilterStateState();
const handelShow = (record: any) => {
setFilter({})
navigate(`${ABILITIES_ENUM.LESSON}/${record?.id}`);
};

View File

@ -26,7 +26,7 @@ const AddPage: React.FC = () => {
const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync();
const { mutate, isLoading, isSuccess } = useAddQuestion();
const { isBseQuestion, setTagsSearch, setSuccess } =
const { isBseQuestion, setTagsSearch, objectToEdit, setSuccess } =
useObjectToEdit();
const [t] = useTranslation();
@ -35,6 +35,7 @@ const AddPage: React.FC = () => {
const handleSubmit = ( values: any) => {
const DataToSend = structuredClone(values);
setTagsSearch(null);
console.log(1);
const canAnswersBeShuffled = DataToSend?.canAnswersBeShuffled ? 1 : 0;
@ -63,7 +64,12 @@ const AddPage: React.FC = () => {
...item,
};
});
console.log(answers);
if (answers?.length > 0) {
const isValidAnswers = answers?.some(
(answer: any) => answer?.isCorrect === 1,
);
}
mutate({
...item,
parent_id: newBseQuestionId,
@ -76,6 +82,8 @@ const AddPage: React.FC = () => {
console.log(newBseQuestionId, "newBseQuestionId");
});
} else {
console.log(1);
const tags = processTags(DataToSend);
const answers = values?.answers?.map((item: any, index: number) => {
return {
@ -104,8 +112,6 @@ const AddPage: React.FC = () => {
const handleValidateSingleQuestion = (values:any)=>{
const haveMoreThanOneAnswer = values?.answers?.length > 1;
const haveOneAnswerRight = haveMoreThanOneAnswer && values?.answers?.some((item:any)=> item?.isCorrect === 1 || item.isCorrect === true )
const haveImageOrContent = haveOneAnswerRight && values?.answers?.some((item:any)=> !(item?.content) && !(item.content_image) )
if(!haveMoreThanOneAnswer){
toast.error(t("validation.it_should_have_more_than_one_answers")) ;
return false ;
@ -114,10 +120,6 @@ const AddPage: React.FC = () => {
toast.error(t("validation.it_should_have_more_than_one_correct_answers")) ;
return false ;
}
if(haveImageOrContent){
toast.error("validation.one_of_image_and_content_should_be_enter")
return false
}
}
@ -126,8 +128,8 @@ const AddPage: React.FC = () => {
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);
const haveImageOrContent = haveOneAnswerRight && values?.answers?.some((item:any)=> !(item?.content) && !(item.content_image) )
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"));
@ -144,11 +146,6 @@ const AddPage: React.FC = () => {
return false;
}
if(haveImageOrContent){
toast.error("validation.one_of_image_and_content_should_be_enter")
return false
}
return true;
});

View File

@ -8,7 +8,6 @@ import CheckboxField from "./CheckboxField";
import TextField from "./TextField";
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd";
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>();
@ -52,16 +51,7 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
name={index}
type="Checkbox"
/>
<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">
<p className="delete_question_options" onClick={handleDeleteChoice}>
{t("header.delete_choice")}
<GoTrash
className="trash_icon"
@ -69,10 +59,6 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
size={17}
/>
</p>
</Popconfirm>
</div>
</div>
<div className="exercise_form_width">

View File

@ -9,7 +9,6 @@ import TextField from "./TextField";
import { toast } from "react-toastify";
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd";
const ChoiceFields = ({
index,
@ -70,16 +69,7 @@ const ChoiceFields = ({
type="Checkbox"
parent_index={parent_index}
/>
<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" >
<p className="delete_question_options" onClick={handleDeleteChoice}>
{t("header.delete_choice")}
<GoTrash
className="trash_icon"
@ -87,8 +77,6 @@ const ChoiceFields = ({
size={17}
/>
</p>
</Popconfirm>
</div>
</div>

View File

@ -7,7 +7,6 @@ import TextField from "./TextField";
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd";
const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>();
@ -48,15 +47,7 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
<ImageBoxField name={`Questions.${index}.content_image`} />
<div className="answer_status" >
<Popconfirm
title={t("header.this_will_un_do_all_your_changes")}
okText={t("practical.yes")}
cancelText={t("practical.no")}
onConfirm={()=>{handleDeleteQuestion()}}
defaultOpen={false}
>
<div className="answer_status" onClick={handleDeleteQuestion}>
<p className="delete_question_options">
{t("header.delete_question")}
<GoTrash
@ -65,10 +56,7 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
size={17}
/>
</p>
</Popconfirm>
</div>
</div>
</div>
</>

View File

@ -25,10 +25,10 @@ export const getValidationSchema = () => {
// validate input
return Yup.object().shape({
content_image: Yup.string().nullable(),
content: Yup.string().required("validation.required").nullable(),
content: Yup.string().required("validation.required"),
answers: Yup.array().of(
Yup.object().shape({
content: Yup.string().nullable(),
content: Yup.string().required("validation.required"),
content_image: Yup.string().nullable(),
isCorrect: Yup.boolean(),
}),
@ -36,12 +36,10 @@ export const getValidationSchema = () => {
"at-least-one-correct",
"At least one answer must be correct",
(answers: any) => {
const hasCorrectAnswer = answers?.some((answer:any) => answer?.isCorrect === true || answer?.isCorrect === 1);
const haveImageOrContent = answers?.some((item:any)=> !(item?.content) && !(item.content_image));
return hasCorrectAnswer && !haveImageOrContent ;
return answers?.some(
(answer: any) =>
answer?.isCorrect === true || answer?.isCorrect === 1,
);
},
),
});
@ -109,11 +107,10 @@ export const getValidationSchemaBase = () => {
"At least one answer must be correct",
(answers: any) => {
const hasCorrectAnswer = answers?.some((answer:any) => answer?.isCorrect === true || answer?.isCorrect === 1);
const haveImageOrContent = answers?.some((item:any)=> !(item?.content) && !(item.content_image));
return hasCorrectAnswer && !haveImageOrContent ;
return answers.some(
(answer: any) =>
answer.isCorrect === true || answer.isCorrect === 1,
);
},
),
}),

View File

@ -14,13 +14,12 @@ import {
} from "../../../../utils/hasAbilityFn";
import { ABILITIES_ENUM } from "../../../../enums/abilities";
import { Subject } from "../../../../types/Subject";
import { useFilterStateState } from "../../../../zustand/Filter";
import { CiImageOff } from "react-icons/ci";
export const useColumns = () => {
const navigate = useNavigate();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const { setIsOpen } = useModalState((state) => state);
const { setFilter } = useFilterStateState();
const handelDelete = (record: Subject) => {
setObjectToEdit(record);
@ -32,7 +31,6 @@ export const useColumns = () => {
};
const handelShow = (record: Subject) => {
setFilter({})
navigate(`${ABILITIES_ENUM?.SUBJECT}/${record?.id}`);
};

View File

@ -1,19 +1,19 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { ABILITIES_ENUM } from "../../enums/abilities";
import useSetPageTitle from "../../Hooks/useSetPageTitle";
import useFilter from "../../Components/FilterField/components/useFilter";
import { Button, Popconfirm } from "antd";
import PageTitle from "../../Layout/Dashboard/PageTitle";
const Dummy = () => {
const [t] = useTranslation();
useSetPageTitle(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
const { FilterButton, FilterBody } = useFilter();
return (
<div className="DummyHomePage">
<PageTitle/>
<FilterButton/>
<FilterBody>
karim
</FilterBody>
karim2
</div>
);
};

View File

@ -10,7 +10,6 @@
height: 100vh;
overflow-y: auto;
overflow-x: hidden;
background: var(--bgSideBar);
color: var(--textSideBar);
position: absolute;
@ -20,12 +19,6 @@
flex-direction: column;
z-index: 2;
&::-webkit-scrollbar {
display: none;
}
.side_bar_header {
height: var(--navBarHeight);
display: flex;

View File

@ -48,8 +48,7 @@
"Sorry, the question must have at least one option": "عذرًا، يجب أن يحتوي السؤال على خيار واحد على الأقل",
"at_least_one_answer_should_be_correct": "يجب أن تكون إجابة واحدة صحيحة",
"it_should_have_more_than_one_answers":"يجب أن يحتوي على أكثر من إجابة",
"it_should_have_more_than_one_correct_answers":"يجب أن يحتوي على إجابة صحيحة",
"File_size_exceeds_2_MB_limit.":"حجم الملف يتجاوز الحد الأقصى البالغ 2 ميجابايت"
"it_should_have_more_than_one_correct_answers":"يجب أن يحتوي على إجابة صحيحة"
},
"header": {
"register_students": "تسجيل الطلاب",