finish_add_exercise
This commit is contained in:
parent
f6909575e4
commit
42dcbd67cb
|
|
@ -108,6 +108,7 @@
|
||||||
border-top-left-radius: 30px !important;
|
border-top-left-radius: 30px !important;
|
||||||
|
|
||||||
transition: 0.5s ease-in-out;
|
transition: 0.5s ease-in-out;
|
||||||
|
height: 2.5vw !important;
|
||||||
// background-color: red !important;
|
// background-color: red !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import {
|
||||||
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
|
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
|
||||||
import LocalSearchField from "./View/LocalSearch";
|
import LocalSearchField from "./View/LocalSearch";
|
||||||
import NumberFormate from "./View/NumberFormate";
|
import NumberFormate from "./View/NumberFormate";
|
||||||
|
import NumberField from "./View/NumberField";
|
||||||
|
|
||||||
const components: { [key: string]: React.FC<any> } = {
|
const components: { [key: string]: React.FC<any> } = {
|
||||||
Select: SelectField,
|
Select: SelectField,
|
||||||
|
|
@ -30,10 +31,11 @@ const components: { [key: string]: React.FC<any> } = {
|
||||||
MaltyFile: MaltyFile,
|
MaltyFile: MaltyFile,
|
||||||
Checkbox: CheckboxField,
|
Checkbox: CheckboxField,
|
||||||
NumberFormate: NumberFormate,
|
NumberFormate: NumberFormate,
|
||||||
|
Number:NumberField
|
||||||
};
|
};
|
||||||
|
|
||||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||||
({ type, ...otherProps }) => {
|
({ type, ...otherProps }:any) => {
|
||||||
const Component = components[type as ValidationFieldType];
|
const Component = components[type as ValidationFieldType];
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
|
import { ValidationFieldPropsInput } from "../utils/types";
|
||||||
|
|
||||||
const Default = ({
|
const Default = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -14,7 +15,7 @@ const Default = ({
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
...props
|
...props
|
||||||
}: any) => {
|
}: ValidationFieldPropsInput) => {
|
||||||
const { errorMsg, isError, t } = useFormField(name, props);
|
const { errorMsg, isError, t } = useFormField(name, props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -50,9 +51,9 @@ const Default = ({
|
||||||
name={name}
|
name={name}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
{...props}
|
|
||||||
// onChange={onChange ? onChange : handleChange}
|
|
||||||
{...(type === "number" && { min: 0 })}
|
{...(type === "number" && { min: 0 })}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
70
src/Components/ValidationField/View/NumberField.tsx
Normal file
70
src/Components/ValidationField/View/NumberField.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { Form, Input, InputNumber } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
|
import { Field } from "formik";
|
||||||
|
import { ValidationFieldPropsInput } from "../utils/types";
|
||||||
|
|
||||||
|
const NumberField = ({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
placeholder,
|
||||||
|
isDisabled,
|
||||||
|
onChange,
|
||||||
|
type,
|
||||||
|
no_label,
|
||||||
|
label_icon,
|
||||||
|
...props
|
||||||
|
}: ValidationFieldPropsInput) => {
|
||||||
|
const { errorMsg, isError, t ,formik} = useFormField(name, props);
|
||||||
|
|
||||||
|
const handleChange = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
|
) => {
|
||||||
|
console.log('Change:', e);
|
||||||
|
formik.setFieldValue(name, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ValidationField w-100">
|
||||||
|
{no_label ? (
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
<span>empty</span>
|
||||||
|
</label>
|
||||||
|
) : label_icon ? (
|
||||||
|
<div className="LabelWithIcon">
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`input.${label ? label : name}`)}
|
||||||
|
</label>
|
||||||
|
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
as={InputNumber}
|
||||||
|
type={type ?? "text"}
|
||||||
|
placeholder={t(
|
||||||
|
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||||
|
)}
|
||||||
|
name={name}
|
||||||
|
disabled={isDisabled}
|
||||||
|
size="large"
|
||||||
|
onChange={handleChange}
|
||||||
|
{...(type === "number" && { min: 0 })}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(NumberField);
|
||||||
|
|
@ -62,6 +62,10 @@
|
||||||
}
|
}
|
||||||
.Checkboxs {
|
.Checkboxs {
|
||||||
padding: 4%;
|
padding: 4%;
|
||||||
|
|
||||||
|
}
|
||||||
|
.ant-checkbox-wrapper{
|
||||||
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
.SearchField {
|
.SearchField {
|
||||||
button {
|
button {
|
||||||
|
|
@ -209,4 +213,17 @@ input:-webkit-autofill:hover {
|
||||||
svg{
|
svg{
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.ValidationField:has(.input_number){
|
||||||
|
max-width: 100px;
|
||||||
|
|
||||||
|
.input_number{
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex{
|
||||||
|
display: flex;
|
||||||
|
gap: 30px ;
|
||||||
|
max-width: 80% !important;
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { InputProps } from "antd";
|
||||||
|
|
||||||
export type ValidationFieldType =
|
export type ValidationFieldType =
|
||||||
| "text"
|
| "text"
|
||||||
| "Select"
|
| "Select"
|
||||||
|
|
@ -159,8 +161,32 @@ export interface ValidationFieldPropstext {
|
||||||
[key: string]: any; // Index signature to allow any additional props
|
[key: string]: any; // Index signature to allow any additional props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///// new
|
||||||
|
export interface BaseField {
|
||||||
|
name: string;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
export type OmitBaseType = 'placeholder' | 'name' | 'label' | 'type';
|
||||||
|
|
||||||
|
export type OmitPicker = OmitBaseType | 'format';
|
||||||
|
|
||||||
|
export interface ValidationFieldPropsInput
|
||||||
|
extends Omit<InputProps, OmitBaseType>,
|
||||||
|
BaseField {
|
||||||
|
type: 'text' | 'number' | 'password' | 'email' | "Number";
|
||||||
|
isDisabled?:boolean
|
||||||
|
no_label?:string
|
||||||
|
label_icon?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export type ValidationFieldProps =
|
export type ValidationFieldProps =
|
||||||
| ValidationFieldPropsText
|
| ValidationFieldPropsInput
|
||||||
| ValidationFieldPropsSelect
|
| ValidationFieldPropsSelect
|
||||||
| ValidationFieldPropsLocalSearch
|
| ValidationFieldPropsLocalSearch
|
||||||
| ValidationFieldPropsDataRange
|
| ValidationFieldPropsDataRange
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ const NavBar = () => {
|
||||||
</span>
|
</span>
|
||||||
<article>
|
<article>
|
||||||
<div className="header_search">
|
<div className="header_search">
|
||||||
<NavBarSelect />
|
{/* <NavBarSelect /> */}
|
||||||
|
|
||||||
<SearchField
|
<SearchField
|
||||||
options={translateArray}
|
options={translateArray}
|
||||||
|
|
|
||||||
|
|
@ -20,63 +20,12 @@ type FormFieldType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormField = ({ isLoading }: FormFieldType) => {
|
const FormField = ({ isLoading }: FormFieldType) => {
|
||||||
const { values } = useFormikContext<FormValues>();
|
|
||||||
const [branchId, setbranchId] = useState(null);
|
|
||||||
const [cycleId, setcycleId] = useState(null);
|
|
||||||
|
|
||||||
const { cycle, branch } = values as any;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setbranchId(branch);
|
|
||||||
}, [branch]);
|
|
||||||
useEffect(() => {
|
|
||||||
setcycleId(cycle);
|
|
||||||
}, [cycle]);
|
|
||||||
|
|
||||||
const { data: branchData } = useGetAllBranch();
|
|
||||||
const { data: CycleData } = useGetAllCycle(
|
|
||||||
{ br_id: branchId },
|
|
||||||
{ enabled: !!branchId },
|
|
||||||
);
|
|
||||||
const { data: TermData } = useGetAllTerm(
|
|
||||||
{ cy_id: cycleId },
|
|
||||||
{ enabled: !!cycleId },
|
|
||||||
);
|
|
||||||
|
|
||||||
const BranchDataSelect = useFormatAuthDataToSelect(branchData?.data);
|
|
||||||
const BranchCycleData = useFormatAuthDataToSelect(CycleData?.data);
|
|
||||||
const BranchTermData = useFormatAuthDataToSelect(TermData?.data);
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Form className="AuthForm">
|
<Form className="AuthForm">
|
||||||
<Image src="../Layout/Logo.png" />
|
<Image src="../Layout/Logo.png" />
|
||||||
<AuthSelect
|
|
||||||
options={BranchDataSelect}
|
|
||||||
name="branch"
|
|
||||||
label={t("models.branch")}
|
|
||||||
placeholder={t("models.branch")}
|
|
||||||
KEY_OBJECT={BRANCH_OBJECT_KEY}
|
|
||||||
/>
|
|
||||||
<AuthSelect
|
|
||||||
options={BranchCycleData}
|
|
||||||
name="cycle"
|
|
||||||
label={t("input.School_Year")}
|
|
||||||
disabled={!branch}
|
|
||||||
placeholder={
|
|
||||||
!branch ? t("input.Enter_branch_first") : t("input.School_Year")
|
|
||||||
}
|
|
||||||
KEY_OBJECT={CYCLE_OBJECT_KEY}
|
|
||||||
/>
|
|
||||||
<AuthSelect
|
|
||||||
options={BranchTermData}
|
|
||||||
name="term"
|
|
||||||
label={t("input.School_Term")}
|
|
||||||
disabled={!cycle}
|
|
||||||
placeholder={
|
|
||||||
!cycle ? t("input.Enter_school_year_first") : t("input.School_Term")
|
|
||||||
}
|
|
||||||
KEY_OBJECT={TERM_OBJECT_KEY}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="AuthInput">
|
<div className="AuthInput">
|
||||||
<label className="form-label" htmlFor="username">
|
<label className="form-label" htmlFor="username">
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@ const LoginForm = () => {
|
||||||
const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
|
const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const handelSubmit = (values: FormValues) => {
|
const handelSubmit = (values: FormValues) => {
|
||||||
if (!values.term || !values.branch || !values.cycle) {
|
|
||||||
toast.error(t("validation.pleas_fill_all_label"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutate(values );
|
mutate(values );
|
||||||
};
|
};
|
||||||
|
|
@ -24,8 +20,8 @@ const LoginForm = () => {
|
||||||
const { login } = useAuthState();
|
const { login } = useAuthState();
|
||||||
const LoginData = {
|
const LoginData = {
|
||||||
...data,
|
...data,
|
||||||
};
|
} as any;
|
||||||
useNavigateOnSuccess(isSuccess, "/", () => login(LoginData as any));
|
useNavigateOnSuccess(isSuccess, "/", () => login(LoginData?.data as any));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="LoginForm">
|
<div className="LoginForm">
|
||||||
|
|
|
||||||
|
|
@ -75,15 +75,15 @@
|
||||||
border-radius: 70px;
|
border-radius: 70px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 12vw;
|
width: 12vw;
|
||||||
height: 3vw;
|
height: 2.5vw !important;
|
||||||
transition: 0.5s ease-in-out;
|
transition: 0.5s ease-in-out;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
font-size: 1.6vw;
|
font-size: 1.6vw;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
.search-input {
|
.search-input {
|
||||||
font-size: 1.2vw;
|
font-size: 1vw;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
|
|
|
||||||
35
src/Pages/Tags/Model/Add.tsx
Normal file
35
src/Pages/Tags/Model/Add.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
import ValidationField from "../../../Components/ValidationField/ValidationField";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import DynamicTags from "../synonyms/DynamicTags";
|
||||||
|
|
||||||
|
const Form = () => {
|
||||||
|
const formik = useFormikContext();
|
||||||
|
const { isOpen } = useModalState((state) => state);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen === "") {
|
||||||
|
formik.setErrors({});
|
||||||
|
}
|
||||||
|
if (isOpen === "isSuccess") {
|
||||||
|
formik.setErrors({});
|
||||||
|
formik.resetForm();
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
return (
|
||||||
|
<Row className="w-100">
|
||||||
|
<Col>
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
</Col>
|
||||||
|
<div>
|
||||||
|
<DynamicTags/>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Form;
|
||||||
66
src/Pages/Tags/Model/AddModel.tsx
Normal file
66
src/Pages/Tags/Model/AddModel.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Modal, Spin } from "antd";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||||
|
import ModelBody from "./Add";
|
||||||
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
|
|
||||||
|
import { useAddTag } from "../../../api/tags";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ModelButtons from "../../../Components/models/ModelButtons";
|
||||||
|
|
||||||
|
const ModalForm: React.FC = () => {
|
||||||
|
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||||
|
const { mutate, isSuccess, isLoading } = useAddTag();
|
||||||
|
const { set_object_to_edit } = useObjectToEdit();
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess) {
|
||||||
|
setIsOpen("isSuccess");
|
||||||
|
set_object_to_edit({});
|
||||||
|
}
|
||||||
|
}, [setIsOpen, isSuccess]);
|
||||||
|
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
|
||||||
|
mutate({
|
||||||
|
...values
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsOpen("");
|
||||||
|
set_object_to_edit({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const [t] = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
className="ModalForm"
|
||||||
|
centered
|
||||||
|
width={"60vw"}
|
||||||
|
footer={null}
|
||||||
|
open={isOpen === ModalEnum?.TAGS_ADD}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
>
|
||||||
|
<FormikForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
initialValues={getInitialValues([])}
|
||||||
|
validationSchema={getValidationSchema}
|
||||||
|
>
|
||||||
|
<header>
|
||||||
|
{t("practical.add")} {t("models.tags")}{" "}
|
||||||
|
</header>
|
||||||
|
<main className="main_modal w-100">
|
||||||
|
<ModelBody />
|
||||||
|
<ModelButtons isLoading={isLoading} />
|
||||||
|
</main>
|
||||||
|
</FormikForm>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalForm;
|
||||||
94
src/Pages/Tags/Model/Delete.tsx
Normal file
94
src/Pages/Tags/Model/Delete.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Input, Modal, Spin } from "antd";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
import { useDeleteTag } from "../../../api/tags";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const ModalForm: React.FC = () => {
|
||||||
|
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||||
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
|
||||||
|
const { mutate, isLoading, isSuccess } = useDeleteTag();
|
||||||
|
const { object_to_edit, set_object_to_edit } = useObjectToEdit();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess) {
|
||||||
|
setIsOpen("");
|
||||||
|
setInputValue("");
|
||||||
|
}
|
||||||
|
}, [isSuccess, setIsOpen]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
mutate({
|
||||||
|
id: Number(object_to_edit?.id),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setInputValue("");
|
||||||
|
setIsOpen("");
|
||||||
|
set_object_to_edit({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// Step 2: Handle changes to the input field
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
};
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
className="ModalForm"
|
||||||
|
centered
|
||||||
|
width={"40vw"}
|
||||||
|
footer={null}
|
||||||
|
open={isOpen === ModalEnum?.TAGS_DELETE}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
>
|
||||||
|
<header>
|
||||||
|
{t("practical.delete")} ({object_to_edit?.name}){" "}
|
||||||
|
</header>
|
||||||
|
<main className="main_modal">
|
||||||
|
<div className="ValidationField w-100 mb-5">
|
||||||
|
<label className="text ">
|
||||||
|
{t("practical.to_confirm_deletion_please_re_enter")}{" "}
|
||||||
|
{t("input.name")} {t("models.tags")}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
size="large"
|
||||||
|
type="text"
|
||||||
|
placeholder={`${t("practical.enter")} ${t("input.name")} ${t("models.tags")} `}
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="buttons">
|
||||||
|
<div onClick={handleCancel}>{t("practical.cancel")}</div>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
object_to_edit?.name !== inputValue ? "disabled_button" : ""
|
||||||
|
}
|
||||||
|
disabled={object_to_edit?.name !== inputValue || isLoading}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
{t("practical.delete")}
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<span className="Spinier_Div">
|
||||||
|
<Spin />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalForm;
|
||||||
24
src/Pages/Tags/Model/Edit.tsx
Normal file
24
src/Pages/Tags/Model/Edit.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
import ValidationField from "../../../Components/ValidationField/ValidationField";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import DynamicTags from "../synonyms/DynamicTags";
|
||||||
|
|
||||||
|
const Form = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row className="w-100">
|
||||||
|
<Col>
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
</Col>
|
||||||
|
<div>
|
||||||
|
<DynamicTags/>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Form;
|
||||||
69
src/Pages/Tags/Model/EditModel.tsx
Normal file
69
src/Pages/Tags/Model/EditModel.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Modal, Spin } from "antd";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||||
|
import ModelBody from "./Edit";
|
||||||
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
import { useUpdateTag } from "../../../api/tags";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ModelButtons from "../../../Components/models/ModelButtons";
|
||||||
|
|
||||||
|
const ModalForm: React.FC = () => {
|
||||||
|
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||||
|
const { object_to_edit, set_object_to_edit } = useObjectToEdit(
|
||||||
|
(state) => state,
|
||||||
|
);
|
||||||
|
const { mutate, isSuccess, isLoading } = useUpdateTag();
|
||||||
|
// console.log(object_to_edit,"object_to_edit");
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
// const contactInformationJson = JSON.stringify({
|
||||||
|
// phone_number: values?.number
|
||||||
|
// });
|
||||||
|
mutate({
|
||||||
|
...values
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsOpen("");
|
||||||
|
set_object_to_edit({});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess) {
|
||||||
|
setIsOpen("");
|
||||||
|
}
|
||||||
|
}, [setIsOpen, isSuccess]);
|
||||||
|
const [t] = useTranslation();
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="ModalForm"
|
||||||
|
centered
|
||||||
|
width={"60vw"}
|
||||||
|
footer={null}
|
||||||
|
open={isOpen === ModalEnum?.TAGS_EDIT}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
>
|
||||||
|
{object_to_edit && (
|
||||||
|
<FormikForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
initialValues={getInitialValues(object_to_edit)}
|
||||||
|
validationSchema={getValidationSchema}
|
||||||
|
>
|
||||||
|
<header>
|
||||||
|
{" "}
|
||||||
|
{t("practical.edit")} {t("practical.details")} {t("models.tags")}{" "}
|
||||||
|
</header>
|
||||||
|
<main className="main_modal w-100">
|
||||||
|
<ModelBody />
|
||||||
|
<ModelButtons isLoading={isLoading} />
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</FormikForm>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalForm;
|
||||||
14
src/Pages/Tags/Model/formUtil.ts
Normal file
14
src/Pages/Tags/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
export const getInitialValues = (objectToEdit: any): any => {
|
||||||
|
return {
|
||||||
|
id: objectToEdit?.id ?? null,
|
||||||
|
name: objectToEdit?.name ?? null,
|
||||||
|
synonyms: objectToEdit?.synonyms ?? [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getValidationSchema = () => {
|
||||||
|
return Yup.object().shape({
|
||||||
|
name: Yup.string().required("validation.required"),
|
||||||
|
});
|
||||||
|
};
|
||||||
55
src/Pages/Tags/Page.tsx
Normal file
55
src/Pages/Tags/Page.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { FaPlus } from "react-icons/fa";
|
||||||
|
|
||||||
|
import useModalHandler from "../../utils/useModalHandler";
|
||||||
|
import { ModalEnum } from "../../enums/Model";
|
||||||
|
|
||||||
|
// import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||||
|
import { hasAbility } from "../../utils/hasAbility";
|
||||||
|
|
||||||
|
import { lazy, Suspense } from 'react';
|
||||||
|
import { Spin } from "antd";
|
||||||
|
const Table = lazy(() => import('./Table'));
|
||||||
|
const AddModalForm = lazy(() => import('./Model/AddModel'));
|
||||||
|
const EditModalForm = lazy(() => import('./Model/EditModel'));
|
||||||
|
const DeleteModalForm = lazy(() => import('./Model/Delete'));
|
||||||
|
const SearchField = lazy(() => import('../../Components/DataTable/SearchField'));
|
||||||
|
|
||||||
|
|
||||||
|
const TableHeader = () => {
|
||||||
|
const { handel_open_model } = useModalHandler();
|
||||||
|
const [t] = useTranslation();
|
||||||
|
// useSetPage_title(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t(`models.${ABILITIES_ENUM.tags}`)}`);
|
||||||
|
const can_add_tags = hasAbility(ABILITIES_ENUM.TAG, ABILITIES_VALUES_ENUM.STORE);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="TableWithHeader">
|
||||||
|
|
||||||
|
<Suspense fallback={<Spin/>}>
|
||||||
|
<header className="d-flex justify-content-between">
|
||||||
|
<SearchField searchBy="name" placeholder={t("practical.search_here")} />
|
||||||
|
|
||||||
|
{can_add_tags && (
|
||||||
|
<div className="Selects">
|
||||||
|
<button
|
||||||
|
onClick={() => handel_open_model(ModalEnum?.TAGS_ADD)}
|
||||||
|
className="add_button"
|
||||||
|
>
|
||||||
|
{t("models.tags")} <FaPlus />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<Table />
|
||||||
|
<DeleteModalForm />
|
||||||
|
<AddModalForm />
|
||||||
|
<EditModalForm />
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHeader;
|
||||||
17
src/Pages/Tags/Table.tsx
Normal file
17
src/Pages/Tags/Table.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useGetAllTag } from "../../api/tags";
|
||||||
|
import DataTable from "../../Layout/Dashboard/Table/DataTable";
|
||||||
|
import { useColumns } from "./useTableColumns";
|
||||||
|
import useSearchQuery from "../../api/utils/useSearchQuery";
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [searchQuery] = useSearchQuery("name");
|
||||||
|
|
||||||
|
const response = useGetAllTag({
|
||||||
|
name: searchQuery,
|
||||||
|
pagination: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <DataTable response={response} useColumns={useColumns} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
17
src/Pages/Tags/index.tsx
Normal file
17
src/Pages/Tags/index.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { useColumns } from "./useTableColumns";
|
||||||
|
import Table from "./Table";
|
||||||
|
|
||||||
|
import { FaPlus } from "react-icons/fa";
|
||||||
|
|
||||||
|
import AddModalForm from "./Model/AddModel";
|
||||||
|
import EditModalForm from "./Model/EditModel";
|
||||||
|
import DeleteModalForm from "./Model/Delete";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Table,
|
||||||
|
useColumns,
|
||||||
|
AddModalForm,
|
||||||
|
EditModalForm,
|
||||||
|
DeleteModalForm,
|
||||||
|
FaPlus
|
||||||
|
};
|
||||||
70
src/Pages/Tags/synonyms/DynamicTags.tsx
Normal file
70
src/Pages/Tags/synonyms/DynamicTags.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { FaCirclePlus } from 'react-icons/fa6'
|
||||||
|
import Tag from './Tag'
|
||||||
|
|
||||||
|
const DynamicTags = () => {
|
||||||
|
|
||||||
|
const formik = useFormikContext<any>()
|
||||||
|
const [t] = useTranslation()
|
||||||
|
|
||||||
|
|
||||||
|
console.log(formik?.values?.synonyms);
|
||||||
|
|
||||||
|
const handleAddChoice = () => {
|
||||||
|
const length = formik?.values?.synonyms.length;
|
||||||
|
const lastElement = formik?.values?.synonyms[length - 1];
|
||||||
|
|
||||||
|
if(lastElement !== ""){
|
||||||
|
formik.setFieldValue('synonyms', [...(formik?.values as any)?.synonyms as any[],""])
|
||||||
|
}else{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
// console.log(formik?.values);
|
||||||
|
// console.log(currentTag);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='DynamicTags'>
|
||||||
|
{formik?.values?.synonyms?.length < 1 &&
|
||||||
|
|
||||||
|
<p className="add_new_button" >
|
||||||
|
<FaCirclePlus size={23} onClick={handleAddChoice} /> {t("header.add_synonyms")}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className="tag_container">
|
||||||
|
<div className="tags">
|
||||||
|
|
||||||
|
{
|
||||||
|
(((formik?.values as any)?.synonyms as any[])||[]) .map((item:any,index:number)=>{
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tag key={index} index={index} data={item}/>
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{formik?.values?.synonyms?.length > 0 &&
|
||||||
|
|
||||||
|
<p className="add_new_button" >
|
||||||
|
<FaCirclePlus onClick={handleAddChoice} size={20} />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DynamicTags
|
||||||
66
src/Pages/Tags/synonyms/Tag.tsx
Normal file
66
src/Pages/Tags/synonyms/Tag.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
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 }: { data: any, index: number }) => {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const { set_Tags_search ,set_currentTag} = useObjectToEdit();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.style.width = `${(formik?.values?.synonyms[index]?.length + 1) * 8}px`;
|
||||||
|
}
|
||||||
|
}, [formik?.values?.synonyms[index]]);
|
||||||
|
console.log(formik?.values?.synonyms);
|
||||||
|
console.log(index);
|
||||||
|
|
||||||
|
|
||||||
|
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// console.log(e.target.value);
|
||||||
|
formik.setFieldValue(`synonyms[${index}]`, e.target.value);
|
||||||
|
set_Tags_search(e.target.value)
|
||||||
|
set_currentTag(index)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputBlur = () => {
|
||||||
|
// set_Tags_search(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteChoice = () => {
|
||||||
|
console.log(data);
|
||||||
|
// Create a copy of current tags array
|
||||||
|
const currentTags = [...formik.values.synonyms];
|
||||||
|
// 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('synonyms', currentTags);
|
||||||
|
|
||||||
|
// Reset search state if needed
|
||||||
|
set_Tags_search(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='tag'>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
className="tagInput"
|
||||||
|
type="text"
|
||||||
|
value={formik?.values?.synonyms[index]}
|
||||||
|
onChange={handleEditInputChange}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
|
||||||
|
/>
|
||||||
|
<FaTrash onClick={handleDeleteChoice}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
95
src/Pages/Tags/useTableColumns.tsx
Normal file
95
src/Pages/Tags/useTableColumns.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { Space, TableColumnsType, Tooltip } from "antd";
|
||||||
|
import { tags } from "../../types/Item";
|
||||||
|
|
||||||
|
import { ModalEnum } from "../../enums/Model";
|
||||||
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
|
import { RiDeleteBin6Fill } from "react-icons/ri";
|
||||||
|
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
|
||||||
|
import { useModalState } from "../../zustand/Modal";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||||
|
import { hasAbility } from "../../utils/hasAbility";
|
||||||
|
|
||||||
|
export const useColumns = () => {
|
||||||
|
const { setIsOpen } = useModalState((state) => state);
|
||||||
|
|
||||||
|
const { set_object_to_edit } = useObjectToEdit((state) => state);
|
||||||
|
const handelDelete = (record: any) => {
|
||||||
|
set_object_to_edit(record);
|
||||||
|
setIsOpen(ModalEnum?.TAGS_DELETE);
|
||||||
|
};
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const can_edit_tags = hasAbility(
|
||||||
|
ABILITIES_ENUM.TAG,
|
||||||
|
ABILITIES_VALUES_ENUM.UPDATE,
|
||||||
|
);
|
||||||
|
const can_delete_tags = hasAbility(
|
||||||
|
ABILITIES_ENUM.TAG,
|
||||||
|
ABILITIES_VALUES_ENUM.DELETE,
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns: TableColumnsType<tags> = [
|
||||||
|
{
|
||||||
|
title: t("columns.id"),
|
||||||
|
dataIndex: "id",
|
||||||
|
key: "id",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("columns.name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
key: "actions",
|
||||||
|
align: "end",
|
||||||
|
width: "25vw",
|
||||||
|
render: (text, record, index) => {
|
||||||
|
const handleEdit = (record: any) => {
|
||||||
|
// console.log(record,"record");
|
||||||
|
set_object_to_edit(record);
|
||||||
|
setIsOpen(ModalEnum?.TAGS_EDIT);
|
||||||
|
};
|
||||||
|
|
||||||
|
const className =
|
||||||
|
index % 2 === 0 ? "even-row buttonAction" : "odd-row buttonAction";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space size="middle" className={className}>
|
||||||
|
{can_edit_tags && (
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
title={t("practical.edit")}
|
||||||
|
color="#E0E0E0"
|
||||||
|
|
||||||
|
>
|
||||||
|
<span onClick={() => handleEdit(record)}>
|
||||||
|
<MdOutlineEdit
|
||||||
|
size={22}
|
||||||
|
style={{ color: "#A098AE" }}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{can_delete_tags && (
|
||||||
|
<RiDeleteBin6Fill
|
||||||
|
onClick={() => handelDelete(record)}
|
||||||
|
size={22}
|
||||||
|
style={{ color: "#C11313" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
};
|
||||||
|
|
@ -10,7 +10,7 @@ const App: React.FC = () => {
|
||||||
const {subject_id} = useParams<ParamsEnum>()
|
const {subject_id} = useParams<ParamsEnum>()
|
||||||
const response = useGetAllUnit({subject_id:subject_id, pagination: true});
|
const response = useGetAllUnit({subject_id:subject_id, pagination: true});
|
||||||
console.log(response?.data?.data,"response?.data");
|
console.log(response?.data?.data,"response?.data");
|
||||||
|
|
||||||
return <DataTable response={response} useColumns={useColumns} />;
|
return <DataTable response={response} useColumns={useColumns} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import * as Yup from "yup";
|
|
||||||
|
|
||||||
export const getInitialValues = (objectToEdit: any): any => {
|
|
||||||
return {
|
|
||||||
id: objectToEdit?.id ?? null,
|
|
||||||
details: objectToEdit?.details ?? "",
|
|
||||||
file: objectToEdit?.file ?? "",
|
|
||||||
unit_id: objectToEdit?.term ?? null,
|
|
||||||
answers:[{details:null,file:null,is_answer:false}],
|
|
||||||
tags :[{name:"karim",id:1,key:1}]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export const getValidationSchema = () => {
|
|
||||||
// validate input
|
|
||||||
return Yup.object().shape({
|
|
||||||
details: Yup.string().required("validation.required"),
|
|
||||||
answers: Yup.array().of(
|
|
||||||
Yup.object().shape({
|
|
||||||
details: Yup.string().required('details name is required'),
|
|
||||||
file: Yup.string().nullable(),
|
|
||||||
is_answer: Yup.boolean()
|
|
||||||
})
|
|
||||||
).required('Params are required')
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
@ -9,11 +9,13 @@ import { FaCirclePlus } from "react-icons/fa6";
|
||||||
import { Choice } from "../../../types/Item";
|
import { Choice } from "../../../types/Item";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import DynamicTags from "./Tags/DynamicTags";
|
import DynamicTags from "./Tags/DynamicTags";
|
||||||
|
import { useGetAllQuestion } from "../../../api/Question";
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
const formik = useFormikContext();
|
const formik = useFormikContext();
|
||||||
const { isOpen } = useModalState((state) => state);
|
const { isOpen } = useModalState((state) => state);
|
||||||
|
// const {data} = useGetAllQuestion();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen === "") {
|
if (isOpen === "") {
|
||||||
formik.setErrors({});
|
formik.setErrors({});
|
||||||
|
|
@ -25,12 +27,12 @@ const Form = () => {
|
||||||
|
|
||||||
|
|
||||||
const handleAddChoice = () => {
|
const handleAddChoice = () => {
|
||||||
formik.setFieldValue('answers', [...(formik?.values as any)?.answers as Choice[],
|
formik.setFieldValue('QuestionOptions', [...(formik?.values as any)?.QuestionOptions as Choice[],
|
||||||
|
|
||||||
{
|
{
|
||||||
details:null,
|
answer:null,
|
||||||
file:null,
|
answer_image:null,
|
||||||
is_answer:false
|
isCorrect:0
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
const [t] = useTranslation()
|
const [t] = useTranslation()
|
||||||
|
|
@ -39,13 +41,24 @@ const Form = () => {
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
<div className="exercise_form">
|
<div className="exercise_form">
|
||||||
|
|
||||||
<ValidationField className="textarea_exercise" name="details" label="details" type="TextArea" />
|
<ValidationField className="textarea_exercise" name="content" label="details" type="TextArea" />
|
||||||
<ValidationField className="file_exercise" name="file" label="attachment" type="File" />
|
<ValidationField className="file_exercise" name="image" label="attachment" type="File" />
|
||||||
|
|
||||||
|
{/* <ValidationField name="isBase" label="isBase" type="Checkbox" /> */}
|
||||||
|
<div>
|
||||||
|
<ValidationField name="max_mark" label="max_mark" type="Number" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className=" flex ">
|
||||||
|
{/* <ValidationField name="min_mark_to_pass" label="min_mark_to_pass" type="Number" /> */}
|
||||||
|
|
||||||
|
{/* <ValidationField name="parent_id" label="parent_id" type="Select" option={[]} /> */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
(((formik?.values as any)?.answers as Choice[])||[]) .map((item:Choice,index:number)=>{
|
(((formik?.values as any)?.QuestionOptions as Choice[])||[]) .map((item:Choice,index:number)=>{
|
||||||
|
|
||||||
return <ChoiceFields key={index} index={index} data={item}/>
|
return <ChoiceFields key={index} index={index} data={item}/>
|
||||||
}
|
}
|
||||||
|
|
@ -1,31 +1,40 @@
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Modal, Spin } from "antd";
|
import { Modal, Spin } from "antd";
|
||||||
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||||
import ModelBody from "./Add";
|
import ModelBody from "./Add";
|
||||||
import { getInitialValues, getValidationSchema } from "./formUtil";
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
import { useAddExercise } from "../../../api/exercise";
|
import { useAddQuestion } from "../../../api/Question";
|
||||||
import { useQueryClient } from "react-query";
|
import { useQueryClient } from "react-query";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { ParamsEnum } from "../../../enums/params";
|
import { ParamsEnum } from "../../../enums/params";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
|
||||||
const ModalForm: React.FC = () => {
|
const ModalForm: React.FC = () => {
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { mutate, isSuccess, isLoading } = useAddExercise();
|
const { mutate, isSuccess, isLoading } = useAddQuestion();
|
||||||
|
const {object_to_edit} = useObjectToEdit()
|
||||||
|
|
||||||
const {unit_id} = useParams<ParamsEnum>()
|
const {subject_id} = useParams<ParamsEnum>()
|
||||||
|
|
||||||
|
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: any) => {
|
||||||
console.log(values,"values");
|
console.log(values,"values");
|
||||||
|
|
||||||
// mutate({ ...values, unit_id: unit_id });
|
mutate({ ...values, subject_id:subject_id });
|
||||||
};
|
};
|
||||||
|
const navigate = useNavigate()
|
||||||
|
useEffect(() => {
|
||||||
|
if(isSuccess){
|
||||||
|
navigate(-1)
|
||||||
|
|
||||||
|
}
|
||||||
|
}, [isSuccess])
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
navigate(-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
@ -35,7 +44,7 @@ const ModalForm: React.FC = () => {
|
||||||
|
|
||||||
<FormikForm
|
<FormikForm
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
initialValues={getInitialValues([])}
|
initialValues={getInitialValues(object_to_edit)}
|
||||||
validationSchema={getValidationSchema}
|
validationSchema={getValidationSchema}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
@ -47,7 +56,7 @@ const ModalForm: React.FC = () => {
|
||||||
<ModelBody />
|
<ModelBody />
|
||||||
<div className="exercise_add_buttons">
|
<div className="exercise_add_buttons">
|
||||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||||
<button disabled={isLoading} type="submit">
|
<button disabled={isLoading} className="relative" type="submit">
|
||||||
{t("practical.add")}
|
{t("practical.add")}
|
||||||
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
77
src/Pages/question/Model/Edit.tsx
Normal file
77
src/Pages/question/Model/Edit.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import ValidationField from "../../../Components/ValidationField/ValidationField";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useModalState } from "../../../zustand/Modal";
|
||||||
|
import PdfUploader from "../../../Components/CustomFields/PdfUploader";
|
||||||
|
import ChoiceFields from "./Field/ChoiceFields";
|
||||||
|
import { FaCirclePlus } from "react-icons/fa6";
|
||||||
|
import { Choice } from "../../../types/Item";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import DynamicTags from "./Tags/DynamicTags";
|
||||||
|
import { useGetAllQuestion } from "../../../api/Question";
|
||||||
|
|
||||||
|
const Form = () => {
|
||||||
|
const formik = useFormikContext();
|
||||||
|
const { isOpen } = useModalState((state) => state);
|
||||||
|
// const {data} = useGetAllQuestion();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen === "") {
|
||||||
|
formik.setErrors({});
|
||||||
|
formik.resetForm();
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
// console.log(formik?.errors);
|
||||||
|
|
||||||
|
|
||||||
|
const handleAddChoice = () => {
|
||||||
|
formik.setFieldValue('QuestionOptions', [...(formik?.values as any)?.QuestionOptions as Choice[],
|
||||||
|
|
||||||
|
{
|
||||||
|
answer:null,
|
||||||
|
answer_image:null,
|
||||||
|
isCorrect:0
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
const [t] = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row className="w-100">
|
||||||
|
<div className="exercise_form">
|
||||||
|
|
||||||
|
<ValidationField className="textarea_exercise" name="content" label="details" type="TextArea" />
|
||||||
|
<ValidationField className="file_exercise" name="image" label="attachment" type="File" />
|
||||||
|
|
||||||
|
{/* <ValidationField name="isBase" label="isBase" type="Checkbox" /> */}
|
||||||
|
<div>
|
||||||
|
<ValidationField name="max_mark" label="max_mark" type="Number" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className=" flex ">
|
||||||
|
{/* <ValidationField name="min_mark_to_pass" label="min_mark_to_pass" type="Number" /> */}
|
||||||
|
|
||||||
|
{/* <ValidationField name="parent_id" label="parent_id" type="Select" option={[]} /> */}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
(((formik?.values as any)?.QuestionOptions as Choice[])||[]) .map((item:Choice,index:number)=>{
|
||||||
|
|
||||||
|
return <ChoiceFields key={index} index={index} data={item}/>
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<p className="add_new_button" >
|
||||||
|
<FaCirclePlus onClick={handleAddChoice} size={23} /> {t("header.add_new_choice")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<DynamicTags/>
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Form;
|
||||||
86
src/Pages/question/Model/EditModel.tsx
Normal file
86
src/Pages/question/Model/EditModel.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Modal, Spin } from "antd";
|
||||||
|
import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
|
||||||
|
import ModelBody from "./Add";
|
||||||
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
|
import { useGetAllQuestion, useUpdateQuestion } from "../../../api/Question";
|
||||||
|
import { useQueryClient } from "react-query";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { ParamsEnum } from "../../../enums/params";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
import { removeStringKeys } from "../../../utils/removeStringKeys";
|
||||||
|
import SpinContainer from "../../../Components/Layout/SpinContainer";
|
||||||
|
|
||||||
|
const ModalForm: React.FC = () => {
|
||||||
|
|
||||||
|
const {question_id,subject_id} = useParams<ParamsEnum>()
|
||||||
|
|
||||||
|
const { mutate, isSuccess, isLoading } = useUpdateQuestion();
|
||||||
|
const {data,isLoading:dataLoading}= useGetAllQuestion({show:question_id})
|
||||||
|
|
||||||
|
const object_to_edit = data?.data ;
|
||||||
|
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
console.log(values, "values");
|
||||||
|
const DataToSend = structuredClone(values);
|
||||||
|
|
||||||
|
const keysToRemove = ['image', 'answer_image'];
|
||||||
|
const updatedObject = removeStringKeys(DataToSend, keysToRemove);
|
||||||
|
|
||||||
|
mutate({ ...updatedObject });
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate(-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(isSuccess){
|
||||||
|
navigate(-1)
|
||||||
|
}
|
||||||
|
}, [isSuccess])
|
||||||
|
|
||||||
|
|
||||||
|
if(dataLoading){
|
||||||
|
return <SpinContainer/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="exercise_add">
|
||||||
|
|
||||||
|
<FormikForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
initialValues={getInitialValues(object_to_edit)}
|
||||||
|
validationSchema={getValidationSchema}
|
||||||
|
>
|
||||||
|
|
||||||
|
<main className="w-100 exercise_add_main">
|
||||||
|
<header className="exercise_add_header mb-4">
|
||||||
|
{" "}
|
||||||
|
{t("practical.edit")} {t("models.exercise")}{" "}
|
||||||
|
</header>
|
||||||
|
<ModelBody />
|
||||||
|
<div className="exercise_add_buttons">
|
||||||
|
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||||
|
<button disabled={isLoading} className="relative" type="submit">
|
||||||
|
{t("practical.edit")}
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<span className="Spinier_Div">
|
||||||
|
<Spin />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</FormikForm>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalForm;
|
||||||
|
|
@ -15,14 +15,16 @@ const CheckboxField = ({
|
||||||
const formik = useFormikContext<any>()
|
const formik = useFormikContext<any>()
|
||||||
const [t] = useTranslation()
|
const [t] = useTranslation()
|
||||||
const CheckboxhandleChange = (value: any) => {
|
const CheckboxhandleChange = (value: any) => {
|
||||||
formik.setFieldValue(`answers[${name}].is_answer`, value?.target?.checked);
|
console.log(value?.target?.checked);
|
||||||
|
|
||||||
|
formik.setFieldValue(`QuestionOptions[${name}].isCorrect`, value?.target?.checked ? 1 : 0);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className={Group ? "d-inline mt-5 Checkbox" : ``}>
|
<div className={Group ? "d-inline mt-5 Checkbox" : ``}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={onChange || CheckboxhandleChange}
|
onChange={onChange || CheckboxhandleChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
checked={formik.values?.answers?.[name]?.is_answer ?? false}
|
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{t(`input.${label ? label : name}`)}
|
{t(`input.${label ? label : name}`)}
|
||||||
|
|
@ -13,11 +13,11 @@ const File = ({
|
||||||
props,
|
props,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
|
|
||||||
const newName = `answers[${name}].file`
|
const newName = `QuestionOptions[${name}].answer_image`
|
||||||
|
|
||||||
const { formik, t, isError,errorMsg } = useFormField(newName, props);
|
const { formik, t, isError,errorMsg } = useFormField(newName, props);
|
||||||
let imageUrl = formik?.values?.answers[name]?.file ?? null;
|
let imageUrl = formik?.values?.QuestionOptions[name]?.answer_image ?? null;
|
||||||
console.log(imageUrl);
|
// console.log(imageUrl);
|
||||||
|
|
||||||
const fileList: UploadFile[] = useMemo(() => {
|
const fileList: UploadFile[] = useMemo(() => {
|
||||||
if (!imageUrl) return [];
|
if (!imageUrl) return [];
|
||||||
|
|
@ -46,7 +46,7 @@ const File = ({
|
||||||
if (value.fileList.length === 0) {
|
if (value.fileList.length === 0) {
|
||||||
formik.setFieldValue(newName, null);
|
formik.setFieldValue(newName, null);
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue(`answers[${name}].file`, value?.file?.originFileObj);
|
formik.setFieldValue(`QuestionOptions[${name}].answer_image`, value?.file?.originFileObj);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -17,7 +17,7 @@ const TextField = ({
|
||||||
label_icon,
|
label_icon,
|
||||||
className
|
className
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const newName = `answers[${name}].details`
|
const newName = `QuestionOptions[${name}].answer`
|
||||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||||
const TextFilehandleChange = (
|
const TextFilehandleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
|
|
@ -4,14 +4,14 @@ import { useTranslation } from 'react-i18next'
|
||||||
import { FaCirclePlus } from 'react-icons/fa6'
|
import { FaCirclePlus } from 'react-icons/fa6'
|
||||||
import Tag from './Tag'
|
import Tag from './Tag'
|
||||||
import { useObjectToEdit } from '../../../../zustand/ObjectToEditState'
|
import { useObjectToEdit } from '../../../../zustand/ObjectToEditState'
|
||||||
import { useGetAllTeacher } from '../../../../api/teacher'
|
import { useGetAllTag } from '../../../../api/tags'
|
||||||
|
|
||||||
const DynamicTags = () => {
|
const DynamicTags = () => {
|
||||||
|
|
||||||
const formik = useFormikContext<any>()
|
const formik = useFormikContext<any>()
|
||||||
const [t] = useTranslation()
|
const [t] = useTranslation()
|
||||||
const { Tags_search ,set_Tags_search,currentTag} = useObjectToEdit();
|
const { Tags_search ,set_Tags_search,currentTag} = useObjectToEdit();
|
||||||
const {data} = useGetAllTeacher({
|
const {data} = useGetAllTag({
|
||||||
name : Tags_search
|
name : Tags_search
|
||||||
})
|
})
|
||||||
const suggests = data?.data
|
const suggests = data?.data
|
||||||
|
|
@ -35,8 +35,8 @@ const DynamicTags = () => {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log(formik?.values);
|
// console.log(formik?.values);
|
||||||
console.log(currentTag);
|
// console.log(currentTag);
|
||||||
|
|
||||||
const handleChoice = (item: any) => {
|
const handleChoice = (item: any) => {
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ const handleChoice = (item: any) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(formik?.values?.tags?.length);
|
// console.log(formik?.values?.tags?.length);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='DynamicTags'>
|
<div className='DynamicTags'>
|
||||||
|
|
@ -7,7 +7,6 @@ const Tag = ({ data, index }: { data: any, index: number }) => {
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
const { set_Tags_search ,set_currentTag} = useObjectToEdit();
|
const { set_Tags_search ,set_currentTag} = useObjectToEdit();
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputRef.current) {
|
if (inputRef.current) {
|
||||||
|
|
@ -16,7 +15,7 @@ const Tag = ({ data, index }: { data: any, index: number }) => {
|
||||||
}, [formik?.values?.tags[index]?.name]);
|
}, [formik?.values?.tags[index]?.name]);
|
||||||
|
|
||||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
console.log(e.target.value);
|
// console.log(e.target.value);
|
||||||
formik.setFieldValue(`tags[${index}].name`, e.target.value);
|
formik.setFieldValue(`tags[${index}].name`, e.target.value);
|
||||||
set_Tags_search(e.target.value)
|
set_Tags_search(e.target.value)
|
||||||
set_currentTag(index)
|
set_currentTag(index)
|
||||||
|
|
@ -62,3 +61,6 @@ const Tag = ({ data, index }: { data: any, index: number }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tag;
|
export default Tag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
42
src/Pages/question/Model/formUtil.ts
Normal file
42
src/Pages/question/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { Question } from "../../../types/Item";
|
||||||
|
|
||||||
|
|
||||||
|
export const getInitialValues = (objectToEdit: Question): any => {
|
||||||
|
const tags = objectToEdit?.tags?.map((item:any,index:number)=>{
|
||||||
|
return {...item,key:index}
|
||||||
|
});
|
||||||
|
console.log(objectToEdit,"objectToEdit");
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: objectToEdit?.id ?? null,
|
||||||
|
content: objectToEdit?.content ?? "",
|
||||||
|
image: objectToEdit?.image ?? "",
|
||||||
|
subject_id:objectToEdit?.subject_id??'',
|
||||||
|
isBase:objectToEdit?.isBase??'',
|
||||||
|
max_mark:objectToEdit?.max_mark??null,
|
||||||
|
min_mark_to_pass:1,
|
||||||
|
parent:objectToEdit?.parent??'',
|
||||||
|
QuestionOptions: objectToEdit?.QuestionOptions ?? [{answer:null,answer_image:null,isCorrect:0}],
|
||||||
|
tags : tags ??[]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const getValidationSchema = () => {
|
||||||
|
// validate input
|
||||||
|
return Yup.object().shape({
|
||||||
|
image: Yup.string().nullable(),
|
||||||
|
content: Yup.string().required("validation.required"),
|
||||||
|
max_mark: Yup.number().required("validation.required").min(Yup.ref("min_mark_to_pass"),"validation.max_mark_must_be_greater_than_min_mark_to_pass"),
|
||||||
|
min_mark_to_pass: Yup.number().required("validation.required"),
|
||||||
|
|
||||||
|
QuestionOptions: Yup.array().of(
|
||||||
|
Yup.object().shape({
|
||||||
|
answer: Yup.string().required('details name is required'),
|
||||||
|
answer_image: Yup.string().nullable(),
|
||||||
|
isCorrect: Yup.boolean()
|
||||||
|
})
|
||||||
|
).required('Params are required')
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -11,7 +11,7 @@ const TableHeader = () => {
|
||||||
<Suspense fallback={<Spin/>}>
|
<Suspense fallback={<Spin/>}>
|
||||||
<header>
|
<header>
|
||||||
<h6>
|
<h6>
|
||||||
{t("models.exercise")}
|
{t("models.Question")}
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { useColumns } from "./useTableColumns";
|
import { useColumns } from "./useTableColumns";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DataTable from "../../Layout/Dashboard/Table/DataTable";
|
import DataTable from "../../Layout/Dashboard/Table/DataTable";
|
||||||
import { useGetAllExercise } from "../../api/exercise";
|
import { useGetAllQuestion } from "../../api/Question";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { ParamsEnum } from "../../enums/params";
|
import { ParamsEnum } from "../../enums/params";
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const {unit_id} = useParams<ParamsEnum>()
|
const {unit_id} = useParams<ParamsEnum>()
|
||||||
const response = useGetAllExercise({ unit_id:unit_id, pagination: true});
|
const response = useGetAllQuestion({ unit_id:unit_id, pagination: true});
|
||||||
|
|
||||||
return <DataTable response={response} useColumns={useColumns} />;
|
return <DataTable response={response} useColumns={useColumns} />;
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Space, TableColumnsType, Tooltip } from "antd";
|
import { Space, TableColumnsType, Tooltip } from "antd";
|
||||||
import { Exercise } from "../../types/Item";
|
import { Question } from "../../types/Item";
|
||||||
import { FaPlus } from "react-icons/fa";
|
import { FaPlus } from "react-icons/fa";
|
||||||
|
|
||||||
import useModalHandler from "../../utils/useModalHandler";
|
import useModalHandler from "../../utils/useModalHandler";
|
||||||
|
|
@ -22,23 +22,23 @@ export const useColumns = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const handelShow = (record: any) => {
|
const handelShow = (record: any) => {
|
||||||
navigate(`${ABILITIES_ENUM.EXERCISE}/${record?.id}`);
|
navigate(`${ABILITIES_ENUM.QUESTION}/${record?.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const can_edit_Exercise = hasAbility(
|
const can_edit_Question = hasAbility(
|
||||||
ABILITIES_ENUM.EXERCISE,
|
ABILITIES_ENUM.QUESTION,
|
||||||
ABILITIES_VALUES_ENUM.UPDATE,
|
ABILITIES_VALUES_ENUM.UPDATE,
|
||||||
);
|
);
|
||||||
const can_delete_Exercise = hasAbility(
|
const can_delete_Question = hasAbility(
|
||||||
ABILITIES_ENUM.EXERCISE,
|
ABILITIES_ENUM.QUESTION,
|
||||||
ABILITIES_VALUES_ENUM.DELETE,
|
ABILITIES_VALUES_ENUM.DELETE,
|
||||||
);
|
);
|
||||||
const can_add_Exercise = hasAbility(
|
const can_add_Question = hasAbility(
|
||||||
ABILITIES_ENUM.EXERCISE,
|
ABILITIES_ENUM.QUESTION,
|
||||||
ABILITIES_VALUES_ENUM.STORE,
|
ABILITIES_VALUES_ENUM.STORE,
|
||||||
);
|
);
|
||||||
const can_show_Exercise = hasAbility(
|
const can_show_Question = hasAbility(
|
||||||
ABILITIES_ENUM.EXERCISE,
|
ABILITIES_ENUM.QUESTION,
|
||||||
ABILITIES_VALUES_ENUM.SHOW,
|
ABILITIES_VALUES_ENUM.SHOW,
|
||||||
);
|
);
|
||||||
const handelDelete = (data: any) => {
|
const handelDelete = (data: any) => {
|
||||||
|
|
@ -47,11 +47,12 @@ export const useColumns = () => {
|
||||||
|
|
||||||
const handleEdit = (record: any) => {
|
const handleEdit = (record: any) => {
|
||||||
set_object_to_edit(record);
|
set_object_to_edit(record);
|
||||||
|
navigate(`${ABILITIES_ENUM?.QUESTION}/${record?.id}`)
|
||||||
|
|
||||||
};
|
};
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const columns: TableColumnsType<Exercise> = [
|
const columns: TableColumnsType<Question> = [
|
||||||
{
|
{
|
||||||
title: t("columns.id"),
|
title: t("columns.id"),
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
|
|
@ -64,7 +65,7 @@ export const useColumns = () => {
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
align: "center",
|
align: "center",
|
||||||
render: (text, record) => record?.name,
|
render: (text, record) => record?.content,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -72,16 +73,16 @@ export const useColumns = () => {
|
||||||
dataIndex: "description",
|
dataIndex: "description",
|
||||||
key: "description",
|
key: "description",
|
||||||
align: "center",
|
align: "center",
|
||||||
render: (text, record) => record?.description,
|
render: (text, record) => record?.max_mark,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: can_add_Exercise ? (
|
title: can_add_Question ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`${ABILITIES_ENUM?.EXERCISE}/add`) }
|
onClick={() => navigate(`${ABILITIES_ENUM?.QUESTION}/add`) }
|
||||||
className="add_button"
|
className="add_button"
|
||||||
>
|
>
|
||||||
{t("practical.add")} {t("models.exercise")} <FaPlus />
|
{t("practical.add")} {t("models.Question")} <FaPlus />
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
|
@ -95,7 +96,7 @@ export const useColumns = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space size="middle" className={className}>
|
<Space size="middle" className={className}>
|
||||||
{can_edit_Exercise && (
|
{can_edit_Question && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="top"
|
placement="top"
|
||||||
title={t("practical.edit")}
|
title={t("practical.edit")}
|
||||||
|
|
@ -110,14 +111,14 @@ export const useColumns = () => {
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{can_delete_Exercise && (
|
{can_delete_Question && (
|
||||||
<RiDeleteBin6Fill
|
<RiDeleteBin6Fill
|
||||||
onClick={() => handelDelete(record)}
|
onClick={() => handelDelete(record)}
|
||||||
size={22}
|
size={22}
|
||||||
style={{ color: "#C11313" }}
|
style={{ color: "#C11313" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {can_show_Exercise && (
|
{/* {can_show_Question && (
|
||||||
|
|
||||||
<BsEyeFill
|
<BsEyeFill
|
||||||
onClick={() => handelShow(record)}
|
onClick={() => handelShow(record)}
|
||||||
|
|
@ -9,11 +9,20 @@ const Dummy = React.lazy(() => import("./Pages/Home/Dummy"));
|
||||||
|
|
||||||
const Subject = React.lazy(() => import("./Pages/subject/Table/Page"));
|
const Subject = React.lazy(() => import("./Pages/subject/Table/Page"));
|
||||||
|
|
||||||
|
const Tags = React.lazy(() => import("./Pages/Tags/Page"));
|
||||||
|
|
||||||
const Unit = React.lazy(() => import("./Pages/Unit/Page"));
|
const Unit = React.lazy(() => import("./Pages/Unit/Page"));
|
||||||
const Lesson = React.lazy(() => import("./Pages/lesson/Page"));
|
const Lesson = React.lazy(() => import("./Pages/lesson/Page"));
|
||||||
const Exercise = React.lazy(() => import('./Pages/exercise/Page'))
|
const Question = React.lazy(() => import('./Pages/question/Page'))
|
||||||
const AddExercise = React.lazy(() => import('./Pages/exercise/Model/AddModel'))
|
const AddQuestion = React.lazy(() => import('./Pages/question/Model/AddModel'))
|
||||||
|
const EditQuestion = React.lazy(() => import('./Pages/question/Model/EditModel'))
|
||||||
|
|
||||||
|
|
||||||
|
// const QuestionChildren = React.lazy(() => import('./Pages/question/children/Page'))
|
||||||
|
// const AddQuestionChildren = React.lazy(() => import('./Pages/question/children/Model/AddModel'))
|
||||||
|
// const EditQuestionChildren = React.lazy(() => import('./Pages/question/children/Model/EditModel'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { hasAbility } from "./utils/hasAbility";
|
import { hasAbility } from "./utils/hasAbility";
|
||||||
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities";
|
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities";
|
||||||
|
|
@ -40,6 +49,16 @@ export const menuItems: TMenuItem[] = [
|
||||||
abilities: ABILITIES_ENUM?.SUBJECT,
|
abilities: ABILITIES_ENUM?.SUBJECT,
|
||||||
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
header: "page_header.tags",
|
||||||
|
element: <Tags />,
|
||||||
|
icon: <FaMoneyBill />,
|
||||||
|
text: "sidebar.tags",
|
||||||
|
path: `/${ABILITIES_ENUM?.TAG}`,
|
||||||
|
abilities: ABILITIES_ENUM?.TAG,
|
||||||
|
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -64,18 +83,46 @@ export const CrudRoute: TCrudRoute[] = [
|
||||||
|
|
||||||
{
|
{
|
||||||
header: "page_header.lesson_details",
|
header: "page_header.lesson_details",
|
||||||
element: <Exercise />,
|
element: <Question />,
|
||||||
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}`,
|
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}`,
|
||||||
abilities: ABILITIES_ENUM?.EXERCISE,
|
abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "page_header.lesson_details",
|
header: "page_header.add_Question",
|
||||||
element: <AddExercise />,
|
element: <AddQuestion />,
|
||||||
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.EXERCISE}/add`,
|
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/add`,
|
||||||
abilities: ABILITIES_ENUM?.EXERCISE,
|
abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
header: "page_header.edit_Question",
|
||||||
|
element: <EditQuestion />,
|
||||||
|
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.QUESTION_ID}`,
|
||||||
|
abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
|
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// header: "page_header.Question_child",
|
||||||
|
// element: <QuestionChildren />,
|
||||||
|
// path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.CHILDREN_QUESTION_ID}/children`,
|
||||||
|
// abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
|
// abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// header: "page_header.add_Question_child",
|
||||||
|
// element: <AddQuestionChildren />,
|
||||||
|
// path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.CHILDREN_QUESTION_ID}/children/add`,
|
||||||
|
// abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
|
// abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// header: "page_header.edit_Question_child",
|
||||||
|
// element: <EditQuestionChildren />,
|
||||||
|
// path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.CHILDREN_QUESTION_ID}/children/:${ParamsEnum?.CHILDREN_QUESTION_ID}`,
|
||||||
|
// abilities: ABILITIES_ENUM?.QUESTION,
|
||||||
|
// abilities_value: ABILITIES_VALUES_ENUM.INDEX,
|
||||||
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -266,3 +266,6 @@ button:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed !important; /* examle: change cursor */
|
cursor: not-allowed !important; /* examle: change cursor */
|
||||||
}
|
}
|
||||||
|
.relative{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
.tags{
|
.tags{
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
.tag{
|
.tag{
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,18 @@ import useGetQuery from "./helper/useGetQuery";
|
||||||
import useUpdateMutation from "./helper/useUpdateMutation";
|
import useUpdateMutation from "./helper/useUpdateMutation";
|
||||||
|
|
||||||
const API = {
|
const API = {
|
||||||
GET: "/exercise",
|
GET: "/question",
|
||||||
ADD: "/exercise",
|
ADD: "/question",
|
||||||
DELETE: "/exercise",
|
DELETE: "/question",
|
||||||
UPDATE: "/exercise",
|
UPDATE: "/question",
|
||||||
};
|
};
|
||||||
|
|
||||||
const KEY = "exercise";
|
const KEY = "question";
|
||||||
|
|
||||||
export const useGetAllExercise = (params?: any) =>
|
export const useGetAllQuestion = (params?: any) =>
|
||||||
useGetQuery(KEY, API.GET, params);
|
useGetQuery(KEY, API.GET, params);
|
||||||
export const useAddExercise = () => useAddMutation(KEY, API.ADD);
|
export const useAddQuestion = () => useAddMutation(KEY, API.ADD);
|
||||||
export const useUpdateExercise = (params?: any) =>
|
export const useUpdateQuestion = (params?: any) =>
|
||||||
useUpdateMutation(KEY, API.GET, params);
|
useUpdateMutation(KEY, API.GET, params);
|
||||||
export const useDeleteExercise = (params?: any) =>
|
export const useDeleteQuestion = (params?: any) =>
|
||||||
useDeleteMutation(KEY, API.DELETE);
|
useDeleteMutation(KEY, API.DELETE);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export const BaseURL = "http://192.168.1.120:8000/api/";
|
export const BaseURL = "http://192.168.1.108:8000/api/";
|
||||||
// export const BaseURL = "https://school-back-dev.point-dev.net/api/";
|
// export const BaseURL = "https://school-back-dev.point-dev.net/api/";
|
||||||
|
|
||||||
export const ImageBaseURL = "http://192.168.1.9:8000/";
|
export const ImageBaseURL = "http://192.168.1.9:8000/";
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@ import { getLocalStorage } from "../../utils/LocalStorage";
|
||||||
|
|
||||||
function useAxios() {
|
function useAxios() {
|
||||||
const { isAuthenticated, token } = useAuthState();
|
const { isAuthenticated, token } = useAuthState();
|
||||||
const term_id = getLocalStorage(TERM_OBJECT_KEY)?.id;
|
|
||||||
const branch_id = getLocalStorage(BRANCH_OBJECT_KEY)?.id;
|
|
||||||
const cycle_id = getLocalStorage(CYCLE_OBJECT_KEY)?.id;
|
|
||||||
|
|
||||||
const { setValidation } = useValidationState((state) => state);
|
const { setValidation } = useValidationState((state) => state);
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
@ -34,9 +30,6 @@ function useAxios() {
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
buildAxios.withHeaders({
|
buildAxios.withHeaders({
|
||||||
"x-cycle-id": cycle_id,
|
|
||||||
"x-branch-id": branch_id,
|
|
||||||
"x-term-id": term_id,
|
|
||||||
Authorization: "Bearer " + token,
|
Authorization: "Bearer " + token,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ function useGetQuery(
|
||||||
|
|
||||||
const { show, pagination, ...remainingParams } = params;
|
const { show, pagination, ...remainingParams } = params;
|
||||||
|
|
||||||
const { term } = useAuthState();
|
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
@ -28,7 +27,7 @@ function useGetQuery(
|
||||||
const filteredParams = filterParams(param_to_send);
|
const filteredParams = filterParams(param_to_send);
|
||||||
|
|
||||||
return useQuery(
|
return useQuery(
|
||||||
[KEY, filteredParams, show, term],
|
[KEY, filteredParams, show],
|
||||||
async () => {
|
async () => {
|
||||||
const response = await axios.get(url + (show ? `/${show}` : ""), {
|
const response = await axios.get(url + (show ? `/${show}` : ""), {
|
||||||
params: filteredParams,
|
params: filteredParams,
|
||||||
|
|
|
||||||
21
src/api/semantics.ts
Normal file
21
src/api/semantics.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import useAddMutation from "./helper/useAddMutation";
|
||||||
|
import useDeleteMutation from "./helper/useDeleteMutation";
|
||||||
|
import useGetQuery from "./helper/useGetQuery";
|
||||||
|
import useUpdateMutation from "./helper/useUpdateMutation";
|
||||||
|
|
||||||
|
const API = {
|
||||||
|
GET: "/semantics",
|
||||||
|
ADD: "/semantics",
|
||||||
|
DELETE: "/semantics",
|
||||||
|
UPDATE: "/semantics",
|
||||||
|
};
|
||||||
|
|
||||||
|
const KEY = "semantics";
|
||||||
|
|
||||||
|
export const useGetAllSemantics = (params?: any, options?: any) =>
|
||||||
|
useGetQuery(KEY, API.GET, params, options);
|
||||||
|
export const useAddSemantics = () => useAddMutation(KEY, API.ADD);
|
||||||
|
export const useUpdateSemantics = (params?: any) =>
|
||||||
|
useUpdateMutation(KEY, API.GET, params);
|
||||||
|
export const useDeleteSemantics = (params?: any) =>
|
||||||
|
useDeleteMutation(KEY, API.DELETE);
|
||||||
21
src/api/tags.ts
Normal file
21
src/api/tags.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import useAddMutation from "./helper/useAddMutation";
|
||||||
|
import useDeleteMutation from "./helper/useDeleteMutation";
|
||||||
|
import useGetQuery from "./helper/useGetQuery";
|
||||||
|
import useUpdateMutation from "./helper/useUpdateMutation";
|
||||||
|
|
||||||
|
const API = {
|
||||||
|
GET: "/tag",
|
||||||
|
ADD: "/tag",
|
||||||
|
DELETE: "/tag",
|
||||||
|
UPDATE: "/tag",
|
||||||
|
};
|
||||||
|
|
||||||
|
const KEY = "tag";
|
||||||
|
|
||||||
|
export const useGetAllTag = (params?: any, options?: any) =>
|
||||||
|
useGetQuery(KEY, API.GET, params, options);
|
||||||
|
export const useAddTag = () => useAddMutation(KEY, API.ADD);
|
||||||
|
export const useUpdateTag = (params?: any) =>
|
||||||
|
useUpdateMutation(KEY, API.GET, params);
|
||||||
|
export const useDeleteTag = (params?: any) =>
|
||||||
|
useDeleteMutation(KEY, API.DELETE);
|
||||||
|
|
@ -131,4 +131,14 @@ export enum ModalEnum {
|
||||||
EXERCISE_EDIT = "EXERCISE.edit",
|
EXERCISE_EDIT = "EXERCISE.edit",
|
||||||
EXERCISE_ADD = "EXERCISE.add",
|
EXERCISE_ADD = "EXERCISE.add",
|
||||||
EXERCISE_DELETE = "EXERCISE.delete",
|
EXERCISE_DELETE = "EXERCISE.delete",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///tags
|
||||||
|
|
||||||
|
|
||||||
|
TAGS_EDIT = "TAGS.edit",
|
||||||
|
TAGS_ADD = "TAGS.add",
|
||||||
|
TAGS_DELETE = "TAGS.delete",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export enum ABILITIES_ENUM {
|
||||||
PRESENCE = "presence",
|
PRESENCE = "presence",
|
||||||
MAIN_PAGE = "main_page",
|
MAIN_PAGE = "main_page",
|
||||||
ROLE = "role",
|
ROLE = "role",
|
||||||
|
QUESTION='Question',
|
||||||
ADMIN = "admin",
|
ADMIN = "admin",
|
||||||
|
|
||||||
////
|
////
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,8 @@ export enum ParamsEnum {
|
||||||
BRANCH_ID = "branch_id",
|
BRANCH_ID = "branch_id",
|
||||||
CYCLE_ID = "cycle_id",
|
CYCLE_ID = "cycle_id",
|
||||||
UNIT_ID="unit_id",
|
UNIT_ID="unit_id",
|
||||||
LESSON_ID="LESSON_ID"
|
LESSON_ID="lesson_id",
|
||||||
|
QUESTION_ID = "question_id",
|
||||||
|
CHILDREN_QUESTION_ID = "children_question_id"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@
|
||||||
"the_possess_done_successful": "تمت العملية بنجاح",
|
"the_possess_done_successful": "تمت العملية بنجاح",
|
||||||
"some_thing_went_wrong": "حدث خطأ ما",
|
"some_thing_went_wrong": "حدث خطأ ما",
|
||||||
"Due_date_must_be_before_assigning_date": "يجب أن يكون تاريخ الاستحقاق بعد تاريخ التعيين",
|
"Due_date_must_be_before_assigning_date": "يجب أن يكون تاريخ الاستحقاق بعد تاريخ التعيين",
|
||||||
"grade_to_pass_must_be_less_than_max_grade": "يجب أن تكون درجة النجاح أقل من الحد الأقصى للدرجة"
|
"grade_to_pass_must_be_less_than_max_grade": "يجب أن تكون درجة النجاح أقل من الحد الأقصى للدرجة",
|
||||||
|
"max_mark_must_be_greater_than_min_mark_to_pass":"يجب ان تكون اكبر من علامة النجاح"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"register_students": "تسجيل الطلاب",
|
"register_students": "تسجيل الطلاب",
|
||||||
|
|
@ -99,8 +100,10 @@
|
||||||
"view_cycle_for_this_branch": "مشاهدة الدورات في هذا الفرع",
|
"view_cycle_for_this_branch": "مشاهدة الدورات في هذا الفرع",
|
||||||
"view_term_for_this_cycle": "مشاهدة الفصول في هذة السنة دراسية",
|
"view_term_for_this_cycle": "مشاهدة الفصول في هذة السنة دراسية",
|
||||||
"add_new_choice":"إضافة خيار آخر",
|
"add_new_choice":"إضافة خيار آخر",
|
||||||
"add_tag":"إضافة كلمة مفتاحية"
|
"add_tag":"إضافة كلمة مفتاحية",
|
||||||
|
"add_synonyms":"إضافة مرادفات",
|
||||||
|
"add_Question":"إضافة اسئلة"
|
||||||
|
|
||||||
},
|
},
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": "الرقم التعريفي",
|
"id": "الرقم التعريفي",
|
||||||
|
|
@ -231,7 +234,9 @@
|
||||||
"subjectAttachmentType": "نوع مرفق المادة",
|
"subjectAttachmentType": "نوع مرفق المادة",
|
||||||
"param": "معامل",
|
"param": "معامل",
|
||||||
"subjectProgress": "تقدم المادة",
|
"subjectProgress": "تقدم المادة",
|
||||||
"main_page": "الصفحة الرئيسية"
|
"main_page": "الصفحة الرئيسية",
|
||||||
|
"tags":"كلمات مفتاحية",
|
||||||
|
"Question":"اسئلة"
|
||||||
},
|
},
|
||||||
"education_class_actions": {
|
"education_class_actions": {
|
||||||
"Student_Records": "سجلات الطلاب",
|
"Student_Records": "سجلات الطلاب",
|
||||||
|
|
@ -322,7 +327,11 @@
|
||||||
"roles": "الدور",
|
"roles": "الدور",
|
||||||
"choice":"الاختيار",
|
"choice":"الاختيار",
|
||||||
"question_details":"نص السؤال ",
|
"question_details":"نص السؤال ",
|
||||||
"The_correct_answer":"الإجابة الصحيحة"
|
"The_correct_answer":"الإجابة الصحيحة",
|
||||||
|
"exercise":"تمارين",
|
||||||
|
"max_mark":"العلامة الكاملة",
|
||||||
|
"min_mark_to_pass" : "علامة النجاح",
|
||||||
|
"isBase":"سؤال رئيسي"
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -622,7 +631,8 @@
|
||||||
"branch": "الفروع",
|
"branch": "الفروع",
|
||||||
"role": "الادوار",
|
"role": "الادوار",
|
||||||
"admin": "المسؤولون",
|
"admin": "المسؤولون",
|
||||||
"subject":"المواد"
|
"subject":"المواد",
|
||||||
|
"tags":"كلمات مفتاحية"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"some_thing_went_wrong": "حدث خطأ ما",
|
"some_thing_went_wrong": "حدث خطأ ما",
|
||||||
|
|
@ -660,6 +670,11 @@
|
||||||
"unit_details":"لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة",
|
"unit_details":"لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة",
|
||||||
"lesson_details":"لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة / تفاصيل الدرس",
|
"lesson_details":"لوحة القيادة / تفاصيل المادة / تفاصيل الوحدة / تفاصيل الدرس",
|
||||||
"exercise_add":"لوحة القيادة / تفاصيل الدرس / إضافة تمارين ",
|
"exercise_add":"لوحة القيادة / تفاصيل الدرس / إضافة تمارين ",
|
||||||
"subject":"لوحة القيادة / المادة"
|
"subject":"لوحة القيادة / المادة",
|
||||||
|
"tags":"لوحة القيادة / كلمات مفتاحية",
|
||||||
|
"Question":"لوحة القيادة /اسئلة ",
|
||||||
|
"add_Question":"لوحة القيادة /إضافة اسئلة ",
|
||||||
|
"edit_Question":"لوحة القيادة /تعديل اسئلة "
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,4 +285,34 @@ export type Exercise = {
|
||||||
|
|
||||||
export type Choice = {
|
export type Choice = {
|
||||||
name:string
|
name:string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type tags = {
|
||||||
|
id:number
|
||||||
|
key?:number
|
||||||
|
name:string;
|
||||||
|
exercise_id:number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuestionOption {
|
||||||
|
id: number;
|
||||||
|
question_id: number;
|
||||||
|
answer: string;
|
||||||
|
answer_image: string | null;
|
||||||
|
isCorrect: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Question {
|
||||||
|
id: number;
|
||||||
|
subject_id: number;
|
||||||
|
parent: any;
|
||||||
|
isBase: number;
|
||||||
|
content: string;
|
||||||
|
max_mark: number;
|
||||||
|
min_mark_to_pass: number;
|
||||||
|
image: string | null;
|
||||||
|
QuestionOptions: QuestionOption[];
|
||||||
|
tags: tags[]; // Assuming tags are strings, adjust as per actual data type
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
export const getLocalStorage = (key: string) => {
|
export const getLocalStorage = (key: string) => {
|
||||||
const data = localStorage.getItem(key) ?? "{}";
|
try {
|
||||||
return JSON.parse(data);
|
const data = localStorage?.getItem(key);
|
||||||
|
return data ? JSON.parse(data) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error parsing JSON from localStorage", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setLocalStorage = (key: string, data: any) => {
|
export const setLocalStorage = (key: string, data: any) => {
|
||||||
localStorage.setItem(key, data);
|
try {
|
||||||
|
const jsonData = JSON.stringify(data);
|
||||||
|
localStorage.setItem(key, jsonData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error stringify data for localStorage", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
75
src/utils/checkChanges.ts
Normal file
75
src/utils/checkChanges.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
type Changes = {
|
||||||
|
path: string;
|
||||||
|
oldValue: any;
|
||||||
|
newValue: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkChanges(oldObject: any, newObject: any): Changes[] {
|
||||||
|
const changes: Changes[] = [];
|
||||||
|
|
||||||
|
// Helper function to recursively find changes
|
||||||
|
function findChanges(oldValue: any, newValue: any, path: string = '') {
|
||||||
|
if (oldValue !== newValue) {
|
||||||
|
changes.push({ path, oldValue, newValue });
|
||||||
|
} else if (Array.isArray(oldValue)) {
|
||||||
|
if (oldValue.length !== newValue.length) {
|
||||||
|
changes.push({ path, oldValue, newValue });
|
||||||
|
} else {
|
||||||
|
oldValue.forEach((item, index) => {
|
||||||
|
findChanges(item, newValue[index], `${path}[${index}]`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (typeof oldValue === 'object' && oldValue !== null && newValue !== null) {
|
||||||
|
Object.keys(newValue).forEach(key => {
|
||||||
|
findChanges(oldValue[key], newValue[key], path ? `${path}.${key}` : key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
findChanges(oldObject, newObject);
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRevertObject(changes: Changes[]): any {
|
||||||
|
const revertObject: any = {};
|
||||||
|
|
||||||
|
changes.forEach(change => {
|
||||||
|
let currentObj = revertObject;
|
||||||
|
const keys = change.path.split('.');
|
||||||
|
keys.forEach((key, index) => {
|
||||||
|
if (index === keys.length - 1) {
|
||||||
|
currentObj[key] = change.oldValue;
|
||||||
|
} else {
|
||||||
|
currentObj[key] = currentObj[key] || {};
|
||||||
|
currentObj = currentObj[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return revertObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
const oldObject = {
|
||||||
|
id: 1,
|
||||||
|
content: "Old content",
|
||||||
|
image: "old.jpg",
|
||||||
|
subject_id: "math",
|
||||||
|
isBase: false,
|
||||||
|
tags: [{ name: "tag1", id: 1 }, { name: "tag2", id: 2 }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const newObject = {
|
||||||
|
id: 1,
|
||||||
|
content: "New content",
|
||||||
|
image: "new.jpg",
|
||||||
|
subject_id: "science",
|
||||||
|
isBase: true,
|
||||||
|
tags: [{ name: "tag1", id: 1 }, { name: "tag3", id: 3 }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const changes = checkChanges(oldObject, newObject);
|
||||||
|
console.log("Changes:", changes);
|
||||||
|
|
||||||
|
const revertObject = generateRevertObject(changes);
|
||||||
|
console.log("Revert Object:", revertObject);
|
||||||
|
|
@ -14,8 +14,10 @@ export function hasAbility(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const abilities: string[] = JSON.parse(abilitiesString);
|
// const abilities: string[] = JSON.parse(abilitiesString);
|
||||||
const abilityString = `${category}::${value}`;
|
// const abilityString = `${category}::${value}`;
|
||||||
|
|
||||||
return abilities.includes(abilityString);
|
// return abilities.includes(abilityString);
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/utils/removeStringKeys.ts
Normal file
14
src/utils/removeStringKeys.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export function removeStringKeys(obj: any, keysToRemove: string[]): any {
|
||||||
|
if (typeof obj === 'object' && obj !== null) {
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
if (keysToRemove.includes(key) && typeof obj[key] === 'string') {
|
||||||
|
delete obj[key];
|
||||||
|
} else {
|
||||||
|
removeStringKeys(obj[key], keysToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
13
src/utils/useFormatDataToSelectQuas.tsx
Normal file
13
src/utils/useFormatDataToSelectQuas.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
const useFormatDataToSelect = (Data: any) => {
|
||||||
|
const format = (data: any) => {
|
||||||
|
if (!Array.isArray(data)) return []; // Check if data is an array
|
||||||
|
return data?.map((item: any) => ({
|
||||||
|
value: item?.id,
|
||||||
|
label: item?.name ?? item?.title,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return format(Data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFormatDataToSelect;
|
||||||
|
|
@ -11,26 +11,22 @@ interface AuthStore {
|
||||||
token: string | null | undefined;
|
token: string | null | undefined;
|
||||||
abilities: any;
|
abilities: any;
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
term: number | null | undefined;
|
|
||||||
|
|
||||||
login: (Data: any) => Promise<void>;
|
login: (Data: any) => Promise<void>;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
setTerm: (newTerm: number) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useAuthState = create<AuthStore>((set) => {
|
const useAuthState = create<AuthStore>((set) => {
|
||||||
const storedToken = localStorage.getItem(TOKEN_KEY);
|
const storedToken = localStorage.getItem(TOKEN_KEY);
|
||||||
const storedAbilities = localStorage.getItem(ABILITIES_KEY);
|
const storedAbilities = localStorage.getItem(ABILITIES_KEY);
|
||||||
const storedTerm = getLocalStorage(TERM_OBJECT_KEY)?.id;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAuthenticated: !!storedToken && !!storedAbilities,
|
isAuthenticated: !!storedToken && !!storedAbilities,
|
||||||
token: storedToken,
|
token: storedToken,
|
||||||
abilities: storedAbilities,
|
abilities: storedAbilities,
|
||||||
term: storedTerm,
|
|
||||||
|
|
||||||
login: async (Data) => {
|
login: async (Data) => {
|
||||||
// console.log(Data);
|
console.log(Data);
|
||||||
localStorage.setItem(TOKEN_KEY, Data?.token);
|
localStorage.setItem(TOKEN_KEY, Data?.token);
|
||||||
localStorage.setItem(USER_KEY, JSON.stringify(Data?.user));
|
localStorage.setItem(USER_KEY, JSON.stringify(Data?.user));
|
||||||
localStorage.setItem(ABILITIES_KEY, JSON.stringify(Data?.abilities));
|
localStorage.setItem(ABILITIES_KEY, JSON.stringify(Data?.abilities));
|
||||||
|
|
@ -41,11 +37,6 @@ const useAuthState = create<AuthStore>((set) => {
|
||||||
abilities: Data?.abilities,
|
abilities: Data?.abilities,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
setTerm: (newTerm: number) => {
|
|
||||||
set(() => ({
|
|
||||||
term: newTerm,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
localStorage.removeItem(TOKEN_KEY);
|
localStorage.removeItem(TOKEN_KEY);
|
||||||
localStorage.removeItem(USER_KEY);
|
localStorage.removeItem(USER_KEY);
|
||||||
|
|
@ -55,7 +46,6 @@ const useAuthState = create<AuthStore>((set) => {
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
token: null,
|
token: null,
|
||||||
abilities: null,
|
abilities: null,
|
||||||
term: null,
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,5 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src", "src/utils/removeStringKeys.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user