finish_add_exercise

This commit is contained in:
karimalden 2024-06-24 15:27:42 +03:00
parent f6909575e4
commit 42dcbd67cb
59 changed files with 1340 additions and 206 deletions

View File

@ -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;
} }

View File

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

View File

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

View 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);

View File

@ -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;
} }

View File

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

View File

@ -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}

View File

@ -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">

View File

@ -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">

View File

@ -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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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
View 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
View 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
View 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
};

View 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

View 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;

View 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;
};

View File

@ -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} />;
}; };

View File

@ -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')
});
};

View File

@ -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}/>
} }

View File

@ -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 && (

View 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;

View 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;

View File

@ -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}`)}

View File

@ -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);
} }
}; };

View File

@ -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>,

View File

@ -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'>

View File

@ -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;

View 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')
});
};

View File

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

View File

@ -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} />;
}; };

View File

@ -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)}

View File

@ -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,
// },
]; ];

View File

@ -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;
}

View File

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

View File

@ -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);

View File

@ -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/";

View File

@ -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,
}); });
} }

View File

@ -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
View 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
View 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);

View File

@ -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",
} }

View File

@ -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",
//// ////

View File

@ -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"
} }

View File

@ -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":"لوحة القيادة /تعديل اسئلة "
} }
} }

View File

@ -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
} }

View File

@ -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
View 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);

View File

@ -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
} }

View 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;
}

View 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;

View File

@ -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,
})); }));
}, },
}; };

View File

@ -16,5 +16,5 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": ["src"] "include": ["src", "src/utils/removeStringKeys.ts"]
} }