Compare commits

..

2 Commits

Author SHA1 Message Date
karimaldeen
a5f54f1794 add protected route 2024-08-21 17:30:31 +03:00
karimaldeen
ee5a3b7b14 end drag and drop 2024-08-21 15:23:33 +03:00
52 changed files with 1189 additions and 61 deletions

View File

@ -5,11 +5,13 @@ import { Spin } from "antd";
import { hasAbility } from "./utils/hasAbility"; import { hasAbility } from "./utils/hasAbility";
import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively"; import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively";
import { RenderRouteElement } from "./Components/Routes/RenderRouteElement"; import { RenderRouteElement } from "./Components/Routes/RenderRouteElement";
import { UserTypeEnum } from "./enums/UserType";
import { RoleByType } from "./utils/RoleByType";
const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage")); const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage"));
const Auth = lazy(() => import("./Pages/Auth/Page")); const Auth = lazy(() => import("./Pages/Auth/Page"));
const App = () => { const App = () => {
return ( return (
<Routes> <Routes>
<Route <Route
@ -31,10 +33,14 @@ const App = () => {
} }
/> />
{renderRoutesRecursively(menuItems)} {renderRoutesRecursively(menuItems)}
{CrudRoute.map((route) => { {CrudRoute.map((route) => {
const useAbility = hasAbility(route.abilities, route.abilities_value); const useAbility = hasAbility(route.abilities, route.abilities_value);
if(!RoleByType(route)){
return false ;
}
if (!useAbility) { if (!useAbility) {
return false; return false;
} }

View File

@ -3,10 +3,17 @@ import { TMenuItem } from "../../types/App";
import { hasAbility } from "../../utils/hasAbility"; import { hasAbility } from "../../utils/hasAbility";
import { Route } from "react-router-dom"; import { Route } from "react-router-dom";
import { RenderRouteElement } from "./RenderRouteElement"; import { RenderRouteElement } from "./RenderRouteElement";
import { UserTypeEnum } from "../../enums/UserType";
import Item from "antd/es/list/Item";
import { RoleByType } from "../../utils/RoleByType";
export const renderRoutesRecursively = (routes: TMenuItem[]) => export const renderRoutesRecursively = (routes: TMenuItem[]) =>
routes.map((route: TMenuItem) => { routes.map((route: TMenuItem) => {
const useAbility = hasAbility(route.abilities, route.abilities_value); const useAbility = hasAbility(route.abilities, route.abilities_value);
if(!RoleByType(route)){
return false ;
}
if (!useAbility) { if (!useAbility) {
return false; return false;
} }

View File

@ -26,7 +26,7 @@ const Date = ({
formik.setFieldValue(name, value); formik.setFieldValue(name, value);
}; };
const Formatter = [DateEnum?.FORMATE, DateEnum?.FORMATE2]; const Formatter = [DateEnum?.FORMATE];
return ( return (
<div className="ValidationField w-100 "> <div className="ValidationField w-100 ">
<ValidationFieldLabel <ValidationFieldLabel

View File

@ -9,6 +9,8 @@ import { useTranslation } from "react-i18next";
import { getLocalStorage } from "../../utils/LocalStorage"; import { getLocalStorage } from "../../utils/LocalStorage";
import { BRANCH_OBJECT_KEY } from "../../config/AppKey"; import { BRANCH_OBJECT_KEY } from "../../config/AppKey";
import { MenuItem } from "../../Components/Layout/SideBar/MenuItem"; import { MenuItem } from "../../Components/Layout/SideBar/MenuItem";
import { UserTypeEnum } from "../../enums/UserType";
import { RoleByType } from "../../utils/RoleByType";
const SideBar = () => { const SideBar = () => {
const location = useLocation(); const location = useLocation();
@ -17,7 +19,6 @@ const SideBar = () => {
const { logout } = useAuthState(); const { logout } = useAuthState();
const [t] = useTranslation(); const [t] = useTranslation();
const branch_name = getLocalStorage(BRANCH_OBJECT_KEY)?.name; const branch_name = getLocalStorage(BRANCH_OBJECT_KEY)?.name;
return ( return (
<div className="side_bar"> <div className="side_bar">
<h1> <h1>
@ -31,6 +32,11 @@ const SideBar = () => {
if (!useAbility) { if (!useAbility) {
return <React.Fragment key={index}></React.Fragment>; return <React.Fragment key={index}></React.Fragment>;
} }
if(!RoleByType(item)){
return <React.Fragment key={index}></React.Fragment> ;
}
return ( return (
<MenuItem <MenuItem
key={index} key={index}

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { Formik } from "formik"; import { Formik } from "formik";
import useAuthState from "../../zustand/AuthState"; import useAuthState from "../../zustand/AuthState";
import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess"; import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess";
@ -8,6 +8,9 @@ import { initialValues } from "./formutils";
import { FormValues } from "../../types/Auth"; import { FormValues } from "../../types/Auth";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { getLocalStorage } from "../../utils/LocalStorage";
import { USER_KEY } from "../../config/AppKey";
const LoginForm = () => { const LoginForm = () => {
const { mutate, isLoading, isSuccess, data } = useLoginAdmin(); const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
@ -16,11 +19,27 @@ const LoginForm = () => {
mutate(values); mutate(values);
}; };
const { login } = useAuthState(); const { login,isAuthenticated} = useAuthState();
const LoginData = { const LoginData = {
...data, ...data,
} as any; } as any;
useNavigateOnSuccess(isSuccess, "/", () => login(LoginData?.data as any)); const navigate = useNavigate()
const LocalType = getLocalStorage(USER_KEY)?.type ?? false ;
useEffect(() => {
if(isSuccess){
login(LoginData?.data as any)
}
}, [isSuccess])
useEffect(() => {
if(LocalType ){
window.location.href = ("/")
}
}, [LocalType])
return ( return (
<div className="LoginForm"> <div className="LoginForm">

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { getInitialValues, getValidationSchema } from "./formUtil"; import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model"; import { ModalEnum } from "../../../enums/Model";
import LayoutModel from "../../../Layout/Dashboard/LayoutModel"; import LayoutModel from "../../../Layout/Dashboard/LayoutModel";
@ -14,7 +14,7 @@ import { PackageInitialValues } from "../../../types/Package";
import { arrayToObject } from "../../../utils/arrayToObject"; import { arrayToObject } from "../../../utils/arrayToObject";
const AddModel: React.FC = () => { const AddModel: React.FC = () => {
const { mutate, status ,isLoading} = useAddPackage(); const { mutate, status ,isLoading,isSuccess} = useAddPackage();
const [t] = useTranslation(); const [t] = useTranslation();
const navigate = useNavigate() const navigate = useNavigate()
const handleSubmit = (values: PackageInitialValues) => { const handleSubmit = (values: PackageInitialValues) => {
@ -29,6 +29,11 @@ const AddModel: React.FC = () => {
...values, ...values,
}); });
}; };
useEffect(() => {
if(isSuccess){
navigate("/package")
}
}, [isSuccess])
useSetPageTitle(t(`page_header.package`)); useSetPageTitle(t(`page_header.package`));

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { getInitialValues, getValidationSchema } from "./formUtil"; import { getInitialValues, getValidationSchema } from "./formUtil";
import ModelForm from "./ModelForm"; import ModelForm from "./ModelForm";
import FormikForm from "../../../Layout/Dashboard/FormikForm"; import FormikForm from "../../../Layout/Dashboard/FormikForm";
@ -14,7 +14,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import SpinContainer from "../../../Components/Layout/SpinContainer"; import SpinContainer from "../../../Components/Layout/SpinContainer";
const EditModel: React.FC = () => { const EditModel: React.FC = () => {
const { mutate, status ,isLoading} = useUpdatePackage(); const { mutate, status ,isLoading,isSuccess} = useUpdatePackage();
const [t] = useTranslation(); const [t] = useTranslation();
const {package_id} = useParams<ParamsEnum>(); const {package_id} = useParams<ParamsEnum>();
const {objectToEdit} = useObjectToEdit(); const {objectToEdit} = useObjectToEdit();
@ -34,6 +34,12 @@ const EditModel: React.FC = () => {
...values, ...values,
}); });
}; };
const navigate = useNavigate()
useEffect(() => {
if(isSuccess){
navigate("/package")
}
}, [isSuccess])
useSetPageTitle(t(`page_header.package`)); useSetPageTitle(t(`page_header.package`));
if(isLoadingData){ if(isLoadingData){

View File

@ -128,8 +128,9 @@ const Form = () => {
<Row className="w-100"> <Row className="w-100">
<Col> <Col>
<ValidationField placeholder="name" label="name" name="name" /> <ValidationField placeholder="name" label="name" name="name" />
<ValidationField placeholder="price" label="price" name="price" type="number" /> <ValidationField placeholder="price" label="price" name="price" type="number" />
<FieldGroup array_name='configuration' title='configuration' /> <FieldGroup array_name='configuration' title='configuration' />
</Col> </Col>
<Col> <Col>

View File

@ -2,7 +2,7 @@ import * as Yup from "yup";
import { Package, PackageInitialValues } from "../../../../types/Package"; import { Package, PackageInitialValues } from "../../../../types/Package";
import { arrayToObject } from "../../../../utils/arrayToObject"; import { arrayToObject } from "../../../../utils/arrayToObject";
import { objectToArray } from "../../../../utils/objectToArray"; import { objectToArray } from "../../../../utils/objectToArray";
export const getInitialValues = (objectToEdit: Partial<Package>): PackageInitialValues => { export const getInitialValues = (objectToEdit: Partial<Package>): any => {
console.log(objectToEdit,"objectToEdit"); console.log(objectToEdit,"objectToEdit");
const configuration = Array.isArray(objectToEdit?.configuration) ? objectToEdit?.configuration : objectToArray(objectToEdit?.configuration) const configuration = Array.isArray(objectToEdit?.configuration) ? objectToEdit?.configuration : objectToArray(objectToEdit?.configuration)
@ -16,6 +16,7 @@ export const getInitialValues = (objectToEdit: Partial<Package>): PackageInitial
subjects_ids: objectToEdit?.subjects_ids ?? [], subjects_ids: objectToEdit?.subjects_ids ?? [],
units_ids: objectToEdit?.units_ids ?? [], units_ids: objectToEdit?.units_ids ?? [],
configuration: configuration ?? [{key:"",value:""}], configuration: configuration ?? [{key:"",value:""}],
moiaed: [{key:"",value:""}],
}; };
}; };

View File

@ -17,7 +17,7 @@ const TableHeader = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const deleteMutation = useDeleteReSeller(); const deleteMutation = useDeleteReSeller();
useSetPageTitle(t(`page_header.ReSeller`)); useSetPageTitle(t(`page_header.reSeller`));
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">

View File

@ -0,0 +1,33 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model";
import LayoutModel from "../../../Layout/Dashboard/LayoutModel";
import { QueryStatusEnum } from "../../../enums/QueryStatus";
import ModelForm from "./ModelForm";
import { useAddStudent } from "../../../api/student";
const AddModel: React.FC = () => {
const { mutate, status } = useAddStudent();
const handleSubmit = (values: any) => {
mutate({
...values,
});
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_ADD}
modelTitle="student"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default AddModel;

View File

@ -0,0 +1,37 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model";
import LayoutModel from "../../../Layout/Dashboard/LayoutModel";
import ModelForm from "./ModelForm";
import { QueryStatusEnum } from "../../../enums/QueryStatus";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import { useUpdateStudent } from "../../../api/student";
import { handelImageState } from "../../../utils/DataToSendImageState";
const EditModel: React.FC = () => {
const { mutate, status } = useUpdateStudent();
const { objectToEdit } = useObjectToEdit((state) => state);
const handleSubmit = (values: any) => {
const Data_to_send = { ...values };
mutate(Data_to_send);
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_EDIT}
modelTitle="student"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default EditModel;

View File

@ -0,0 +1,58 @@
import { Col, Row } from "reactstrap";
import ValidationField from "../../../Components/ValidationField/ValidationField";
import { useGetAllGrade } from "../../../api/grade";
import { useValidationValidationParamState } from "../../../Components/ValidationField/state/ValidationValidationParamState";
const Form = ({ isEdit = false }: { isEdit?: boolean }) => {
const { ValidationParamState } = useValidationValidationParamState();
const {
GradeName, GradeCurrentPage,
} = ValidationParamState;
const { data: Grade, isLoading: isLoadingGrade } = useGetAllGrade({
name: GradeName,
page: GradeCurrentPage
});
const GradeOption = Grade?.data ?? []
const canChangeGradePage = !!Grade?.links?.next;
const GradePage = Grade?.meta?.currentPage;
const sex = [
{name:"male" , id :"male"},
{name:"female" , id :"female"}
]
return (
<Row className="w-100">
<Col>
<ValidationField name="first_name" placeholder="first_name" label="first_name" />
<ValidationField name="last_name" placeholder="last_name" label="last_name" />
<ValidationField name="username" placeholder="username" label="username" />
{!isEdit &&
<ValidationField name="password" placeholder="password" label="password" />
}
</Col>
<Col>
<ValidationField name="phone_number" placeholder="contact_number1" label="contact_number1" />
<ValidationField
searchBy="GradeName"
name="grade_id"
label="grade"
type="Search"
option={GradeOption}
isLoading={isLoadingGrade}
canChangePage={canChangeGradePage}
PageName={"GradeCurrentPage"}
page={GradePage}
/>
<ValidationField type="Select" name="sex" option={sex} />
</Col>
</Row>
);
};
export default Form;

View File

@ -0,0 +1,27 @@
import * as Yup from "yup";
import { Student, StudentInitialValues } from "../../../types/Student";
export const getInitialValues = (
objectToEdit: Partial<Student>,
): StudentInitialValues => {
return {
id: objectToEdit?.user_id,
first_name: objectToEdit?.first_name ?? "",
last_name: objectToEdit?.last_name ?? "",
// address: objectToEdit?.address ?? "",
// birthday: objectToEdit?.birthday ?? "",
// city: objectToEdit?.city ?? "",
grade_id: objectToEdit?.grade_id ,
// image: objectToEdit?.image ?? "",
sex: objectToEdit?.sex ,
};
};
export const getValidationSchema = () => {
// validate input
return Yup.object().shape({
first_name: Yup.string().required("validation.required"),
last_name: Yup.string().required("validation.required"),
});
};

View File

@ -0,0 +1,39 @@
import { useTranslation } from "react-i18next";
import { lazy, Suspense } from "react";
import { Spin } from "antd";
import useSetPageTitle from "../../Hooks/useSetPageTitle";
import { ModalEnum } from "../../enums/Model";
import { useDeleteStudent } from "../../api/student";
const Table = lazy(() => import("./Table"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModalForm = lazy(
() => import("../../Layout/Dashboard/DeleteModels"),
);
const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteStudent();
useSetPageTitle(t(`page_header.student`));
return (
<div className="TableWithHeader">
<Suspense fallback={<Spin />}>
<header>
<h6>{t("models.student")}</h6>
</header>
<Table />
<AddModalForm />
<EditModalForm />
<DeleteModalForm
deleteMutation={deleteMutation}
ModelEnum={ModalEnum?.STUDENT_DELETE}
/>
</Suspense>
</div>
);
};
export default TableHeader;

View File

@ -0,0 +1,13 @@
import { useColumns } from "./useTableColumns";
import React from "react";
import DataTable from "../../Layout/Dashboard/Table/DataTable";
import { useGetAllStudent } from "../../api/student";
const App: React.FC = () => {
const response = useGetAllStudent({ pagination: true });
return <DataTable response={response} useColumns={useColumns} />;
};
export default App;

View File

@ -0,0 +1,101 @@
import { TableColumnsType } from "antd";
import { Student } from "../../types/Student";
import { FaPlus } from "react-icons/fa";
import useModalHandler from "../../utils/useModalHandler";
import { ModalEnum } from "../../enums/Model";
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import {
canAddStudent,
canDeleteStudent,
canEditStudent,
canShowStudent,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const handelShow = (record: Student) => {
navigate(`${record?.user_id}`);
};
const handelDelete = (data: Student) => {
setObjectToEdit(data);
handel_open_model(ModalEnum?.STUDENT_DELETE);
};
const handleEdit = (record: Student) => {
setObjectToEdit(record);
handel_open_model(ModalEnum?.STUDENT_EDIT);
};
const [t] = useTranslation();
const columns: TableColumnsType<Student> = [
{
title: t("columns.id"),
dataIndex: "id",
key: "id",
align: "center",
render: (_text, record) => record?.user_id,
},
{
title: `${t("columns.first_name")}`,
dataIndex: "first_name",
key: "first_name",
align: "center",
render: (_text, record) => record?.first_name,
},
{
title: `${t("columns.last_name")}`,
dataIndex: "last_name",
key: "last_name",
align: "center",
render: (_text, record) => record?.last_name,
},
{
title: `${t("columns.sex")}`,
dataIndex: "sex",
key: "sex",
align: "center",
render: (_text, record) => record?.sex,
},
{
title: canAddStudent ? (
<button
onClick={() => handel_open_model(ModalEnum?.STUDENT_ADD)}
className="add_button"
>
{t("practical.add")} {t("models.student")} <FaPlus />
</button>
) : (
""
),
key: "actions",
align: "end",
width: "25vw",
render: (_text, record, index) => {
return (
<ActionButtons
canDelete={canDeleteStudent}
canEdit={canEditStudent}
canShow={canShowStudent}
index={index}
onDelete={() => handelDelete(record)}
onEdit={() => handleEdit(record)}
onShow={() => handelShow(record)}
/>
);
},
},
];
return columns;
};

View File

@ -12,10 +12,13 @@ import {
} from "@dnd-kit/sortable"; } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
import { Button, Table } from "antd"; import { Button, Table } from "antd";
import type { TableColumnsType } from "antd";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params"; import { ParamsEnum } from "../../enums/params";
import { useGetAllUnit } from "../../api/unit"; import { useGetAllUnit, useUpdateUnitOrder } from "../../api/unit";
import Loading from "../../Components/DataState/Loading";
import EmptyData from "../../Components/DataState/EmptyData";
import { useTranslation } from "react-i18next";
import { useColumns } from "./useTableColumns";
interface DataType { interface DataType {
id: string; // Unique identifier for each row id: string; // Unique identifier for each row
@ -32,7 +35,7 @@ interface RowContextProps {
const RowContext = React.createContext<RowContextProps>({}); const RowContext = React.createContext<RowContextProps>({});
const DragHandle: React.FC = () => { export const DragHandleUnit: React.FC = () => {
const { setActivatorNodeRef, listeners } = useContext(RowContext); const { setActivatorNodeRef, listeners } = useContext(RowContext);
return ( return (
<Button <Button
@ -46,12 +49,6 @@ const DragHandle: React.FC = () => {
); );
}; };
const columns: TableColumnsType<DataType> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandle /> },
{ title: "Name", dataIndex: "name" },
{ title: "Age", dataIndex: "age" },
{ title: "Address", dataIndex: "address" },
];
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> { interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
"data-row-key": string; "data-row-key": string;
@ -96,13 +93,10 @@ const DrapableTable: React.FC = () => {
response?.data?.data?.map((item: any, index: number) => ({ response?.data?.data?.map((item: any, index: number) => ({
id: item.id, // Ensure this is a unique identifier id: item.id, // Ensure this is a unique identifier
order: index + 1, // Assign order based on index order: index + 1, // Assign order based on index
name: item.name, ...item
age: item.age,
address: item.address,
})) ?? []; })) ?? [];
const [dataSource, setDataSource] = React.useState<DataType[]>(data); const [dataSource, setDataSource] = React.useState<DataType[]>(data);
console.log(dataSource, "dataSource");
useEffect(() => { useEffect(() => {
// Update dataSource when the fetched data changes // Update dataSource when the fetched data changes
@ -110,28 +104,51 @@ const DrapableTable: React.FC = () => {
setDataSource(sortedData); setDataSource(sortedData);
}, [response?.data?.data]); }, [response?.data?.data]);
const {mutate:orderUnit} = useUpdateUnitOrder({},{
retry:false
})
const onDragEnd = ({ active, over }: DragEndEvent) => { const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) { if (active.id !== over?.id) {
setDataSource((prevState) => { setDataSource((prevState) => {
const activeIndex = prevState.findIndex( const activeIndex = prevState.findIndex(
(record) => record.id === active.id, (record) => record.id === active.id,
); );
const overIndex = prevState.findIndex( const overIndex = prevState.findIndex(
//@ts-ignore
(record) => record.id === over.id, (record) => record.id === over.id,
); );
// Move the items in the array // Move the items in the array
const newState = arrayMove(prevState, activeIndex, overIndex); const newState = arrayMove(prevState, activeIndex, overIndex);
const orderedNewState = newState.map((item, index) => ({
...item,
order: index + 1, // Update the order based on the new index
}));
// Update the order based on the new positions // Update the order based on the new positions
return newState.map((item, index) => ({ const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
...item, return {
order: index + 1, // Update the order based on the new index "unit_id":item?.id,
})); "order":item?.order
}
})
orderUnit({units: orderedNewStateWithNewChape, _method:"PUT"})
return orderedNewState
}); });
} }
}; };
const getRowClassName = (record: any, index: number): string => {
return index % 2 === 0 ? "even-row" : "odd-row";
};
const isRefetching = response?.isRefetching;
const [t] = useTranslation()
const columns = useColumns();
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
console.log(sortedDataSource,"sortedDataSource");
return ( return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}> <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext <SortableContext
@ -141,9 +158,26 @@ const DrapableTable: React.FC = () => {
<Table <Table
rowKey="id" rowKey="id"
components={{ body: { row: Row } }} components={{ body: { row: Row } }}
//@ts-ignore
columns={columns} columns={columns}
dataSource={dataSource.sort((a, b) => a.order - b.order)} // Sort by order for rendering dataSource={sortedDataSource}
pagination={false} pagination={false}
rowClassName={(record, index) => getRowClassName(record, index)}
className="DataTable"
loading={{
spinning: response?.isLoading || isRefetching,
indicator: <Loading />,
size: "large",
}}
locale={{
emptyText: (
<EmptyData
loading={response?.isLoading}
header={t("Table.header")}
info={t("Table.info")}
/>
),
}}
/> />
</SortableContext> </SortableContext>
</DndContext> </DndContext>

View File

@ -19,6 +19,7 @@ const Form = () => {
placeholder="term" placeholder="term"
label="term" label="term"
option={termsArray} option={termsArray}
fieldNames={{label:"label",value:"value"}}
/> />
</Col> </Col>
</Row> </Row>

View File

@ -60,7 +60,7 @@ const TableHeader = () => {
<header> <header>
<h6>{t("models.units")}</h6> <h6>{t("models.units")}</h6>
</header> </header>
<Table /> <DrapableTable />
<AddModalForm /> <AddModalForm />
<EditModalForm /> <EditModalForm />
<DeleteModalForm <DeleteModalForm

View File

@ -17,7 +17,7 @@ import {
} from "../../utils/hasAbilityFn"; } from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
import { Unit } from "../../types/Unit"; import { Unit } from "../../types/Unit";
import { DragHandleUnit } from "./DrapableTable";
export const useColumns = () => { export const useColumns = () => {
const { handel_open_model } = useModalHandler(); const { handel_open_model } = useModalHandler();
@ -40,6 +40,7 @@ export const useColumns = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const columns: TableColumnsType<Unit> = [ const columns: TableColumnsType<Unit> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandleUnit /> },
{ {
title: t("columns.id"), title: t("columns.id"),
dataIndex: "id", dataIndex: "id",
@ -60,7 +61,13 @@ export const useColumns = () => {
dataIndex: "term", dataIndex: "term",
key: "term", key: "term",
align: "center", align: "center",
render: (text, record) => record?.term, render: (text, record) => {
console.log(record);
return (
record?.term
)
},
}, },
{ {

View File

@ -0,0 +1,185 @@
import React, { useContext, useEffect, useMemo } from "react";
import { HolderOutlined } from "@ant-design/icons";
import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Button, Table } from "antd";
import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params";
import { useGetAllLesson, useUpdateLessonOrder } from "../../api/lesson";
import Loading from "../../Components/DataState/Loading";
import EmptyData from "../../Components/DataState/EmptyData";
import { useTranslation } from "react-i18next";
import { useColumns } from "./useTableColumns";
interface DataType {
id: string; // Unique identifier for each row
order: number;
name: string;
age: number;
address: string;
}
interface RowContextProps {
setActivatorNodeRef?: (element: HTMLElement | null) => void;
listeners?: SyntheticListenerMap;
}
const RowContext = React.createContext<RowContextProps>({});
export const DragHandleLesson: React.FC = () => {
const { setActivatorNodeRef, listeners } = useContext(RowContext);
return (
<Button
type="text"
size="small"
icon={<HolderOutlined />}
style={{ cursor: "move" }}
ref={setActivatorNodeRef}
{...listeners}
/>
);
};
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
"data-row-key": string;
}
const Row: React.FC<RowProps> = (props) => {
const {
attributes,
listeners,
setNodeRef,
setActivatorNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: props["data-row-key"] });
const style: React.CSSProperties = {
...props.style,
transform: CSS.Translate.toString(transform),
transition,
...(isDragging ? { position: "relative", zIndex: 9999 } : {}),
};
const contextValue = useMemo<RowContextProps>(
() => ({ setActivatorNodeRef, listeners }),
[setActivatorNodeRef, listeners],
);
return (
<RowContext.Provider value={contextValue}>
<tr {...props} ref={setNodeRef} style={style} {...attributes} />
</RowContext.Provider>
);
};
const DrapableTable: React.FC = () => {
const { subject_id } = useParams<ParamsEnum>();
const response = useGetAllLesson({ subject_id: subject_id, pagination: false });
// Assuming the response contains a unique id for each item
const data =
response?.data?.data?.map((item: any, index: number) => ({
id: item.id, // Ensure this is a unique identifier
order: index + 1, // Assign order based on index
...item
})) ?? [];
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
useEffect(() => {
// Update dataSource when the fetched data changes
const sortedData = data.sort((a: any, b: any) => a.order - b.order);
setDataSource(sortedData);
}, [response?.data?.data]);
const {mutate:orderLesson} = useUpdateLessonOrder()
const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) {
setDataSource((prevState) => {
const activeIndex = prevState.findIndex(
(record) => record.id === active.id,
);
const overIndex = prevState.findIndex(
//@ts-ignore
(record) => record.id === over.id,
);
// Move the items in the array
const newState = arrayMove(prevState, activeIndex, overIndex);
const orderedNewState = newState.map((item, index) => ({
...item,
order: index + 1, // Update the order based on the new index
}));
// Update the order based on the new positions
const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
return {
"lesson_id":item?.id,
"order":item?.order
}
})
orderLesson({lessons: orderedNewStateWithNewChape, _method:"PUT"})
return orderedNewState
});
}
};
const getRowClassName = (record: any, index: number): string => {
return index % 2 === 0 ? "even-row" : "odd-row";
};
const isRefetching = response?.isRefetching;
const [t] = useTranslation()
const columns = useColumns();
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
console.log(sortedDataSource,"sortedDataSource");
return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext
items={dataSource.map((i) => i.id)}
strategy={verticalListSortingStrategy}
>
<Table
rowKey="id"
components={{ body: { row: Row } }}
//@ts-ignore
columns={columns}
dataSource={sortedDataSource}
pagination={false}
rowClassName={(record, index) => getRowClassName(record, index)}
className="DataTable"
loading={{
spinning: response?.isLoading || isRefetching,
indicator: <Loading />,
size: "large",
}}
locale={{
emptyText: (
<EmptyData
loading={response?.isLoading}
header={t("Table.header")}
info={t("Table.info")}
/>
),
}}
/>
</SortableContext>
</DndContext>
);
};
export default DrapableTable;

View File

@ -12,14 +12,14 @@ import { useModalState } from "../../../zustand/Modal";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
const AddModel: React.FC = () => { const AddModel: React.FC = () => {
const { isOpen, setIsOpen } = useModalState((state) => state); const { setIsOpen } = useModalState((state) => state);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { mutate, isSuccess, status } = useAddLesson(); const { mutate, isSuccess, status } = useAddLesson();
const { OldObjectToEdit } = useObjectToEdit(); const { OldObjectToEdit } = useObjectToEdit();
const { unit_id } = useParams<ParamsEnum>(); const { unit_id } = useParams<ParamsEnum>();
useEffect(() => { useEffect(() => {
if (isSuccess) { if (isSuccess) {
setIsOpen(""); setIsOpen("isSuccess");
queryClient.invalidateQueries(["Lesson"]); queryClient.invalidateQueries(["Lesson"]);
} }
}, [setIsOpen, isSuccess, queryClient]); }, [setIsOpen, isSuccess, queryClient]);
@ -40,8 +40,9 @@ const AddModel: React.FC = () => {
ModelEnum={ModalEnum.LESSON_ADD} ModelEnum={ModalEnum.LESSON_ADD}
modelTitle="lesson" modelTitle="lesson"
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})} getInitialValues={getInitialValues({name:null})}
getValidationSchema={getValidationSchema} getValidationSchema={getValidationSchema}
width="40vw"
> >
<ModelForm /> <ModelForm />
</LayoutModel> </LayoutModel>

View File

@ -14,7 +14,7 @@ const ModalForm: React.FC = () => {
const { isOpen, setIsOpen } = useModalState((state) => state); const { isOpen, setIsOpen } = useModalState((state) => state);
const { mutate, isSuccess, status } = useUpdateLesson(); const { mutate, isSuccess, status } = useUpdateLesson();
const { objectToEdit, setObjectToEdit } = useObjectToEdit(); const { objectToEdit } = useObjectToEdit();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -43,6 +43,7 @@ const ModalForm: React.FC = () => {
getInitialValues={getInitialValues(objectToEdit)} getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema} getValidationSchema={getValidationSchema}
isAddModal={false} isAddModal={false}
width="40vw"
> >
<ModelForm /> <ModelForm />
</LayoutModel> </LayoutModel>

View File

@ -1,9 +1,11 @@
import * as Yup from "yup"; import * as Yup from "yup";
export const getInitialValues = (objectToEdit: any): any => { export const getInitialValues = (objectToEdit: any): any => {
console.log(objectToEdit,"objectToEdit");
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,
name: objectToEdit?.name ?? "", name: objectToEdit?.name ?? null,
unit_id: objectToEdit?.term ?? null, unit_id: objectToEdit?.term ?? null,
}; };
}; };

View File

@ -11,7 +11,7 @@ import { useGetAllGrade } from "../../api/grade";
import { useGetAllCurriculum } from "../../api/curriculum"; import { useGetAllCurriculum } from "../../api/curriculum";
import { useGetAllSubject } from "../../api/subject"; import { useGetAllSubject } from "../../api/subject";
const Table = lazy(() => import("./Table")); const Table = lazy(() => import("./DrapableTable"));
const AddModalForm = lazy(() => import("./Model/AddModel")); const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel")); const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModelsForm = lazy( const DeleteModelsForm = lazy(

View File

@ -14,6 +14,7 @@ import {
canShowLesson, canShowLesson,
} from "../../utils/hasAbilityFn"; } from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
import { DragHandleLesson } from "./DrapableTable";
export const useColumns = () => { export const useColumns = () => {
const { handel_open_model } = useModalHandler(); const { handel_open_model } = useModalHandler();
@ -37,6 +38,7 @@ export const useColumns = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const columns: TableColumnsType<Lesson> = [ const columns: TableColumnsType<Lesson> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandleLesson /> },
{ {
title: t("columns.id"), title: t("columns.id"),
dataIndex: "id", dataIndex: "id",

View File

@ -0,0 +1,33 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model";
import LayoutModel from "../../../Layout/Dashboard/LayoutModel";
import { QueryStatusEnum } from "../../../enums/QueryStatus";
import ModelForm from "./ModelForm";
import { useAddStudentPackage } from "../../../api/StudentPackage";
const AddModel: React.FC = () => {
const { mutate, status } = useAddStudentPackage();
const handleSubmit = (values: any) => {
mutate({
...values,
});
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_PACKAGE_ADD}
modelTitle="StudentPackage"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default AddModel;

View File

@ -0,0 +1,38 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model";
import LayoutModel from "../../../Layout/Dashboard/LayoutModel";
import ModelForm from "./ModelForm";
import { QueryStatusEnum } from "../../../enums/QueryStatus";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import { useUpdateStudentPackage } from "../../../api/StudentPackage";
import { handelImageState } from "../../../utils/DataToSendImageState";
const EditModel: React.FC = () => {
const { mutate, status } = useUpdateStudentPackage();
const { objectToEdit } = useObjectToEdit((state) => state);
const handleSubmit = (values: any) => {
const Data_to_send = { ...values };
const handelImage = handelImageState(Data_to_send, "icon");
mutate(handelImage);
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_PACKAGE_EDIT}
modelTitle="StudentPackage"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default EditModel;

View File

@ -0,0 +1,71 @@
import { Col, Row } from "reactstrap";
import ValidationField from "../../../Components/ValidationField/ValidationField";
import { useGetAllPackage } from "../../../api/package";
import { useValidationValidationParamState } from "../../../Components/ValidationField/state/ValidationValidationParamState";
import { useGetAllStudent } from "../../../api/student";
const Form = () => {
const { ValidationParamState } = useValidationValidationParamState();
const {
PackageName, PackageCurrentPage,
StudentName, StudentCurrentPage,
} = ValidationParamState;
/// Package_id
const { data: Package, isLoading: isLoadingPackage } = useGetAllPackage({
name: PackageName,
page: PackageCurrentPage
});
const PackageOption = Package?.data ?? []
const canChangePackagePage = !!Package?.links?.next;
const PackagePage = Package?.meta?.currentPage;
/// Student_id
const { data: Student, isLoading: isLoadingStudent } = useGetAllStudent({
name: StudentName,
page: StudentCurrentPage
});
const StudentOption = Student?.data ?? []
const canChangeStudentPage = !!Student?.links?.next;
const StudentPage = Student?.meta?.currentPage;
return (
<Row className="w-100">
<Col>
<ValidationField
searchBy="StudentName"
name="student_id"
label="Student"
type="Search"
option={StudentOption}
isLoading={isLoadingStudent}
canChangePage={canChangeStudentPage}
PageName={"StudentCurrentPage"}
page={StudentPage}
/>
<ValidationField
searchBy="PackageName"
name="package_id"
label="Package"
type="Search"
option={PackageOption}
isLoading={isLoadingPackage}
canChangePage={canChangePackagePage}
PageName={"PackageCurrentPage"}
page={PackagePage}
/>
</Col>
<Col>
<ValidationField name="activation_date" type="Date" />
<ValidationField name="expiration_date" type="Date" />
</Col>
</Row>
);
};
export default Form;

View File

@ -0,0 +1,23 @@
import * as Yup from "yup";
import { StudentPackage, StudentPackageInitialValues } from "../../../types/studentPackage";
import { formateDateInitialValue } from "../../../utils/formateDateInitialValue";
export const getInitialValues = (
objectToEdit: Partial<StudentPackage>,
): StudentPackageInitialValues => {
return {
id: objectToEdit?.id ?? null,
package_id: objectToEdit?.package?.id ?? null,
student_id: objectToEdit?.student?.user_id ?? null,
activation_date: formateDateInitialValue(objectToEdit?.activation_date) ,
expiration_date: formateDateInitialValue(objectToEdit?.expiration_date) ,
};
};
export const getValidationSchema = () => {
// validate input
return Yup.object().shape({
name: Yup.string().required("validation.required"),
});
};

View File

@ -0,0 +1,39 @@
import { useTranslation } from "react-i18next";
import { lazy, Suspense } from "react";
import { Spin } from "antd";
import useSetPageTitle from "../../Hooks/useSetPageTitle";
import { ModalEnum } from "../../enums/Model";
import { useDeleteStudentPackage } from "../../api/StudentPackage";
const Table = lazy(() => import("./Table"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModalForm = lazy(
() => import("../../Layout/Dashboard/DeleteModels"),
);
const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteStudentPackage();
useSetPageTitle(t(`page_header.studentPackage`));
return (
<div className="TableWithHeader">
<Suspense fallback={<Spin />}>
<header>
<h6>{t("models.StudentPackage")}</h6>
</header>
<Table />
<AddModalForm />
<EditModalForm />
<DeleteModalForm
deleteMutation={deleteMutation}
ModelEnum={ModalEnum?.STUDENT_PACKAGE_DELETE}
/>
</Suspense>
</div>
);
};
export default TableHeader;

View File

@ -0,0 +1,12 @@
import { useColumns } from "./useTableColumns";
import React from "react";
import DataTable from "../../Layout/Dashboard/Table/DataTable";
import { useGetAllStudentPackage } from "../../api/StudentPackage";
const App: React.FC = () => {
const response = useGetAllStudentPackage({ pagination: true });
return <DataTable response={response} useColumns={useColumns} />;
};
export default App;

View File

@ -0,0 +1,87 @@
import { TableColumnsType } from "antd";
import { StudentPackage } from "../../types/studentPackage";
import { FaPlus } from "react-icons/fa";
import useModalHandler from "../../utils/useModalHandler";
import { ModalEnum } from "../../enums/Model";
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import { useTranslation } from "react-i18next";
import { ABILITIES_ENUM } from "../../enums/abilities";
import { useNavigate } from "react-router-dom";
import {
canAddStudentPackage,
canDeleteStudentPackage,
canEditStudentPackage,
canShowStudentPackage,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const handelShow = (record: StudentPackage) => {
navigate(`${record?.id}`);
};
const handelDelete = (data: StudentPackage) => {
setObjectToEdit(data);
handel_open_model(ModalEnum?.STUDENT_PACKAGE_DELETE);
};
const handleEdit = (record: StudentPackage) => {
setObjectToEdit(record);
handel_open_model(ModalEnum?.STUDENT_PACKAGE_EDIT);
};
const [t] = useTranslation();
const columns: TableColumnsType<StudentPackage> = [
{
title: t("columns.id"),
dataIndex: "id",
key: "id",
align: "center",
render: (_text, record) => record?.id,
},
{
title: `${t("columns.name")}`,
dataIndex: "name",
key: "name",
align: "center",
render: (_text, record) => record?.name,
},
{
title: canAddStudentPackage ? (
<button
onClick={() => handel_open_model(ModalEnum?.STUDENT_PACKAGE_ADD)}
className="add_button"
>
{t("practical.add")} {t("models.StudentPackage")} <FaPlus />
</button>
) : (
""
),
key: "actions",
align: "end",
width: "25vw",
render: (_text, record, index) => {
return (
<ActionButtons
canDelete={canDeleteStudentPackage}
canEdit={canEditStudentPackage}
canShow={canShowStudentPackage}
index={index}
onDelete={() => handelDelete(record)}
onEdit={() => handleEdit(record)}
onShow={() => handelShow(record)}
/>
);
},
},
];
return columns;
};

View File

@ -12,6 +12,8 @@ const Package = React.lazy(() => import("./Pages/Package/Page"));
const Curriculum = React.lazy(() => import("./Pages/Curriculum/Page")); const Curriculum = React.lazy(() => import("./Pages/Curriculum/Page"));
const PackageItemPage = React.lazy(() => import("./Pages/Package/PackageItem/Page")); const PackageItemPage = React.lazy(() => import("./Pages/Package/PackageItem/Page"));
const ReSeller = React.lazy(() => import("./Pages/ReSeller/Page")); const ReSeller = React.lazy(() => import("./Pages/ReSeller/Page"));
const StudentPackage = React.lazy(() => import("./Pages/studentPackage/Page"));
const Student = React.lazy(() => import("./Pages/Student/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"));
@ -28,6 +30,8 @@ const EditPackageItemPage = React.lazy(() => import("./Pages/Package/PackageItem
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";
import { ParamsEnum } from "./enums/params"; import { ParamsEnum } from "./enums/params";
import { BsPeople } from "react-icons/bs";
import { UserTypeEnum } from "./enums/UserType";
export const menuItems: TMenuItem[] = [ export const menuItems: TMenuItem[] = [
{ {
@ -39,6 +43,7 @@ export const menuItems: TMenuItem[] = [
abilities: ABILITIES_ENUM?.PASS, abilities: ABILITIES_ENUM?.PASS,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
type:UserTypeEnum?.PASS
}, },
{ {
@ -91,6 +96,28 @@ export const menuItems: TMenuItem[] = [
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
{
header: "page_header.student",
element: <Student />,
icon: <FaSellcast />,
text: "sidebar.student",
path: `/${ABILITIES_ENUM?.STUDENT}`,
abilities: ABILITIES_ENUM?.STUDENT,
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0,
},
{
header: "page_header.studentPackage",
element: <StudentPackage />,
icon: <BsPeople />,
text: "sidebar.studentPackage",
path: `/${ABILITIES_ENUM?.STUDENT_PACKAGE}`,
abilities: ABILITIES_ENUM?.STUDENT_PACKAGE,
abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0,
type:UserTypeEnum.RE_SELLER
},
]; ];
export const CrudRoute: TCrudRoute[] = [ export const CrudRoute: TCrudRoute[] = [

View File

@ -23,7 +23,7 @@
.exercise_add_main { .exercise_add_main {
background: var(--bg); background: var(--bg);
padding: 2vw; padding: 2vw;
} };
.exercise_add_buttons { .exercise_add_buttons {
display: flex; display: flex;
gap: 2%; gap: 2%;
@ -59,6 +59,7 @@
.add_new_button { .add_new_button {
margin-bottom: 0; margin-bottom: 0;
color: var(--primary) !important;
} }
} }
.tags { .tags {
@ -125,3 +126,11 @@
display: flex; display: flex;
gap: 5px; gap: 5px;
} }
.add_new_button{
margin-bottom: 20px;
svg{
color: var(--primary);
}
}

21
src/api/StudentPackage.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: "/resellers/studentPackage",
ADD: "/resellers/studentPackage",
DELETE: "/resellers/studentPackage",
UPDATE: "/resellers/studentPackage",
};
const KEY = "StudentPackage";
export const useGetAllStudentPackage = (params?: any, options?: any) =>
useGetQuery(KEY, API.GET, params, options);
export const useAddStudentPackage = () => useAddMutation(KEY, API.ADD);
export const useUpdateStudentPackage = (params?: any) =>
useUpdateMutation(KEY, API.GET);
export const useDeleteStudentPackage = (params?: any) =>
useDeleteMutation(KEY, API.DELETE);

View File

@ -8,6 +8,8 @@ function useAddMutation(
key: string, key: string,
url: string, url: string,
toast: boolean = true, toast: boolean = true,
params: any = {},
options: any = {},
): UseMutationResult<AxiosResponse, unknown, any, unknown> { ): UseMutationResult<AxiosResponse, unknown, any, unknown> {
const axios = useAxios(); const axios = useAxios();
return useMutation<AxiosResponse, unknown, any, unknown>( return useMutation<AxiosResponse, unknown, any, unknown>(
@ -20,9 +22,12 @@ function useAddMutation(
["X-Custom-Message"]: toast, ["X-Custom-Message"]: toast,
[HEADER_KEY]: key, [HEADER_KEY]: key,
}, },
}); });
return data; return data;
}, },
options,
); );
} }

View File

@ -8,6 +8,7 @@ const API = {
ADD: "/lesson", ADD: "/lesson",
DELETE: "/lesson", DELETE: "/lesson",
UPDATE: "/lesson", UPDATE: "/lesson",
ORDER: "/lesson/order",
}; };
const KEY = "lesson"; const KEY = "lesson";
@ -19,3 +20,5 @@ export const useUpdateLesson = (params?: any) =>
useUpdateMutation(KEY, API.GET); useUpdateMutation(KEY, API.GET);
export const useDeleteLesson = (params?: any) => export const useDeleteLesson = (params?: any) =>
useDeleteMutation(KEY, API.DELETE); useDeleteMutation(KEY, API.DELETE);
export const useUpdateLessonOrder = (params?: any) => useAddMutation("karim", API.ORDER);

View File

@ -8,6 +8,7 @@ const API = {
ADD: "/unit", ADD: "/unit",
DELETE: "/unit", DELETE: "/unit",
UPDATE: "/unit", UPDATE: "/unit",
ORDER: "/unit/order",
}; };
const KEY = "unit"; const KEY = "unit";
@ -15,5 +16,6 @@ export const useGetAllUnit = (params?: any, options?: any) =>
useGetQuery(KEY, API.GET, params, options); useGetQuery(KEY, API.GET, params, options);
export const useAddUnit = () => useAddMutation(KEY, API.ADD); export const useAddUnit = () => useAddMutation(KEY, API.ADD);
export const useUpdateUnit = (params?: any) => useUpdateMutation(KEY, API.GET); export const useUpdateUnit = (params?: any) => useUpdateMutation(KEY, API.GET);
export const useUpdateUnitOrder = (params?: any,option?: any,) => useAddMutation("karim", API.ORDER,true,params,option);
export const useDeleteUnit = (params?: any) => export const useDeleteUnit = (params?: any) =>
useDeleteMutation(KEY, API.DELETE); useDeleteMutation(KEY, API.DELETE);

View File

@ -1,3 +1,4 @@
export enum DateEnum { export enum DateEnum {
FORMATE = "YYYY-MM-DD", FORMATE = "YYYY-MM-DD",
SEND_DATE_FORMAT = "YYYY-MM-DD",
} }

View File

@ -163,4 +163,9 @@ export enum ModalEnum {
RE_SELLER_EDIT = "ReSeller.edit", RE_SELLER_EDIT = "ReSeller.edit",
RE_SELLER_ADD = "ReSeller.add", RE_SELLER_ADD = "ReSeller.add",
RE_SELLER_DELETE = "ReSeller.delete", RE_SELLER_DELETE = "ReSeller.delete",
/// studentPackage
STUDENT_PACKAGE_EDIT = "studentPackage.edit",
STUDENT_PACKAGE_ADD = "studentPackage.add",
STUDENT_PACKAGE_DELETE = "studentPackage.delete",
} }

View File

@ -1,5 +1,6 @@
export enum UserTypeEnum { export enum UserTypeEnum {
ADMIN = "ADMIN", ADMIN = "admin",
RE_SELLER = "RE_SELLER", RE_SELLER = "reseller",
PASS="pass"
} }

View File

@ -7,7 +7,7 @@ export enum ABILITIES_ENUM {
EARLY_DEPARTURE = "earlyDeparture", EARLY_DEPARTURE = "earlyDeparture",
EDUCATION_CLASS = "eduClass", EDUCATION_CLASS = "eduClass",
GRADE = "grade", GRADE = "grade",
Package = "package", Package = "packages",
HOMEWORK_ATTACHMENT = "homeworkAttachment", HOMEWORK_ATTACHMENT = "homeworkAttachment",
HOMEWORK = "homework", HOMEWORK = "homework",
LATE_ARRIVAL = "lateArrival", LATE_ARRIVAL = "lateArrival",
@ -44,7 +44,8 @@ export enum ABILITIES_ENUM {
ADMIN = "admin", ADMIN = "admin",
CURRICULUM = "curriculum", CURRICULUM = "curriculum",
PACKAGE_ITEM = "package_item", PACKAGE_ITEM = "package_item",
RE_SELLER = "ReSeller" RE_SELLER = "ReSeller",
STUDENT_PACKAGE='studentPackage'
//// ////
} }

View File

@ -279,7 +279,8 @@
"package":"حزمة", "package":"حزمة",
"package_details":"تفاصيل الحزمة", "package_details":"تفاصيل الحزمة",
"add_package":"اضافة حزمة", "add_package":"اضافة حزمة",
"ReSeller":"بائع" "ReSeller":"بائع",
"StudentPackage":"حزمة الطالب "
}, },
"education_class_actions": { "education_class_actions": {
"Student_Records": "سجلات الطلاب", "Student_Records": "سجلات الطلاب",
@ -694,7 +695,9 @@
"grade": "الدرجات", "grade": "الدرجات",
"curriculum": "مقرر", "curriculum": "مقرر",
"package":"حزمة", "package":"حزمة",
"reSeller":"البائع" "reSeller":"البائع",
"studentPackage":"حزمة الطالب ",
"student":"الطالب"
}, },
"message": { "message": {
"some_thing_went_wrong": "حدث خطأ ما", "some_thing_went_wrong": "حدث خطأ ما",
@ -719,7 +722,10 @@
"lesson": "الدرس", "lesson": "الدرس",
"curriculum": "مقرر", "curriculum": "مقرر",
"subject": "المادة", "subject": "المادة",
"question": "السؤال" "question": "السؤال",
"studentPackage":"حزمة الطالب "
}, },
"page_header": { "page_header": {
"dashboard": "لوحة القيادة / الصفحة الرئيسية", "dashboard": "لوحة القيادة / الصفحة الرئيسية",
@ -752,6 +758,8 @@
"grade": "لوحة القيادة /الدرجات ", "grade": "لوحة القيادة /الدرجات ",
"curriculum": "لوحة القيادة / تعديل مقرر ", "curriculum": "لوحة القيادة / تعديل مقرر ",
"package":"لوحة القيادة / الحزم ", "package":"لوحة القيادة / الحزم ",
"ReSeller":"لوحة القيادة / البائع " "reSeller":"لوحة القيادة / البائع ",
"studentPackage":"لوحة القيادة / حزمة الطالب ",
"student":"لوحة القيادة / الطالب "
} }
} }

View File

@ -2,6 +2,7 @@ import { ReactElement, LazyExoticComponent, ReactNode } from "react";
import { Mark_State, Payment_type, term_type } from "./Item"; import { Mark_State, Payment_type, term_type } from "./Item";
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../enums/abilities"; import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../enums/abilities";
import { UserTypeEnum } from "../enums/UserType"; import { UserTypeEnum } from "../enums/UserType";
import dayjs from "dayjs";
export type ChildrenType = { export type ChildrenType = {
children: ReactNode; children: ReactNode;
@ -39,7 +40,7 @@ export type TCrudRoute = {
element: ReactElement | LazyExoticComponent<any>; element: ReactElement | LazyExoticComponent<any>;
abilities: ABILITIES_ENUM; abilities: ABILITIES_ENUM;
abilities_value: ABILITIES_VALUES_ENUM; abilities_value: ABILITIES_VALUES_ENUM;
type?:ABILITIES_ENUM type?:UserTypeEnum
prevPath: number; prevPath: number;
}; };
@ -408,3 +409,4 @@ export interface showAdmin {
} }
export type Nullable<T> = { [K in keyof T]: T[K] | null }; export type Nullable<T> = { [K in keyof T]: T[K] | null };
export type DateType = string | dayjs.Dayjs | null | undefined;

34
src/types/Student.ts Normal file
View File

@ -0,0 +1,34 @@
import { DateType, Nullable } from "./App";
// Define the Teacher interface
export interface Student {
first_name: string; // The first name of the user
last_name: string; // The last name of the user
city: string | null; // The city of the user, can be null
sex: string; // The sex of the user, using a union type for possible values
image: string | null; // The URL of the user's image, can be null
address: string | null; // The address of the user, can be null
card: string | null; // The card information, can be null
birthday: DateType; // The birthday of the user, can be null
grade_id: number | string; // The ID of the user's grade
user_id: number; // The unique ID of the user
}
export interface InitialValues {
id: number;
first_name: string; // The first name of the user
last_name: string; // The last name of the user
city: string | null; // The city of the user, can be null
sex: string; // The sex of the user, using a union type for possible values
image: string | null; // The URL of the user's image, can be null
address: string | null; // The address of the user, can be null
card: string | null; // The card information, can be null
birthday: DateType; // The birthday of the user, can be null
grade_id: number | string; // The ID of the user's grade
user_id: number; // The unique ID of the user
}
export type StudentInitialValues = Partial<Nullable<InitialValues>>;

View File

@ -0,0 +1,54 @@
import { TermEnum } from "../enums/Term";
import { DateType, Nullable } from "./App";
// Define the type for the object
interface StudentPackageStudent {
first_name: string;
last_name: string;
city: string | null;
sex: string | null;
image: string | null;
address: string | null;
card: string | null;
birthday: string | null;
grade_id: number;
user_id: number;
}
interface StudentPackagePackageConfiguration {
key: string;
value: string;
}
interface StudentPackagePackage {
id: number;
name: string;
configuration: StudentPackagePackageConfiguration[];
price: number;
grade_id: number;
}
export interface StudentPackage {
id: number;
activation_date: string;
expiration_date: string;
student: StudentPackageStudent;
package: StudentPackagePackage;
}
export interface InitialValues {
id: number;
activation_date: DateType;
expiration_date: DateType;
student: StudentPackageStudent;
package: StudentPackagePackage;
student_id: number | string;
package_id: number | string;
}
export type StudentPackageInitialValues = Partial<Nullable<InitialValues>>;

28
src/utils/RoleByType.ts Normal file
View File

@ -0,0 +1,28 @@
import { USER_KEY } from "../config/AppKey";
import { UserTypeEnum } from "../enums/UserType";
import { getLocalStorage } from "./LocalStorage";
export const RoleByType = (item: { type?: string }):boolean=>{
const type = item?.type ?? UserTypeEnum.ADMIN;
const LocalType = getLocalStorage(USER_KEY)?.type ?? undefined ;
const isAdmin = LocalType === UserTypeEnum.ADMIN ;
const isReSeller = LocalType === UserTypeEnum.RE_SELLER;
const isAdminRoute = type === UserTypeEnum.ADMIN ;
const isReSellerRoute = type === UserTypeEnum.RE_SELLER;
console.log(LocalType);
if(!LocalType){
return false
}
if(type === UserTypeEnum.PASS) { return true } ;
if(isAdmin && isReSellerRoute ){
return false ;
}
if(isReSeller && !isReSellerRoute ){
return false ;
}
return true;
}

View File

@ -0,0 +1,10 @@
import dayjs from "dayjs";
import { DateType } from "../types/App";
import { DateEnum } from "../enums/Date";
export const formateDateInitialValue = (date: DateType) => {
if (date) {
return dayjs(date, DateEnum.SEND_DATE_FORMAT);
}
return dayjs(Date());
};

View File

@ -604,3 +604,24 @@ export const canShowReSeller = hasAbility(
ABILITIES_ENUM.RE_SELLER, ABILITIES_ENUM.RE_SELLER,
ABILITIES_VALUES_ENUM.SHOW, ABILITIES_VALUES_ENUM.SHOW,
); );
/// StudentPackage
export const canAddStudentPackage = hasAbility(
ABILITIES_ENUM.RE_SELLER,
ABILITIES_VALUES_ENUM.STORE,
);
export const canEditStudentPackage = hasAbility(
ABILITIES_ENUM.RE_SELLER,
ABILITIES_VALUES_ENUM.UPDATE,
);
export const canDeleteStudentPackage = hasAbility(
ABILITIES_ENUM.RE_SELLER,
ABILITIES_VALUES_ENUM.DELETE,
);
export const canShowStudentPackage = hasAbility(
ABILITIES_ENUM.RE_SELLER,
ABILITIES_VALUES_ENUM.SHOW,
);

View File

@ -1,12 +1,11 @@
import { create } from "zustand"; import { create } from "zustand";
import { ABILITIES_KEY, TOKEN_KEY, USER_KEY } from "../config/AppKey"; import { ABILITIES_KEY, TOKEN_KEY, USER_KEY } from "../config/AppKey";
import { useNavigate } from "react-router-dom"; import { RoleByType } from "../utils/RoleByType";
import { getLocalStorage } from "../utils/LocalStorage";
interface AuthStore { interface AuthStore {
token: string | null | undefined; token: string | null | undefined;
abilities: any; abilities: any;
isAuthenticated: boolean; isAuthenticated: boolean;
login: (Data: any) => Promise<void>; login: (Data: any) => Promise<void>;
logout: () => Promise<void>; logout: () => Promise<void>;
} }
@ -14,9 +13,11 @@ interface AuthStore {
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 storedType = getLocalStorage(USER_KEY) ;
console.log(RoleByType(storedType));
return { return {
isAuthenticated: true, isAuthenticated: !!storedToken,
token: storedToken, token: storedToken,
abilities: storedAbilities, abilities: storedAbilities,