add animations

This commit is contained in:
karimaldeen 2024-09-18 09:20:55 +03:00
parent 9409c7256d
commit b87a96e358
17 changed files with 170 additions and 73 deletions

View File

@ -37,14 +37,14 @@ export const MenuItem = ({ item, location, index, isOpen }: any) => {
className="DropDownIcon" className="DropDownIcon"
onClick={() => handleDropdown(index)} onClick={() => handleDropdown(index)}
> >
<MdExpandLess className="sidebar_menu_icaon" /> <MdExpandLess className="sidebar_menu_icon" />
</div> </div>
) : ( ) : (
<div <div
className="DropDownIcon" className="DropDownIcon"
onClick={() => handleDropdown(index)} onClick={() => handleDropdown(index)}
> >
<MdExpandMore className="sidebar_menu_icaon"/> <MdExpandMore className="sidebar_menu_icon"/>
</div> </div>
)} )}
</> </>

View File

@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
import { QueryStatusEnum } from "../../enums/QueryStatus"; import { QueryStatusEnum } from "../../enums/QueryStatus";
import SpinContainer from "../../Components/Layout/SpinContainer"; import SpinContainer from "../../Components/Layout/SpinContainer";
import { MdCancel } from "react-icons/md"; import { MdCancel } from "react-icons/md";
import { Form, Formik } from "formik";
interface LayoutModalProps { interface LayoutModalProps {
isAddModal?: boolean; isAddModal?: boolean;
@ -65,12 +66,29 @@ const LayoutModel = ({
open={isOpen === ModelEnum} open={isOpen === ModelEnum}
onCancel={handleCancel} onCancel={handleCancel}
> >
<FormikForm
handleSubmit={handleSubmit} <Formik
initialValues={getInitialValues} enableReinitialize={true}
validationSchema={getValidationSchema} initialValues={getInitialValues}
> validationSchema={getValidationSchema}
<header> onSubmit={handleSubmit}
>
{(formik) => {
useEffect(() => {
if (isOpen === "" || isOpen === "isSuccess") {
formik.setErrors({});
formik.resetForm();
}
}, [isOpen]);
console.log(formik.initialValues);
console.log(formik?.values);
return <Form className="w-100">
<header>
<span> <span>
{t(`practical.${isAddModal ? "add" : "edit"}`)}{" "} {t(`practical.${isAddModal ? "add" : "edit"}`)}{" "}
{t(`models.${modelTitle}`)}{" "} {t(`models.${modelTitle}`)}{" "}
@ -88,7 +106,7 @@ const LayoutModel = ({
</div> </div>
<button <button
className="add_button" className="add_button"
disabled={status === QueryStatusEnum.LOADING} disabled={status === QueryStatusEnum.LOADING || !formik.dirty}
type="submit" type="submit"
> >
{t(`practical.${isAddModal ? "add" : "edit"}`)} {t(`practical.${isAddModal ? "add" : "edit"}`)}
@ -100,7 +118,10 @@ const LayoutModel = ({
</button> </button>
</div> </div>
</main> </main>
</FormikForm>
</Form>;
}}
</Formik>
</Modal> </Modal>
</> </>
); );

View File

@ -26,7 +26,7 @@ const AddPage: React.FC = () => {
const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync(); const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync();
const { mutate, isLoading, isSuccess } = useAddQuestion(); const { mutate, isLoading, isSuccess } = useAddQuestion();
const { isBseQuestion, setTagsSearch, setSuccess } = const { isBseQuestion, setTagsSearch, setSuccess , ShowHint,setShowHint } =
useObjectToEdit(); useObjectToEdit();
const [t] = useTranslation(); const [t] = useTranslation();

View File

@ -1,5 +1,5 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Modal, Spin } from "antd"; import { Checkbox, Modal, Popover, Spin } from "antd";
import { import {
getInitialValues, getInitialValues,
getValidationSchema, getValidationSchema,
@ -26,10 +26,12 @@ import { toast } from "react-toastify";
import { deletePathSegments } from "../../../utils/deletePathSegments"; import { deletePathSegments } from "../../../utils/deletePathSegments";
import { Form, Formik } from "formik"; import { Form, Formik } from "formik";
import { MdOutlineArrowForwardIos } from "react-icons/md"; import { MdOutlineArrowForwardIos } from "react-icons/md";
import { SettingFilled } from "@ant-design/icons";
import { CheckboxProps } from "antd/lib";
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>();
const { isBseQuestion, setIsBseQuestion, setTagsSearch, DeletedQuestions } = const { isBseQuestion, setIsBseQuestion, setTagsSearch, DeletedQuestions , ShowHint,setShowHint } =
useObjectToEdit(); useObjectToEdit();
const { mutate, isSuccess, isLoading } = useUpdateQuestion(); const { mutate, isSuccess, isLoading } = useUpdateQuestion();
@ -60,6 +62,7 @@ const EditPage: React.FC = () => {
setTagsSearch(null); setTagsSearch(null);
if (isBseQuestion) { if (isBseQuestion) {
const UpdateBseQuestion = { const UpdateBseQuestion = {
id: DataToSend?.id, id: DataToSend?.id,
content: DataToSend?.content, content: DataToSend?.content,
@ -74,7 +77,7 @@ const EditPage: React.FC = () => {
console.log(DeletedQuestions, "DeletedQuestions"); console.log(DeletedQuestions, "DeletedQuestions");
console.log(UpdateBseQuestion); console.log(UpdateBseQuestion);
mutate(UpdateBseQuestion); // mutate(UpdateBseQuestion);
DeletedQuestions?.map((item: any) => { DeletedQuestions?.map((item: any) => {
DeleteQuestion({ id: item?.id }); DeleteQuestion({ id: item?.id });
@ -97,8 +100,16 @@ const EditPage: React.FC = () => {
const oldAnswers = [] as any; const oldAnswers = [] as any;
const newAnswers = [] as any; const newAnswers = [] as any;
if(updatedObject?.content_image === null){
updatedObject["content_image"] = ""
}
updatedObject?.answers?.forEach((item: any) => { updatedObject?.answers?.forEach((item: any) => {
if (item?.id) { if (item?.id) {
if(item?.content_image === null){
item["content_image"] = ""
}
oldAnswers.push({ ...item, isCorrect: item?.isCorrect ? 1 : 0 }); oldAnswers.push({ ...item, isCorrect: item?.isCorrect ? 1 : 0 });
} else { } else {
newAnswers.push({ ...item, isCorrect: item?.isCorrect ? 1 : 0 }); newAnswers.push({ ...item, isCorrect: item?.isCorrect ? 1 : 0 });
@ -108,11 +119,12 @@ const EditPage: React.FC = () => {
old: oldAnswers, old: oldAnswers,
new: newAnswers, new: newAnswers,
}; };
console.log(answers); const emptyTag = tags?.new?.length === 0 && tags?.old?.length === 0
const tagToSend = emptyTag ? "" : tags
mutate({ mutate({
...updatedObject, ...updatedObject,
answers, answers,
tags, tags:tagToSend,
}); });
} else { } else {
console.log(values?.id); console.log(values?.id);
@ -247,6 +259,22 @@ const handleNavigateToPage = () => {
} }
}, [isSuccess]); }, [isSuccess]);
const onChange: CheckboxProps['onChange'] = (e) => {
setShowHint(e.target.checked);
};
const contentSetting = (
<div>
<Checkbox checked={ShowHint} onChange={onChange}>
{ t("header.show_hint")}
</Checkbox>
</div>
);
const Loading = LoadingAsync || isLoading const Loading = LoadingAsync || isLoading
if (dataLoading || QuestionsDataLoading) { if (dataLoading || QuestionsDataLoading) {
@ -277,7 +305,13 @@ const handleNavigateToPage = () => {
<div> <div>
{t("practical.edit")} {t("models.exercise")}{" "} {t("practical.edit")} {t("models.exercise")}{" "}
</div> </div>
<div className="SettingEdit">
<Popover trigger="click" content={contentSetting}>
<SettingFilled/>
</Popover>
<div>{t("header.exercise")}</div> <div>{t("header.exercise")}</div>
</div>
</header> </header>
<BaseForm /> <BaseForm />
<div className="exercise_add_buttons"> <div className="exercise_add_buttons">
@ -321,7 +355,7 @@ const handleNavigateToPage = () => {
handleSubmit(values); handleSubmit(values);
}} }}
> >
{({ values,handleSubmit }) => ( {({ values,handleSubmit , dirty }) => (
<Form className="w-100"> <Form className="w-100">
<main className="w-100 exercise_add_main"> <main className="w-100 exercise_add_main">
{/* <Header/> */} {/* <Header/> */}
@ -335,13 +369,13 @@ const handleNavigateToPage = () => {
<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 <button
disabled={Loading} disabled={Loading || !dirty}
className="relative" className="relative"
onClick={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values)}} onClick={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values)}}
onSubmit={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values) }} onSubmit={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values) }}
type="submit" type="submit"
> {t("practical.edit")} > {t("practical.edit")}
{Loading && ( {Loading && (
<span className="Spinier_Div"> <span className="Spinier_Div">
<Spin /> <Spin />

View File

@ -11,21 +11,23 @@ import { GoTrash } from "react-icons/go";
import { Popconfirm } from "antd"; import { Popconfirm } from "antd";
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => { const ChoiceFields = ({ index }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const [t] = useTranslation(); const [t] = useTranslation();
const { ShowHint } = useObjectToEdit(); const { ShowHint } = useObjectToEdit();
const handleDeleteChoice = () => { const handleDeleteChoice = () => {
console.log(index); document.getElementById(`ChoiceField_${index}`)?.classList.add("exit")
console.log(formik.values.answers[index]);
const updatedAnswers = formik.values.answers.filter( const updatedAnswers = formik.values.answers.filter(
(_: any, i: any) => i !== index, (_: any, i: any) => i !== index,
); );
setTimeout(() => {
formik.setFieldValue("answers", updatedAnswers);
document.getElementById(`ChoiceField_${index}`)?.classList.remove("exit")
}, 500);
formik.setFieldValue("answers", updatedAnswers);
}; };
const values = formik?.values?.answers?.[index] ; const values = formik?.values?.answers?.[index] ;
@ -40,7 +42,7 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
return ( return (
<> <>
<div className="ChoiceFields"> <div id={`ChoiceField_${index}`} className="ChoiceFields">
<TextField <TextField
className="textarea_exercise" className="textarea_exercise"
placeholder={"choice"} placeholder={"choice"}
@ -87,7 +89,6 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
{t("header.delete_choice")} {t("header.delete_choice")}
<GoTrash <GoTrash
className="trash_icon" className="trash_icon"
size={17} size={17}
/> />
</p> </p>

View File

@ -26,25 +26,19 @@ const ChoiceFields = ({
const [t] = useTranslation(); const [t] = useTranslation();
const { ShowHint } = useObjectToEdit(); const { ShowHint } = useObjectToEdit();
const handleDeleteChoice = () => { const handleDeleteChoice = () => {
const arrayLength = formik.values.Questions?.[parent_index].answers?.length; document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.add("exit")
console.log(arrayLength);
if (arrayLength === 1) {
toast.error(
t("validation.Sorry, the question must have at least one option"),
);
return;
}
const updatedAnswers = formik.values.Questions?.[ const updatedAnswers = formik.values.Questions?.[
parent_index parent_index
].answers.filter((_: any, i: any) => i !== index); ].answers.filter((_: any, i: any) => i !== index);
formik.setFieldValue(`Questions[${parent_index}].answers`, updatedAnswers); setTimeout(() => {
formik.setFieldValue(`Questions[${parent_index}].answers`, updatedAnswers);
document.getElementById(`ChoiceField_${parent_index}_${index}`)?.classList.remove("exit")
}, 500);
}; };
const values = formik.values.Questions?.[parent_index]?.answers?.[index] ; const values = formik.values.Questions?.[parent_index]?.answers?.[index] ;
console.log(values,"values");
const handelCanDeleteAnswers = ()=>{ const handelCanDeleteAnswers = ()=>{
const content = values?.content ; const content = values?.content ;
@ -58,7 +52,7 @@ const ChoiceFields = ({
return ( return (
<> <>
<div className="ChoiceFields"> <div id={`ChoiceField_${parent_index}_${index}`} className="ChoiceFields">
<TextField <TextField
className="textarea_exercise" className="textarea_exercise"
placeholder={"choice"} placeholder={"choice"}

View File

@ -26,7 +26,6 @@ const Form = () => {
parent_index: number, parent_index: number,
fromKeyCombination: boolean = false, fromKeyCombination: boolean = false,
) => { ) => {
console.log(parent_index);
formik.setFieldValue(`Questions.[${parent_index}].answers`, [ formik.setFieldValue(`Questions.[${parent_index}].answers`, [
...((formik?.values as any)?.Questions?.[parent_index] ...((formik?.values as any)?.Questions?.[parent_index]

View File

@ -51,6 +51,7 @@ const TableHeader = () => {
{name:LessonName, path:`lesson/${lesson_id }`} {name:LessonName, path:`lesson/${lesson_id }`}
]); ]);
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">
<Suspense fallback={<Spin />}> <Suspense fallback={<Spin />}>

View File

@ -24,7 +24,7 @@ export const getValidationSchema = () => {
// validate input // validate input
return Yup.object().shape({ return Yup.object().shape({
content_image: Yup.string().nullable(), content_image: Yup.string().nullable(),
content: Yup.string().required("validation.required").nullable(), content: Yup.string().nullable(),
answers: Yup.array().of( answers: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
content: Yup.string().nullable(), content: Yup.string().nullable(),
@ -97,7 +97,7 @@ export const getValidationSchemaBase = () => {
answers: Yup.array() answers: Yup.array()
.of( .of(
Yup.object().shape({ Yup.object().shape({
content: Yup.string().required("validation.required"), content: Yup.string(),
answer_image: Yup.string().nullable(), answer_image: Yup.string().nullable(),
isCorrect: Yup.boolean(), isCorrect: Yup.boolean(),
}), }),

View File

@ -10,7 +10,6 @@ type FormFieldType = {
const FormField = ({ isLoading }: FormFieldType) => { const FormField = ({ isLoading }: FormFieldType) => {
const [t] = useTranslation(); const [t] = useTranslation();
const {isValid} = useFormikContext(); const {isValid} = useFormikContext();
console.log(isValid,"isValid");
return ( return (
<Form className="AuthForm"> <Form className="AuthForm">
@ -20,20 +19,6 @@ const FormField = ({ isLoading }: FormFieldType) => {
<ValidationField name="username" label="username" /> <ValidationField name="username" label="username" />
<ValidationField name="password" label="password" /> <ValidationField name="password" label="password" />
{/* <div className="AuthInput">
<label className="form-label" htmlFor="password">
{t("input.Password")}
</label>
<Field
placeholder={t("input.Password")}
as={Input.Password}
type="password"
id="password"
name="password"
className="passwordInput"
size="large"
/>
</div> */}
<button disabled={ !isValid || isLoading} type="submit" className="auth_submit_button"> <button disabled={ !isValid || isLoading} type="submit" className="auth_submit_button">
{t("practical.login")} {t("practical.login")}

View File

@ -1,12 +1,10 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Formik } from "formik"; import { Formik } from "formik";
import useAuthState from "../../zustand/AuthState"; import useAuthState from "../../zustand/AuthState";
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, validationSchema } from "./formutils"; import { initialValues, validationSchema } from "./formUtils";
import { FormValues } from "../../types/Auth"; import { FormValues } from "../../types/Auth";
import { toast } from "react-toastify";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getLocalStorage } from "../../utils/LocalStorage"; import { getLocalStorage } from "../../utils/LocalStorage";
import { USER_KEY } from "../../config/AppKey"; import { USER_KEY } from "../../config/AppKey";
@ -39,7 +37,7 @@ const LoginForm = () => {
return ( return (
<div className="LoginForm"> <div className="LoginForm">
<Formik initialValues={initialValues} onSubmit={handelSubmit} isInitialValid={false} validationSchema={validationSchema} > <Formik initialValues={initialValues} onSubmit={handelSubmit} validateOnMount={true} validationSchema={validationSchema} >
{(formikProps) => <FormField isLoading={isLoading} />} {(formikProps) => <FormField isLoading={isLoading} />}
</Formik> </Formik>
</div> </div>

View File

@ -5,7 +5,7 @@ import { Select } from "antd";
const Dummy = () => { const Dummy = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const { FilterButton, FilterBody } = useFilter(); // const { FilterButton, FilterBody } = useFilter();
return ( return (
<div className="DummyHomePage"> <div className="DummyHomePage">
@ -16,8 +16,8 @@ const Dummy = () => {
<Select <Select
style={{width:"200px"}} style={{width:"200px"}}
showSearch showSearch
/> */} />
*/}
</div> </div>
); );
}; };

View File

@ -81,8 +81,25 @@ svg {
} }
.DropDownIcon{ .DropDownIcon{
.sidebar_menu_icaon{ .sidebar_menu_icon{
background: transparent !important; background: transparent !important;
color: var(--primary) !important; color: var(--primary) !important;
} }
}
.back_button,button{
cursor: pointer;
transition: .5s ease-in-out;
&:hover{
scale: 1.02;
}
}
svg{
cursor: pointer;
transition: .5s ease-in-out;
&:hover{
scale: 1.1;
}
} }

View File

@ -58,8 +58,14 @@
justify-content: center; justify-content: center;
gap: 7px; gap: 7px;
font-size: 14px; font-size: 14px;
svg { svg {
font-size: 16px; font-size: 16px;
} }
transition: .4s ease-in-out;
&:hover{
scale: 1.1;
}
} }

View File

@ -1,7 +1,7 @@
.DataTable { .DataTable {
width: 100%; width: 100%;
border-radius: 0 0 10px 10px; border-radius: 0 0 10px 10px;
box-shadow: 2px 2px 8px 3px rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 32px 2px #080F3414;
} }
.ant-table-cell { .ant-table-cell {

View File

@ -65,6 +65,11 @@
gap: 5px; gap: 5px;
font-size: 1vw; font-size: 1vw;
width: 10vw; width: 10vw;
cursor: pointer;
transition: .5s ease-in-out;
&:hover{
scale: 1.1;
}
} }
} }
} }
@ -144,6 +149,10 @@
div{ div{
margin-left: 25px; margin-left: 25px;
display: flex; display: flex;
align-items: center;
}
.SettingEdit{
display: flex; gap: 20px;
} }
img { img {
cursor: pointer; cursor: pointer;
@ -268,9 +277,9 @@
.Choices{ .Choices{
padding-inline: 20px; padding-inline: 40px;
.textarea_exercise,.hint{ .textarea_exercise,.hint{
width: calc(50vw - 10px) !important; width: calc(50vw - 30px) !important;
} }
.ValidationField{ .ValidationField{
@ -281,9 +290,9 @@
} }
.QuestionFIeld{ .QuestionFIeld{
padding-inline: 10px; padding-inline: 20px;
.textarea_exercise{ .textarea_exercise{
width: calc(50vw - 10px) !important; width: calc(50vw - 20px) !important;
} }
@ -296,7 +305,7 @@
.Choices.ChoicesMalty{ .Choices.ChoicesMalty{
.textarea_exercise,.hint{ .textarea_exercise,.hint{
width: calc(50vw - 20px) !important; width: calc(50vw - 40px) !important;
} }
.ValidationField{ .ValidationField{
@ -309,4 +318,36 @@
.question_header_setting{ .question_header_setting{
margin-inline: 40px; margin-inline: 40px;
}
.Choices {
transition: 0.5s ease-in-out ;
animation: fadeIn .5s ease-in-out ;
}
.exit {
transition: 0.5s ease-in-out ;
animation: fadeOut .5s ease-in-out ;
}
@keyframes fadeIn {
0% {
transform: translateY(-40px);
opacity: 0;
}
100% {
transform: translateY(0px);
opacity: 1;
}
}
@keyframes fadeOut {
0% {
transform: translateY(0px);
opacity: 1;
}
100% {
transform: translateY(-40px);
opacity: 0;
}
} }

View File

@ -52,6 +52,6 @@ export const useObjectToEdit = create<ModelState>((set) => ({
setDeletedQuestions: (data) => set(() => ({ DeletedQuestions: data })), setDeletedQuestions: (data) => set(() => ({ DeletedQuestions: data })),
SavedQuestionData: [], SavedQuestionData: [],
setSavedQuestionData: (data) => set(() => ({ SavedQuestionData: data })), setSavedQuestionData: (data) => set(() => ({ SavedQuestionData: data })),
ShowHint: true, ShowHint: false,
setShowHint: (data) => set(() => ({ ShowHint: data })), setShowHint: (data) => set(() => ({ ShowHint: data })),
})); }));