Compare commits

..

No commits in common. "0f54e215462df94a10f9b9e3fc47f5cd262d7727" and "e981ce748198aa2abf69d09aa1bbbc477c749487" have entirely different histories.

56 changed files with 664 additions and 619 deletions

View File

@ -13,7 +13,6 @@
"Popconfirm",
"queryqlent",
"registraion",
"Sellcast",
"SENDNOTIFICATION",
"setdateparams",
"szhsin",

View File

@ -63,7 +63,7 @@ const ImageBoxField = ({ name }: any) => {
<div className="VisibleHidden">hidden</div>
)}
</div>
<div className="ImageBox" onClick={handleButtonClick}>
<div className="ImageBox">
{imagePreview ? (
<img src={imagePreview} onClick={handleButtonClick} alt="Preview" className="imagePreview" />
) : (

View File

@ -25,7 +25,6 @@ const TextField = ({
) => {
formik.setFieldValue(name, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
<ValidationFieldLabel
@ -46,8 +45,8 @@ const TextField = ({
size="large"
showCount
maxLength={1000}
autoSize={{ minRows: 4, maxRows: 10 }}
onChange={onChange || TextFilehandleChange}
style={{ height: 120 }}
id={name}
{...props}
/>

View File

@ -15,8 +15,6 @@
.text,
.ant-form-item {
margin-bottom: 7px !important;
font-weight: bold;
font-size: 19px;
> span {
color: transparent;
}
@ -67,15 +65,3 @@
.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;
}
}

View File

@ -72,11 +72,11 @@ const Header = () => {
>
<GoArrowSwitch className="m-2" />
</Popconfirm>
{isBseQuestion || values?.isBase === 1
? t("header.malty_exercise")
: t("header.exercise")}
</Popconfirm>
</div>
</header>
);

View File

@ -1,4 +1,5 @@
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";
@ -7,7 +8,8 @@ import useModalHandler from "../../utils/useModalHandler";
import { MdOutlineArrowForwardIos } from "react-icons/md";
import { deletePathSegments } from "../../utils/deletePathSegments";
import { getPrevPathRoute } from "../../utils/getPrevPathRoute";
import PageTitleComponent from "./PageTitle";
import { usePageTitleState } from "../../zustand/PageTitleState";
import FillterForm from "../Ui/FillterForm";
const PageHeader = ({
canAdd,
@ -33,20 +35,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 /> <PageTitleComponent/>
<MdOutlineArrowForwardIos /> {PageTitle}
</span>
</span>
{ addModal ? canAdd && (

View File

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

View File

@ -20,10 +20,7 @@ const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteGrade();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.grade`)}`, path:"grade"}
]);
useSetPageTitle(t(`page_header.grade`));
return (
<div className="TableWithHeader">

View File

@ -18,11 +18,7 @@ const DeleteModalForm = lazy(
const TableHeader = () => {
const [t] = useTranslation();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.param`)}`, path:"param"}
]);
useSetPageTitle(t(`page_header.param`));
const deleteMutation = useDeleteParam();
return (
<div className="TableWithHeader">

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +0,0 @@
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"),
});
};

View File

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

View File

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

View File

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

View File

@ -21,12 +21,9 @@ const SearchField = lazy(
);
const TableHeader = () => {
const { handel_open_model } = useModalHandler();
const [t] = useTranslation();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.report`)}`, path:"report"}
]);
useSetPageTitle(t(`page_header.report`));
const deleteMutation = useDeleteTag();
return (
<div className="TableWithHeader">

View File

@ -24,10 +24,7 @@ const SearchField = lazy(
const TableHeader = () => {
const [t] = useTranslation();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.reseller`)}`, path:"reseller"}
]);
useSetPageTitle(t(`page_header.reseller`));
const deleteMutation = useDeleteTag();
return (
<div className="TableWithHeader">

View File

@ -20,10 +20,8 @@ const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteStudent();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.student`)}`, path:"student"}
]);
useSetPageTitle(t(`page_header.student`));
return (
<div className="TableWithHeader">
<Suspense fallback={<Spin />}>

View File

@ -16,15 +16,14 @@ 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([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.tags`)}`, path:"tag"}
]);
useSetPageTitle(t(`page_header.tags`));
const deleteMutation = useDeleteTag();
return (
<div className="TableWithHeader">

View File

@ -35,13 +35,15 @@ const TableHeader = () => {
});
const gradeName = grade?.data?.name ?? "";
const SubjectName = Subject?.data?.name ?? "";
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}`}
]);
useSetPageTitle(
t(`page_header.grade`) +
" / " +
` ${t("header.subject_of_class")} (${gradeName})` +
" / " +
SubjectName,
);
return (
<div className="TableWithHeader">

View File

@ -18,10 +18,7 @@ const DeleteModalForm = lazy(
const TableHeader = () => {
const [t] = useTranslation();
useSetPageTitle([
{name:`${t(`page_header.home`)}`, path:"/"},
{name:`${t(`page_header.user`)}`, path:"user"}
]);
useSetPageTitle(t(`page_header.user`));
const deleteMutation = useDeleteUser();
return (
<div className="TableWithHeader">

View File

@ -26,7 +26,7 @@ const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteLesson();
const { unit_id, grade_id, subject_id } =
const { unit_id, curriculum_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([
{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}`}
]);
useSetPageTitle(
t(`page_header.grade`) +
" / " +
` ${t("header.subject_of_class")} (${gradeName})` +
" / " +
SubjectName +
" / " +
unitName,
);
return (
<div className="TableWithHeader">

View File

@ -1,12 +1,13 @@
import React, { Suspense, lazy, useEffect } from "react";
import { Spin } from "antd";
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
import {
getInitialValues,
getValidationSchema,
getInitialValuesBase,
getValidationSchemaBase,
processTags,
} from "./formUtil";
} from "./Model/formUtil";
import { useAddQuestion, useAddQuestionAsync } from "../../../api/Question";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate, useParams } from "react-router-dom";
@ -20,6 +21,7 @@ import ModelForm from "./Model/ModelForm";
import { toast } from "react-toastify";
import { Form, Formik } from "formik";
import { MdOutlineArrowForwardIos } from "react-icons/md";
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
const AddPage: React.FC = () => {
const location = useLocation();
@ -161,8 +163,7 @@ const AddPage: React.FC = () => {
const handleCancel = () => {
navigate(-1);
};
const Loading = LoadingAsync || isLoading;
const Loading = LoadingAsync || isLoading
useEffect(() => {
if (isSuccess) {
setSuccess(true);
@ -210,7 +211,9 @@ const AddPage: React.FC = () => {
</Form>
)}
</Formik>
<Suspense fallback={<Spin />}>
<AcceptModal />
</Suspense>
</div>
</div>
@ -261,6 +264,9 @@ const AddPage: React.FC = () => {
)}
</Formik>
<Suspense fallback={<Spin />}>
<AcceptModal />
</Suspense>
</div>
</div>
);

View File

@ -1,12 +1,13 @@
import React, { useEffect } from "react";
import { Modal, Spin } from "antd";
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
import {
getInitialValues,
getValidationSchema,
getInitialValuesBase,
getValidationSchemaBase,
processTags,
} from "./formUtil";
} from "./Model/formUtil";
import {
useAddQuestion,
useDeleteQuestion,

View File

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

View File

@ -70,7 +70,6 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
type="TextArea"
style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
autoSize={{ minRows: 2, maxRows: 10 }}
/>
</div>
</>

View File

@ -1,6 +1,7 @@
import React from "react";
import { Checkbox } from "antd";
import { useFormikContext } from "formik";
import useFormField from "../../../../../../Hooks/useFormField";
import { Checkbox, Form } from "antd";
import { useFormik, useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
const CheckboxField = ({
name,

View File

@ -6,7 +6,10 @@ 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";
@ -89,7 +92,6 @@ const ChoiceFields = ({
type="TextArea"
style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
autoSize={{ minRows: 2, maxRows: 10 }}
/>
</div>
</>

View File

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

View File

@ -0,0 +1,54 @@
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);

View File

@ -87,12 +87,6 @@ const Form = () => {
);
useEffect(() => {
if (Success) {
formik.resetForm()
setSuccess(false);
}
}, [Success]);
return (
<Row className="w-100 exercise_form_container">
@ -142,7 +136,6 @@ const Form = () => {
label="hint_question"
type="TextArea"
style={{ width: "100%" , height: 60,resize:"none" }}
autoSize={{ minRows: 2, maxRows: 10 }}
showCount={false}
/>
<MaltySelectTag parent_index={parent_index} />

View File

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

View File

@ -0,0 +1,52 @@
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);

View File

@ -1,10 +1,16 @@
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";

View File

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

View File

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

View File

@ -77,7 +77,6 @@ const Form = () => {
type="TextArea"
style={{ width: "100%" , height: 60,resize:"none" }}
showCount={false}
autoSize={{ minRows: 2, maxRows: 10 }}
/>
<SelectTag />

View File

@ -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 => {

View File

@ -42,15 +42,17 @@ const TableHeader = () => {
const unitName = unit?.data?.name ?? "";
const LessonName = Lesson?.data?.name ?? "";
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 }`}
]);
useSetPageTitle(
t(`page_header.grade`) +
" / " +
` ${t("header.subject_of_class")} (${gradeName})` +
" / " +
SubjectName +
" / " +
unitName +
" / " +
LessonName,
);
return (
<div className="TableWithHeader">

View File

@ -11,7 +11,7 @@ const App: React.FC = () => {
const { filterState } = useFilterState();
const response = useGetAllQuestion({
lessonsIds: [lesson_id],
lesson_id: lesson_id,
pagination: true,
...filterState,
});

View File

@ -45,59 +45,19 @@ export const useColumns = () => {
},
{
title: `${t("columns.content")}`,
dataIndex: "content",
key: "content",
dataIndex: "name",
key: "name",
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 ?? "_"}</>
);
},
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"),
title: t("columns.isBase"),
dataIndex: "isBase",
key: "isBase",
align: "center",
render: (text, record) =>
record?.isBase ? t("columns.base_question") : t("columns.normal_question"),
record?.isBase ? t("practical.yes") : t("practical.no"),
},
{

View File

@ -32,15 +32,11 @@ const TableWithHeader = () => {
});
const gradeName = grade?.data?.name ?? "";
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}`}
]);
useSetPageTitle(
t(`page_header.grade`) +
" / " +
` ${t("header.subject_of_class")} (${gradeName})`,
);
return (
<div className="TableWithHeader">

View File

@ -1,5 +1,18 @@
import { Form, useFormikContext } from "formik";
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 { useTranslation } from "react-i18next";
import ValidationField from "../../Components/ValidationField/ValidationField";
@ -9,9 +22,6 @@ 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" /> */}
@ -35,7 +45,7 @@ const FormField = ({ isLoading }: FormFieldType) => {
/>
</div> */}
<button disabled={ !isValid || isLoading} type="submit" className="auth_submit_button">
<button disabled={isLoading} type="submit" className="auth_submit_button">
{t("practical.login")}
</button>
</Form>

View File

@ -39,7 +39,7 @@ const LoginForm = () => {
return (
<div className="LoginForm">
<Formik initialValues={initialValues} onSubmit={handelSubmit} isInitialValid={false} validationSchema={validationSchema} >
<Formik initialValues={initialValues} onSubmit={handelSubmit} validationSchema={validationSchema} >
{(formikProps) => <FormField isLoading={isLoading} />}
</Formik>
</div>

View File

@ -4,7 +4,6 @@ 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();
@ -12,7 +11,8 @@ const Dummy = () => {
const { FilterButton, FilterBody } = useFilter();
return (
<div className="DummyHomePage">
<PageTitle/>
{/* <FilterButton />
<FilterBody>karim</FilterBody> */}
</div>
);

View File

@ -32,7 +32,6 @@ 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(
@ -130,18 +129,6 @@ 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 /////
{

View File

@ -9,9 +9,6 @@
}
.page_links {
color: var(--opacity);
display: flex;
align-items: center;
gap: 10px;
}
}
}
@ -19,12 +16,3 @@
.filter_header_top {
color: #202c4b;
}
.PageTitle{
display: flex;
gap: 10px;
.PageTitleItems{
cursor: pointer;
}
}

View File

@ -127,14 +127,12 @@
display: flex;
justify-content: space-between;
width: 100%;
padding: 14px 20px;
padding: 14px 10px;
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;
}
@ -247,11 +245,4 @@
>header{
padding: 30px 2vw 10px 2vw;
}
}
.SelectTag{
label{
font-weight: bold;
font-size: 19px;
}
}

View File

@ -5,4 +5,4 @@ const API = {
LOGIN: `login`,
LOGOUT: `logout`,
};
export const useLoginAdmin = () => useAddMutation(KEY, API.LOGIN, true);
export const useLoginAdmin = () => useAddMutation(KEY, API.LOGIN, false);

View File

@ -50,8 +50,6 @@ 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;

View File

@ -186,11 +186,4 @@ 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",
}

View File

@ -47,7 +47,6 @@ export enum ABILITIES_ENUM {
User = "user",
RE_SELLER = "reseller",
Student_Package = "student_package",
QUESTION_BANK = "QuestionBank"
////
}

View File

@ -187,12 +187,7 @@
"subject":"المادة",
"quiz_status":"حالة الاختبار",
"creator_name":"اسم المنشئ",
"created_by":"أنشئ بواسطة",
"question_type": "نوع تمرين",
"base_question": " تمرين متعدد ",
"normal_question": " تمرين عادي",
"hint":"شرح ",
"tags":"كلمات مفتاحية"
"created_by":"أنشئ بواسطة"
},
"practical": {
"to_confirm_deletion_please_re_enter": "لتأكيد الحذف، يرجى إعادة الإدخال",
@ -758,8 +753,7 @@
"reseller": "البائعين",
"param": "معامل",
"student_package": "حزمة الطالب",
"quiz":"الاختبارات",
"questionBank":"بنك الأسئلة"
"quiz":"الاختبارات"
},
"message": {
"some_thing_went_wrong": "حدث خطأ ما",
@ -794,18 +788,17 @@
"grade": "الصفوف",
"report": "تقرير",
"tags": "كلمات مفتاحية",
"reseller":"البائعين",
"QuestionBank":"بنك الأسئلة"
"reseller":"البائعين"
},
"page_header": {
"home": "لوحة القيادة",
"course": " الصفوف ",
"teacher": " المعلمون",
"payment": " الدفعات",
"branch": " الفروع",
"role": " الادوار",
"student": " قائمة الطلاب ",
"admin": " المسؤولون",
"dashboard": "لوحة القيادة / الصفحة الرئيسية",
"course": " لوحة القيادة / الصفوف ",
"teacher": " لوحة القيادة / المعلمون",
"payment": " لوحة القيادة / الدفعات",
"branch": " لوحة القيادة / الفروع",
"role": " لوحة القيادة / الادوار",
"student": " لوحة القيادة / قائمة الطلاب ",
"admin": " لوحة القيادة / المسؤولون",
"student_details": "تفاصيل الطالب",
"create_student": "إنشاء طالب",
"course_details": "تفاصيل الصف",
@ -813,34 +806,32 @@
"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": "حزمة الطالب",
"QuestionBank":"بنك الأسئلة"
"student_package": "حزمة الطالب"
},
"table": {
"student": "قائمة الطلاب",
"reseller": "البائعين",
"grade": "قائمة الصفوف",
"subjects": "مواد الصف",
"QuestionBank":"بنك الأسئلة"
"subjects": "مواد الصف"
},
"alphabet": {
"A": "A",

View File

@ -677,25 +677,3 @@ 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,
);