Compare commits

..

2 Commits

Author SHA1 Message Date
karimalden
3407250e92 add_localStorage_question 2024-06-29 12:03:49 +03:00
karimalden
7bf6fef55a add filltere by lesson_id 2024-06-27 17:04:23 +03:00
15 changed files with 293 additions and 49 deletions

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,3 +16,29 @@ export const setLocalStorage = (key: string, data: any) => {
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

@ -0,0 +1,15 @@
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

@ -0,0 +1,71 @@
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

@ -0,0 +1,87 @@
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;
}
};