Compare commits

..

No commits in common. "3407250e9261123958aac2a809637d6886f35d30" and "7506c9850d5910ec06b24d3654513aa1e527f589" have entirely different histories.

15 changed files with 49 additions and 293 deletions

File diff suppressed because one or more lines are too long

View File

@ -3,26 +3,22 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; 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';
const Header = () => { const Header = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const { values, setFieldValue,setValues } = useFormikContext<any>(); const { values, setFieldValue,setValues } = useFormikContext<any>();
const {isBseQuestion,set_isBseQuestion} = useObjectToEdit() const {isBseQuestion,set_isBseQuestion} = useObjectToEdit()
const {set_SavedQuestionData} = useObjectToEdit()
const handleChange = () => { const handleChange = () => {
set_SavedQuestionData(null)
localStorage.removeItem(QUESTION_OBJECT_KEY)
if (isBseQuestion) { if (isBseQuestion) {
set_isBseQuestion(false) set_isBseQuestion(false)
setValues(null) setValues(null)
setFieldValue("isBase",0)
} else { } else {
set_isBseQuestion(true) set_isBseQuestion(true)
setValues(null) setValues(null)
setFieldValue("isBase",1)
} }
}; };
@ -33,7 +29,7 @@ const Header = () => {
</div> </div>
<div> <div>
<GoArrowSwitch onClick={handleChange} className="m-2" /> <GoArrowSwitch onClick={handleChange} className="m-2" />
{isBseQuestion || values?.isBase === 1 ? t("header.malty_exercise") :t("header.exercise") } {isBseQuestion ? t("header.malty_exercise") :t("header.exercise") }
</div> </div>
</header> </header>
); );

View File

