Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev
This commit is contained in:
commit
2949b8b4be
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -10,8 +10,10 @@
|
|||
"Groupbutton",
|
||||
"handelnavigate",
|
||||
"Karim",
|
||||
"Popconfirm",
|
||||
"queryqlent",
|
||||
"registraion",
|
||||
"Sellcast",
|
||||
"SENDNOTIFICATION",
|
||||
"setdateparams",
|
||||
"szhsin",
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ const ImageBoxField = ({ name }: any) => {
|
|||
<div className="VisibleHidden">hidden</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ImageBox">
|
||||
<div className="ImageBox" onClick={handleButtonClick}>
|
||||
{imagePreview ? (
|
||||
<img src={imagePreview} alt="Preview" className="imagePreview" />
|
||||
<img src={imagePreview} onClick={handleButtonClick} alt="Preview" className="imagePreview" />
|
||||
) : (
|
||||
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ const TextField = ({
|
|||
placeholder,
|
||||
isDisabled,
|
||||
onChange,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
className,
|
||||
...props
|
||||
}: any) => {
|
||||
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
||||
const TextFilehandleChange = (
|
||||
|
|
@ -25,6 +25,7 @@ const TextField = ({
|
|||
) => {
|
||||
formik.setFieldValue(name, e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
||||
<ValidationFieldLabel
|
||||
|
|
@ -45,9 +46,10 @@ const TextField = ({
|
|||
size="large"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
autoSize={{ minRows: 4, maxRows: 10 }}
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ height: 120 }}
|
||||
id={name}
|
||||
{...props}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
.text,
|
||||
.ant-form-item {
|
||||
margin-bottom: 7px !important;
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
> span {
|
||||
color: transparent;
|
||||
}
|
||||
|
|
@ -65,3 +67,15 @@
|
|||
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// malty select
|
||||
///
|
||||
.ant-select-multiple{
|
||||
height: auto !important;
|
||||
min-height: 40px;
|
||||
.ant-select-selector{
|
||||
min-height: 40px;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,16 @@ import { useTranslation } from "react-i18next";
|
|||
import { GoArrowSwitch } from "react-icons/go";
|
||||
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
|
||||
import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
|
||||
import { Popover } from "antd";
|
||||
import { Popconfirm, Popover } from "antd";
|
||||
import { CombinationKeyEnum } from "../../enums/CombinationKeyEnum";
|
||||
import { PopconfirmProps } from "antd/lib";
|
||||
|
||||
const Header = () => {
|
||||
const [t] = useTranslation();
|
||||
const { values, setFieldValue, setValues } = useFormikContext<any>();
|
||||
const { isBseQuestion, setIsBseQuestion } = useObjectToEdit();
|
||||
const { setSavedQuestionData } = useObjectToEdit();
|
||||
|
||||
const handleChange = () => {
|
||||
setSavedQuestionData(null);
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
|
|
@ -26,6 +28,14 @@ const Header = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const confirm: PopconfirmProps['onConfirm'] = (e) => {
|
||||
console.log(e);
|
||||
setTimeout(() => {
|
||||
handleChange()
|
||||
}, 500);
|
||||
|
||||
};
|
||||
|
||||
const content = (
|
||||
<div>
|
||||
<p>
|
||||
|
|
@ -52,10 +62,21 @@ const Header = () => {
|
|||
</div>
|
||||
</article>
|
||||
<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" />
|
||||
{isBseQuestion || values?.isBase === 1
|
||||
? t("header.malty_exercise")
|
||||
: t("header.exercise")}
|
||||
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useEffect } from "react";
|
|||
|
||||
const useFormField = (name: string, props?: any) => {
|
||||
const [field, meta] = useField({ name, ...props });
|
||||
|
||||
const { t } = useTranslation();
|
||||
const formik = useFormikContext<any>();
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const DeleteModels: React.FC<ModalFormProps> = ({
|
|||
<Modal
|
||||
className="ModalForm"
|
||||
centered
|
||||
width={"40vw"}
|
||||
width={"500px"}
|
||||
footer={null}
|
||||
open={isOpen === ModelEnum}
|
||||
onCancel={handleCancel}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
|
|||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
>
|
||||
{(formik) => {
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from "react";
|
||||
import SearchBar from "../../Components/Ui/SearchBar/SearchBar";
|
||||
|
||||
import { Button } from "antd";
|
||||
import { BsPlusCircleFill } from "react-icons/bs";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
|
@ -8,8 +7,7 @@ import useModalHandler from "../../utils/useModalHandler";
|
|||
import { MdOutlineArrowForwardIos } from "react-icons/md";
|
||||
import { deletePathSegments } from "../../utils/deletePathSegments";
|
||||
import { getPrevPathRoute } from "../../utils/getPrevPathRoute";
|
||||
import { usePageTitleState } from "../../zustand/PageTitleState";
|
||||
import FillterForm from "../Ui/FillterForm";
|
||||
import PageTitleComponent from "./PageTitle";
|
||||
|
||||
const PageHeader = ({
|
||||
canAdd,
|
||||
|
|
@ -35,20 +33,20 @@ const PageHeader = ({
|
|||
if (PrevPath === 0) {
|
||||
return;
|
||||
}
|
||||
navigate(deletePathSegments(location.pathname, PrevPath));
|
||||
// navigate(deletePathSegments(location.pathname, PrevPath));
|
||||
};
|
||||
const handleNavigateToPage = (location: string) => {
|
||||
navigate(location);
|
||||
// navigate(location);
|
||||
};
|
||||
|
||||
console.log();
|
||||
const { PageTitle } = usePageTitleState();
|
||||
return (
|
||||
<div className="page_header">
|
||||
<header className="d-flex justify-content-between">
|
||||
<span className="page_header_links" onClick={handelNavigate}>
|
||||
<h1 className="page_title">{t(`PageTitle.${pageTitle}`)}</h1>
|
||||
<span className="page_links">
|
||||
<MdOutlineArrowForwardIos /> {PageTitle}
|
||||
<MdOutlineArrowForwardIos /> <PageTitleComponent/>
|
||||
</span>
|
||||
</span>
|
||||
{ addModal ? canAdd && (
|
||||
|
|
|
|||
30
src/Layout/Dashboard/PageTitle.tsx
Normal file
30
src/Layout/Dashboard/PageTitle.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { usePageTitleState } from '../../zustand/PageTitleState'
|
||||
|
||||
const PageTitleComponent = () => {
|
||||
|
||||
const {PageTitle,setPageTitle} = usePageTitleState()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const handleNavigate = (path:string)=>{
|
||||
const currentPath = location.pathname ;
|
||||
const newPath = currentPath?.split(path)?.[0] + path ;
|
||||
if(newPath !== currentPath){
|
||||
navigate(newPath)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className='PageTitle'>
|
||||
{(Array.isArray(PageTitle) ? PageTitle : [])?.map((item,index)=>{
|
||||
return (
|
||||
<div key={index} className='PageTitleItems' onClick={()=>handleNavigate(item?.path)}>
|
||||
{item?.name} /
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageTitleComponent
|
||||
|
|
@ -20,7 +20,10 @@ const TableHeader = () => {
|
|||
const [t] = useTranslation();
|
||||
const deleteMutation = useDeleteGrade();
|
||||
|
||||
useSetPageTitle(t(`page_header.grade`));
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.grade`)}`, path:"grade"}
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ const DeleteModalForm = lazy(
|
|||
|
||||
const TableHeader = () => {
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.param`));
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.param`)}`, path:"param"}
|
||||
]);
|
||||
const deleteMutation = useDeleteParam();
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
33
src/Pages/Admin/QuestionBank/Model/AddModel.tsx
Normal file
33
src/Pages/Admin/QuestionBank/Model/AddModel.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||
import { ModalEnum } from "../../../../enums/Model";
|
||||
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
|
||||
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
|
||||
import ModelForm from "./ModelForm";
|
||||
import { useAddGrade } from "../../../../api/grade";
|
||||
|
||||
const AddModel: React.FC = () => {
|
||||
const { mutate, status } = useAddGrade();
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
mutate({
|
||||
...values,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<LayoutModel
|
||||
status={status as QueryStatusEnum}
|
||||
ModelEnum={ModalEnum.GRADE_ADD}
|
||||
modelTitle="grade"
|
||||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddModel;
|
||||
38
src/Pages/Admin/QuestionBank/Model/EditModel.tsx
Normal file
38
src/Pages/Admin/QuestionBank/Model/EditModel.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import React from "react";
|
||||
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||
import { ModalEnum } from "../../../../enums/Model";
|
||||
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
|
||||
import ModelForm from "./ModelForm";
|
||||
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
|
||||
import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
||||
import { useUpdateQuestion } from "../../../../api/Question";
|
||||
import { handelImageState } from "../../../../utils/DataToSendImageState";
|
||||
|
||||
const EditModel: React.FC = () => {
|
||||
const { mutate, status } = useUpdateQuestion();
|
||||
const { objectToEdit } = useObjectToEdit((state) => state);
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
const Data_to_send = { ...values };
|
||||
const handelImage = handelImageState(Data_to_send, "icon");
|
||||
mutate(handelImage);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<LayoutModel
|
||||
status={status as QueryStatusEnum}
|
||||
ModelEnum={ModalEnum.QUESTION_BANK_EDIT}
|
||||
modelTitle="QuestionBank"
|
||||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditModel;
|
||||
20
src/Pages/Admin/QuestionBank/Model/FilterForm.tsx
Normal file
20
src/Pages/Admin/QuestionBank/Model/FilterForm.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import React from "react";
|
||||
import ValidationField from "../../../../Components/ValidationField/ValidationField";
|
||||
import { Col, Row } from "reactstrap";
|
||||
import { useFormikContext } from "formik";
|
||||
|
||||
const FilterForm = () => {
|
||||
const formik = useFormikContext();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col>
|
||||
<ValidationField placeholder="name" label="name" name="name" />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterForm;
|
||||
17
src/Pages/Admin/QuestionBank/Model/ModelForm.tsx
Normal file
17
src/Pages/Admin/QuestionBank/Model/ModelForm.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import ValidationField from "../../../../Components/ValidationField/ValidationField";
|
||||
|
||||
const Form = () => {
|
||||
return (
|
||||
<Row className="w-100">
|
||||
<Col>
|
||||
<ValidationField name="name" placeholder="name" label="name" />
|
||||
</Col>
|
||||
<Col>
|
||||
<ValidationField name="icon" type="File" />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default Form;
|
||||
19
src/Pages/Admin/QuestionBank/Model/formUtil.ts
Normal file
19
src/Pages/Admin/QuestionBank/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import * as Yup from "yup";
|
||||
import { Grade, GradeInitialValues } from "../../../../types/Grade";
|
||||
|
||||
export const getInitialValues = (
|
||||
objectToEdit: Partial<Grade>,
|
||||
): GradeInitialValues => {
|
||||
return {
|
||||
id: objectToEdit?.id,
|
||||
name: objectToEdit?.name ?? "",
|
||||
icon: objectToEdit?.icon ?? "",
|
||||
};
|
||||
};
|
||||
|
||||
export const getValidationSchema = () => {
|
||||
// validate input
|
||||
return Yup.object().shape({
|
||||
name: Yup.string().required("validation.required"),
|
||||
});
|
||||
};
|
||||
49
src/Pages/Admin/QuestionBank/Page.tsx
Normal file
49
src/Pages/Admin/QuestionBank/Page.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { Spin } from "antd";
|
||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||
import { ModalEnum } from "../../../enums/Model";
|
||||
import { useDeleteQuestion } from "../../../api/Question";
|
||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||
import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
||||
import FilterForm from "./Model/FilterForm";
|
||||
import { canAddQuestionBank } from "../../../utils/hasAbilityFn";
|
||||
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModalForm = lazy(
|
||||
() => import("../../../Layout/Dashboard/DeleteModels"),
|
||||
);
|
||||
|
||||
const TableHeader = () => {
|
||||
const [t] = useTranslation();
|
||||
const deleteMutation = useDeleteQuestion();
|
||||
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.Question`)}`, path:"Question"}
|
||||
]);
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
<PageHeader
|
||||
pageTitle="QuestionBank"
|
||||
ModelAbility={ModalEnum?.QUESTION_BANK_ADD}
|
||||
canAdd={canAddQuestionBank}
|
||||
/>
|
||||
<FilterLayout sub_children={<FilterForm />} filterTitle="table.QuestionBank" />
|
||||
<Table />
|
||||
<AddModalForm />
|
||||
<EditModalForm />
|
||||
<DeleteModalForm
|
||||
deleteMutation={deleteMutation}
|
||||
ModelEnum={ModalEnum?.QUESTION_BANK_DELETE}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableHeader;
|
||||
18
src/Pages/Admin/QuestionBank/Table.tsx
Normal file
18
src/Pages/Admin/QuestionBank/Table.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { useColumns } from "./useTableColumns";
|
||||
import React from "react";
|
||||
import DataTable from "../../../Layout/Dashboard/Table/DataTable";
|
||||
import { useGetAllQuestion } from "../../../api/Question";
|
||||
import { useFilterState } from "../../../Components/Utils/Filter/FilterState";
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { filterState } = useFilterState();
|
||||
|
||||
const response = useGetAllQuestion({
|
||||
pagination: true,
|
||||
...filterState,
|
||||
});
|
||||
|
||||
return <DataTable response={response} useColumns={useColumns} />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
123
src/Pages/Admin/QuestionBank/useTableColumns.tsx
Normal file
123
src/Pages/Admin/QuestionBank/useTableColumns.tsx
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import { TableColumnsType } from "antd";
|
||||
import { Question } from "../../../types/Item";
|
||||
import { FaPlus } from "react-icons/fa";
|
||||
import { ModalEnum } from "../../../enums/Model";
|
||||
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ABILITIES_ENUM } from "../../../enums/abilities";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useModalState } from "../../../zustand/Modal";
|
||||
import {
|
||||
canAddQuestion,
|
||||
canDeleteQuestion,
|
||||
canEditQuestion,
|
||||
} from "../../../utils/hasAbilityFn";
|
||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { setObjectToEdit } = useObjectToEdit((state) => state);
|
||||
const navigate = useNavigate();
|
||||
const { setIsOpen } = useModalState((state) => state);
|
||||
|
||||
const handelAdd = () => {
|
||||
setObjectToEdit({});
|
||||
navigate(`${ABILITIES_ENUM?.QUESTION}/add`);
|
||||
};
|
||||
|
||||
const handelDelete = (data: any) => {
|
||||
setObjectToEdit(data);
|
||||
setIsOpen(ModalEnum?.QUESTION_DELETE);
|
||||
};
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
setObjectToEdit(record);
|
||||
navigate(`${ABILITIES_ENUM?.QUESTION}/${record?.id}`);
|
||||
};
|
||||
const [t] = useTranslation();
|
||||
|
||||
const columns: TableColumnsType<Question> = [
|
||||
{
|
||||
title: t("columns.id"),
|
||||
dataIndex: "id",
|
||||
key: "id",
|
||||
align: "center",
|
||||
render: (text, record) => record?.id,
|
||||
},
|
||||
{
|
||||
title: `${t("columns.content")}`,
|
||||
dataIndex: "content",
|
||||
key: "content",
|
||||
align: "center",
|
||||
render: (text, record) => record?.content,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: `${t("columns.hint")}`,
|
||||
dataIndex: "hint",
|
||||
key: "hint",
|
||||
align: "center",
|
||||
render: (text, record) => {
|
||||
|
||||
return (
|
||||
<>{record?.hint ?? "karim"}</>
|
||||
);
|
||||
},
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: `${t("columns.tags")}`,
|
||||
dataIndex: "tags",
|
||||
key: "tags",
|
||||
align: "center",
|
||||
|
||||
render: (text, record) => {
|
||||
const tags = record?.tags?.map((item:any)=>{
|
||||
return item?.name
|
||||
}) ?? [];
|
||||
return (
|
||||
<div>
|
||||
{tags.length > 0 ? (
|
||||
tags.map((tag, index) => (
|
||||
<span key={index}>
|
||||
{tag}
|
||||
{index < tags.length - 1 && ', '}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span>_</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("columns.question_type"),
|
||||
dataIndex: "isBase",
|
||||
key: "isBase",
|
||||
align: "center",
|
||||
render: (text, record) =>
|
||||
record?.isBase ? t("columns.base_question") : t("columns.normal_question"),
|
||||
},
|
||||
|
||||
{
|
||||
title: "#",
|
||||
key: "actions",
|
||||
align: "center",
|
||||
|
||||
render: (_text, record, index) => {
|
||||
return (
|
||||
<ActionButtons
|
||||
canDelete={canDeleteQuestion}
|
||||
canEdit={canEditQuestion}
|
||||
index={index}
|
||||
onDelete={() => handelDelete(record)}
|
||||
onEdit={() => handleEdit(record)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
|
@ -21,9 +21,12 @@ const SearchField = lazy(
|
|||
);
|
||||
|
||||
const TableHeader = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.report`));
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.report`)}`, path:"report"}
|
||||
]);
|
||||
const deleteMutation = useDeleteTag();
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ const SearchField = lazy(
|
|||
|
||||
const TableHeader = () => {
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.reseller`));
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.reseller`)}`, path:"reseller"}
|
||||
]);
|
||||
const deleteMutation = useDeleteTag();
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ const TableHeader = () => {
|
|||
const [t] = useTranslation();
|
||||
const deleteMutation = useDeleteStudent();
|
||||
|
||||
useSetPageTitle(t(`page_header.student`));
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.student`)}`, path:"student"}
|
||||
]);
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
|
|
|
|||
|
|
@ -16,14 +16,15 @@ const EditModalForm = lazy(() => import("./Model/EditModel"));
|
|||
const DeleteModalForm = lazy(
|
||||
() => import("../../../Layout/Dashboard/DeleteModels"),
|
||||
);
|
||||
const SearchField = lazy(
|
||||
() => import("../../../Components/DataTable/SearchField"),
|
||||
);
|
||||
|
||||
const TableHeader = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.tags`));
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.tags`)}`, path:"tag"}
|
||||
]);
|
||||
|
||||
const deleteMutation = useDeleteTag();
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -35,15 +35,13 @@ const TableHeader = () => {
|
|||
});
|
||||
|
||||
const gradeName = grade?.data?.name ?? "";
|
||||
|
||||
const SubjectName = Subject?.data?.name ?? "";
|
||||
useSetPageTitle(
|
||||
t(`page_header.grade`) +
|
||||
" / " +
|
||||
` ${t("header.subject_of_class")} (${gradeName})` +
|
||||
" / " +
|
||||
SubjectName,
|
||||
);
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.grade`)}`, path:"grade"},
|
||||
{name:` ${t("header.subject_of_class")} (${gradeName})`, path:`grade/${grade_id}`},
|
||||
{name:SubjectName, path:`subject/${subject_id}`}
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ const DeleteModalForm = lazy(
|
|||
|
||||
const TableHeader = () => {
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.user`));
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.user`)}`, path:"user"}
|
||||
]);
|
||||
const deleteMutation = useDeleteUser();
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const TableHeader = () => {
|
|||
const [t] = useTranslation();
|
||||
const deleteMutation = useDeleteLesson();
|
||||
|
||||
const { unit_id, curriculum_id, grade_id, subject_id } =
|
||||
const { unit_id, grade_id, subject_id } =
|
||||
useParams<ParamsEnum>();
|
||||
const { data: unit } = useGetAllUnit({ show: unit_id });
|
||||
|
||||
|
|
@ -41,15 +41,15 @@ const TableHeader = () => {
|
|||
const SubjectName = Subject?.data?.name ?? "";
|
||||
const unitName = unit?.data?.name ?? "";
|
||||
|
||||
useSetPageTitle(
|
||||
t(`page_header.grade`) +
|
||||
" / " +
|
||||
` ${t("header.subject_of_class")} (${gradeName})` +
|
||||
" / " +
|
||||
SubjectName +
|
||||
" / " +
|
||||
unitName,
|
||||
);
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.grade`)}`, path:"grade"},
|
||||
{name:` ${t("header.subject_of_class")} (${gradeName})`, path:`grade/${grade_id}`},
|
||||
{name:SubjectName, path:`subject/${subject_id}`},
|
||||
{name:unitName, path:`unit/${unit_id}`}
|
||||
]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import React, { Suspense, lazy, useEffect } from "react";
|
||||
import { Spin } from "antd";
|
||||
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||
import {
|
||||
getInitialValues,
|
||||
getValidationSchema,
|
||||
getInitialValuesBase,
|
||||
getValidationSchemaBase,
|
||||
processTags,
|
||||
} from "./Model/formUtil";
|
||||
} from "./formUtil";
|
||||
import { useAddQuestion, useAddQuestionAsync } from "../../../api/Question";
|
||||
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 { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||
|
||||
|
|
@ -19,9 +18,12 @@ import { Question } from "../../../types/Item";
|
|||
import BaseForm from "./Model/Malty/Form";
|
||||
import ModelForm from "./Model/ModelForm";
|
||||
import { toast } from "react-toastify";
|
||||
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
|
||||
import { Form, Formik } from "formik";
|
||||
import { MdOutlineArrowForwardIos } from "react-icons/md";
|
||||
|
||||
const AddPage: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const { mutateAsync,isLoading:LoadingAsync } = useAddQuestionAsync();
|
||||
const { mutate, isLoading, isSuccess } = useAddQuestion();
|
||||
const { isBseQuestion, setTagsSearch, objectToEdit, setSuccess } =
|
||||
|
|
@ -30,12 +32,7 @@ const AddPage: React.FC = () => {
|
|||
const [t] = useTranslation();
|
||||
const { subject_id, lesson_id } = useParams<ParamsEnum>();
|
||||
|
||||
console.log(objectToEdit, "objectToEdit");
|
||||
|
||||
const handleSubmit = (
|
||||
values: any,
|
||||
{ resetForm }: { resetForm: () => void },
|
||||
) => {
|
||||
const handleSubmit = ( values: any) => {
|
||||
const DataToSend = structuredClone(values);
|
||||
setTagsSearch(null);
|
||||
console.log(1);
|
||||
|
|
@ -95,17 +92,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 = {
|
||||
...values,
|
||||
|
|
@ -122,14 +108,62 @@ 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 )
|
||||
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 handleNavigateToPage = () => {
|
||||
const cleanedUrl = location.pathname?.replace("/Question/add", "");
|
||||
navigate(cleanedUrl);
|
||||
};
|
||||
const handleCancel = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
const Loading = LoadingAsync || isLoading
|
||||
const Loading = LoadingAsync || isLoading;
|
||||
|
||||
useEffect(() => {
|
||||
console.log("all api success");
|
||||
if (isSuccess) {
|
||||
setSuccess(true);
|
||||
}
|
||||
|
|
@ -137,19 +171,32 @@ const AddPage: React.FC = () => {
|
|||
|
||||
if (isBseQuestion) {
|
||||
return (
|
||||
|
||||
<div className="QuestionPractical">
|
||||
<header>
|
||||
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.add_new_question")}
|
||||
</header>
|
||||
|
||||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase(objectToEdit)}
|
||||
validationSchema={getValidationSchemaBase}
|
||||
<Formik
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase({} as any)}
|
||||
validationSchema={getValidationSchemaBase}
|
||||
enableReinitialize
|
||||
>
|
||||
{({ values,handleSubmit }) => (
|
||||
|
||||
<Form className="w-100">
|
||||
<main className="w-100 exercise_add_main">
|
||||
<Header />
|
||||
<BaseForm />
|
||||
|
||||
<div className="exercise_add_buttons">
|
||||
<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")}
|
||||
|
||||
{Loading && (
|
||||
|
|
@ -160,41 +207,61 @@ const AddPage: React.FC = () => {
|
|||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</FormikForm>
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AcceptModal />
|
||||
</Suspense>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
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 onClick={handleCancel}>{t("practical.back")}</div>
|
||||
<button disabled={Loading} className="relative" type="submit">
|
||||
{t("practical.add")}
|
||||
<div className="QuestionPractical">
|
||||
<header>
|
||||
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.add_new_question")}
|
||||
</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 />
|
||||
|
||||
{Loading && (
|
||||
<span className="Spinier_Div">
|
||||
<Spin />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</FormikForm>
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AcceptModal />
|
||||
</Suspense>
|
||||
<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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Modal, Spin } from "antd";
|
||||
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||
import {
|
||||
getInitialValues,
|
||||
getValidationSchema,
|
||||
getInitialValuesBase,
|
||||
getValidationSchemaBase,
|
||||
processTags,
|
||||
} from "./Model/formUtil";
|
||||
} from "./formUtil";
|
||||
import {
|
||||
useAddQuestion,
|
||||
useDeleteQuestion,
|
||||
|
|
@ -25,6 +24,8 @@ import BaseForm from "./Model/Malty/Form";
|
|||
import { Question } from "../../../types/Item";
|
||||
import { toast } from "react-toastify";
|
||||
import { deletePathSegments } from "../../../utils/deletePathSegments";
|
||||
import { Form, Formik } from "formik";
|
||||
import { MdOutlineArrowForwardIos } from "react-icons/md";
|
||||
|
||||
const EditPage: React.FC = () => {
|
||||
const { subject_id, lesson_id, question_id } = useParams<ParamsEnum>();
|
||||
|
|
@ -187,6 +188,56 @@ const EditPage: 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 )
|
||||
|
||||
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(() => {
|
||||
if (isSuccess) {
|
||||
toast.success(t("validation.the_possess_done_successful"));
|
||||
|
|
@ -201,13 +252,24 @@ const EditPage: React.FC = () => {
|
|||
}
|
||||
if (objectToEdit?.isBase) {
|
||||
return (
|
||||
|
||||
|
||||
<div className="QuestionPractical">
|
||||
<header>
|
||||
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.edit_question")}
|
||||
</header>
|
||||
|
||||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase(objectToEdit)}
|
||||
validationSchema={getValidationSchemaBase}
|
||||
<Formik
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase(objectToEdit)}
|
||||
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 className="exercise_add_header mb-4">
|
||||
<div>
|
||||
|
|
@ -218,8 +280,10 @@ const EditPage: React.FC = () => {
|
|||
<BaseForm />
|
||||
<div className="exercise_add_buttons">
|
||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||
<button disabled={Loading} className="relative" type="submit">
|
||||
{t("practical.edit")}
|
||||
<button disabled={Loading} className="relative" type="submit"
|
||||
onClick={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values)}}
|
||||
onSubmit={()=>{handleValidateBaseQuestion(values) ;handleSubmit(values) }}
|
||||
> {t("practical.edit")}
|
||||
|
||||
{Loading && (
|
||||
<span className="Spinier_Div">
|
||||
|
|
@ -229,19 +293,35 @@ const EditPage: React.FC = () => {
|
|||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</FormikForm>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className="QuestionPractical">
|
||||
<header>
|
||||
<MdOutlineArrowForwardIos onClick={handleNavigateToPage} /> {t("header.edit_question")}
|
||||
</header>
|
||||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValues(objectToEdit)}
|
||||
validationSchema={getValidationSchema}
|
||||
>
|
||||
<main className="w-100 exercise_add_main">
|
||||
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
initialValues={getInitialValues(objectToEdit)}
|
||||
validationSchema={getValidationSchema}
|
||||
onSubmit={(values) => {
|
||||
handleSubmit(values);
|
||||
}}
|
||||
>
|
||||
{({ values,handleSubmit }) => (
|
||||
<Form className="w-100">
|
||||
<main className="w-100 exercise_add_main">
|
||||
{/* <Header/> */}
|
||||
<header className="exercise_add_header mb-4">
|
||||
<div>
|
||||
|
|
@ -252,8 +332,13 @@ const EditPage: React.FC = () => {
|
|||
<ModelForm />
|
||||
<div className="exercise_add_buttons">
|
||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||
<button disabled={Loading} className="relative" type="submit">
|
||||
{t("practical.edit")}
|
||||
<button
|
||||
disabled={Loading}
|
||||
className="relative"
|
||||
onClick={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values)}}
|
||||
onSubmit={()=>{handleValidateSingleQuestion(values) ;handleSubmit(values) }}
|
||||
type="submit"
|
||||
> {t("practical.edit")}
|
||||
|
||||
{Loading && (
|
||||
<span className="Spinier_Div">
|
||||
|
|
@ -263,8 +348,14 @@ const EditPage: React.FC = () => {
|
|||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</FormikForm>
|
||||
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
import React from "react";
|
||||
import { Modal } from "antd";
|
||||
import { useModalState } from "../../../../zustand/Modal";
|
||||
import { ModalEnum } from "../../../../enums/Model";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { QUESTION_OBJECT_KEY } from "../../../../config/AppKey";
|
||||
|
||||
const AcceptModal: React.FC = () => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = () => {
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
console.log("Handle submit clicked");
|
||||
setIsOpen("");
|
||||
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsOpen("");
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
className="ModalForm"
|
||||
centered
|
||||
width={"40vw"}
|
||||
footer={null}
|
||||
open={isOpen === ModalEnum?.QUESTION_ACCEPT}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<header> {t("practical.accept_back")}</header>
|
||||
|
||||
<main className="main_modal">
|
||||
<div className="ValidationField w-100 mb-5">
|
||||
<label className="text h1 ">
|
||||
{t(
|
||||
"practical.Are you sure you want to go back and not save any changes?",
|
||||
)}{" "}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="buttons">
|
||||
<div onClick={handleCancel}>{t("practical.cancel")}</div>
|
||||
<div onClick={handleSubmit}>{t("practical.accept")}</div>
|
||||
</div>
|
||||
</main>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AcceptModal;
|
||||
|
|
@ -39,6 +39,7 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
name={index}
|
||||
id={`choice_${index + 1}`}
|
||||
type="TextArea"
|
||||
|
||||
/>
|
||||
|
||||
<ImageBoxField name={`answers.${index}.content_image`} />
|
||||
|
|
@ -50,11 +51,11 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
name={index}
|
||||
type="Checkbox"
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
<p className="delete_question_options" onClick={handleDeleteChoice}>
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash
|
||||
className="trash_icon"
|
||||
onClick={handleDeleteChoice}
|
||||
|
||||
size={17}
|
||||
/>
|
||||
</p>
|
||||
|
|
@ -66,8 +67,10 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
placeholder="_"
|
||||
name={`answers.${index}.hint`}
|
||||
label="hint"
|
||||
type="text"
|
||||
style={{ width: "100%" }}
|
||||
type="TextArea"
|
||||
style={{ width: "100%" , height: 60,resize:"none" }}
|
||||
showCount={false}
|
||||
autoSize={{ minRows: 2, maxRows: 10 }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -36,12 +36,21 @@ const Choices = () => {
|
|||
};
|
||||
|
||||
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">
|
||||
{(provided) => (
|
||||
<div {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{formik?.values?.answers?.map((item: Choice, index: number) => {
|
||||
// Use a unique identifier for draggableId
|
||||
const draggableId = item.name
|
||||
? item.name.toString()
|
||||
: `item-${index}`;
|
||||
|
|
@ -67,11 +76,12 @@ const Choices = () => {
|
|||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{provided.placeholder} {/* Placeholder for spacing */}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</DragDropContext> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { useFormik, useFormikContext } from "formik";
|
||||
import { Checkbox } from "antd";
|
||||
import { useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const CheckboxField = ({
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import { useTranslation } from "react-i18next";
|
|||
import { getCharFromNumber } from "../../../../../../utils/getCharFromNumber";
|
||||
import CheckboxField from "./CheckboxField";
|
||||
import TextField from "./TextField";
|
||||
import File from "./File";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
import { toast } from "react-toastify";
|
||||
import HintField from "./HintField";
|
||||
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
|
||||
|
|
@ -72,11 +69,11 @@ const ChoiceFields = ({
|
|||
type="Checkbox"
|
||||
parent_index={parent_index}
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
<p className="delete_question_options" onClick={handleDeleteChoice}>
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash
|
||||
className="trash_icon"
|
||||
onClick={handleDeleteChoice}
|
||||
|
||||
size={17}
|
||||
/>
|
||||
</p>
|
||||
|
|
@ -89,8 +86,10 @@ const ChoiceFields = ({
|
|||
placeholder="_"
|
||||
name={`Questions.${parent_index}.answers.${index}.hint`}
|
||||
label="hint"
|
||||
type="text"
|
||||
style={{ width: "100%" }}
|
||||
type="TextArea"
|
||||
style={{ width: "100%" , height: 60,resize:"none" }}
|
||||
showCount={false}
|
||||
autoSize={{ minRows: 2, maxRows: 10 }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,28 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
|
|||
|
||||
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">
|
||||
{(provided) => (
|
||||
<div {...provided.droppableProps} ref={provided.innerRef}>
|
||||
|
|
@ -87,7 +108,7 @@ const Choices = ({ parent_index }: { parent_index: number }) => {
|
|||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</DragDropContext> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
import { Button, Upload, UploadFile } from "antd";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const File = ({
|
||||
name,
|
||||
label,
|
||||
onChange,
|
||||
isDisabled,
|
||||
placholder,
|
||||
className,
|
||||
parent_index,
|
||||
props,
|
||||
}: any) => {
|
||||
const newName = `Questions[${parent_index}].answers[${name}].answer_image`;
|
||||
|
||||
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
||||
let imageUrl =
|
||||
formik?.values?.Questions?.[parent_index]?.answers[name]?.answer_image ??
|
||||
null;
|
||||
// console.log(imageUrl);
|
||||
|
||||
const fileList: UploadFile[] = useMemo(() => {
|
||||
if (!imageUrl) return [];
|
||||
|
||||
return [
|
||||
typeof imageUrl === "string"
|
||||
? {
|
||||
uid: "-1",
|
||||
name: "uploaded-image",
|
||||
status: "done",
|
||||
url: imageUrl,
|
||||
thumbUrl: imageUrl,
|
||||
}
|
||||
: {
|
||||
uid: imageUrl.uid || "-1",
|
||||
name: imageUrl.name || "uploaded-image",
|
||||
status: "done",
|
||||
originFileObj: imageUrl,
|
||||
},
|
||||
];
|
||||
}, [imageUrl]);
|
||||
// console.log(1);
|
||||
|
||||
const FilehandleChange = (value: any) => {
|
||||
// console.log(value,"filevalue");
|
||||
if (value.fileList.length === 0) {
|
||||
formik.setFieldValue(newName, null);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].answers[${name}].answer_image`,
|
||||
value?.file?.originFileObj,
|
||||
);
|
||||
}
|
||||
};
|
||||
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
|
||||
onSuccess();
|
||||
};
|
||||
return (
|
||||
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`input.${label || name}`)}
|
||||
</label>
|
||||
|
||||
<Upload
|
||||
disabled={isDisabled}
|
||||
listType="picture"
|
||||
maxCount={1}
|
||||
fileList={[...fileList]}
|
||||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={` w-100`}
|
||||
>
|
||||
<Button
|
||||
className={isError ? "isError w-100 " : " w-100"}
|
||||
icon={<UploadOutlined />}
|
||||
>
|
||||
{placholder ?? t("input.Click_to_upload_the_image")}
|
||||
</Button>
|
||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||
{errorMsg}
|
||||
</Upload>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default File;
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import { Form, Input } from "antd";
|
||||
import React from "react";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { Field } from "formik";
|
||||
|
||||
const HintField = ({
|
||||
name,
|
||||
label,
|
||||
label2,
|
||||
placeholder,
|
||||
isDisabled,
|
||||
onChange,
|
||||
props,
|
||||
parent_index,
|
||||
id,
|
||||
className,
|
||||
}: any) => {
|
||||
const newName = `Questions[${parent_index}].answers[${name}].hint`;
|
||||
|
||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||
const TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
// console.log('Change:', e.target.value);
|
||||
formik.setFieldValue(newName, e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
||||
<label htmlFor={name} className="text">
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Field
|
||||
as={Input}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
name={newName}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ width: 200 }}
|
||||
id={id}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(HintField);
|
||||
|
|
@ -86,6 +86,14 @@ const Form = () => {
|
|||
},
|
||||
);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (Success) {
|
||||
formik.resetForm()
|
||||
setSuccess(false);
|
||||
}
|
||||
}, [Success]);
|
||||
|
||||
return (
|
||||
<Row className="w-100 exercise_form_container">
|
||||
<div className="exercise_form">
|
||||
|
|
@ -99,7 +107,7 @@ const Form = () => {
|
|||
|
||||
<div></div>
|
||||
</div>
|
||||
<div className=" flex "></div>
|
||||
<div className="flex"></div>
|
||||
|
||||
{((formik?.values as any)?.Questions || [])?.map(
|
||||
(item: Choice, parent_index: number) => {
|
||||
|
|
@ -132,8 +140,10 @@ const Form = () => {
|
|||
placeholder="_"
|
||||
name={`Questions.${parent_index}.hint`}
|
||||
label="hint_question"
|
||||
type="text"
|
||||
style={{ width: "100%" }}
|
||||
type="TextArea"
|
||||
style={{ width: "100%" , height: 60,resize:"none" }}
|
||||
autoSize={{ minRows: 2, maxRows: 10 }}
|
||||
showCount={false}
|
||||
/>
|
||||
<MaltySelectTag parent_index={parent_index} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
import { Button, Upload, UploadFile } from "antd";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const File = ({
|
||||
name,
|
||||
label,
|
||||
onChange,
|
||||
isDisabled,
|
||||
placholder,
|
||||
className,
|
||||
props,
|
||||
}: any) => {
|
||||
const newName = `Questions[${name}].image`;
|
||||
|
||||
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
||||
let imageUrl = formik?.values?.Questions?.[name]?.image ?? null;
|
||||
// console.log(imageUrl);
|
||||
|
||||
const fileList: UploadFile[] = useMemo(() => {
|
||||
if (!imageUrl) return [];
|
||||
|
||||
return [
|
||||
typeof imageUrl === "string"
|
||||
? {
|
||||
uid: "-1",
|
||||
name: "uploaded-image",
|
||||
status: "done",
|
||||
url: imageUrl,
|
||||
thumbUrl: imageUrl,
|
||||
}
|
||||
: {
|
||||
uid: imageUrl.uid || "-1",
|
||||
name: imageUrl.name || "uploaded-image",
|
||||
status: "done",
|
||||
originFileObj: imageUrl,
|
||||
},
|
||||
];
|
||||
}, [imageUrl]);
|
||||
// console.log(1);
|
||||
|
||||
const FilehandleChange = (value: any) => {
|
||||
// console.log(value,"filevalue");
|
||||
if (value.fileList.length === 0) {
|
||||
formik.setFieldValue(newName, null);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
`Questions[${name}].image`,
|
||||
value?.file?.originFileObj,
|
||||
);
|
||||
}
|
||||
};
|
||||
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
|
||||
onSuccess();
|
||||
};
|
||||
return (
|
||||
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`input.${label || name}`)}
|
||||
</label>
|
||||
|
||||
<Upload
|
||||
disabled={isDisabled}
|
||||
listType="picture"
|
||||
maxCount={1}
|
||||
fileList={[...fileList]}
|
||||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={` w-100`}
|
||||
>
|
||||
<Button
|
||||
className={isError ? "isError w-100 " : " w-100"}
|
||||
icon={<UploadOutlined />}
|
||||
>
|
||||
{placholder ?? t("input.Click_to_upload_the_image")}
|
||||
</Button>
|
||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||
{errorMsg}
|
||||
</Upload>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default File;
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import { Form, Input } from "antd";
|
||||
import React from "react";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { Field } from "formik";
|
||||
|
||||
const HintField = ({
|
||||
name,
|
||||
label,
|
||||
label2,
|
||||
placeholder,
|
||||
isDisabled,
|
||||
onChange,
|
||||
props,
|
||||
id,
|
||||
className,
|
||||
}: any) => {
|
||||
const newName = `Questions[${name}].hint`;
|
||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||
const TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
// console.log('Change:', e.target.value);
|
||||
formik.setFieldValue(newName, e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
||||
<label htmlFor={name} className="text">
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Field
|
||||
as={Input}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
name={newName}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ width: 200 }}
|
||||
id={id}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(HintField);
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Choice } from "../../../../../../types/Item";
|
||||
import ValidationField from "../../../../../../Components/ValidationField/ValidationField";
|
||||
import { useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getCharFromNumber } from "../../../../../../utils/getCharFromNumber";
|
||||
import TextField from "./TextField";
|
||||
import File from "./File";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
||||
import { toast } from "react-toastify";
|
||||
import CheckboxField from "./CheckboxField";
|
||||
import HintField from "./HintField";
|
||||
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
|
||||
|
|
@ -53,12 +47,12 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
|||
|
||||
<ImageBoxField name={`Questions.${index}.content_image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
<div className="answer_status" onClick={handleDeleteQuestion}>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_question")}
|
||||
<GoTrash
|
||||
className="trash_icon"
|
||||
onClick={handleDeleteQuestion}
|
||||
|
||||
size={17}
|
||||
/>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaCirclePlus } from "react-icons/fa6";
|
||||
import Tag from "./Tag";
|
||||
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
||||
import { useGetAllTag } from "../../../../../../api/tags";
|
||||
|
||||
const DynamicTags = ({ parent_index }: { parent_index: number }) => {
|
||||
const formik = useFormikContext<any>();
|
||||
const [t] = useTranslation();
|
||||
const { TagsSearch, setTagsSearch, currentTag, currentParentIndex } =
|
||||
useObjectToEdit();
|
||||
const { data } = useGetAllTag({
|
||||
name: TagsSearch,
|
||||
});
|
||||
const suggests = data?.data;
|
||||
|
||||
const handleAddChoice = () => {
|
||||
const length = formik?.values?.Questions?.[parent_index]?.tags.length;
|
||||
const lastElement =
|
||||
formik?.values?.Questions?.[parent_index]?.tags[length - 1]?.name;
|
||||
setTagsSearch(null);
|
||||
|
||||
if (lastElement !== "") {
|
||||
formik.setFieldValue(`Questions.[${parent_index}].tags`, [
|
||||
...((formik?.values as any)?.Questions?.[parent_index]?.tags as any[]),
|
||||
|
||||
{
|
||||
id: length + "_new",
|
||||
name: "",
|
||||
key: length,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
}
|
||||
};
|
||||
// console.log(formik?.values);
|
||||
// console.log(currentTag);
|
||||
|
||||
const handleChoice = (item: any) => {
|
||||
const length = formik?.values?.Questions?.[parent_index]?.tags?.length;
|
||||
console.log(currentTag);
|
||||
|
||||
formik.setFieldValue(`Questions.[${parent_index}].tags[${currentTag}]`, {
|
||||
...item,
|
||||
key: length,
|
||||
});
|
||||
setTagsSearch(null);
|
||||
};
|
||||
|
||||
// console.log(formik?.values?.tags?.length);
|
||||
|
||||
return (
|
||||
<div className="DynamicTags">
|
||||
{formik?.values?.Questions?.[parent_index]?.tags?.length < 1 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus size={23} onClick={handleAddChoice} />{" "}
|
||||
{t("header.add_tag")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="tag_container">
|
||||
<div className="tags">
|
||||
{(
|
||||
((formik?.values as any)?.Questions?.[parent_index]
|
||||
?.tags as any[]) || []
|
||||
).map((item: any, index: number) => {
|
||||
return (
|
||||
<Tag
|
||||
key={index}
|
||||
parent_index={parent_index}
|
||||
index={index}
|
||||
data={item}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{formik?.values?.Questions?.[parent_index]?.tags?.length > 0 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus onClick={handleAddChoice} size={20} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{TagsSearch && currentParentIndex === parent_index && (
|
||||
<div className="suggests">
|
||||
{suggests?.map((item: any, index: number) => {
|
||||
console.log(currentParentIndex === parent_index);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="suggested"
|
||||
key={index}
|
||||
onClick={() => handleChoice(item)}
|
||||
>
|
||||
{item?.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicTags;
|
||||
|
|
@ -41,14 +41,10 @@ const MaltySelectTag = ({ parent_index }: { parent_index: number }) => {
|
|||
? [{ id: searchValue, name: searchValue }]
|
||||
: [];
|
||||
|
||||
console.log(options);
|
||||
const value =
|
||||
formik?.values?.Questions[parent_index]?.tags?.map(
|
||||
(item: any) => item?.id ?? item,
|
||||
) ?? [];
|
||||
console.log(formik?.values?.Questions[parent_index]);
|
||||
|
||||
console.log(value);
|
||||
|
||||
const AllOptions = [...options, ...additionalData];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
|
||||
const Tag = ({
|
||||
data,
|
||||
index,
|
||||
parent_index,
|
||||
}: {
|
||||
data: any;
|
||||
index: number;
|
||||
parent_index: number;
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const DEBOUNCE_DELAY = 500;
|
||||
const formik = useFormikContext<any>();
|
||||
const { setTagsSearch, setCurrentTag, setCurrentParentIndex } =
|
||||
useObjectToEdit();
|
||||
console.log(formik?.values?.Questions);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.style.width = `${(formik?.values?.Questions?.[parent_index]?.tags[index]?.name?.length + 1) * 8}px`;
|
||||
}
|
||||
}, [formik?.values?.Questions?.[parent_index]?.tags[index]?.name]);
|
||||
|
||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// console.log(e.target.value);
|
||||
|
||||
formik.setFieldValue(`Questions.[${parent_index}].tags[${index}]`, {
|
||||
key: parent_index,
|
||||
name: e.target.value,
|
||||
id: `${parent_index}_key`,
|
||||
});
|
||||
setCurrentTag(index);
|
||||
setCurrentParentIndex(parent_index);
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
setTagsSearch(e.target.value);
|
||||
}, DEBOUNCE_DELAY);
|
||||
};
|
||||
|
||||
const handleDeleteChoice = () => {
|
||||
console.log(data);
|
||||
|
||||
// Create a copy of current tags array
|
||||
const currentTags = [...formik.values.tags];
|
||||
|
||||
// Remove the item at the specified index from the array
|
||||
currentTags.splice(index, 1);
|
||||
|
||||
console.log(currentTags); // Log the updated tags array
|
||||
|
||||
// Update formik field value with the updated tags array
|
||||
formik.setFieldValue(`Questions.[${parent_index}].tags`, currentTags);
|
||||
|
||||
// Reset search state if needed
|
||||
setTagsSearch(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="tag">
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="tagInput"
|
||||
type="text"
|
||||
value={formik?.values?.Questions?.[parent_index]?.tags[index]?.name}
|
||||
onChange={handleEditInputChange}
|
||||
/>
|
||||
<FaTrash onClick={handleDeleteChoice} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
|
|
@ -20,7 +20,7 @@ const Form = () => {
|
|||
|
||||
const handleAddChoice = (fromKeyCombination: boolean = false) => {
|
||||
formik.setFieldValue("answers", [
|
||||
...((formik?.values as any)?.answers as Choice[]),
|
||||
...(formik?.values?.answers ?? []) ,
|
||||
{
|
||||
content: null,
|
||||
content_image: null,
|
||||
|
|
@ -42,13 +42,12 @@ const Form = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (Success) {
|
||||
formik?.setValues({});
|
||||
formik.setErrors({});
|
||||
formik.resetForm()
|
||||
setSuccess(false);
|
||||
console.log(formik.errors);
|
||||
}
|
||||
}, [Success]);
|
||||
|
||||
|
||||
return (
|
||||
<Row className="w-100 exercise_form_container">
|
||||
<div className="exercise_form">
|
||||
|
|
@ -62,7 +61,7 @@ const Form = () => {
|
|||
</div>
|
||||
|
||||
<Choices />
|
||||
{formik?.values?.answers?.length < 5 && (
|
||||
{(formik?.values?.answers === null || formik?.values?.answers === undefined || formik?.values?.answers?.length < 5) && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus onClick={() => handleAddChoice()} size={23} />{" "}
|
||||
{t("header.add_new_choice")}
|
||||
|
|
@ -75,8 +74,11 @@ const Form = () => {
|
|||
placeholder="_"
|
||||
name="hint"
|
||||
label="hint_question"
|
||||
type="text"
|
||||
style={{ width: "100%" }}
|
||||
type="TextArea"
|
||||
style={{ width: "100%" , height: 60,resize:"none" }}
|
||||
showCount={false}
|
||||
autoSize={{ minRows: 2, maxRows: 10 }}
|
||||
|
||||
/>
|
||||
<SelectTag />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import DeleteModels from "../../../Layout/Dashboard/DeleteModels";
|
|||
import { ModalEnum } from "../../../enums/Model";
|
||||
import { useGetAllSubject } from "../../../api/subject";
|
||||
import { useGetAllGrade } from "../../../api/grade";
|
||||
import { useGetAllCurriculum } from "../../../api/curriculum";
|
||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||
import { ABILITIES_ENUM } from "../../../enums/abilities";
|
||||
import { canAddQuestion } from "../../../utils/hasAbilityFn";
|
||||
|
|
@ -43,17 +42,15 @@ const TableHeader = () => {
|
|||
const unitName = unit?.data?.name ?? "";
|
||||
const LessonName = Lesson?.data?.name ?? "";
|
||||
|
||||
useSetPageTitle(
|
||||
t(`page_header.grade`) +
|
||||
" / " +
|
||||
` ${t("header.subject_of_class")} (${gradeName})` +
|
||||
" / " +
|
||||
SubjectName +
|
||||
" / " +
|
||||
unitName +
|
||||
" / " +
|
||||
LessonName,
|
||||
);
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.grade`)}`, path:"grade"},
|
||||
{name:` ${t("header.subject_of_class")} (${gradeName})`, path:`grade/${grade_id}`},
|
||||
{name:SubjectName, path:`subject/${subject_id}`},
|
||||
{name:unitName, path:`unit/${unit_id}`},
|
||||
{name:LessonName, path:`lesson/${lesson_id }`}
|
||||
]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const App: React.FC = () => {
|
|||
const { filterState } = useFilterState();
|
||||
|
||||
const response = useGetAllQuestion({
|
||||
lesson_id: lesson_id,
|
||||
lessonsIds: [lesson_id],
|
||||
pagination: true,
|
||||
...filterState,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as Yup from "yup";
|
||||
import { Question } from "../../../../types/Item";
|
||||
import { Question } from "../../../types/Item";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const getInitialValues = (objectToEdit: Question): any => {
|
||||
|
|
@ -16,7 +16,7 @@ export const getInitialValues = (objectToEdit: Question): any => {
|
|||
hint: objectToEdit?.hint ?? null,
|
||||
isBase: 0,
|
||||
parent_id: objectToEdit?.parent_id ?? "",
|
||||
answers: objectToEdit?.answers ?? [],
|
||||
answers: objectToEdit?.answers ?? null,
|
||||
tags: tags ?? [],
|
||||
};
|
||||
};
|
||||
|
|
@ -32,6 +32,15 @@ export const getValidationSchema = () => {
|
|||
content_image: Yup.string().nullable(),
|
||||
isCorrect: Yup.boolean(),
|
||||
}),
|
||||
).min(2).test(
|
||||
"at-least-one-correct",
|
||||
"At least one answer must be correct",
|
||||
(answers: any) => {
|
||||
return answers?.some(
|
||||
(answer: any) =>
|
||||
answer?.isCorrect === true || answer?.isCorrect === 1,
|
||||
);
|
||||
},
|
||||
),
|
||||
});
|
||||
};
|
||||
|
|
@ -42,10 +51,18 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
|
|||
id: tag?.id,
|
||||
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 {
|
||||
...item,
|
||||
answer:newAnswers,
|
||||
canAnswersBeShuffled: objectToEdit?.canAnswersBeShuffled ? 1 : 0,
|
||||
hint: objectToEdit?.hint ?? "",
|
||||
isBase: 0,
|
||||
|
|
@ -53,7 +70,7 @@ export const getInitialValuesBase = (objectToEdit: Question): any => {
|
|||
};
|
||||
});
|
||||
|
||||
const questions = newQuestions ?? [];
|
||||
const questions = newQuestions ?? [{answers:[]}];
|
||||
|
||||
return {
|
||||
id: objectToEdit?.id ?? null,
|
||||
|
|
@ -84,12 +101,11 @@ export const getValidationSchemaBase = () => {
|
|||
answer_image: Yup.string().nullable(),
|
||||
isCorrect: Yup.boolean(),
|
||||
}),
|
||||
)
|
||||
).min(2)
|
||||
.test(
|
||||
"at-least-one-correct",
|
||||
"At least one answer must be correct",
|
||||
(answers: any) => {
|
||||
console.log(answers, "answers");
|
||||
|
||||
return answers.some(
|
||||
(answer: any) =>
|
||||
|
|
@ -98,7 +114,7 @@ export const getValidationSchemaBase = () => {
|
|||
},
|
||||
),
|
||||
}),
|
||||
),
|
||||
).min(1),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -45,19 +45,59 @@ export const useColumns = () => {
|
|||
},
|
||||
{
|
||||
title: `${t("columns.content")}`,
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
dataIndex: "content",
|
||||
key: "content",
|
||||
align: "center",
|
||||
render: (text, record) => record?.content,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("columns.isBase"),
|
||||
title: `${t("columns.hint")}`,
|
||||
dataIndex: "hint",
|
||||
key: "hint",
|
||||
align: "center",
|
||||
render: (text, record) => {
|
||||
|
||||
return (
|
||||
<>{record?.hint ?? "_"}</>
|
||||
);
|
||||
},
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: `${t("columns.tags")}`,
|
||||
dataIndex: "tags",
|
||||
key: "tags",
|
||||
align: "center",
|
||||
|
||||
render: (text, record) => {
|
||||
const tags = record?.tags?.map((item:any)=>{
|
||||
return item?.name
|
||||
}) ?? [];
|
||||
return (
|
||||
<div>
|
||||
{tags.length > 0 ? (
|
||||
tags.map((tag, index) => (
|
||||
<span key={index}>
|
||||
{tag}
|
||||
{index < tags.length - 1 && ', '}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span>_</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("columns.question_type"),
|
||||
dataIndex: "isBase",
|
||||
key: "isBase",
|
||||
align: "center",
|
||||
render: (text, record) =>
|
||||
record?.isBase ? t("practical.yes") : t("practical.no"),
|
||||
record?.isBase ? t("columns.base_question") : t("columns.normal_question"),
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,11 +32,15 @@ const TableWithHeader = () => {
|
|||
});
|
||||
|
||||
const gradeName = grade?.data?.name ?? "";
|
||||
useSetPageTitle(
|
||||
t(`page_header.grade`) +
|
||||
" / " +
|
||||
` ${t("header.subject_of_class")} (${gradeName})`,
|
||||
);
|
||||
|
||||
|
||||
useSetPageTitle([
|
||||
{name:`${t(`page_header.home`)}`, path:"/"},
|
||||
{name:`${t(`page_header.grade`)}`, path:"grade"},
|
||||
{name:` ${t("header.subject_of_class")} (${gradeName})`, path:`grade/${grade_id}`}
|
||||
]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
|
|
|
|||
|
|
@ -1,19 +1,7 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Field, useFormikContext } from "formik";
|
||||
import Image from "../../Components/Ui/Image";
|
||||
import { Input } from "antd";
|
||||
import AuthSelect from "../../Components/Ui/Custom/AuthSelect";
|
||||
// import { useGetAllCycle } from "../../api/cycle";
|
||||
// import { useGetAllBranch } from "../../api/branch";
|
||||
import { useGetAllTerm } from "../../api/term";
|
||||
import { FormValues } from "../../types/Auth";
|
||||
import {
|
||||
BRANCH_OBJECT_KEY,
|
||||
CYCLE_OBJECT_KEY,
|
||||
TERM_OBJECT_KEY,
|
||||
} from "../../config/AppKey";
|
||||
import useFormatAuthDataToSelect from "../../utils/useFormatAuthDataToSelect";
|
||||
import { Form, useFormikContext } from "formik";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ValidationField from "../../Components/ValidationField/ValidationField";
|
||||
|
||||
type FormFieldType = {
|
||||
isLoading: boolean;
|
||||
|
|
@ -21,26 +9,18 @@ type FormFieldType = {
|
|||
|
||||
const FormField = ({ isLoading }: FormFieldType) => {
|
||||
const [t] = useTranslation();
|
||||
const {isValid} = useFormikContext();
|
||||
console.log(isValid,"isValid");
|
||||
|
||||
return (
|
||||
<Form className="AuthForm">
|
||||
{/* <Image style={{background:"#000"}} src="../App/Logo.png" /> */}
|
||||
<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">
|
||||
{t("input.Password")}
|
||||
</label>
|
||||
|
|
@ -53,9 +33,9 @@ const FormField = ({ isLoading }: FormFieldType) => {
|
|||
className="passwordInput"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<button disabled={isLoading} type="submit" className="auth_submit_button">
|
||||
<button disabled={ !isValid || isLoading} type="submit" className="auth_submit_button">
|
||||
{t("practical.login")}
|
||||
</button>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import useAuthState from "../../zustand/AuthState";
|
|||
import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess";
|
||||
import { useLoginAdmin } from "../../api/auth";
|
||||
import FormField from "./FormField";
|
||||
import { initialValues } from "./formutils";
|
||||
import { initialValues, validationSchema } from "./formutils";
|
||||
import { FormValues } from "../../types/Auth";
|
||||
import { toast } from "react-toastify";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
|
@ -39,7 +39,7 @@ const LoginForm = () => {
|
|||
|
||||
return (
|
||||
<div className="LoginForm">
|
||||
<Formik initialValues={initialValues} onSubmit={handelSubmit}>
|
||||
<Formik initialValues={initialValues} onSubmit={handelSubmit} isInitialValid={false} validationSchema={validationSchema} >
|
||||
{(formikProps) => <FormField isLoading={isLoading} />}
|
||||
</Formik>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import * as Yup from "yup";
|
|||
import { FormValues } from "../../types/Auth";
|
||||
|
||||
export const validationSchema = Yup.object().shape({
|
||||
username: Yup.string().required("Username is required"),
|
||||
password: Yup.string().required("Password is required"),
|
||||
username: Yup.string().required("validation.required"),
|
||||
password: Yup.string().required("validation.required").min(8,"validation.Password_must_be_at_least_8_characters_long"),
|
||||
});
|
||||
|
||||
export const initialValues: FormValues = {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ 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();
|
||||
|
|
@ -10,8 +12,8 @@ const Dummy = () => {
|
|||
const { FilterButton, FilterBody } = useFilter();
|
||||
return (
|
||||
<div className="DummyHomePage">
|
||||
{/* <FilterButton />
|
||||
<FilterBody>karim</FilterBody> */}
|
||||
<PageTitle/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TCrudRoute, TMenuItem } from "./types/App";
|
||||
import { FaHome, FaMoneyBill, FaSellcast } from "react-icons/fa";
|
||||
import { FaCashRegister, FaHome, FaMoneyBill, FaPaperclip, FaSellcast, FaTag, FaUser } from "react-icons/fa";
|
||||
import React from "react";
|
||||
|
||||
const Dummy = React.lazy(() => import("./Pages/Home/Dummy"));
|
||||
|
|
@ -33,6 +33,7 @@ const EditReSeller = React.lazy(
|
|||
const User = React.lazy(() => import("./Pages/Admin/User/Page"));
|
||||
|
||||
const Param = React.lazy(() => import("./Pages/Admin/Param/Page"));
|
||||
const QuestionBank = React.lazy(() => import("./Pages/Admin/QuestionBank/Page"));
|
||||
|
||||
/// RESELLER ///
|
||||
const Student_Package = React.lazy(
|
||||
|
|
@ -44,12 +45,14 @@ import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities";
|
|||
import { ParamsEnum } from "./enums/params";
|
||||
import { TbCategory } from "react-icons/tb";
|
||||
import { UserTypeEnum } from "./enums/UserType";
|
||||
import { FaTags } from "react-icons/fa6";
|
||||
import { MdGrade } from "react-icons/md";
|
||||
|
||||
export const menuItems: TMenuItem[] = [
|
||||
{
|
||||
header: "page_header.dashboard",
|
||||
element: <Dummy />,
|
||||
icon: <TbCategory />,
|
||||
icon: <FaHome />,
|
||||
text: "sidebar.dashboard",
|
||||
path: "/",
|
||||
abilities: ABILITIES_ENUM?.PASS,
|
||||
|
|
@ -60,7 +63,7 @@ export const menuItems: TMenuItem[] = [
|
|||
{
|
||||
header: "page_header.grade",
|
||||
element: <Grade />,
|
||||
icon: <FaMoneyBill />,
|
||||
icon: <TbCategory />,
|
||||
text: "sidebar.grade",
|
||||
path: `/${ABILITIES_ENUM?.GRADE}`,
|
||||
abilities: ABILITIES_ENUM?.GRADE,
|
||||
|
|
@ -70,7 +73,7 @@ export const menuItems: TMenuItem[] = [
|
|||
{
|
||||
header: "page_header.tags",
|
||||
element: <Tags />,
|
||||
icon: <FaMoneyBill />,
|
||||
icon: <FaTags />,
|
||||
text: "sidebar.tags",
|
||||
path: `/${ABILITIES_ENUM?.TAG}`,
|
||||
abilities: ABILITIES_ENUM?.TAG,
|
||||
|
|
@ -78,9 +81,9 @@ export const menuItems: TMenuItem[] = [
|
|||
prevPath: 0,
|
||||
},
|
||||
{
|
||||
header: "page_header.tags",
|
||||
header: "page_header.report",
|
||||
element: <Report />,
|
||||
icon: <FaMoneyBill />,
|
||||
icon: <FaPaperclip />,
|
||||
text: "sidebar.report",
|
||||
path: `/${ABILITIES_ENUM?.Report}`,
|
||||
abilities: ABILITIES_ENUM?.Report,
|
||||
|
|
@ -90,7 +93,7 @@ export const menuItems: TMenuItem[] = [
|
|||
{
|
||||
header: "page_header.student",
|
||||
element: <Student />,
|
||||
icon: <FaMoneyBill />,
|
||||
icon: <MdGrade />,
|
||||
text: "sidebar.student",
|
||||
path: `/${ABILITIES_ENUM?.STUDENT}`,
|
||||
abilities: ABILITIES_ENUM?.STUDENT,
|
||||
|
|
@ -100,7 +103,7 @@ export const menuItems: TMenuItem[] = [
|
|||
{
|
||||
header: "page_header.reSeller",
|
||||
element: <ReSeller />,
|
||||
icon: <FaSellcast />,
|
||||
icon: <FaCashRegister />,
|
||||
text: "sidebar.reseller",
|
||||
path: `/${ABILITIES_ENUM?.RE_SELLER}`,
|
||||
abilities: ABILITIES_ENUM?.RE_SELLER,
|
||||
|
|
@ -110,7 +113,7 @@ export const menuItems: TMenuItem[] = [
|
|||
{
|
||||
header: "page_header.user",
|
||||
element: <User />,
|
||||
icon: <FaSellcast />,
|
||||
icon: <FaUser />,
|
||||
text: "sidebar.user",
|
||||
path: `/${ABILITIES_ENUM?.USER}`,
|
||||
abilities: ABILITIES_ENUM?.USER,
|
||||
|
|
@ -128,6 +131,18 @@ export const menuItems: TMenuItem[] = [
|
|||
prevPath: 0,
|
||||
},
|
||||
|
||||
{
|
||||
header: "page_header.questionBank",
|
||||
element: <QuestionBank />,
|
||||
icon: <FaSellcast />,
|
||||
text: "sidebar.questionBank",
|
||||
path: `/${ABILITIES_ENUM?.QUESTION}`,
|
||||
abilities: ABILITIES_ENUM?.QUESTION,
|
||||
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||
prevPath: 0,
|
||||
},
|
||||
|
||||
|
||||
/// RESELLER /////
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Popover {
|
||||
.ant-popover-inner {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
x
|
||||
|
||||
.Color_type_checkbox.false {
|
||||
.ant-checkbox-checked .ant-checkbox-inner {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
}
|
||||
.page_links {
|
||||
color: var(--opacity);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,3 +19,12 @@
|
|||
.filter_header_top {
|
||||
color: #202c4b;
|
||||
}
|
||||
|
||||
|
||||
.PageTitle{
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
.PageTitleItems{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
// padding: 2vw;
|
||||
.exercise_add_main {
|
||||
background: var(--bg);
|
||||
padding: 2vw;
|
||||
padding: 10px 2vw;
|
||||
}
|
||||
.exercise_add_buttons {
|
||||
display: flex;
|
||||
|
|
@ -127,12 +127,14 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 14px 10px;
|
||||
padding: 14px 20px;
|
||||
background: #f2f4f8;
|
||||
border-radius: 10px 10px 0 0;
|
||||
margin: 0 !important;
|
||||
box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.1);
|
||||
|
||||
div{
|
||||
margin-left: 25px;
|
||||
}
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -144,7 +146,8 @@
|
|||
.delete_question_options {
|
||||
margin-top: 25px;
|
||||
color: var(--warning);
|
||||
|
||||
z-index: 9999;
|
||||
cursor: pointer;
|
||||
padding-left: 10px;
|
||||
.trash_icon {
|
||||
margin-right: 10px !important;
|
||||
|
|
@ -211,3 +214,44 @@
|
|||
transform: translateY(55px);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.SelectTag{
|
||||
label{
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,4 +5,4 @@ const API = {
|
|||
LOGIN: `login`,
|
||||
LOGOUT: `logout`,
|
||||
};
|
||||
export const useLoginAdmin = () => useAddMutation(KEY, API.LOGIN, false);
|
||||
export const useLoginAdmin = () => useAddMutation(KEY, API.LOGIN, true);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ function useAxios() {
|
|||
return response;
|
||||
},
|
||||
function (error) {
|
||||
console.log(error?.response);
|
||||
|
||||
const status = error?.request?.status;
|
||||
const errorMsg = error?.response?.data?.message;
|
||||
const errorField = error?.response?.data;
|
||||
|
|
|
|||
|
|
@ -186,4 +186,11 @@ export enum ModalEnum {
|
|||
Student_Package_EDIT = "Student_Package.edit",
|
||||
Student_Package_ADD = "Student_Package.add",
|
||||
Student_Package_DELETE = "Student_Package.delete",
|
||||
|
||||
|
||||
///QuestionBank
|
||||
|
||||
QUESTION_BANK_ADD = "QuestionBank.add",
|
||||
QUESTION_BANK_EDIT = "QuestionBank.edit",
|
||||
QUESTION_BANK_DELETE = "QuestionBank.delete",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export enum ABILITIES_ENUM {
|
|||
User = "user",
|
||||
RE_SELLER = "reseller",
|
||||
Student_Package = "student_package",
|
||||
QUESTION_BANK = "QuestionBank"
|
||||
|
||||
////
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@
|
|||
"grade_to_pass_must_be_less_than_max_grade": "يجب أن تكون درجة النجاح أقل من الحد الأقصى للدرجة",
|
||||
"max_mark_must_be_greater_than_min_mark_to_pass": "يجب ان تكون اكبر من علامة النجاح",
|
||||
"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": {
|
||||
"register_students": "تسجيل الطلاب",
|
||||
|
|
@ -123,7 +125,9 @@
|
|||
"personal_information": "المعلومات الشخصية",
|
||||
"address": "العنوان",
|
||||
"attachment": "المرفق",
|
||||
"subject_of_class": "مواد الصف"
|
||||
"subject_of_class": "مواد الصف",
|
||||
"this_will_un_do_all_your_changes":"سوف يؤدي هذا إلى إلغاء جميع تغييراتك",
|
||||
"edit_question":"تعديل سؤال"
|
||||
},
|
||||
"columns": {
|
||||
"id": "الرقم التعريفي",
|
||||
|
|
@ -183,7 +187,12 @@
|
|||
"subject":"المادة",
|
||||
"quiz_status":"حالة الاختبار",
|
||||
"creator_name":"اسم المنشئ",
|
||||
"created_by":"أنشئ بواسطة"
|
||||
"created_by":"أنشئ بواسطة",
|
||||
"question_type": "نوع تمرين",
|
||||
"base_question": " تمرين متعدد ",
|
||||
"normal_question": " تمرين عادي",
|
||||
"hint":"شرح ",
|
||||
"tags":"كلمات مفتاحية"
|
||||
},
|
||||
"practical": {
|
||||
"to_confirm_deletion_please_re_enter": "لتأكيد الحذف، يرجى إعادة الإدخال",
|
||||
|
|
@ -749,7 +758,8 @@
|
|||
"reseller": "البائعين",
|
||||
"param": "معامل",
|
||||
"student_package": "حزمة الطالب",
|
||||
"quiz":"الاختبارات"
|
||||
"quiz":"الاختبارات",
|
||||
"questionBank":"بنك الأسئلة"
|
||||
},
|
||||
"message": {
|
||||
"some_thing_went_wrong": "حدث خطأ ما",
|
||||
|
|
@ -784,17 +794,18 @@
|
|||
"grade": "الصفوف",
|
||||
"report": "تقرير",
|
||||
"tags": "كلمات مفتاحية",
|
||||
"reseller":"البائعين"
|
||||
"reseller":"البائعين",
|
||||
"QuestionBank":"بنك الأسئلة"
|
||||
},
|
||||
"page_header": {
|
||||
"dashboard": "لوحة القيادة / الصفحة الرئيسية",
|
||||
"course": " لوحة القيادة / الصفوف ",
|
||||
"teacher": " لوحة القيادة / المعلمون",
|
||||
"payment": " لوحة القيادة / الدفعات",
|
||||
"branch": " لوحة القيادة / الفروع",
|
||||
"role": " لوحة القيادة / الادوار",
|
||||
"student": " لوحة القيادة / قائمة الطلاب ",
|
||||
"admin": " لوحة القيادة / المسؤولون",
|
||||
"home": "لوحة القيادة",
|
||||
"course": " الصفوف ",
|
||||
"teacher": " المعلمون",
|
||||
"payment": " الدفعات",
|
||||
"branch": " الفروع",
|
||||
"role": " الادوار",
|
||||
"student": " قائمة الطلاب ",
|
||||
"admin": " المسؤولون",
|
||||
"student_details": "تفاصيل الطالب",
|
||||
"create_student": "إنشاء طالب",
|
||||
"course_details": "تفاصيل الصف",
|
||||
|
|
@ -802,32 +813,34 @@
|
|||
"student_payment": "دفع الطالب",
|
||||
"student_note": "ملاحظات الطالب",
|
||||
"student_status": "حالة الطالب",
|
||||
"education_class": "لوحة القيادة / الصفوف / الشعب",
|
||||
"education_class_details": " لوحة القيادة / الصفوف / الشعب / تفاصيل الشعبة",
|
||||
"subject_details": " لوحة القيادة / تفاصيل المادة ",
|
||||
"cycle": "لوحة القيادة / السنة دراسية ",
|
||||
"term": "لوحة القيادة / الفصل ",
|
||||
"unit_details": "لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة",
|
||||
"lesson_details": "لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة / تفاصيل الدرس",
|
||||
"exercise_add": "لوحة القيادة / تفاصيل الدرس / إضافة تمارين ",
|
||||
"subject": "لوحة القيادة / المادة",
|
||||
"tags": "لوحة القيادة / كلمات مفتاحية",
|
||||
"Question": "لوحة القيادة /اسئلة ",
|
||||
"add_Question": "لوحة القيادة /إضافة اسئلة ",
|
||||
"edit_Question": "لوحة القيادة /تعديل اسئلة ",
|
||||
"grade": "لوحة القيادة / الصفوف",
|
||||
"education_class": "الصفوف / الشعب",
|
||||
"education_class_details": " الصفوف / الشعب / تفاصيل الشعبة",
|
||||
"subject_details": " تفاصيل المادة ",
|
||||
"cycle": " السنة دراسية ",
|
||||
"term": "الفصل ",
|
||||
"unit_details": "تفاصيل المادة / تفاصيل الوحدة",
|
||||
"lesson_details": "تفاصيل المادة / تفاصيل الوحدة / تفاصيل الدرس",
|
||||
"exercise_add": "تفاصيل الدرس / إضافة تمارين ",
|
||||
"subject": "المادة",
|
||||
"tags": "كلمات مفتاحية",
|
||||
"Question": "سئلة ",
|
||||
"add_Question": "ضافة اسئلة ",
|
||||
"edit_Question": "عديل اسئلة ",
|
||||
"grade": "الصفوف",
|
||||
"report": "تقرير",
|
||||
"users": "لوحة القيادة / المستخدمون",
|
||||
"reseller": " لوحة القيادة / البائعين",
|
||||
"add_reseller": " لوحة القيادة / البائعين / إضافة بائع ",
|
||||
"users": "المستخدمون",
|
||||
"reseller": " البائعين",
|
||||
"add_reseller": " البائعين / إضافة بائع ",
|
||||
"param": "معامل",
|
||||
"student_package": "حزمة الطالب"
|
||||
"student_package": "حزمة الطالب",
|
||||
"QuestionBank":"بنك الأسئلة"
|
||||
},
|
||||
"table": {
|
||||
"student": "قائمة الطلاب",
|
||||
"reseller": "البائعين",
|
||||
"grade": "قائمة الصفوف",
|
||||
"subjects": "مواد الصف"
|
||||
"subjects": "مواد الصف",
|
||||
"QuestionBank":"بنك الأسئلة"
|
||||
},
|
||||
"alphabet": {
|
||||
"A": "A",
|
||||
|
|
|
|||
|
|
@ -677,3 +677,25 @@ export const canDeleteStudent_Package = hasAbility(
|
|||
);
|
||||
|
||||
|
||||
|
||||
|
||||
/// QuestionBank
|
||||
|
||||
export const canAddQuestionBank = hasAbility(
|
||||
ABILITIES_ENUM.QUESTION_BANK,
|
||||
ABILITIES_VALUES_ENUM.STORE,
|
||||
);
|
||||
|
||||
export const canEditQuestionBank = hasAbility(
|
||||
ABILITIES_ENUM.QUESTION_BANK,
|
||||
ABILITIES_VALUES_ENUM.UPDATE,
|
||||
);
|
||||
export const canDeleteQuestionBank = hasAbility(
|
||||
ABILITIES_ENUM.QUESTION_BANK,
|
||||
ABILITIES_VALUES_ENUM.DELETE,
|
||||
);
|
||||
|
||||
export const canShowQuestionBank = hasAbility(
|
||||
ABILITIES_ENUM.QUESTION_BANK,
|
||||
ABILITIES_VALUES_ENUM.SHOW,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user