diff --git a/src/Pages/Admin/Manager/Edit/Page.tsx b/src/Pages/Admin/Manager/Edit/Page.tsx new file mode 100644 index 0000000..71a2fa3 --- /dev/null +++ b/src/Pages/Admin/Manager/Edit/Page.tsx @@ -0,0 +1,54 @@ +import { useTranslation } from "react-i18next"; +import useSetPageTitle from "../../../../Hooks/useSetPageTitle"; +import PageHeader from "../../../../Layout/Dashboard/PageHeader"; +import { Suspense } from "react"; +import { Spin } from "antd"; +import { ModalEnum } from "../../../../enums/Model"; +import { canAddReSeller } from "../../../../utils/hasAbilityFn"; +import PersonalDetailsForm from "../Form/PersonalDetailsForm"; +import { Formik, Form } from "formik"; +import { getInitialValues, getValidationSchema } from "../Form/formUtils"; +import TitleDetailsForm from "../Form/TitleDetailsForm"; +import AttachmentForm from "../Form/AttachmentForm"; +import PasswordDetailsForm from "../Form/PasswordDetailsForm"; + +const TableHeader = () => { + const [t] = useTranslation(); + useSetPageTitle(t(`page_header.add_reseller`)); + const handelSubmit = (values: any) => { + console.log(values, "values"); + }; + return ( +
+ }> + +
+ +
+ + + + +
+ + +
+ +
+
+
+
+ ); +}; + +export default TableHeader; diff --git a/src/Pages/Admin/Manager/Form/PasswordDetailsForm.tsx b/src/Pages/Admin/Manager/Form/PasswordDetailsForm.tsx new file mode 100644 index 0000000..3f19706 --- /dev/null +++ b/src/Pages/Admin/Manager/Form/PasswordDetailsForm.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; + +const PasswordDetailsForm = () => { + const [t] = useTranslation(); + + return ( +
+
+ +

{t("header.password")}

+
+
+ + + +
+
+ ); +}; + +export default PasswordDetailsForm; diff --git a/src/Pages/Admin/Manager/Form/PersonalDetailsForm.tsx b/src/Pages/Admin/Manager/Form/PersonalDetailsForm.tsx index 5815b15..2cdf203 100644 --- a/src/Pages/Admin/Manager/Form/PersonalDetailsForm.tsx +++ b/src/Pages/Admin/Manager/Form/PersonalDetailsForm.tsx @@ -16,15 +16,15 @@ const PersonalDetailsForm = () => {
{ /> { type="text" /> { + + const {t} = useTranslation(); + return ( +
+
{t(label)}
+

{t(value)}

+
+ ) +} + +export default CollectionInfoCard diff --git a/src/Pages/ReSeller/Collections/Model/FilterForm.tsx b/src/Pages/ReSeller/Collections/Model/FilterForm.tsx new file mode 100644 index 0000000..d601efb --- /dev/null +++ b/src/Pages/ReSeller/Collections/Model/FilterForm.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import { Col, Row } from "reactstrap"; + +const FilterForm = () => { + return ( +
+ + + + {/* */} + + +
+ ); +}; + +export default FilterForm; diff --git a/src/Pages/ReSeller/Collections/Page.tsx b/src/Pages/ReSeller/Collections/Page.tsx new file mode 100644 index 0000000..37b68ad --- /dev/null +++ b/src/Pages/ReSeller/Collections/Page.tsx @@ -0,0 +1,40 @@ +import { useTranslation } from "react-i18next"; +import { lazy, Suspense } from "react"; +import { Spin } from "antd"; +import useSetPageTitle from "../../../Hooks/useSetPageTitle"; +import PageHeader from "../../../Layout/Dashboard/PageHeader"; +import FilterLayout from "../../../Layout/Dashboard/FilterLayout"; +import FilterForm from "./Model/FilterForm"; +import CollectionInfoCard from "./Model/CollectionInfoCard"; +import { CollectionData } from "../../../faker/item"; +const Table = lazy(() => import("./Table")); + +const TableHeader = () => { + const [t] = useTranslation(); + useSetPageTitle([ + {name:`${t(`page_header.home`)}`, path:"/"}, + {name:`${t(`page_header.collections`)}`, path:"collections"} + ]); + + return ( +
+ }> + +
+ {CollectionData?.map((collection:any)=>( + + ))} +
+ } + filterTitle="table.collections" + /> + + + + ); +}; + +export default TableHeader; diff --git a/src/Pages/ReSeller/Collections/Table.tsx b/src/Pages/ReSeller/Collections/Table.tsx new file mode 100644 index 0000000..3c2f8c8 --- /dev/null +++ b/src/Pages/ReSeller/Collections/Table.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import DataTable from "../../../Layout/Dashboard/Table/DataTable"; +import { useColumns } from "./useTableColumns"; +import useSearchQuery from "../../../api/utils/useSearchQuery"; +import { useFilterState } from "../../../Components/Utils/Filter/FilterState"; +import { useGetAllSales } from "../../../api/sales"; + +const App: React.FC = () => { + const [searchQuery] = useSearchQuery("name"); + const { filterState } = useFilterState(); + + const response = useGetAllSales({ + name: searchQuery, + pagination: true, + ...filterState, + }); + + return ; +}; + +export default App; diff --git a/src/Pages/ReSeller/Collections/index.tsx b/src/Pages/ReSeller/Collections/index.tsx new file mode 100644 index 0000000..447f22d --- /dev/null +++ b/src/Pages/ReSeller/Collections/index.tsx @@ -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 "../../"; + +export { + Table, + useColumns, + AddModalForm, + EditModalForm, + // DeleteModalForm, + FaPlus, +}; diff --git a/src/Pages/ReSeller/Collections/useTableColumns.tsx b/src/Pages/ReSeller/Collections/useTableColumns.tsx new file mode 100644 index 0000000..b5057b1 --- /dev/null +++ b/src/Pages/ReSeller/Collections/useTableColumns.tsx @@ -0,0 +1,43 @@ +import { TableColumnsType } from "antd"; +import { Collection } from "../../../types/Item"; +import { useTranslation } from "react-i18next"; + +export const useColumns = () => { + const [t] = useTranslation(); + + + const columns: TableColumnsType = [ + { + title: t("columns.ID"), + dataIndex: "id", + key: "id", + align: "center", + }, + { + title: t("columns.amount"), + dataIndex: "amount", + key: "amount", + align: "center", + }, + { + title: t("columns.date_of_receipt"), + dataIndex: "date_of_receipt", + key: "date_of_receipt", + align: "center", + }, + { + title: t("columns.description"), + dataIndex: "description", + key: "description", + align: "center", + }, + { + title: t("columns.residual"), + dataIndex: "residual", + key: "residual", + align: "center", + }, + ]; + + return columns; +}; diff --git a/src/Pages/ReSeller/Notifications/Card.tsx b/src/Pages/ReSeller/Notifications/Card.tsx new file mode 100644 index 0000000..e936b14 --- /dev/null +++ b/src/Pages/ReSeller/Notifications/Card.tsx @@ -0,0 +1,29 @@ +import TrashButton from "../../../Components/Ui/TrashButton" +import { notifications } from "../../../types/Notifications" + + +const Card = ({name,date,image,id,pop,setPop}:notifications) => { + const handleDeleteOne = () => { + setPop(pop?.filter((item:any)=> item?.id !== id)) + } + + return ( +
+
+ {name} +
+
{name}
+

{date}

+
+
+
+ +
+
+ ) +} + +export default Card \ No newline at end of file diff --git a/src/Pages/ReSeller/Notifications/NotificationArray.ts b/src/Pages/ReSeller/Notifications/NotificationArray.ts new file mode 100644 index 0000000..b5b00ed --- /dev/null +++ b/src/Pages/ReSeller/Notifications/NotificationArray.ts @@ -0,0 +1,16 @@ +import { notifications } from "../../../types/Notifications"; + +export const NotificationArray:notifications[] = [ + {id:1,name:"تم إضافة تحصيل جديد بواسطة شاون",date:"1/10/2010",image:"/Image/faker_user.png"}, + {id:2,name:"moa",date:"منذ ساعة",image:"/Image/faker_user.png"}, + {id:3,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + {id:4,name:"hello",date:"1/10/2010",image:"/Image/faker_user.png"}, + {id:5,name:"nop",date:"1/10/2010",image:"/Image/faker_user.png"}, + {id:6,name:"hey",date:"1/10/2010",image:"/Image/faker_user.png"}, + // {id:2,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + // {id:2,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + // {id:2,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + // {id:2,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + // {id:2,name:"moaz",date:"1/10/2010",image:"/Image/faker_user.png"}, + +] \ No newline at end of file diff --git a/src/Pages/ReSeller/Notifications/Page.tsx b/src/Pages/ReSeller/Notifications/Page.tsx new file mode 100644 index 0000000..4ad956c --- /dev/null +++ b/src/Pages/ReSeller/Notifications/Page.tsx @@ -0,0 +1,41 @@ +import { Divider } from 'antd'; +import { useTranslation } from 'react-i18next' +import { NotificationArray } from './NotificationArray'; +import { notifications } from '../../../types/Notifications'; +import Card from './Card'; +import TrashButton from '../../../Components/Ui/TrashButton'; +import { useState } from 'react'; + +const Page = () => { + const {t} = useTranslation(); + const [pop, setPop] = useState(NotificationArray) + + const handleDeleteAll = () => { + setPop([]) + } + + return ( +
+
+

{t("header.notifications")}

+ +
+ +
+ {pop?.map((not:notifications)=>( + + ))} +
+
+ ) +} + +export default Page \ No newline at end of file diff --git a/src/Pages/ReSeller/Profile/Form/AttachmentForm.tsx b/src/Pages/ReSeller/Profile/Form/AttachmentForm.tsx new file mode 100644 index 0000000..da3397c --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/AttachmentForm.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { FaImage } from "react-icons/fa"; +import ImageBoxField from "./ImageBoxField/ImageBoxField"; + +const AttachmentForm = () => { + const [t] = useTranslation(); + + return ( +
+
+
+ +

{t("header.attachments")}

+
+
+
+ + +
+
+ ); +}; + +export default AttachmentForm; diff --git a/src/Pages/ReSeller/Profile/Form/HeaderForm.tsx b/src/Pages/ReSeller/Profile/Form/HeaderForm.tsx new file mode 100644 index 0000000..eb9a94e --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/HeaderForm.tsx @@ -0,0 +1,24 @@ +import { Button } from 'antd'; +import React from 'react' +import { useTranslation } from 'react-i18next' + +const HeaderForm = ({name,Icon,ButtonIcon, isHaveButtonIcon= true,buttonName = "edit"}:{name:string,Icon:any,ButtonIcon?:any,isHaveButtonIcon?:boolean,buttonName?:string}) => { + const {t} = useTranslation(); + return ( + <> +
+ +

{t(`header.${name}`)}

+
+
+ +
+ + ) +} + +export default HeaderForm \ No newline at end of file diff --git a/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.scss b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.scss new file mode 100644 index 0000000..66e2f2f --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.scss @@ -0,0 +1,38 @@ +.ImageBoxField { + .ImageBox { + width: 120px; + height: 120px; + display: flex; + align-items: center; + justify-content: center; + border: max(1.5px, 0.1vw) dashed #a9c3f1; + margin-block: 10px; + border-radius: 5px; + z-index: 9999999 !important; + .ImageBoxIcon { + cursor: pointer; + } + .imagePreview { + max-width: 99%; + height: auto; + max-height: 99%; + object-fit: contain; + border-radius: 5px; + } + } + .ImageHeader { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + } + + .ImageCancelIcon { + width: 16px !important; + height: 16px !important; + } + .ImageBoxIcon { + width: 20px !important; + height: 20px !important; + } +} diff --git a/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.tsx b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.tsx new file mode 100644 index 0000000..d163048 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageBoxField.tsx @@ -0,0 +1,87 @@ +import { useFormikContext } from "formik"; +import { useState, useRef, useEffect } from "react"; +import "./ImageBoxField.scss"; +import ImageIcon from "./ImageIcon"; +import ImageCancelIcon from "./ImageCancelIcon"; +import { generateImagePreview } from "./generateImagePreview"; +import { getNestedValue } from "../../../../../utils/getNestedValue"; +import { useTranslation } from "react-i18next"; + +// Helper function to generate image preview from a File + +const ImageBoxField = ({ name }: any) => { + const formik = useFormikContext(); + const value = getNestedValue(formik.values, name); + const [imagePreview, setImagePreview] = useState(null); + const fileInputRef = useRef(null); + + useEffect(() => { + if (value instanceof File) { + generateImagePreview(value, setImagePreview); + } else if (typeof value === "string") { + setImagePreview(value); + } else { + setImagePreview(null); + } + }, [value]); + + const handleFileChange = (event: any) => { + const file = event.target.files[0]; + if (file) { + generateImagePreview(file, setImagePreview); + formik.setFieldValue(name, file); + } + }; + + const handleButtonClick = () => { + const fileInput = fileInputRef.current; + if (fileInput) { + fileInput.click(); + } + }; + + const handleCancel = () => { + setImagePreview(""); + formik.setFieldValue(name, ""); + + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }; + const [t] = useTranslation(); + return ( +
+
{t(`input.${name}`)}
+
+ {imagePreview ? ( + <> + + + + ) : ( +
hidden
+ )} +
+
+ {imagePreview ? ( + Preview + ) : ( + + )} +
+ +
+ ); +}; + +export default ImageBoxField; diff --git a/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageCancelIcon.tsx b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageCancelIcon.tsx new file mode 100644 index 0000000..d42ba53 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageCancelIcon.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface ImageCancelIconProps extends React.HTMLAttributes {} + +const ImageCancelIcon: React.FC = (props) => { + return ( +
+ + + +
+ ); +}; + +export default ImageCancelIcon; diff --git a/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageIcon.tsx b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageIcon.tsx new file mode 100644 index 0000000..4ca597a --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/ImageBoxField/ImageIcon.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface ImageIconProps extends React.HTMLAttributes {} + +const ImageIcon: React.FC = (props) => { + return ( +
+ + + +
+ ); +}; + +export default ImageIcon; diff --git a/src/Pages/ReSeller/Profile/Form/ImageBoxField/generateImagePreview.ts b/src/Pages/ReSeller/Profile/Form/ImageBoxField/generateImagePreview.ts new file mode 100644 index 0000000..3f754d3 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/ImageBoxField/generateImagePreview.ts @@ -0,0 +1,10 @@ +export const generateImagePreview = ( + file: File, + setImagePreview: (result: string) => void, +) => { + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreview(reader.result as string); + }; + reader.readAsDataURL(file); +}; diff --git a/src/Pages/ReSeller/Profile/Form/PasswordDetailsForm.tsx b/src/Pages/ReSeller/Profile/Form/PasswordDetailsForm.tsx new file mode 100644 index 0000000..c6e9edb --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/PasswordDetailsForm.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import HeaderForm from "./HeaderForm"; + +const PasswordDetailsForm = () => { + const [t] = useTranslation(); + + return ( +
+
+ +
+
+ +
+
+ ); +}; + +export default PasswordDetailsForm; diff --git a/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx b/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx new file mode 100644 index 0000000..96d9ed5 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx @@ -0,0 +1,56 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import { statusType } from "../../../../config/statusType"; +import { Button } from "antd"; +import { CiEdit } from "react-icons/ci"; +import HeaderForm from "./HeaderForm"; + +const PersonalDetailsForm = () => { + const [t] = useTranslation(); + return ( +
+
+ +
+
+ + + + + + + + +
+
+ ); +}; + +export default PersonalDetailsForm; diff --git a/src/Pages/ReSeller/Profile/Form/TitleDetailsForm.tsx b/src/Pages/ReSeller/Profile/Form/TitleDetailsForm.tsx new file mode 100644 index 0000000..1e62655 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/TitleDetailsForm.tsx @@ -0,0 +1,33 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import { nationalities } from "../../../../types/App"; +import { CiEdit } from "react-icons/ci"; +import HeaderForm from "./HeaderForm"; + +const TitleDetailsForm = () => { + const [t] = useTranslation(); + + return ( +
+
+ +
+
+ + +
+
+ ); +}; + +export default TitleDetailsForm; diff --git a/src/Pages/ReSeller/Profile/Form/formUtils.ts b/src/Pages/ReSeller/Profile/Form/formUtils.ts new file mode 100644 index 0000000..b14915a --- /dev/null +++ b/src/Pages/ReSeller/Profile/Form/formUtils.ts @@ -0,0 +1,13 @@ +import * as Yup from "yup"; + +export const getInitialValues = (objectToEdit: Partial) => { + return { + id: objectToEdit?.id ?? null, + name: objectToEdit?.name ?? null, + }; +}; + +export const getValidationSchema = () => { + // validate input + return Yup.object().shape({}); +}; diff --git a/src/Pages/ReSeller/Profile/Page.tsx b/src/Pages/ReSeller/Profile/Page.tsx new file mode 100644 index 0000000..def9cc2 --- /dev/null +++ b/src/Pages/ReSeller/Profile/Page.tsx @@ -0,0 +1,58 @@ +import { useTranslation } from "react-i18next"; +import useSetPageTitle from "../../../Hooks/useSetPageTitle"; +import PageHeader from "../../../Layout/Dashboard/PageHeader"; +import { Suspense } from "react"; +import { Spin } from "antd"; +import { ModalEnum } from "../../../enums/Model"; +import { canAddReSeller } from "../../../utils/hasAbilityFn"; +import { Formik, Form } from "formik"; +import { getInitialValues, getValidationSchema } from "./Form/formUtils"; +import PersonalDetailsForm from "./Form/PersonalDetailsForm"; +import TitleDetailsForm from "./Form/TitleDetailsForm"; +import AttachmentForm from "./Form/AttachmentForm"; +import PasswordDetailsForm from "./Form/PasswordDetailsForm"; + +const Page = () => { + const [t] = useTranslation(); + useSetPageTitle(t(`page_header.add_reseller`)); + const handelSubmit = (values: any) => { + console.log(values, "values"); + }; + useSetPageTitle([ + {name:`${t(`page_header.home`)}`, path:"/"}, + {name:`${t(`page_header.profile`)}`, path:"tag"} + ]); + return ( +
+ }> + +
+ +
+ + + + +
+ + +
+ +
+
+
+
+ ); +}; + +export default Page; diff --git a/src/Pages/ReSeller/Sales/Model/AddModel.tsx b/src/Pages/ReSeller/Sales/Model/AddModel.tsx new file mode 100644 index 0000000..dd0547e --- /dev/null +++ b/src/Pages/ReSeller/Sales/Model/AddModel.tsx @@ -0,0 +1,34 @@ +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"; +import { useAddSales } from "../../../../api/sales"; + +const AddModel: React.FC = () => { + const { mutate, status } = useAddSales(); + + const handleSubmit = (values: any) => { + mutate({ + ...values, + }); + }; + return ( + <> + + + + + ); +}; + +export default AddModel; diff --git a/src/Pages/ReSeller/Sales/Model/FilterForm.tsx b/src/Pages/ReSeller/Sales/Model/FilterForm.tsx new file mode 100644 index 0000000..d601efb --- /dev/null +++ b/src/Pages/ReSeller/Sales/Model/FilterForm.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import { Col, Row } from "reactstrap"; + +const FilterForm = () => { + return ( +
+ +
+ + {/* */} + + + + ); +}; + +export default FilterForm; diff --git a/src/Pages/ReSeller/Sales/Model/ModelForm.tsx b/src/Pages/ReSeller/Sales/Model/ModelForm.tsx new file mode 100644 index 0000000..4cace32 --- /dev/null +++ b/src/Pages/ReSeller/Sales/Model/ModelForm.tsx @@ -0,0 +1,26 @@ +import { Col, Row } from "reactstrap"; +import ValidationField from "../../../../Components/ValidationField/ValidationField"; +import useFormatDataToSelect from "../../../../utils/useFormatDataToSelect"; + +const Form = ({ isEdit }: { isEdit?: boolean }) => { + const typeOptions = [ + { id: "student", name: "student" }, + { id: "reseller", name: "reseller" }, + { id: "admin", name: "admin" }, + ]; + const typeArray = useFormatDataToSelect(typeOptions); + + return ( + + + + + + ); +}; + +export default Form; diff --git a/src/Pages/ReSeller/Sales/Model/formUtil.ts b/src/Pages/ReSeller/Sales/Model/formUtil.ts new file mode 100644 index 0000000..d2aa9da --- /dev/null +++ b/src/Pages/ReSeller/Sales/Model/formUtil.ts @@ -0,0 +1,13 @@ +import * as Yup from "yup"; + +export const getInitialValues = (objectToEdit: any): any => { + return { + id: objectToEdit?.id ?? null, + phone_number: objectToEdit?.phone_number ?? null, + }; +}; +export const getValidationSchema = () => { + return Yup.object().shape({ + phone_number: Yup.number().required("validation.required"), + }); +}; diff --git a/src/Pages/ReSeller/Sales/Page.tsx b/src/Pages/ReSeller/Sales/Page.tsx new file mode 100644 index 0000000..f0856a9 --- /dev/null +++ b/src/Pages/ReSeller/Sales/Page.tsx @@ -0,0 +1,38 @@ +import { ModalEnum } from "../../../enums/Model"; +import { useTranslation } from "react-i18next"; +import { lazy, Suspense } from "react"; +import { Spin } from "antd"; +import { canAddSales } from "../../../utils/hasAbilityFn"; +import useSetPageTitle from "../../../Hooks/useSetPageTitle"; +import { useDeleteTag } from "../../../api/tags"; +import PageHeader from "../../../Layout/Dashboard/PageHeader"; +import FilterLayout from "../../../Layout/Dashboard/FilterLayout"; +import FilterForm from "./Model/FilterForm"; +const Table = lazy(() => import("./Table")); +const AddModalForm = lazy(() => import("./Model/AddModel")); + +const TableHeader = () => { + const [t] = useTranslation(); + useSetPageTitle([ + {name:`${t(`page_header.home`)}`, path:"/"}, + {name:`${t(`page_header.sales`)}`, path:"sales"} + ]); return ( +
+ }> + + } + filterTitle="table.sales" + /> +
+ + + + ); +}; + +export default TableHeader; diff --git a/src/Pages/ReSeller/Sales/Table.tsx b/src/Pages/ReSeller/Sales/Table.tsx new file mode 100644 index 0000000..9981329 --- /dev/null +++ b/src/Pages/ReSeller/Sales/Table.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import DataTable from "../../../Layout/Dashboard/Table/DataTable"; +import { useColumns } from "./useTableColumns"; +import useSearchQuery from "../../../api/utils/useSearchQuery"; +import { useFilterState } from "../../../Components/Utils/Filter/FilterState"; +import { useGetAllSales } from "../../../api/sales"; +const App: React.FC = () => { + const [searchQuery] = useSearchQuery("name"); + const { filterState } = useFilterState(); + + const response = useGetAllSales({ + name: searchQuery, + pagination: true, + ...filterState, + }); + + return ; +}; + +export default App; diff --git a/src/Pages/ReSeller/Sales/index.tsx b/src/Pages/ReSeller/Sales/index.tsx new file mode 100644 index 0000000..447f22d --- /dev/null +++ b/src/Pages/ReSeller/Sales/index.tsx @@ -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 "../../"; + +export { + Table, + useColumns, + AddModalForm, + EditModalForm, + // DeleteModalForm, + FaPlus, +}; diff --git a/src/Pages/ReSeller/Sales/useTableColumns.tsx b/src/Pages/ReSeller/Sales/useTableColumns.tsx new file mode 100644 index 0000000..ea81cfc --- /dev/null +++ b/src/Pages/ReSeller/Sales/useTableColumns.tsx @@ -0,0 +1,49 @@ +import { TableColumnsType } from "antd"; +import { Sales } from "../../../types/Item"; +import { useTranslation } from "react-i18next"; + +export const useColumns = () => { + const [t] = useTranslation(); + + + const columns: TableColumnsType = [ + { + title: t("columns.id"), + dataIndex: "id", + key: "id", + align: "center", + }, + { + title: t("columns.student_full_name"), + dataIndex: "activation_date", + key: "activation_date", + align: "center", + }, + { + title: t("columns.grade"), + dataIndex: "grade", + key: "grade", + align: "center", + }, + { + title: t("columns.package"), + dataIndex: "package", + key: "package", + align: "center", + }, + { + title: t("columns.amount_paid"), + dataIndex: "amount_paid", + key: "amount_paid", + align: "center", + }, + { + title: t("columns.sale_date"), + dataIndex: "sale_date", + key: "sale_date", + align: "center", + }, + ]; + + return columns; +}; diff --git a/src/Routes.tsx b/src/Routes.tsx index 8f6c45e..02ddcf1 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -25,6 +25,7 @@ const Student = React.lazy(() => import("./Pages/Admin/Student/Page")); const ShowStudent = React.lazy(() => import("./Pages/Admin/Student/show/Page")); const Manager = React.lazy(() => import("./Pages/Admin/Manager/Page")); const AddManager = React.lazy(() => import("./Pages/Admin/Manager/Add/Page")); +const EditManager = React.lazy(() => import("./Pages/Admin/Manager/Edit/Page")); const ReSeller = React.lazy(() => import("./Pages/Admin/Reseller/Page")); @@ -44,9 +45,12 @@ const Report = React.lazy(() => import("./Pages/Admin/Report/Page")); const Param = React.lazy(() => import("./Pages/Admin/Param/Page")); /// RESELLER /// -const Student_Package = React.lazy( - () => import("./Pages/ReSeller/StudentPackage/Page"), -); +const Sales = React.lazy(() => import("./Pages/ReSeller/Sales/Page")); +const Collections = React.lazy(() => import("./Pages/ReSeller/Collections/Page")); + +const NotificationReSeller = React.lazy(() => import("./Pages/ReSeller/Notifications/Page")); +const ProfileReSeller = React.lazy(() => import("./Pages/ReSeller/Profile/Page")); + import { hasAbility } from "./utils/hasAbility"; import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities"; @@ -97,16 +101,16 @@ export const menuItems: TMenuItem[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, - // { - // header: "page_header.managers", - // element: , - // icon: , - // text: "sidebar.managers", - // path: `/${ABILITIES_ENUM?.MANAGERS}`, - // abilities: ABILITIES_ENUM?.MANAGERS, - // abilities_value: ABILITIES_VALUES_ENUM.INDEX, - // prevPath: 0, - // }, + { + header: "page_header.managers", + element: , + icon: , + text: "sidebar.managers", + path: `/${ABILITIES_ENUM?.MANAGERS}`, + abilities: ABILITIES_ENUM?.MANAGERS, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + }, ] }, { @@ -174,14 +178,35 @@ export const menuItems: TMenuItem[] = [ /// RESELLER ///// - { - header: "page_header.student_package", - element: , + header: "page_header.sales", + element: , icon: , - text: "sidebar.student_package", - path: `/${ABILITIES_ENUM?.Student_Package}`, - abilities: ABILITIES_ENUM?.Student_Package, + text: "sidebar.sales", + path: `/${ABILITIES_ENUM?.Sales}`, + abilities: ABILITIES_ENUM?.Sales, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + type: UserTypeEnum.RE_SELLER, + }, + { + header: "page_header.collections", + element: , + icon: , + text: "sidebar.collections", + path: `/${ABILITIES_ENUM?.Collections}`, + abilities: ABILITIES_ENUM?.Collections, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + type: UserTypeEnum.RE_SELLER, + }, + { + header: "page_header.profile", + element: , + icon: , + text: "sidebar.profile", + path: `//${ABILITIES_ENUM?.PROFILE}`, + abilities: ABILITIES_ENUM?.Profile_RE_SELLER, abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, type: UserTypeEnum.RE_SELLER, @@ -281,14 +306,6 @@ export const CrudRoute: TCrudRoute[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, - { - header: "page_header.notifications", - element: , - path: `/${ABILITIES_ENUM?.NOTIFICATIONS}`, - abilities: ABILITIES_ENUM?.NOTIFICATIONS, - abilities_value: ABILITIES_VALUES_ENUM.INDEX, - prevPath: 0, - }, { header: "page_header.permissions", element: , @@ -297,6 +314,14 @@ export const CrudRoute: TCrudRoute[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, + { + header: "page_header.notifications", + element: , + path: `/${ABILITIES_ENUM?.NOTIFICATIONS}`, + abilities: ABILITIES_ENUM?.NOTIFICATIONS, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + }, { header: "page_header.add_manager", element: , @@ -305,6 +330,24 @@ export const CrudRoute: TCrudRoute[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, + { + header: "page_header.edit_manager", + element: , + path: `/${ABILITIES_ENUM?.MANAGERS}/edit`, + abilities: ABILITIES_ENUM?.MANAGERS, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + }, + //// RE_SELLER + { + header: "page_header.notifications", + element: , + path: `/${ABILITIES_ENUM?.NOTIFICATIONS}`, + abilities: ABILITIES_ENUM?.NOTIFICATIONS_RE_SELLER, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + type: UserTypeEnum.RE_SELLER, + }, ]; export const AppRoutes: Record = Object.fromEntries( diff --git a/src/Styles/Antd/Model.scss b/src/Styles/Antd/Model.scss index 61be301..45a3171 100644 --- a/src/Styles/Antd/Model.scss +++ b/src/Styles/Antd/Model.scss @@ -5,6 +5,7 @@ .ant-modal .ant-modal-content { padding: 0; border-radius: 10px; + .ant-modal-close { display: none !important; } @@ -12,10 +13,12 @@ .ModalForm { position: relative; + .ant-divider-horizontal { // width: 90% !important; margin: 10px 0; } + header { display: flex; justify-content: space-between; @@ -26,12 +29,14 @@ color: var(--black); border-top-left-radius: 10px; border-top-right-radius: 10px; + span { font-size: 25px; } + svg { font-size: 30px; - color: #6a7287; + color: var(--value); } } @@ -41,6 +46,7 @@ flex-direction: column; width: 100%; } + main { .buttons { padding-inline: 0 10px; @@ -48,8 +54,9 @@ gap: 10px; align-items: end; justify-content: end; - > button, - > div { + + >button, + >div { position: relative; outline: none; border: none; @@ -63,6 +70,7 @@ align-items: center; justify-content: center; gap: 5px; + &:nth-child(1) { background: var(--white); color: var(--primary); @@ -71,13 +79,16 @@ } } } + label { color: var(--secondary) !important; } } + .ant-modal-root .ant-modal-centered .ant-modal { width: 45vw; } + .ant-modal .ant-modal-body { padding-inline: 1vw !important; -} +} \ No newline at end of file diff --git a/src/Styles/App/App.scss b/src/Styles/App/App.scss index 2ecd94d..139918e 100644 --- a/src/Styles/App/App.scss +++ b/src/Styles/App/App.scss @@ -13,6 +13,7 @@ body { font-weight: 600; font-size: 16px; } + h1, h2, h3, @@ -42,26 +43,32 @@ svg { justify-content: center; text-align: center; padding-top: 30px; - .not_found_container{ - display: flex; flex-direction: column; + + .not_found_container { + display: flex; + flex-direction: column; align-items: center; gap: 20px; - p{ - color: #6A7287; + + p { + color: var(--value); font-size: 17px; } - h3{ + + h3 { font-weight: 900; } - .not_found_button{ + + .not_found_button { @include Flex; width: 240px; - padding: 24px 22px ; + padding: 24px 22px; background: var(--primary); color: var(--white); border: none; transition: ease-in-out .3s; - &:hover{ + + &:hover { transform: scale(1.05); background: var(--primary); color: var(--white); diff --git a/src/Styles/App/Varibils.scss b/src/Styles/App/Varibils.scss index 9d6875d..300a964 100644 --- a/src/Styles/App/Varibils.scss +++ b/src/Styles/App/Varibils.scss @@ -5,10 +5,12 @@ --text: #202C4B; --subtext: #9d9d9d; --label: #0d5190; + --value: #6A7287; --bg: rgb(255, 255, 255); --bg2: #f4f7fe; --bg3: rgba(255, 255, 255, 0.82); --border-color: #b0d9ff; + --border-color-2: #E9EDF4; --shadow: rgba(0, 0, 0, 0.15); --white: white; --bgSideBar: #0f0c1c; @@ -36,5 +38,4 @@ --borderColor: #ffffff91; --opacity: #bcbcbc; --fieldHeight: 40px; -} - +} \ No newline at end of file diff --git a/src/Styles/DataTable/SearchField.scss b/src/Styles/DataTable/SearchField.scss index e7c31f4..2886f12 100644 --- a/src/Styles/DataTable/SearchField.scss +++ b/src/Styles/DataTable/SearchField.scss @@ -4,17 +4,20 @@ border-radius: 8px; width: 280px; direction: ltr; - color: #6a7287b2; + color: var(--value)b2; } + .NavBar { .search-header { color: var(--white) !important; background-color: inherit; border-radius: 8px; border: 1px solid var(--borderColor); - svg{ + + svg { font-size: 24px; } + input { color: #fff !important; text-align: end; @@ -22,6 +25,7 @@ } } } + .search-header { display: flex; align-items: center; @@ -49,6 +53,7 @@ outline: none; border: none; position: relative; + .search__input_text { color: var(--white); position: absolute; @@ -57,6 +62,7 @@ font-size: 30px; width: 100px !important; } + font-weight: normal; } @@ -76,6 +82,7 @@ direction: rtl; scroll-behavior: smooth; scroll-padding: 10rem; + &::-webkit-scrollbar { width: 10px; } @@ -83,19 +90,23 @@ /* Handle */ &::-webkit-scrollbar-thumb { background-color: transparent; - border-radius: 5px; /* Adjust border-radius as needed */ + border-radius: 5px; + /* Adjust border-radius as needed */ } /* Track */ &::-webkit-scrollbar-track { - border-radius: 5px; /* Adjust border-radius as needed */ - background-color: transparent; /* Set to desired background color */ + border-radius: 5px; + /* Adjust border-radius as needed */ + background-color: transparent; + /* Set to desired background color */ } } @mixin CustomScrollbar($color) { scroll-behavior: smooth; scroll-padding: 10rem; + &::-webkit-scrollbar { width: 10px; } @@ -103,13 +114,16 @@ /* Handle */ &::-webkit-scrollbar-thumb { background-color: $color; - border-radius: 5px; /* Adjust border-radius as needed */ + border-radius: 5px; + /* Adjust border-radius as needed */ } /* Track */ &::-webkit-scrollbar-track { - border-radius: 5px; /* Adjust border-radius as needed */ - background-color: transparent; /* Set to desired background color */ + border-radius: 5px; + /* Adjust border-radius as needed */ + background-color: transparent; + /* Set to desired background color */ } } @@ -157,4 +171,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/Styles/Layout/FilterLayout.scss b/src/Styles/Layout/FilterLayout.scss index b138b96..a0a42c2 100644 --- a/src/Styles/Layout/FilterLayout.scss +++ b/src/Styles/Layout/FilterLayout.scss @@ -3,13 +3,15 @@ border-radius: 10px 10px 0 0; box-shadow: 0px 0px 32px 2px #080F3414; - > div { + >div { display: flex; align-items: center; justify-content: space-between; + .filter_and_order_by { display: flex; gap: 10px; + .filter_button { display: flex; justify-content: space-between; @@ -21,41 +23,51 @@ border: 1px solid var(--opacity) !important; transition: ease-in-out 0.4s; cursor: pointer; + &:hover { background: var(--bg4); } + svg { font-size: 25px; } } } + span { display: flex; align-items: center; - color: #6A7287; + color: var(--value); + .pagination_select, .order_by_select, .filter_modal_select { border: 1px solid var(--opacity); border-radius: 10px; + .addition_select_icon { font-size: 25px; } - div{ - color: #6A7287; + + div { + color: var(--value); } } + p { padding-inline: 10px; } } } + .search-field { border: 1px solid rgba(0, 0, 0, 0.1); - .search__input{ + + .search__input { width: 90%; - text-align: end ; + text-align: end; } + .search__icon { color: var(--primary); } @@ -64,16 +76,12 @@ .pagination_column, .order_by_filter { - .ant-select-outlined:not(.ant-select-disabled):not( - .ant-select-customize-input - ):not(.ant-pagination-size-changer):hover - .ant-select-selector, - .ant-select-outlined:not(.ant-select-disabled):not( - .ant-select-customize-input - ):not(.ant-pagination-size-changer):hover - .ant-select-selector { + + .ant-select-outlined:not(.ant-select-disabled):not(.ant-select-customize-input):not(.ant-pagination-size-changer):hover .ant-select-selector, + .ant-select-outlined:not(.ant-select-disabled):not(.ant-select-customize-input):not(.ant-pagination-size-changer):hover .ant-select-selector { transition: ease-in-out 0.4s !important; cursor: pointer !important; + &:hover { background: var(--bg4) !important; } @@ -86,6 +94,7 @@ } @media screen and (max-width: 800px) { + .filter_modal_add_button, .filter_modal_cancel_button { font-size: 8px !important; @@ -93,6 +102,6 @@ } -.model_sub_children{ +.model_sub_children { padding-bottom: 30px; } diff --git a/src/Styles/Layout/PageHeader.scss b/src/Styles/Layout/PageHeader.scss index 723ff39..a5be4fe 100644 --- a/src/Styles/Layout/PageHeader.scss +++ b/src/Styles/Layout/PageHeader.scss @@ -1,12 +1,15 @@ .page_header { margin-bottom: 0px !important; + .page_header_links { display: flex; flex-direction: column; + .page_title { font-weight: bolder; font-size: 24px; } + .page_links { color: var(--opacity); display: flex; @@ -17,24 +20,25 @@ } .filter_header_top { - h4{ + h4 { color: var(--secondary); - font-weight:600; + font-weight: 600; font-size: 18px; } } -.PageTitle{ +.PageTitle { display: flex; - gap: 10px; - margin-block: 10px; - .PageTitleItems{ - cursor: pointer; - color: #6A7287; - } + gap: 10px; + margin-block: 10px; + + .PageTitleItems { + cursor: pointer; + color: var(--value); + } } -.PageTitleLastItem{ +.PageTitleLastItem { color: #202C4B !important; } \ No newline at end of file diff --git a/src/Styles/Pages/InfoCard.scss b/src/Styles/Pages/InfoCard.scss index 1041fd7..2f125e1 100644 --- a/src/Styles/Pages/InfoCard.scss +++ b/src/Styles/Pages/InfoCard.scss @@ -28,7 +28,7 @@ padding: 20px 15px; p { - color: #6A7287; + color: var(--value); } .info_card_header { @@ -68,7 +68,8 @@ font-size: 20px; } } - .info_card_button{ + + .info_card_button { background: var(--primary); color: var(--white); padding: 25px 0px; @@ -80,15 +81,18 @@ .address_card { margin-block: 30px; + .address_card_body { display: flex; - flex-direction: column; + flex-direction: column; align-items: center; - div{ + + div { margin-block: 20px; gap: 15px; display: flex !important; } + svg { @include Flex; border-radius: 5px; @@ -99,8 +103,9 @@ } } } -.tab_icon{ - font-size: 30px ; + +.tab_icon { + font-size: 30px; } @media screen and (max-width:1250px) { @@ -131,32 +136,41 @@ .student_table { width: 100%; } - .reseller_info{ - display: flex; flex-wrap: wrap; + + .reseller_info { + display: flex; + flex-wrap: wrap; + .address_card, .attachments_card, - .info_card{ + .info_card { width: 45%; - div{ - span{ + + div { + span { font-size: 1.6vw !important; } - h4{ + + h4 { font-size: 1.8vw; } - h2{ + + h2 { font-size: 1.6vw !important; } - h6{ - font-size: 1.6vw !important; + + h6 { + font-size: 1.6vw !important; } - p{ + + p { font-size: 1.6vw !important; } } } + .address_card, - .attachments_card{ + .attachments_card { max-height: 40vh !important; } } diff --git a/src/Styles/Pages/collections.scss b/src/Styles/Pages/collections.scss new file mode 100644 index 0000000..ba14340 --- /dev/null +++ b/src/Styles/Pages/collections.scss @@ -0,0 +1,26 @@ +.collection_infos { + margin-block: 20px; + display: flex;justify-content: space-between; + gap: 5px; + .collection_info_card { + display: flex; + justify-content: center; + flex-direction: column; + width: 15vw; + padding: 1.2vw 1.2vw; + box-shadow: 0px 0px 32px 0px #080F3414; + border: 1px solid var(--border-color-2) !important; + border-radius: 8px; + gap: 10px; + h5{ + color: var(--secondary); + font-size: 1vw; + font-weight: bold; + } + p{ + font-size: .9vw ; + color: var(--value); + } + } + +} \ No newline at end of file diff --git a/src/Styles/Pages/index.scss b/src/Styles/Pages/index.scss index 5f71030..a1122fc 100644 --- a/src/Styles/Pages/index.scss +++ b/src/Styles/Pages/index.scss @@ -12,4 +12,5 @@ @import './reSeller.scss'; @import './InfoCard.scss'; @import './notifications.scss'; -@import './profile.scss'; \ No newline at end of file +@import './profile.scss'; +@import './collections.scss' diff --git a/src/Styles/Pages/notifications.scss b/src/Styles/Pages/notifications.scss index 2cd760f..35f5982 100644 --- a/src/Styles/Pages/notifications.scss +++ b/src/Styles/Pages/notifications.scss @@ -1,52 +1,68 @@ -.notification_container{ - display: flex; flex-direction: column; +.notification_container { + display: flex; + flex-direction: column; width: 96%; margin-inline: auto; - padding: 30px 20px ; + padding: 30px 20px; margin-block: 20px 50px; box-shadow: 2px 2px 8px 3px rgba(0, 0, 0, 0.1); border: 1.5px solid #E9EDF4; border-radius: 10px; background: #fff; - .notification_header{ - display: flex;align-items: center;justify-content: space-between; + + .notification_header { + display: flex; + align-items: center; + justify-content: space-between; padding-inline: 20px; - h4{ + + h4 { color: var(--secondary); } } - .notification_body{ - display: flex; flex-direction: column; + + .notification_body { + display: flex; + flex-direction: column; justify-content: center; - padding-inline: 10px ; + padding-inline: 10px; gap: 30px; - .notification_card{ - display: flex; justify-content: space-between; + + .notification_card { + display: flex; + justify-content: space-between; padding: 20px 20px; @include Shadow; border: 2px solid #E9EDF4; border-radius: 10px; background: #fff; cursor: pointer; - >div{ - display: flex;align-items: center; + + >div { + display: flex; + align-items: center; gap: 14px; - h5{ + + h5 { color: var(--secondary); } - img{ + + img { width: 70px; } - p{ + + p { margin-top: 12px; - color: #6A7287; + color: var(--value); } - .trash_button{ + + .trash_button { visibility: hidden; } } - &:hover{ - .trash_button{ + + &:hover { + .trash_button { visibility: visible; } } @@ -55,16 +71,18 @@ } -.trash_button{ +.trash_button { background: #E93553; @include Flex; color: var(--white); - padding: 20px 23px ; + padding: 20px 23px; border: none !important; - svg{ + + svg { font-size: 22px; } - &:hover{ + + &:hover { background: #E93553 !important; color: var(--white) !important; border: none !important; diff --git a/src/api/collections.ts b/src/api/collections.ts new file mode 100644 index 0000000..3546a5c --- /dev/null +++ b/src/api/collections.ts @@ -0,0 +1,10 @@ +import useGetQuery from "./helper/useGetQuery"; + +const API = { + GET: "/collections", +}; + +const KEY = "collections"; + +export const useGetAllSales = (params?: any, options?: any) => + useGetQuery(KEY, API.GET, params, options); diff --git a/src/api/sales.ts b/src/api/sales.ts new file mode 100644 index 0000000..fb15fcf --- /dev/null +++ b/src/api/sales.ts @@ -0,0 +1,15 @@ +import useAddMutation from "./helper/useAddMutation"; +import useGetQuery from "./helper/useGetQuery"; + +const API = { + GET: "/sales", + ADD: "/sales", + DELETE: "/sales", + UPDATE: "/sales", +}; + +const KEY = "sales"; + +export const useGetAllSales = (params?: any, options?: any) => + useGetQuery(KEY, API.GET, params, options); +export const useAddSales = () => useAddMutation(KEY, API.ADD); \ No newline at end of file diff --git a/src/enums/Model.ts b/src/enums/Model.ts index f3f1739..8480f8c 100644 --- a/src/enums/Model.ts +++ b/src/enums/Model.ts @@ -206,8 +206,13 @@ export enum ModalEnum { QUESTION_BANK_DELETE = "QuestionBank.delete", /// permission + PERMISSION_EDIT = "PERMISSION.edit", PERMISSION_ADD = "PERMISSION.add", PERMISSION_DELETE = "PERMISSION.delete", + + /// sales + + Sales_ADD = "Sales.add", } diff --git a/src/enums/abilities.ts b/src/enums/abilities.ts index 929b3ea..cac07e7 100644 --- a/src/enums/abilities.ts +++ b/src/enums/abilities.ts @@ -52,6 +52,10 @@ export enum ABILITIES_ENUM { PROFILE = "profile", PERMISSIONS = "permissions", MANAGERS = "managers", + NOTIFICATIONS_RE_SELLER = "notification_re_seller" , + Profile_RE_SELLER = "profile_re_seller" , + Sales = "sales", + Collections = "collections" //// } diff --git a/src/faker/item.ts b/src/faker/item.ts new file mode 100644 index 0000000..bb4990b --- /dev/null +++ b/src/faker/item.ts @@ -0,0 +1,7 @@ +export const CollectionData = [ + {label:"إجمالي المبيعات",value:"2.000.000"}, + {label:"نسبة الأرباح (10%)",value:"2.000.000"}, + {label:"المستحقات",value:"2.000.000"}, + {label:"تم تحصيله",value:"2.000.000"}, + {label:"المتبقي",value:"2.000.000"}, + ] \ No newline at end of file diff --git a/src/translate/ar.json b/src/translate/ar.json index b9b2379..1247b79 100644 --- a/src/translate/ar.json +++ b/src/translate/ar.json @@ -140,6 +140,7 @@ "change": "تغيير", "role_list": "قائمة الأدوار", "managers":"مدراء", + "sales":"المبيعات", "hide_hint":"اخفاء الشرح", "show_hint":"عرض الشرح" }, @@ -282,7 +283,8 @@ "id_photo": "صورة الهوية", "sorry_something_went_wrong": "عفوا ، حدث خطأ ما", "error_404_Page_not_found._Sorry,_the_page_you_are_looking_for_does_not_exist": "خطأ 404 لم يتم العثور على الصفحة. عذرا الصفحة التي تبحث عنها غير موجودة ", - "return_to_the_dashboard": "العودة إلى لوحة القيادة" + "return_to_the_dashboard": "العودة إلى لوحة القيادة", + "save_changes":"حفظ التغييرات" }, "Table": { "header": "", @@ -359,7 +361,9 @@ "role_details": "تفاصيل الأدوار", "created_at": "تم إنشاؤه في", "managers":"مدراء", - "manager":"مدير" + "manager":"مدير", + "sale":"عملية بيع", + "collections": "التحصيلات" }, "education_class_actions": { "Student_Records": "سجلات الطلاب", @@ -485,7 +489,9 @@ "current_password": "كلمة المرور الحالية", "created_at": "تم إنشاؤه في", "empty":"", - "role":"الدور" + "role":"الدور", + "submit_password":"تأكيد كلمة المرور", + "join_date":"تاريخ الانضمام" }, "select": { "enums": { @@ -818,7 +824,9 @@ "notifications": "الإشعارات", "profile": "الملف الشخصي", "users":"المستخدمون", - "managers":"مدراء" + "managers":"مدراء", + "sales":"المبيعات", + "collections": "التحصيلات" }, "message": { "some_thing_went_wrong": "حدث خطأ ما", @@ -860,7 +868,10 @@ "profile": "الملف الشخصي", "permissions":"اذونات", "managers":"مدراء", - "add_manager":"إضافة مدير" + "add_manager":"إضافة مدير", + "collections": "التحصيلات", + "sales":"المبيعات", + "edit_manager":"تعديل مدير" }, "page_header": { "home": "لوحة القيادة", @@ -904,7 +915,9 @@ "profile": "الملف الشخصي", "user": "مستخدم", "permissions":"اذونات", - "managers":"مدراء" + "managers":"مدراء", + "collections": "التحصيلات", + "sales":"المبيعات" }, "table": { "student": "قائمة الطلاب", @@ -913,7 +926,9 @@ "subjects": "مواد الصف", "QuestionBank": "بنك الأسئلة", "managers":"مدراء", - "managers_list":"قائمة المدراء" + "managers_list":"قائمة المدراء", + "sales":"المبيعات", + "collections": "التحصيلات" }, "alphabet": { "A": "A", diff --git a/src/types/Item.ts b/src/types/Item.ts index fec4221..dbd4a49 100644 --- a/src/types/Item.ts +++ b/src/types/Item.ts @@ -354,4 +354,20 @@ type student = { first_name:string; last_name:string; sex:string -} \ No newline at end of file +} + + +export type Sales = { + id: number; + student: student; + expiration_date: string; + activation_date: string; +}; + + +export type Collection = { + id: number; + student: student; + expiration_date: string; + activation_date: string; +}; diff --git a/src/utils/hasAbilityFn.ts b/src/utils/hasAbilityFn.ts index e6de73d..6cc137b 100644 --- a/src/utils/hasAbilityFn.ts +++ b/src/utils/hasAbilityFn.ts @@ -719,3 +719,20 @@ export const canShowQuestionBank = hasAbility( ABILITIES_ENUM.QUESTION_BANK, ABILITIES_VALUES_ENUM.SHOW, ); + + +/// User + +export const canAddSales = hasAbility( + ABILITIES_ENUM.Sales, + ABILITIES_VALUES_ENUM.STORE, +); + +export const canEditSales = hasAbility( + ABILITIES_ENUM.Sales, + ABILITIES_VALUES_ENUM.UPDATE, +); +export const canDeleteSales = hasAbility( + ABILITIES_ENUM.Sales, + ABILITIES_VALUES_ENUM.DELETE, +);