@ -1,32 +1,20 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { setLocalStorageQuestions } from '../utils/setLocalStorageQuestions';
import { setLocalStorageBaseQuestions } from '../utils/setLocalStorageBaseQuestions';
const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, SavedQuestionData: any) => { const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, SavedQuestionData: any) => {
useEffect(() => { useEffect(() => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => { const handleBeforeUnload = (event: BeforeUnloadEvent) => {
console.log("disconnect"); console.log("disconnect");
if (noChange) { if (noChange) {
if(SavedQuestionData?.isBase ===1){ const jsonData = JSON.stringify(SavedQuestionData);
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData); localStorage.setItem(QUESTION_OBJECT_KEY, jsonData);
}else{
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
}
} }
}; };
const handleOffline = () => { const handleOffline = () => {
console.log("disconnect"); console.log("disconnect");
if (noChange) { if (noChange) {
if(SavedQuestionData?.isBase ===1){ const jsonData = JSON.stringify(SavedQuestionData);
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData); localStorage.setItem(QUESTION_OBJECT_KEY, jsonData);
}else{
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
}
} }
}; };

View File

@ -1,8 +1,9 @@
import React, { Suspense, lazy, useEffect } from "react"; import React, { useEffect } from "react";
import { Modal, Spin } from "antd"; import { Modal, Spin } from "antd";
import FormikForm from "../../Layout/Dashboard/FormikFormModel"; import FormikForm from "../../Layout/Dashboard/FormikFormModel";
import ModelBody from "./Model/Add";
import { getInitialValues, getValidationSchema ,getInitialValuesBase, getValidationSchemaBase, processTags} from "./Model/formUtil"; import { getInitialValues, getValidationSchema ,getInitialValuesBase, getValidationSchemaBase, processTags} from "./Model/formUtil";
import { useAddQuestion, useAddQuestionAsync } from "../../api/Question"; import { useAddQuestion } from "../../api/Question";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params"; import { ParamsEnum } from "../../enums/params";
@ -13,33 +14,33 @@ import { Question } from "../../types/Item";
import BaseForm from './Model/Malty/Add' import BaseForm from './Model/Malty/Add'
import Form from './Model/Add' import Form from './Model/Add'
import { toast } from "react-toastify"; import { toast } from "react-toastify";
const AcceptModal = lazy(() => import('./Model/AcceptModal')); import AcceptModal from "./Model/AcceptModal";
import { useModalState } from "../../zustand/Modal"; import { useModalState } from "../../zustand/Modal";
import { ModalEnum } from "../../enums/Model"; import { ModalEnum } from "../../enums/Model";
import { cleanObject } from "../../utils/cleanObject"; import { cleanObject } from "../../utils/cleanObject";
import { hasItems } from "../../utils/hasItems"; import { hasItems } from "../../utils/hasItems";
import { QUESTION_OBJECT_KEY } from "../../config/AppKey"; import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
import useSaveOnDisconnect from "../../Hooks/useSaveOnDisconnect"; import useSaveOnDisconnect from "../../Hooks/useSaveOnDisconnect";
import { getLocalStorageQuestions } from "../../utils/setLocalStorageQuestions"; import { getLocalStorage } from "../../utils/LocalStorage";
const AddPage: React.FC = () => { const AddPage: React.FC = () => {
const {isSuccess:isSuccessAsync,mutateAsync} = useAddQuestionAsync()
const { mutate,isSuccess, isLoading} = useAddQuestion(); const { mutate, isSuccess, isLoading ,mutateAsync} = useAddQuestion();
const {isBseQuestion,set_Tags_search,set_object_to_edit,set_Success,SavedQuestionData} = useObjectToEdit() const {object_to_edit,set_Tags_search,set_object_to_edit,set_Success,SavedQuestionData} = useObjectToEdit()
const {subject_id,lesson_id} = useParams<ParamsEnum>() const {subject_id,lesson_id} = useParams<ParamsEnum>()
const {isBseQuestion,set_isBseQuestion} = useObjectToEdit()
const { setIsOpen } = useModalState((state) => state); const { setIsOpen } = useModalState((state) => state);
const handleSubmit = (values: any, { resetForm }: { resetForm: () => void }) => { const handleSubmit = (values: any, { resetForm }: { resetForm: () => void }) => {
const DataToSend = structuredClone(values); const DataToSend = structuredClone(values);
console.log(DataToSend, "DataToSend");
set_Tags_search(null); set_Tags_search(null);
console.log(isBseQuestion);
if (isBseQuestion) {
if (isBseQuestion || DataToSend?.isBase === 1) {
const newBseQuestion = { const newBseQuestion = {
"subject_id": subject_id, "subject_id": subject_id,
"content": DataToSend?.content, "content": DataToSend?.content,
@ -50,15 +51,13 @@ const AddPage: React.FC = () => {
mutateAsync(newBseQuestion).then((data:any) => { mutateAsync(newBseQuestion).then((data) => {
const newBseQuestionId = (data as any)?.data?.id; const newBseQuestionId = (data as any)?.data?.id;
const Questions = DataToSend?.Questions; const Questions = DataToSend?.Questions;
console.log(1);
Questions?.map((item: Question) => { Questions?.map((item: Question) => {
const tags = processTags(item); const tags = processTags(item);
console.log(item);
mutate({ mutate({
...item, ...item,
parent_id: newBseQuestionId, parent_id: newBseQuestionId,
@ -74,32 +73,27 @@ const AddPage: React.FC = () => {
} else { } else {
const tags = processTags(DataToSend); const tags = processTags(DataToSend);
mutate({ ...values, subject_id: subject_id, tags , "lessons_ids":[lesson_id] }) mutate({ ...values, subject_id: subject_id, tags , "lessons_ids":[lesson_id] })
} }
}; };
const navigate = useNavigate() const navigate = useNavigate()
useEffect(() => { useEffect(() => {
if(isSuccess){
if (isSuccessAsync && ( SavedQuestionData?.Questions?.length > 0 ? isSuccess: true )) { toast.success(t("validation.the_possess_done_successful"))
set_object_to_edit(null) set_object_to_edit(null)
set_Success(true) set_Success(true)
localStorage.removeItem(QUESTION_OBJECT_KEY) localStorage.removeItem(QUESTION_OBJECT_KEY)
} }
}, [isSuccess,isSuccessAsync]) }, [isSuccess])
let cleanedQuestionOptions = cleanObject(SavedQuestionData); let cleanedQuestionOptions = cleanObject(SavedQuestionData);
let noChange =hasItems(cleanedQuestionOptions) let noChange =hasItems(cleanedQuestionOptions)
useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData); useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY) const SavedData = {} as any
console.log(SavedData); console.log(SavedData);
const handleCancel = () => { const handleCancel = () => {
if(!noChange){ if(!noChange){
@ -114,17 +108,16 @@ const AddPage: React.FC = () => {
const [t] = useTranslation(); const [t] = useTranslation();
console.log(SavedData?.isBase === 1);
if(isBseQuestion || SavedData?.isBase === 1){
if(isBseQuestion){
return ( return (
<div className="exercise_add"> <div className="exercise_add">
<FormikForm <FormikForm
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
initialValues={getInitialValuesBase(SavedData)} initialValues={getInitialValuesBase(object_to_edit)}
validationSchema={getValidationSchemaBase} validationSchema={getValidationSchemaBase}
> >
@ -145,10 +138,7 @@ const AddPage: React.FC = () => {
</div> </div>
</main> </main>
</FormikForm> </FormikForm>
<Suspense fallback={<Spin/>}> <AcceptModal/>
<AcceptModal/>
</Suspense>
</div> </div>
); );
} }
@ -157,7 +147,7 @@ const AddPage: React.FC = () => {
<FormikForm <FormikForm
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
initialValues={getInitialValues(SavedData)} initialValues={getInitialValues(object_to_edit)}
validationSchema={getValidationSchema} validationSchema={getValidationSchema}
> >
@ -180,9 +170,7 @@ const AddPage: React.FC = () => {
</div> </div>
</main> </main>
</FormikForm> </FormikForm>
<Suspense fallback={<Spin/>}> <AcceptModal/>
<AcceptModal/>
</Suspense>
</div> </div>
); );
}; };

View File

@ -22,7 +22,7 @@ const File = ({
const fileList: UploadFile[] = useMemo(() => { const fileList: UploadFile[] = useMemo(() => {
if (!imageUrl) return []; if (!imageUrl) return [];
return [ return [
typeof imageUrl === 'string' typeof imageUrl === 'string'
? { ? {

View File

@ -17,7 +17,7 @@ const Form = () => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const { isOpen } = useModalState((state) => state); const { isOpen } = useModalState((state) => state);
// const {data} = useGetAllQuestion(); // const {data} = useGetAllQuestion();
const{set_Success,Success,set_SavedQuestionData} = useObjectToEdit() const{set_Success,Success} = useObjectToEdit()
useEffect(() => { useEffect(() => {
if (Success) { if (Success) {
@ -29,10 +29,6 @@ const Form = () => {
} }
}, [Success]); }, [Success]);
useEffect(() => {
set_SavedQuestionData(formik.values)
}, [formik?.values])
// console.log(formik?.errors); // console.log(formik?.errors);
console.log(formik?.values?.Questions,"formik?.values?.Questions"); console.log(formik?.values?.Questions,"formik?.values?.Questions");

View File

@ -1,22 +1,18 @@
import * as Yup from "yup"; import * as Yup from "yup";
import { Question } from "../../../types/Item"; import { Question } from "../../../types/Item";
import { getLocalStorage } from "../../../utils/LocalStorage";
import { QUESTION_OBJECT_KEY } from "../../../config/AppKey";
export const getInitialValues = (objectToEdit: Question): any => { export const getInitialValues = (objectToEdit: Question): any => {
const tags = objectToEdit?.tags?.map((item: any, index: number) => { const tags = objectToEdit?.tags?.map((item: any, index: number) => {
return { ...item, key: index } return { ...item, key: index }
}); });
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,
content: objectToEdit?.content ?? "", content: objectToEdit?.content ?? "",
image: objectToEdit?.image ?? "", image: objectToEdit?.image ?? "",
subject_id: objectToEdit?.subject_id ?? '', subject_id: objectToEdit?.subject_id ?? '',
isBase: 0, isBase: objectToEdit?.isBase,
parent_id: objectToEdit?.parent_id ?? '', parent_id: objectToEdit?.parent_id ?? '',
QuestionOptions: objectToEdit?.QuestionOptions ?? [], QuestionOptions: objectToEdit?.QuestionOptions ?? [],
tags: tags ?? [], tags: tags ?? [],
@ -67,7 +63,7 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
content: objectToEdit?.content ?? "", content: objectToEdit?.content ?? "",
image: objectToEdit?.image ?? "", image: objectToEdit?.image ?? "",
subject_id: objectToEdit?.subject_id ?? '', subject_id: objectToEdit?.subject_id ?? '',
isBase: 1, isBase: objectToEdit?.isBase,
parent_id: objectToEdit?.parent_id ?? '', parent_id: objectToEdit?.parent_id ?? '',
Questions: questions, Questions: questions,
}; };

View File

@ -6,8 +6,8 @@ import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params"; import { ParamsEnum } from "../../enums/params";
const App: React.FC = () => { const App: React.FC = () => {
const {lesson_id} = useParams<ParamsEnum>() const {subject_id} = useParams<ParamsEnum>()
const response = useGetAllQuestion({ lesson_id:lesson_id, pagination: true }); const response = useGetAllQuestion({ subject_id:subject_id, pagination: true });
return <DataTable response={response} useColumns={useColumns} />; return <DataTable response={response} useColumns={useColumns} />;
}; };

View File

@ -11,13 +11,10 @@ const API = {
}; };
const KEY = "question"; const KEY = "question";
const KEY2 = "questionBases";
export const useGetAllQuestion = (params?: any) => export const useGetAllQuestion = (params?: any) =>
useGetQuery(KEY, API.GET, params); useGetQuery(KEY, API.GET, params);
export const useAddQuestion = () => useAddMutation(KEY, API.ADD,false); export const useAddQuestion = () => useAddMutation(KEY, API.ADD,false);
export const useAddQuestionAsync = () => useAddMutation(KEY2, API.ADD);
export const useUpdateQuestion = (params?: any) => export const useUpdateQuestion = (params?: any) =>
useUpdateMutation(KEY, API.GET,false); useUpdateMutation(KEY, API.GET,false);
export const useDeleteQuestion = (params?: any) => export const useDeleteQuestion = (params?: any) =>

View File

@ -7,24 +7,19 @@ import { AxiosResponse } from "../../types/Axios";
function useAddMutation( function useAddMutation(
key: string, key: string,
url: string, url: string,
toast: boolean = true toast:boolean = true
): UseMutationResult<AxiosResponse, unknown, any, unknown> { ): UseMutationResult<AxiosResponse, unknown, any, unknown> {
const axios = useAxios(); const axios = useAxios();
console.log(toast,key);
return useMutation<AxiosResponse, unknown, any, unknown>( return useMutation<AxiosResponse, unknown, any, unknown>(
async (dataToSend) => { async (dataToSend) => {
const filterDataToSend = filterData(dataToSend); const filterDataToSend = filterData(dataToSend);
const { data } = await axios.post(url, filterDataToSend, { const { data } = await axios.post(url, filterDataToSend, {
headers: { headers: {
"Content-Type": "multipart/form-data", "Content-Type": "multipart/form-data",
["X-Custom-Message"] : toast ,
[HEADER_KEY]: key, [HEADER_KEY]: key,
["X-Custom-Message"] : toast
}, },
}); });
return data; return data;

View File

@ -43,8 +43,7 @@ function useAxios() {
const key = response.config.headers[HEADER_KEY]; const key = response.config.headers[HEADER_KEY];
const isToasted = response.config.headers["X-Custom-Message"]; const isToasted = response.config.headers["X-Custom-Message"];
console.log(isToasted);
const ResponseMessage = const ResponseMessage =
responseMsg || t("validation.the_possess_done_successful"); responseMsg || t("validation.the_possess_done_successful");
if (method !== AxiosQueryEnum?.GET) { if (method !== AxiosQueryEnum?.GET) {

View File

@ -16,29 +16,3 @@ export const setLocalStorage = (key: string, data: any) => {
console.error("Error stringify data for localStorage", error); console.error("Error stringify data for localStorage", error);
} }
}; };
export const setLocalStorageWithFile = async (key: string, data: any) => {
try {
if (data.image instanceof File) {
const base64String = await convertFileToBase64(data.image);
data.image = base64String;
}
const jsonData = JSON.stringify(data);
localStorage.setItem(key, jsonData);
} catch (error) {
console.error("Error stringifying data for localStorage", error);
}
};
export const convertFileToBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(reader.result as string);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
};

View File

@ -1,15 +0,0 @@
export const base64StringToFile = (base64String:string) => {
// Convert base64 to blob
const byteCharacters = atob(base64String.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/jpeg' });
// Create a File object from Blob (optional: provide a filename)
const file = new File([blob], 'image.jpg', { type: 'image/jpeg' });
return file;
};

View File

@ -1,71 +0,0 @@
import { convertFileToBase64 } from "./LocalStorage";
import { base64StringToFile } from "./base64StringToFile";
export const setLocalStorageBaseQuestions = async (key: string, data: any) => {
try {
// Convert the main image if it is a File
if (data.image instanceof File) {
data.image = await convertFileToBase64(data.image);
}
if (Array.isArray(data.Questions)) {
for (const option of data.Questions) {
if (option.image instanceof File) {
option.image = await convertFileToBase64(option.image);
}
}
}
if (Array.isArray(data.Questions.QuestionOptions)) {
for (const option of data.Questions.QuestionOptions) {
if (option.answer_image instanceof File) {
option.answer_image = await convertFileToBase64(option.answer_image);
}
}
}
const jsonData = JSON.stringify(data);
localStorage.setItem(key, jsonData);
} catch (error) {
console.error("Error stringifying data for localStorage", error);
}
};
export const getLocalStorageBaseQuestions = (key: string): any | null => {
try {
const jsonData = localStorage.getItem(key);
if (!jsonData) return null;
const data = JSON.parse(jsonData);
// Convert the main image from base64 to File if necessary
if (typeof data.image === 'string' && data.image.length > 0) {
data.image = base64StringToFile(data.image);
}
// Convert each image in Questions from base64 to File if necessary
if (Array.isArray(data.Questions)) {
for (const option of data.Questions) {
if (typeof option.image === 'string' ) {
option.image = base64StringToFile(option.image);
}
}
}
if (Array.isArray(data.Questions.QuestionOptions)) {
for (const option of data.Questions.QuestionOptions) {
if (typeof option.answer_image === 'string' ) {
option.answer_image = base64StringToFile(option.answer_image);
}
}
}
return data;
} catch (error) {
console.error("Error parsing data from localStorage", error);
return null;
}
};

View File

@ -1,87 +0,0 @@
import { convertFileToBase64 } from "./LocalStorage";
import { base64StringToFile } from "./base64StringToFile";
export const setLocalStorageQuestions = async (key: string, data: any) => {
try {
// Convert the main image if it is a File
if (data.image instanceof File) {
data.image = await convertFileToBase64(data.image);
}
// Check for QuestionOptions array and convert answer_image if it's a File
if (Array.isArray(data.QuestionOptions)) {
for (const option of data.QuestionOptions) {
if (option.answer_image instanceof File) {
option.answer_image = await convertFileToBase64(option.answer_image);
}
}
}
// Check for Questions array and convert image if it's a File
if (Array.isArray(data.Questions)) {
for (const question of data.Questions) {
if (question.image instanceof File) {
question.image = await convertFileToBase64(question.image);
}
// Check for nested QuestionOptions and convert answer_image if it's a File
if (Array.isArray(question.QuestionOptions)) {
for (const option of question.QuestionOptions) {
if (option.answer_image instanceof File) {
option.answer_image = await convertFileToBase64(option.answer_image);
}
}
}
}
}
const jsonData = JSON.stringify(data);
localStorage.setItem(key, jsonData);
} catch (error) {
console.error("Error stringifying data for localStorage", error);
}
};
export const getLocalStorageQuestions = (key: string): any | null => {
try {
const jsonData = localStorage.getItem(key);
if (!jsonData) return null;
const data = JSON.parse(jsonData);
// Convert back the main image if it's a base64 string
if (typeof data.image === 'string' && data.image.length > 0) {
data.image = base64StringToFile(data.image);
}
// Check for QuestionOptions array and convert answer_image if it's a base64 string
if (Array.isArray(data.QuestionOptions)) {
for (const option of data.QuestionOptions) {
if (typeof option.answer_image === 'string' && option.answer_image.length > 0) {
option.answer_image = base64StringToFile(option.answer_image);
}
}
}
// Check for Questions array and convert image if it's a base64 string
if (Array.isArray(data.Questions)) {
for (const question of data.Questions) {
if (typeof question.image === 'string' && question.image.length > 0) {
question.image = base64StringToFile(question.image);
}
// Check for nested QuestionOptions and convert answer_image if it's a base64 string
if (Array.isArray(question.QuestionOptions)) {
for (const option of question.QuestionOptions) {
if (typeof option.answer_image === 'string' && option.answer_image.length > 0) {
option.answer_image = base64StringToFile(option.answer_image);
}
}
}
}
}
return data;
} catch (error) {
console.error("Error parsing data from localStorage", error);
return null;
}
};