diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..894437f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+pnpm-lock.ymal
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-lock.yaml
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..6a521b2
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,11 @@
+{
+ "cSpell.words": [
+ "aldeen",
+ "Datepicker",
+ "formik",
+ "Karim",
+ "queryqlent",
+ "szhsin",
+ "Viewelement"
+ ]
+}
\ No newline at end of file
diff --git a/db.json b/db.json
new file mode 100644
index 0000000..7d19fac
--- /dev/null
+++ b/db.json
@@ -0,0 +1,76 @@
+{
+ "example": [
+ {
+ "id": 1,
+ "name": "ibrahim",
+ "email": "ibrahim@gmail.com"
+ },
+ {
+ "id": 2,
+ "name": "gregr",
+ "email": "ibrahimgmail.com"
+ },
+ {
+ "id": 3,
+ "name": "mhmad",
+ "email": "mhmad@gmial.com"
+ },
+ {
+ "id": 4,
+ "name": "soso",
+ "email": "soso@gmail.com"
+ },
+ {
+ "id": 5,
+ "name": "few",
+ "email": "jfpwrej"
+ },
+ {
+ "id": 6,
+ "name": "sos",
+ "email": "fdwf"
+ },
+ {
+ "id": 7,
+ "name": "sos",
+ "email": "fdwf"
+ },
+ {
+ "id": 8,
+ "name": "sos",
+ "email": "fdwf"
+ }
+ ],
+ "test2":[
+
+
+ {
+ "id":1,
+ "email":"admin@adamin.com"
+ }
+ ,
+ {
+ "id":1,
+ "email":"admin@adamin.com"
+ }
+ ,
+ {
+ "id":1,
+ "email":"admin@adamin.com"
+ }
+
+ , {
+ "id":1,
+ "email":"admin@adamin.com"
+ }
+
+ ],
+ "users": [
+ {
+ "id": 1,
+ "email": "admin@adamin.com",
+ "password": "password",
+ "token": "token"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6084c26
--- /dev/null
+++ b/package.json
@@ -0,0 +1,104 @@
+{
+ "name": "my-app",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@ant-design/icons": "^4.8.3",
+ "@emotion/styled": "^11.11.0",
+ "@mui/icons-material": "^5.14.19",
+ "@mui/x-charts": "^6.19.4",
+ "@react-google-maps/api": "^2.19.2",
+ "@szhsin/react-menu": "github:szhsin/react-menu",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "@tinymce/tinymce-react": "^4.3.2",
+ "@types/jest": "^27.5.2",
+ "@types/node": "^16.18.60",
+ "@types/react": "^18.2.33",
+ "@types/react-dom": "^18.2.14",
+ "@types/socket.io-client": "^3.0.0",
+ "@wojtekmaj/react-daterange-picker": "^5.4.4",
+ "antd": "^5.12.1",
+ "apexcharts": "^3.44.2",
+ "axios": "^1.6.0",
+ "bootstrap": "^5.3.2",
+ "chart.js": "^4.4.0",
+ "dayjs": "^1.11.10",
+ "formik": "^2.4.5",
+ "history": "^5.3.0",
+ "i18next": "^23.6.0",
+ "i18next-browser-languagedetector": "^7.1.0",
+ "json-server": "^0.17.4",
+ "moment": "^2.30.1",
+ "prop-types": "^15.8.1",
+ "react": "^18.2.0",
+ "react-apexcharts": "^1.4.1",
+ "react-bootstrap": "^2.9.1",
+ "react-bootstrap-sweetalert": "^5.2.0",
+ "react-chartjs-2": "^5.2.0",
+ "react-confirm-alert": "^3.0.6",
+ "react-data-table-component": "^7.5.4",
+ "react-dom": "^18.2.0",
+ "react-feather": "^2.0.10",
+ "react-i18next": "^13.3.1",
+ "react-icons": "^4.11.0",
+ "react-input-range": "^1.3.0",
+ "react-number-format": "^5.3.4",
+ "react-query": "^3.39.3",
+ "react-redux": "^8.1.3",
+ "react-router-dom": "^6.18.0",
+ "react-scripts": "5.0.1",
+ "react-select": "^5.7.7",
+ "react-simple-star-rating": "^5.1.7",
+ "react-switch": "^7.0.0",
+ "react-tabs": "^6.0.2",
+ "react-toastify": "^9.1.3",
+ "react-toggle": "^4.1.3",
+ "reactstrap": "^9.2.0",
+ "redux": "^4.2.1",
+ "sass": "^1.69.5",
+ "socket.io-client": "^4.7.2",
+ "styled-components": "5.3.3",
+ "ts-xlsx": "^0.0.11",
+ "typescript": "^4.9.5",
+ "web-vitals": "^2.1.4",
+ "xlsx": "^0.18.5",
+ "yup": "^1.3.2",
+ "zustand": "^4.4.5"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject",
+ "g:api": "node src/Extensions/FileGenerator/generateApi.js",
+ "g:column": "node src/Extensions/FileGenerator/generateColumn.js",
+ "g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js",
+ "g:page": "node src/Extensions/FileGenerator/generatePage.js",
+ "g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js ",
+ "g:modal:add": "node src/Extensions/FileGenerator/generateEditModal.js ",
+ "g:model:edit": "node src/Extensions/FileGenerator/generateEditModal.js "
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "devDependencies": {
+ "@types/react-toggle": "^4.0.5"
+ }
+}
diff --git a/public/Layout/LoginBg.jpg b/public/Layout/LoginBg.jpg
new file mode 100644
index 0000000..fc3a090
Binary files /dev/null and b/public/Layout/LoginBg.jpg differ
diff --git a/public/Logo.png b/public/Logo.png
new file mode 100644
index 0000000..6d64fe9
Binary files /dev/null and b/public/Logo.png differ
diff --git a/public/Logo.svg b/public/Logo.svg
new file mode 100644
index 0000000..b4fa18d
--- /dev/null
+++ b/public/Logo.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/Logo_2.png b/public/Logo_2.png
new file mode 100644
index 0000000..bdd8276
Binary files /dev/null and b/public/Logo_2.png differ
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..b5667fe
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ Hijab - App
+
+
+
+
+
+
diff --git a/public/language/ar.svg b/public/language/ar.svg
new file mode 100644
index 0000000..c409129
--- /dev/null
+++ b/public/language/ar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/language/china.svg b/public/language/china.svg
new file mode 100644
index 0000000..f109281
--- /dev/null
+++ b/public/language/china.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/language/de.svg b/public/language/de.svg
new file mode 100644
index 0000000..543a1bc
--- /dev/null
+++ b/public/language/de.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/language/en.svg b/public/language/en.svg
new file mode 100644
index 0000000..b1db6ff
--- /dev/null
+++ b/public/language/en.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/manifest.json b/public/manifest.json
new file mode 100644
index 0000000..080d6c7
--- /dev/null
+++ b/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000..722fd7d
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,69 @@
+import { Fragment, lazy, Suspense } from 'react';
+import { Route, Routes } from 'react-router-dom'
+import Loading from './Components/Utils/Loading/Loading';
+import { RoutesLinks } from './Routes';
+import Layout from './Layout/app/Layout';
+import Auth from './Pages/Auth/Page';
+const Page404 = lazy(() => import("./Layout/app/NotFoundPage"))
+
+const App = () => {
+
+
+ return (
+
+ {/* 404 Page */}
+ }> } />
+ {/* Login Page */}
+ }> } />
+
+ {/* route not in navigation */}
+
+
+ {/* All Routes */}
+ {RoutesLinks?.map((item: any, index: number) => (
+
+
+
+
+ if(item?.element){
+ } >
+
+ {item?.element ?? "Please Add Element Props in Routes"}
+
+
+ }
+ />
+ }else{
+ item?.children?.map((item:any)=>{
+ return(
+ } >
+
+ {item?.element ?? "Please Add Element Props in Routes"}
+
+
+ }
+ />
+ )
+ })
+
+ }
+
+
+
+ ))
+ }
+
+
+
+
+ )
+}
+
+export default App
\ No newline at end of file
diff --git a/src/Components/Columns/ColumnsImage.tsx b/src/Components/Columns/ColumnsImage.tsx
new file mode 100644
index 0000000..11f5486
--- /dev/null
+++ b/src/Components/Columns/ColumnsImage.tsx
@@ -0,0 +1,86 @@
+import {
+ DownloadOutlined,
+ RotateLeftOutlined,
+ RotateRightOutlined,
+ SwapOutlined,
+ ZoomInOutlined,
+ ZoomOutOutlined,
+} from '@ant-design/icons';
+import React from 'react';
+import { Image, Space } from 'antd';
+import { ImageBaseURL } from '../../api/config';
+import useImageError from '../../Hooks/useImageError';
+
+
+const ColumnsImage= ({src}:any) => {
+ const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
+
+ const imageUrl = ImageBaseURL + src || ErrorImage;
+ console.log(imageUrl);
+
+ const handleError = useImageError;
+ // or you can download flipped and rotated image
+ // https://codesandbox.i`o/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
+ const onDownload = () => {
+ fetch(src)
+ .then((response) => response.blob())
+ .then((blob) => {
+ const url = URL.createObjectURL(new Blob([blob]));
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = 'image.png';
+ document.body.appendChild(link);
+ link.click();
+ URL.revokeObjectURL(url);
+ link.remove();
+ });
+ };
+
+ return (
+ (
+
+
+
+
+
+
+
+
+
+ ),
+ }}
+ onError={handleError}
+
+ />
+ );
+};
+
+export default ColumnsImage;
+
+
+
+
+
+
+
+// {
+// name: t("image"),
+// center: "true",
+// cell: (row: any) => {
+// return (
+//
+// )
+// }
+// },
\ No newline at end of file
diff --git a/src/Components/Columns/ColumnsSwitch.tsx b/src/Components/Columns/ColumnsSwitch.tsx
new file mode 100644
index 0000000..1b155b4
--- /dev/null
+++ b/src/Components/Columns/ColumnsSwitch.tsx
@@ -0,0 +1,42 @@
+import { Switch } from 'antd'
+import React from 'react'
+import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
+import { useFormikContext } from 'formik';
+export interface ColumnsSwitchProps {
+ name: string;
+ Front?: string;
+ Back?: string;
+ onChange?: (checked:any,event:any) => any;
+ icon?: boolean
+ Checked?:boolean
+
+}
+const ColumnsSwitch = (props: ColumnsSwitchProps) => {
+ const { name, Front, Back, icon, onChange } = props;
+ const formik = useFormikContext();
+ const onSwitchChange = (checked: boolean, event: Event) => {
+ // formik.setFieldValue("status", checked)
+
+
+
+ };
+ return (
+ : Front}
+ unCheckedChildren={icon ? : Back}
+ onChange={ (checked:any,event:any)=> onChange ? onChange(checked,event) : onSwitchChange(checked,event)}
+ />
+ )
+}
+
+export default ColumnsSwitch
+
+
+ColumnsSwitch.defaultProps = {
+ Front: "Front",
+ Back: "Back",
+ onChange: undefined,
+ icon: false,
+ Checked:false
+
+};
\ No newline at end of file
diff --git a/src/Components/Ui/Alert.tsx b/src/Components/Ui/Alert.tsx
new file mode 100644
index 0000000..c232160
--- /dev/null
+++ b/src/Components/Ui/Alert.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+import { confirmAlert } from "react-confirm-alert";
+import SweetAlert from "react-bootstrap-sweetalert";
+import { useTranslation } from "react-i18next";
+
+export default function CustomConfirmAlert(options : any) {
+ confirmAlert({
+ customUI: ({ onClose }) => ,
+ });
+}
+
+type CustomUIProps ={
+ onClose :()=> void
+ options:{
+ title?:string
+ confirmBtnText:string
+ cancelBtnText:string
+ body:string
+ onConfirm:()=>void
+
+
+ }
+}
+function CustomUI({ onClose, options }:CustomUIProps) {
+
+
+ const [t] = useTranslation()
+ return (
+
+ {
+ options.onConfirm();
+ onClose();
+ }}
+ onCancel={onClose}
+
+ />
+
+
+ );
+}
diff --git a/src/Components/Ui/CheckboxesVuexy.tsx b/src/Components/Ui/CheckboxesVuexy.tsx
new file mode 100644
index 0000000..5bc635e
--- /dev/null
+++ b/src/Components/Ui/CheckboxesVuexy.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+
+interface CheckBoxesVuexyProps {
+ className?: string;
+ color: string;
+ defaultChecked?: boolean;
+ checked?: boolean;
+ value?: string;
+ disabled?: boolean;
+ onClick?: () => void;
+ onChange?: () => void;
+ size?: string;
+ icon: React.ReactNode;
+ label: string;
+}
+
+const CheckBoxesVuexy: React.FC = ({
+ className = '',
+ color,
+ defaultChecked,
+ checked,
+ value,
+ disabled,
+ onClick,
+ onChange,
+ size = 'md',
+ icon,
+ label,
+}) => {
+ return (
+
+
+
+ {icon}
+
+ {label}
+
+ );
+};
+
+export default CheckBoxesVuexy;
diff --git a/src/Components/Ui/FileInput.tsx b/src/Components/Ui/FileInput.tsx
new file mode 100644
index 0000000..94c5df0
--- /dev/null
+++ b/src/Components/Ui/FileInput.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import 'bootstrap/dist/css/bootstrap.min.css';
+import { Input } from 'reactstrap';
+import { useFormikContext } from 'formik';
+
+type FileInputProps = {
+ name:string,
+ label:string,
+ accept:string,
+ onChange:any
+}
+function FileInput({name , accept="image/*" ,label , onChange} :FileInputProps) {
+
+
+ return (
+
+
+
+
+
+ )
+}
+
+export default FileInput
\ No newline at end of file
diff --git a/src/Components/Ui/HovarableImage.tsx b/src/Components/Ui/HovarableImage.tsx
new file mode 100644
index 0000000..c56b31c
--- /dev/null
+++ b/src/Components/Ui/HovarableImage.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+import { Tooltip } from "reactstrap";
+
+const tooltipStyle = {
+ backgroundColor: "white",
+ border: "2px solid lightgrey",
+ maxWidth: "400px",
+};
+
+const HovarableImage = ({ id, src, imgPreviewProps = {}, ...props }:any) => {
+ const [isOpen, setIsOpen] = React.useState(false);
+ const ID = `image_hover_tooltip_${id}`;
+ const toggleTooltip = React.useCallback(() => setIsOpen((prev) => !prev), []);
+
+ return (
+
+

+
+
+
+
+ );
+};
+
+export default HovarableImage;
+
diff --git a/src/Components/Ui/ImagePreview.tsx b/src/Components/Ui/ImagePreview.tsx
new file mode 100644
index 0000000..04e2177
--- /dev/null
+++ b/src/Components/Ui/ImagePreview.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+
+const ImagePreview = ({ preview, height = 200 }:any) => {
+ const [t] = useTranslation();
+
+ return (
+
+ {preview ? (
+

+ ) : (
+
{t("image_preview")}
+ )}
+
+ );
+};
+
+export default ImagePreview;
diff --git a/src/Components/Ui/LoadingButton.tsx b/src/Components/Ui/LoadingButton.tsx
new file mode 100644
index 0000000..248c486
--- /dev/null
+++ b/src/Components/Ui/LoadingButton.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { Button, Spinner } from "reactstrap";
+
+const LoadingButton = ({ isLoading = false, ...props }) => {
+ return (
+
+ );
+};
+
+export { LoadingButton };
diff --git a/src/Components/Ui/LoadingSpinner.tsx b/src/Components/Ui/LoadingSpinner.tsx
new file mode 100644
index 0000000..efa5055
--- /dev/null
+++ b/src/Components/Ui/LoadingSpinner.tsx
@@ -0,0 +1,10 @@
+import { Spin } from 'antd'
+import React from 'react'
+
+function LoadingSpinner() {
+ return (
+
+ )
+}
+
+export default LoadingSpinner
\ No newline at end of file
diff --git a/src/Components/Ui/PasswordField/PasswordField.tsx b/src/Components/Ui/PasswordField/PasswordField.tsx
new file mode 100644
index 0000000..32489d7
--- /dev/null
+++ b/src/Components/Ui/PasswordField/PasswordField.tsx
@@ -0,0 +1,57 @@
+import React, { FC, useState } from "react";
+import { ErrorMessage, useField, Field } from "formik";
+import { FormGroup } from "reactstrap";
+// import PropTypes from "prop-types";
+import { Eye, EyeOff } from "react-feather";
+import "./index.css";
+
+interface PasswordFieldProps {
+ name: string;
+ label?: string;
+}
+
+const PasswordField: FC = ({ name, label, ...props }) => {
+ const [field, meta] = useField({ name, ...props });
+ const [showPassword, setShowPassword] = useState(false);
+ const EyeIcon = showPassword ? Eye : EyeOff;
+
+ const toggleShow = () => {
+ setShowPassword((prev) => !prev);
+ };
+
+ return (
+ <>
+ {label && }
+
+
+
+
+
+
+
+ >
+ );
+};
+
+// PasswordField.propTypes = {
+// name: PropTypes.string.isRequired,
+// };
+
+export { PasswordField };
\ No newline at end of file
diff --git a/src/Components/Ui/PasswordField/index.css b/src/Components/Ui/PasswordField/index.css
new file mode 100644
index 0000000..7a26d93
--- /dev/null
+++ b/src/Components/Ui/PasswordField/index.css
@@ -0,0 +1,10 @@
+.back-icon {
+ font-size: 18px;
+ margin: 0;
+ margin-right: 5px;
+}
+
+.back-btn {
+ padding-left: 0.8rem;
+ padding-right: 1rem;
+}
diff --git a/src/Components/Ui/ProgressBar.tsx b/src/Components/Ui/ProgressBar.tsx
new file mode 100644
index 0000000..146339f
--- /dev/null
+++ b/src/Components/Ui/ProgressBar.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { Progress } from "reactstrap";
+
+const ProgressBar = ({ value,isLoading, isSuccess, isError }:any) => {
+
+
+ let color = "";
+ if (!isLoading && isSuccess) {
+ color = "success";
+ }
+ if (!isLoading && isError) {
+ color = "danger";
+ }
+ return (
+
+ );
+};
+
+
+export default ProgressBar;
diff --git a/src/Components/Ui/SelectField.tsx b/src/Components/Ui/SelectField.tsx
new file mode 100644
index 0000000..f794e31
--- /dev/null
+++ b/src/Components/Ui/SelectField.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import Select from "react-select";
+import { ValidatedField } from "./ValidatedField";
+import { useFormikContext } from "formik";
+
+const SelectField = ({ label, name, options, ...props }:any) => {
+ const formik = useFormikContext();
+
+ return (
+ opt.value === formik?.values[name]) || ""}
+ onChange={(opt:any) => formik.setFieldValue(name, opt.value)}
+ onBlur={() => formik.setFieldTouched(name)}
+ key={name}
+ {...props}
+ />
+ );
+};
+
+
+export default SelectField;
+
+
diff --git a/src/Components/Ui/StaticsCard/StaticCard.tsx b/src/Components/Ui/StaticsCard/StaticCard.tsx
new file mode 100644
index 0000000..bd5fddc
--- /dev/null
+++ b/src/Components/Ui/StaticsCard/StaticCard.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+import { Card, CardBody } from "reactstrap";
+import { ChartTypeEnum } from "../../../enums/ChartTypeEnum";
+import { history } from "../../../ProviderContainer";
+import { useNavigate } from "react-router-dom";
+
+interface StatisticsCardProps {
+ className?: string;
+ iconLeft?: boolean;
+ icon: React.ReactNode;
+ count?: string;
+ CardTitle?: string;
+ CardContent?: string;
+ height?: number;
+ pathWhenClick :string ;
+
+}
+
+const StatisticsCard = (props :StatisticsCardProps) => {
+
+ const navigate = useNavigate()
+ const {
+ className,
+ iconLeft = false ,
+ icon,
+ count,
+ CardTitle,
+ CardContent,
+ pathWhenClick,
+ height,
+ ...rest
+ } = props;
+
+ return (
+ navigate(pathWhenClick )}>
+
+
+
+ {/*
{CardTitle}
*/}
+
{icon}
+
+
+
+ {/*
{count}
*/}
+
{CardContent}
+
+
+
+ );
+};
+
+export default StatisticsCard;
diff --git a/src/Components/Ui/StatusBadge.tsx b/src/Components/Ui/StatusBadge.tsx
new file mode 100644
index 0000000..1fb55e6
--- /dev/null
+++ b/src/Components/Ui/StatusBadge.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { Badge } from "reactstrap";
+
+const StatusBadge = ({ status }:any) => {
+ const [t] = useTranslation();
+
+ return (
+
+ {status ? t("active") : t("inacticve")}
+
+ );
+};
+
+
+
+export default StatusBadge;
diff --git a/src/Components/Ui/TableActions.tsx b/src/Components/Ui/TableActions.tsx
new file mode 100644
index 0000000..73c9563
--- /dev/null
+++ b/src/Components/Ui/TableActions.tsx
@@ -0,0 +1,38 @@
+import React , {ReactNode} from "react";
+import confirmAlert from "./Alert";
+import { FaEdit, FaTrash } from "react-icons/fa";
+
+type TableActionsProps = {
+ onDelete: () => any;
+ onEdit: () => void;
+ showEdit?: boolean;
+ showDelete?: boolean;
+ children?: ReactNode;
+};
+
+
+const TableActions = ({ onDelete , onEdit,showEdit=true,showDelete=true, children }:TableActionsProps) => {
+
+
+ return (
+
+ {showEdit && }
+ {showDelete && (
+
+ confirmAlert({
+ onConfirm: () => {
+ onDelete();
+
+ },
+ })
+ }
+ className="cursor-pointer"
+ size={20}
+ />
+ )}
+ {children}
+
+);
+};
+export default TableActions;
diff --git a/src/Components/Ui/ThreeSwitchState/TripleSwitch.tsx b/src/Components/Ui/ThreeSwitchState/TripleSwitch.tsx
new file mode 100644
index 0000000..c36ffbd
--- /dev/null
+++ b/src/Components/Ui/ThreeSwitchState/TripleSwitch.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import type { RadioChangeEvent } from 'antd';
+import { Radio } from 'antd';
+
+const onChange = (e: RadioChangeEvent) => {
+ console.log(`radio checked:${e.target.value}`);
+};
+
+const App: React.FC = () => (
+ <>
+
+
+
+ Hangzhou
+
+ Shanghai
+
+ Beijing
+ Chengdu
+
+
+
+ Hangzhou
+ Shanghai
+ Beijing
+ Chengdu
+
+ >
+);
+
+export default App;
diff --git a/src/Components/Ui/ToggleStatus.tsx b/src/Components/Ui/ToggleStatus.tsx
new file mode 100644
index 0000000..40df144
--- /dev/null
+++ b/src/Components/Ui/ToggleStatus.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+import Toggle from "react-toggle";
+import "react-toggle/style.css";
+import StatusBadge from "./StatusBadge";
+import { useTranslation } from "react-i18next";
+
+interface ToggleStatusProps {
+
+}
+export const ToggleStatus = ({ object, handleSwitch, toggleMutation, ...props }:any) => {
+ const [t] = useTranslation();
+
+ // const handleSwitch = () => {
+ // toggleMutation.mutate({
+ // id: object.id,
+ // new_status: !object.is_active,
+ // });
+ // };
+
+ return (
+ <>
+
+
+ {object.is_active ? t("active") : t("inactive")}
+
+
+
+ >
+ );
+};
diff --git a/src/Components/Ui/ValidatedField.tsx b/src/Components/Ui/ValidatedField.tsx
new file mode 100644
index 0000000..c2bd089
--- /dev/null
+++ b/src/Components/Ui/ValidatedField.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import { ErrorMessage, useField, Field } from "formik";
+import { FormGroup } from "reactstrap";
+import { useTranslation } from "react-i18next";
+
+const ValidatedField = ({
+ name,
+ label,
+ CustomField,
+ icon: Icon,
+ optional,
+ labelIcon = null,
+ formProps,
+ isRequired,
+
+ ...props
+}:any) => {
+
+ const [field, meta] = useField({ name, ...props });
+ const [t] = useTranslation();
+
+ let Wrapper = Field;
+
+ if (CustomField) {
+ Wrapper = CustomField;
+ }
+ const fieldProps = props.type === "file" ? {} : { ...field };
+
+ return (
+ <>
+ {label && (
+
+ )}
+
+
+ {Icon && (
+
+
+
+ )}
+
+ {(msg) => {t(msg)}}
+
+
+ >
+ );
+};
+
+
+export { ValidatedField };
diff --git a/src/Components/Ui/index.tsx b/src/Components/Ui/index.tsx
new file mode 100644
index 0000000..ae6a6f0
--- /dev/null
+++ b/src/Components/Ui/index.tsx
@@ -0,0 +1,17 @@
+import Checkbox from './CheckboxesVuexy'
+import ImagePreview from './ImagePreview'
+import SelectField from './SelectField'
+import { useImagePreview } from './useImagePreview'
+import {ValidatedField} from './ValidatedField'
+import StatusBadge from './StatusBadge'
+import HovarableImage from './HovarableImage'
+
+export {
+ Checkbox,
+ ImagePreview,
+ SelectField,
+ useImagePreview,
+ ValidatedField,
+ StatusBadge,
+ HovarableImage
+}
\ No newline at end of file
diff --git a/src/Components/Ui/tables/Actions.tsx b/src/Components/Ui/tables/Actions.tsx
new file mode 100644
index 0000000..f78e1ad
--- /dev/null
+++ b/src/Components/Ui/tables/Actions.tsx
@@ -0,0 +1,58 @@
+import React , {ReactNode} from "react";
+import { FaEdit, FaEye, FaTrash } from "react-icons/fa";
+import CustomConfirmAlert from "../Alert";
+import { usePageState } from "../../../lib/state mangment/LayoutPagestate";
+
+type TableActionsProps = {
+ onDelete?: () => any;
+ onEdit?: () => any;
+ onView?:() => any;
+ showView?: boolean;
+ showEdit?: boolean;
+ showDelete?: boolean;
+ children?: ReactNode;
+ objectToEdit:any
+ className?:string
+
+};
+
+
+const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children,className }:TableActionsProps) => {
+// const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children }:TableActionsProps) => {
+
+ // console.log(objectToEdit);
+
+ const {setObjectToEdit , setIsOpenEditModel} = usePageState()
+ return (
+
+ {showEdit && {
+ setObjectToEdit(objectToEdit)
+ setIsOpenEditModel()
+ onEdit()
+
+ }} className="cursor-pointer m-2" size={20} />}
+ {showView && }
+
+
+ {showDelete && (
+
+ CustomConfirmAlert({
+ onConfirm: () => {
+
+
+ onDelete();
+
+ },
+ })
+ }
+ className="cursor-pointer"
+ size={20}
+ />
+ )}
+
+ {children}
+
+);
+};
+export default TableActions;
diff --git a/src/Components/Ui/tables/ConfirmAlert.tsx b/src/Components/Ui/tables/ConfirmAlert.tsx
new file mode 100644
index 0000000..bb99f66
--- /dev/null
+++ b/src/Components/Ui/tables/ConfirmAlert.tsx
@@ -0,0 +1,40 @@
+import React from "react";
+import { confirmAlert } from "react-confirm-alert";
+import SweetAlert from "react-bootstrap-sweetalert";
+
+interface CustomUIProps {
+ onClose: () => void;
+ options: {
+ title?: string;
+ confirmBtnText?: string;
+ cancelBtnText?: string;
+ onConfirm: () => void;
+ body?: string;
+ };
+}
+
+export default function CustomConfirmAlert(options: any) {
+ confirmAlert({
+ customUI: ({ onClose }) => ,
+ });
+}
+
+function CustomUI({ onClose, options }: CustomUIProps) {
+ const sweetAlertProps: any = {
+ title: options.title || `DELETE, Are you sure?`,
+ warning: true,
+ show: true,
+ showCancel: true,
+ reverseButtons: true,
+ cancelBtnBsStyle: "danger",
+ confirmBtnText: options.confirmBtnText || "Yes, delete it!",
+ cancelBtnText: options.cancelBtnText || "Cancel",
+ onConfirm: () => {
+ options.onConfirm();
+ onClose();
+ },
+ onCancel: onClose,
+ };
+
+ return {options.body || "You won't be able to revert this!"};
+}
diff --git a/src/Components/Ui/useImagePreview.tsx b/src/Components/Ui/useImagePreview.tsx
new file mode 100644
index 0000000..818c68e
--- /dev/null
+++ b/src/Components/Ui/useImagePreview.tsx
@@ -0,0 +1,24 @@
+import { useState, useEffect } from "react";
+
+export const useImagePreview = (defaultValue:any = null) => {
+ const [preview, setPreview] = useState(defaultValue || null);
+
+ useEffect(() => {
+ return () => {
+ URL.revokeObjectURL(preview);
+
+ };
+ }, [preview]);
+
+ const handleImageChange = (event:any) => {
+
+ setPreview(URL.createObjectURL(event.target.files[0]));
+
+ };
+
+ return {
+ preview,
+ handleImageChange,
+ setPreview,
+ };
+};
diff --git a/src/Components/Utils/BlockModal.tsx b/src/Components/Utils/BlockModal.tsx
new file mode 100644
index 0000000..d6c47f0
--- /dev/null
+++ b/src/Components/Utils/BlockModal.tsx
@@ -0,0 +1,62 @@
+
+import React, { useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { BsExclamationCircle } from 'react-icons/bs';
+import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
+import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
+import { LoadingButton } from '../Ui/LoadingButton';
+
+interface BlockModelProps {
+ Mutation:any,
+ type :'customer' |'driver'
+}
+
+const BlockModel: React.FC = ({Mutation ,type}) => {
+ const {t} = useTranslation();
+ const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
+
+ const {isOpenBlock:isOpen , objectID , setIsopenBlock:setIsOpen} = useCommonModelState()
+
+ const handleSubmit = () => {
+ const blockInput = document.getElementById('block_input') as HTMLInputElement;
+ if (blockInput) {
+ Mutation.mutate({ [key_to_api]: objectID, block_timer: blockInput.value });
+ }
+ };
+
+ useEffect(() => {
+ if (Mutation.isSuccess) {
+ setIsOpen();
+ }
+ }, [Mutation.isSuccess, setIsOpen]);
+
+ return (
+
+ setIsOpen()}>
+ {t("al")}{type}{t('_block_page')}
+
+
+
+
+
{t('blocking_')}{type}
+
+
+
+
+
+
+
+ {t('add_block_for_')}{type}
+
+
+
+
+
+
+
+ );
+};
+
+export default BlockModel;
diff --git a/src/Components/Utils/GiftModal.tsx b/src/Components/Utils/GiftModal.tsx
new file mode 100644
index 0000000..bbd8d3e
--- /dev/null
+++ b/src/Components/Utils/GiftModal.tsx
@@ -0,0 +1,63 @@
+import React, { useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { BsExclamationCircle } from 'react-icons/bs';
+import { Button, Card, CardBody, Col, Input, Label, Modal, ModalHeader, Row } from 'reactstrap';
+import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
+import { LoadingButton } from '../Ui/LoadingButton';
+
+
+interface GiftModalProps {
+ Mutation:any,
+ type :'customer' |'driver'
+}
+
+const GiftModal: React.FC = ({Mutation ,type }) => {
+ const {t} = useTranslation();
+
+ const {isOpenGift:isOpen , objectID , setIsopenGift:setIsOpen} = useCommonModelState()
+
+ useEffect(() => {
+ if (Mutation.isSuccess) {
+ setIsOpen();
+ }
+ }, [Mutation.isSuccess, setIsOpen]);
+
+ const handleGift = () => {
+ const enterCodesInput = document.getElementById('enter_codes') as HTMLInputElement;
+ if (enterCodesInput && enterCodesInput.value) {
+ Mutation.mutate({ type, id: objectID, value: enterCodesInput.value });
+ }
+ };
+
+ return (
+
+ setIsOpen()}>
+ {t("al")}{type} {t('_gift_page')}
+
+
+
+
+
+
+
+
+
+
+ {t('give')}
+
+
+
+
+
+
+
+ );
+};
+
+export default GiftModal;
diff --git a/src/Components/Utils/Loading/Loading.scss b/src/Components/Utils/Loading/Loading.scss
new file mode 100644
index 0000000..9ffb658
--- /dev/null
+++ b/src/Components/Utils/Loading/Loading.scss
@@ -0,0 +1,93 @@
+.Loading{
+
+ .wrapper {
+ width: 200px;
+ height: 60px;
+ position: relative;
+ left: 40%;
+ z-index: 1;
+ }
+
+ .circle {
+ width: 20px;
+ height: 20px;
+ position: absolute;
+ border-radius: 50%;
+ background-color: var(--primary);
+ left: 15%;
+ transform-origin: 50%;
+ animation: circle7124 .5s alternate infinite ease;
+ }
+
+ @keyframes circle7124 {
+ 0% {
+ top: 60px;
+ height: 5px;
+ border-radius: 50px 50px 25px 25px;
+ transform: scaleX(1.7);
+ }
+
+ 40% {
+ height: 20px;
+ border-radius: 50%;
+ transform: scaleX(1);
+ }
+
+ 100% {
+ top: 0%;
+ }
+ }
+
+ .circle:nth-child(2) {
+ left: 45%;
+ animation-delay: .2s;
+ }
+
+ .circle:nth-child(3) {
+ left: auto;
+ right: 15%;
+ animation-delay: .3s;
+ }
+
+ .shadow {
+ width: 20px;
+ height: 4px;
+ border-radius: 50%;
+ background-color: rgba(0,0,0,0.9);
+ position: absolute;
+ top: 62px;
+ transform-origin: 50%;
+ z-index: -1;
+ left: 15%;
+ filter: blur(1px);
+ animation: shadow046 .5s alternate infinite ease;
+ }
+
+ @keyframes shadow046 {
+ 0% {
+ transform: scaleX(1.5);
+ }
+
+ 40% {
+ transform: scaleX(1);
+ opacity: .7;
+ }
+
+ 100% {
+ transform: scaleX(.2);
+ opacity: .4;
+ }
+ }
+
+ .shadow:nth-child(4) {
+ left: 45%;
+ animation-delay: .2s
+ }
+
+ .shadow:nth-child(5) {
+ left: auto;
+ right: 15%;
+ animation-delay: .3s;
+ }
+
+}
diff --git a/src/Components/Utils/Loading/Loading.tsx b/src/Components/Utils/Loading/Loading.tsx
new file mode 100644
index 0000000..19651b4
--- /dev/null
+++ b/src/Components/Utils/Loading/Loading.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import './Loading.scss'
+const Loading = () => {
+
+ return (
+
+ )
+}
+
+export default Loading
\ No newline at end of file
diff --git a/src/Components/Utils/SearchBar/SearchBar.scss b/src/Components/Utils/SearchBar/SearchBar.scss
new file mode 100644
index 0000000..6834dcf
--- /dev/null
+++ b/src/Components/Utils/SearchBar/SearchBar.scss
@@ -0,0 +1,43 @@
+.SearchBar{
+ .group {
+ display: flex;
+ align-items: center;
+ position: relative;
+ max-width: 190px;
+ }
+
+ .input {
+ width: 100%;
+ height: 40px;
+ padding: 0 1rem;
+ padding-left: 2.5rem;
+ border-radius: 8px;
+ outline: none;
+ font-weight: 500;
+ background: var(--primary);
+ color: var(--bg);
+ border: none;
+ box-shadow: 2px 2px 7px 0 var(--primary);
+
+ }
+
+ .input::placeholder {
+ color: var(--bg);
+ }
+
+
+
+ .icon {
+ position: absolute;
+ left: 1rem;
+ fill: var(--bg);
+ width: 1rem;
+ height: 1rem;
+ }
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/Components/Utils/SearchBar/SearchBar.tsx b/src/Components/Utils/SearchBar/SearchBar.tsx
new file mode 100644
index 0000000..f1f5cd2
--- /dev/null
+++ b/src/Components/Utils/SearchBar/SearchBar.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import './SearchBar.scss'
+const SearchBar = () => {
+ return (
+
+ )
+}
+
+export default SearchBar
\ No newline at end of file
diff --git a/src/Components/Utils/Theme.tsx b/src/Components/Utils/Theme.tsx
new file mode 100644
index 0000000..7681ffd
--- /dev/null
+++ b/src/Components/Utils/Theme.tsx
@@ -0,0 +1,84 @@
+import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
+import { useTranslation } from 'react-i18next';
+import { usePageState } from '../../lib/state mangment/LayoutPagestate';
+import { BsFillMoonStarsFill, BsFillSunFill, BsSunglasses } from 'react-icons/bs';
+
+
+let What_the_Theme = localStorage.getItem('theme') ?? "light";
+
+if (What_the_Theme === "dark") {
+
+ document.body.classList.add('dark')}
+ else if (What_the_Theme === "glass") {
+
+ document.body.classList.add('glass')
+ }
+
+
+
+
+export default function Theme() {
+ const {t} = useTranslation();
+
+ const {setThemChange} = usePageState()
+
+ const changeTheme = (newTheme : any) => {
+
+
+ if(newTheme === "dark"){
+ document.body.classList.remove('glass');
+ document.body.classList.add('dark');localStorage.setItem("theme", "dark");
+ What_the_Theme = "dark"
+ }
+ else if(newTheme === "light"){
+ document.body.classList.remove('glass');
+ document.body.classList.remove('dark');localStorage.setItem("theme", "light");
+ What_the_Theme = "light"
+
+ }
+ else if(newTheme === "glass"){
+ document.body.classList.remove('dark'); document.body.classList.add('glass'); localStorage.setItem("theme", "glass");
+ What_the_Theme = "glass"
+
+ }
+ setThemChange()
+ };
+ /// BsSunglasses BsFillSunFill BsFillMoonStarsFill
+
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/Components/Utils/Translate.tsx b/src/Components/Utils/Translate.tsx
new file mode 100644
index 0000000..a5aa816
--- /dev/null
+++ b/src/Components/Utils/Translate.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { Menu, Dropdown } from 'antd';
+import { useLanguage, useLanguageMenu } from '../../Hooks/useChangeLanguage';
+import i18next from 'i18next';
+import type { MenuProps } from 'antd';
+
+export default function Translate() {
+ const { changeLanguage } = useLanguage();
+ const { languageOptions } = useLanguageMenu();
+
+ const handleLanguageChange = (newLanguage:string) => {
+ changeLanguage(newLanguage);
+ };
+
+ const items : MenuProps['items'] = languageOptions.map((option:any,index:number) => ({
+ key: index,
+ label: (
+ handleLanguageChange(option.code)}>
+

+ {option.label}
+
+ )
+ }));
+
+
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/Components/Utils/UnBlockModal.tsx b/src/Components/Utils/UnBlockModal.tsx
new file mode 100644
index 0000000..cf3fac1
--- /dev/null
+++ b/src/Components/Utils/UnBlockModal.tsx
@@ -0,0 +1,60 @@
+
+import React, { useEffect } from 'react';
+import { BsExclamationCircle } from 'react-icons/bs';
+import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
+import { useTranslation } from 'react-i18next';
+import { LoadingButton } from '../Ui/LoadingButton';
+import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
+import { CiLock } from "react-icons/ci";
+
+interface UnBlockModalProps {
+
+ Mutation:any,
+ type :'customer' |'driver'
+}
+
+const UnBlockModal: React.FC = ({Mutation ,type }) => {
+ const {t} = useTranslation();
+
+ const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
+
+ const {isOpenUnBlock:isOpen , objectID , setIsopenUnBlock:setIsopen} = useCommonModelState()
+
+ const handleSubmit = () => {
+ Mutation.mutate({ [key_to_api]: objectID });
+ };
+
+ useEffect(() => {
+ if (Mutation.isSuccess) {
+ setIsopen();
+ }
+ }, [Mutation.isSuccess, setIsopen]);
+
+ return (
+
+ setIsopen()}>
+ {t("al")}{type} {t('un_block_page')}
+
+
+
+
+
{t('un_blocking_')}{type}
+
+
+
+
+
+ {t('un_block_for_')}{type}
+
+
+
+
+
+
+
+ );
+};
+
+export default UnBlockModal;
diff --git a/src/Components/ValidationField/Ui/KarimSpinner.tsx b/src/Components/ValidationField/Ui/KarimSpinner.tsx
new file mode 100644
index 0000000..12206f9
--- /dev/null
+++ b/src/Components/ValidationField/Ui/KarimSpinner.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import { Spin } from 'antd';
+
+interface Props {
+ loading: boolean;
+ children: React.ReactNode;
+ className?: string;
+}
+
+const KarimSpinner: React.FC = ({ loading, className, children }) => {
+ return (
+
+ {loading ?
: children}
+
+ );
+};
+
+export default KarimSpinner;
diff --git a/src/Components/ValidationField/Ui/SearchBar.scss b/src/Components/ValidationField/Ui/SearchBar.scss
new file mode 100644
index 0000000..2ddadd9
--- /dev/null
+++ b/src/Components/ValidationField/Ui/SearchBar.scss
@@ -0,0 +1,48 @@
+.SearchBar{
+ // margin-top: 20px;
+ .group {
+ display: flex;
+ align-items: center;
+ position: relative;
+ max-width: 350px;
+ width: 350px;
+
+ }
+
+ .input {
+ width: 100%;
+ height: 40px;
+ padding: 0 1rem;
+ padding-left: 2.5rem;
+ border-radius: 8px;
+ outline: none;
+ font-weight: 500;
+ background: var(--bg);
+ color: var(--text);
+ border: none;
+ box-shadow: 2px 2px 7px 0 var(--bg);
+
+ }
+
+ .input::placeholder {
+ color: var(--subtext);
+ opacity: .4;
+
+ }
+
+
+
+ .icon {
+ position: absolute;
+ left: 1rem;
+ fill: var(--subtext);
+ width: 1rem;
+ height: 1rem;
+ }
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/Components/ValidationField/Ui/SearchBar.tsx b/src/Components/ValidationField/Ui/SearchBar.tsx
new file mode 100644
index 0000000..f70b289
--- /dev/null
+++ b/src/Components/ValidationField/Ui/SearchBar.tsx
@@ -0,0 +1,35 @@
+import React, { useState } from 'react'
+import './SearchBar.scss'
+import { useNavigate, useSearchParams } from 'react-router-dom';
+const SearchBar = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [searchParams] = useSearchParams()
+ const navigate = useNavigate();
+
+ const handleChange = (event:any) => {
+ const { value } = event.target;
+ setSearchQuery(value);
+ updateUrlParams(value);
+ };
+
+ const updateUrlParams = (value:any) => {
+ navigate(`?search=${value}`);
+
+
+ };
+
+ return (
+
+ )
+}
+
+export default SearchBar
\ No newline at end of file
diff --git a/src/Components/ValidationField/ValidationField.scss b/src/Components/ValidationField/ValidationField.scss
new file mode 100644
index 0000000..97e9f5e
--- /dev/null
+++ b/src/Components/ValidationField/ValidationField.scss
@@ -0,0 +1,78 @@
+.ValidationField{
+ >*{
+ width: 100%;
+ }
+ .text,.ant-form-item{
+ margin-bottom:7px !important;
+
+ }
+
+ >span{
+ margin-bottom: 0px !important;
+ &:focus-within{
+ border-color: var(--primary) ;
+ box-shadow: 0 0 0 1px var(--primary);
+ cursor: pointer;
+ }
+ &:has(.is-invalid){
+ border-color: red !important;
+
+ }
+ input{
+ color: var(--text);
+ background: var(--bg);
+ }
+
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active{
+ -webkit-box-shadow: 0 0 0 30px white inset !important;
+}
+ }
+}
+
+.ant-upload-select{
+ width: 100%;
+}
+.Checkboxs{
+ padding: 4%;
+}
+.SearchField{
+ button{
+ background: var(--primary);
+ }
+}
+.text{
+ color: var(--text);
+}
+
+input:disabled{
+ color: var(--text) !important;
+}
+
+.isError{
+ outline: red 1px solid;
+ color: red;
+}
+.Error_color{
+ color: red;
+
+}
+input:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
+}
+input:-webkit-autofill:focus {
+ -webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
+}
+
+/* Remove autofill background color on hover */
+input:-webkit-autofill:hover {
+ -webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
+}
+
+
+.ant-upload-list .ant-upload-list-item{
+ height:78px !important;
+ }
+
\ No newline at end of file
diff --git a/src/Components/ValidationField/ValidationField.tsx b/src/Components/ValidationField/ValidationField.tsx
new file mode 100644
index 0000000..6e25672
--- /dev/null
+++ b/src/Components/ValidationField/ValidationField.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import "./ValidationField.scss";
+import { Date, Time, File, DataRange, SelectField, Default, CheckboxField ,TextAreaField} from './View';
+import { ValidationFieldProps } from "./types";
+import MaltyFile from "./View/MaltyFile";
+import SearchField from "./View/SearchField";
+
+const ValidationField: React.FC = ({type , ...otherProps}) => {
+
+ switch (type) {
+ case 'Select':
+ return ;
+ case 'Search':
+ return ;
+ case "DataRange":
+ return ;
+ case "Date":
+ return ;
+ case "Time":
+ return ;
+ case "File":
+ return ;
+ case "MaltyFile":
+ return ;
+ case "Checkbox":
+ return ;
+ case "TextArea":
+ return ;
+ default:
+ return ;
+ }
+};
+
+export default React.memo(ValidationField);
diff --git a/src/Components/ValidationField/View/CheckboxField.tsx b/src/Components/ValidationField/View/CheckboxField.tsx
new file mode 100644
index 0000000..21c419e
--- /dev/null
+++ b/src/Components/ValidationField/View/CheckboxField.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+import { Checkbox, Form } from 'antd';
+
+const CheckboxField = ({ name, label, isDisabled, onChange,Group,className, props }: any) => {
+
+ const { t, formik,isError,errorMsg} = useFormField(name, props)
+ const CheckboxhandleChange = (value: any) => {
+ formik.setFieldValue(name, value?.target?.checked)
+
+ };
+ return (
+
+
+
+ {t(`${label ? label : name}`)}
+
+
+
+
+ )
+}
+
+export default CheckboxField
\ No newline at end of file
diff --git a/src/Components/ValidationField/View/DataRange.tsx b/src/Components/ValidationField/View/DataRange.tsx
new file mode 100644
index 0000000..742e6b5
--- /dev/null
+++ b/src/Components/ValidationField/View/DataRange.tsx
@@ -0,0 +1,44 @@
+import { Form, DatePicker } from 'antd'
+
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+
+const { RangePicker } = DatePicker;
+
+const DataRange = ({ name, label, Format, props, onChange, isDisabled, placeholder, className }: any) => {
+
+ const { errorMsg, isError, t, formik } = useFormField(name, props)
+ const onCalendarChange = (value: any) => {
+
+ formik.setFieldValue(name, value)
+
+ };
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default DataRange
\ No newline at end of file
diff --git a/src/Components/ValidationField/View/Date.tsx b/src/Components/ValidationField/View/Date.tsx
new file mode 100644
index 0000000..dde451d
--- /dev/null
+++ b/src/Components/ValidationField/View/Date.tsx
@@ -0,0 +1,51 @@
+import { Form, DatePicker } from 'antd';
+import React from 'react';
+import useFormField from '../../../Hooks/useFormField';
+import dayjs from 'dayjs';
+
+const DateField = ({ name, label, picker = "date", isDisabled, props, onChange, placeholder, className, Format }: any) => {
+
+ const { errorMsg, isError, t, formik } = useFormField(name, props);
+
+ const onCalendarChange = (value: any) => {
+ formik.setFieldValue(name, value);
+ };
+
+ // Function to check if a date is valid
+ const isValidDate = (date: any) => {
+ return date && !isNaN(date.valueOf()) && dayjs(date).isValid();
+ };
+
+ // Set a default invalid date if the provided defaultValue is invalid
+ const getDefaultDate = () => {
+ const defaultValue = formik.values[name];
+ return isValidDate(defaultValue) ? defaultValue : null; // Set to null or any other default invalid date
+ };
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default DateField;
diff --git a/src/Components/ValidationField/View/Default.tsx b/src/Components/ValidationField/View/Default.tsx
new file mode 100644
index 0000000..377facc
--- /dev/null
+++ b/src/Components/ValidationField/View/Default.tsx
@@ -0,0 +1,37 @@
+import { Form, Input } from 'antd'
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+
+const Default = ({ name, label, placeholder, isDisabled, onChange, props,type }: any) => {
+ const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
+
+ // console.log(isError);
+
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default React.memo(Default);
+;
diff --git a/src/Components/ValidationField/View/File.tsx b/src/Components/ValidationField/View/File.tsx
new file mode 100644
index 0000000..ab9a43f
--- /dev/null
+++ b/src/Components/ValidationField/View/File.tsx
@@ -0,0 +1,64 @@
+import { Button, Upload, UploadFile } from 'antd'
+import useFormField from '../../../Hooks/useFormField';
+import { UploadOutlined } from '@ant-design/icons';
+import { ImageBaseURL } from '../../../api/config';
+
+
+const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
+ const { formik, t ,isError,errorMsg} = useFormField(name, props)
+ let FormikName = formik.values[name];
+ const imageUrl = formik.values[name] ? ImageBaseURL + FormikName : '';
+ const fileList: UploadFile[] = [
+
+ {
+ uid: '-1',
+ name: '',
+ status: 'done',
+ url: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage"),
+ thumbUrl: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage")
+ }
+ ];
+ const FilehandleChange = (value:any) => {
+
+ formik.setFieldValue(name, value.file.originFileObj)
+
+ };
+ const customRequest = async ({ onSuccess}: any) => {
+ onSuccess();
+ };
+ return (
+
+
+
+
+ }>
+ {placholder ?? t("upload_image") }
+
+
+ {isError ? "required" : ""}
+
+
+
+
+
+ )
+}
+
+export default File
\ No newline at end of file
diff --git a/src/Components/ValidationField/View/MaltyFile.tsx b/src/Components/ValidationField/View/MaltyFile.tsx
new file mode 100644
index 0000000..767feaa
--- /dev/null
+++ b/src/Components/ValidationField/View/MaltyFile.tsx
@@ -0,0 +1,92 @@
+import { Button, Upload } from "antd";
+import { UploadOutlined } from "@ant-design/icons";
+import useFormField from "../../../Hooks/useFormField";
+import { ImageBaseURL } from "../../../api/config";
+
+const MaltyFile = ({
+ name,
+ label,
+ onChange,
+ isDisabled,
+ placeholder,
+ className,
+ props,
+}: any) => {
+ const { formik, t, isError } = useFormField(name, props);
+
+ let imageUrl = formik?.values?.[name] ?? null;
+ // console.log(imageUrl);
+
+ // Mapping formik values to fileList format
+ const fileList = imageUrl
+ ? imageUrl.map((file: any, index: number) => {
+ let fileUrl = "";
+
+ if (typeof file === "string") {
+ fileUrl = file.replace("public", "/storage");
+ }
+ console.log(file instanceof File);
+
+ return file instanceof File
+ ? {
+ uid: index,
+ name: file?.name,
+ status: "done",
+ originFileObj: file,
+ }
+ : {
+ uid: index,
+ id: file?.id,
+ name: file?.name,
+ status: "done",
+ url: ImageBaseURL + fileUrl || "",
+ thumbUrl: ImageBaseURL + fileUrl || "",
+ };
+ })
+ : [];
+
+ const FilehandleChange = ({ fileList }: any) => {
+ if (fileList.length === 0) {
+ formik.setFieldValue(name, null);
+ } else {
+ formik.setFieldValue(
+ name,
+ fileList.map((file: any) => file?.originFileObj ?? file),
+ );
+ }
+ };
+
+ // Custom request function
+ const customRequest = async ({ onSuccess }: any) => {
+ // Perform any necessary actions before onSuccess is called
+ onSuccess();
+ };
+
+ return (
+
+
+
+
+ }
+ >
+ {t(`input.` + placeholder) ?? t("input.upload_image")}
+
+ {isError ? "required" : ""}
+
+
+ );
+};
+
+export default MaltyFile;
diff --git a/src/Components/ValidationField/View/SearchField.tsx b/src/Components/ValidationField/View/SearchField.tsx
new file mode 100644
index 0000000..47f7271
--- /dev/null
+++ b/src/Components/ValidationField/View/SearchField.tsx
@@ -0,0 +1,69 @@
+import { Form, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import useFormField from '../../../Hooks/useFormField';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+const SearchField = ({ name, label, placeholder, isDisabled, searchBy, option, isMulti, onChange, className, loading,props }: any) => {
+ const { errorMsg, isError, t, formik } = useFormField(name, props);
+ const [searchQuery, setSearchQuery] = useState('');
+ const location = useLocation()
+
+ const navigate = useNavigate()
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location?.search);
+ setSearchQuery(searchParams?.get('search') || '');
+ console.log(searchParams);
+
+ }, [location]);
+
+
+
+ const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
+ formik?.setFieldValue(name, value);
+
+ console.log(value);
+ };
+ const SearchHandleChange = (value:any) => {
+ if (value || value !== "") {
+ navigate(`${window?.location?.pathname}?${searchBy}=${value}`);
+ } else {
+ const params = new URLSearchParams(location.search);
+ params.delete(searchBy ?? "search");
+ navigate(`${window?.location.pathname}?${params.toString()}`);
+ }
+
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default React.memo(SearchField);
diff --git a/src/Components/ValidationField/View/SelectField.tsx b/src/Components/ValidationField/View/SelectField.tsx
new file mode 100644
index 0000000..b923d8b
--- /dev/null
+++ b/src/Components/ValidationField/View/SelectField.tsx
@@ -0,0 +1,41 @@
+import { Form, Select } from 'antd'
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+
+const SelectField = ({ name, label, placeholder, isDisabled,option,isMulti,onChange,className, props}: any) => {
+
+ const { errorMsg, isError, t ,formik} = useFormField(name, props)
+ const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
+ formik.setFieldValue(name, value)
+
+ };
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(SelectField);
diff --git a/src/Components/ValidationField/View/TextArea.tsx b/src/Components/ValidationField/View/TextArea.tsx
new file mode 100644
index 0000000..62b9794
--- /dev/null
+++ b/src/Components/ValidationField/View/TextArea.tsx
@@ -0,0 +1,45 @@
+import { Form, Input } from 'antd'
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+const { TextArea } = Input;
+
+const TextAreaField = ({ name, label, placeholder, isDisabled, onChange, props,type }: any) => {
+ const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
+
+
+
+ const handleChange = (e: React.ChangeEvent) => {
+ console.log('Change:', e.target.value);
+ formik.setFieldValue(name, e.target.value)
+
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default React.memo(TextAreaField);
+;
diff --git a/src/Components/ValidationField/View/Time.tsx b/src/Components/ValidationField/View/Time.tsx
new file mode 100644
index 0000000..3d3bb0c
--- /dev/null
+++ b/src/Components/ValidationField/View/Time.tsx
@@ -0,0 +1,41 @@
+import { Form, TimePicker } from 'antd'
+import React from 'react'
+import useFormField from '../../../Hooks/useFormField';
+
+const Time = ({ name, label,className,isDisabled,onChange,props }: any) => {
+
+ const { errorMsg, isError, t, formik } = useFormField(name, props)
+ const onCalendarChange = (value: any) => {
+
+ formik.setFieldValue(name, value)
+
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Time
\ No newline at end of file
diff --git a/src/Components/ValidationField/View/index.tsx b/src/Components/ValidationField/View/index.tsx
new file mode 100644
index 0000000..606e8e6
--- /dev/null
+++ b/src/Components/ValidationField/View/index.tsx
@@ -0,0 +1,22 @@
+import Time from "./Time";
+import SelectField from "./SelectField";
+import Date from "./Date";
+import DataRange from "./DataRange";
+import CheckboxField from "./CheckboxField";
+import Default from "./Default";
+import File from "./File";
+import TextAreaField from './TextArea'
+
+
+
+export {
+ Time,
+ SelectField,
+ Date,
+ DataRange,
+ CheckboxField,
+ Default,
+ File,
+ TextAreaField
+
+}
\ No newline at end of file
diff --git a/src/Components/ValidationField/index.tsx b/src/Components/ValidationField/index.tsx
new file mode 100644
index 0000000..ee84bc7
--- /dev/null
+++ b/src/Components/ValidationField/index.tsx
@@ -0,0 +1,21 @@
+import { useState } from 'react';
+import { ErrorMessage, useField, Field, useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+import { FaExclamationCircle } from 'react-icons/fa';
+import Select from 'react-select';
+import { convert_data_to_select } from '../../Layout/app/Const';
+
+
+
+
+
+export {
+ useState,
+ ErrorMessage, useField, Field, useFormikContext,
+ useTranslation,
+ FaExclamationCircle,
+ Select,
+ convert_data_to_select,
+
+
+}
\ No newline at end of file
diff --git a/src/Components/ValidationField/types.ts b/src/Components/ValidationField/types.ts
new file mode 100644
index 0000000..539f2bd
--- /dev/null
+++ b/src/Components/ValidationField/types.ts
@@ -0,0 +1,132 @@
+
+// export interface ValidationFieldProps {
+// name: string;
+// type?: "text" | "Select" | "DataRange" | "Date" | "Time" | "File" | "number" | "Checkbox" | "password";
+// placeholder?: string;
+// label?: string;
+// className?: string;
+// option?: any[];
+// isMulti?: boolean;
+// isDisabled?: boolean;
+// picker?: "data" | "week" | "month" | "quarter" | "year";
+// Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
+// onChange?: (value: any) => void;
+// Group?: boolean
+// dir?:'ltr' | "rtl"
+// }
+
+ export interface ValidationFieldPropsText {
+ name: string;
+ type: "text";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+ }
+
+ export interface ValidationFieldPropsSelect {
+ name: string;
+ type: "Select";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?:any;
+ dir?:'ltr' | "rtl";
+ option: any[];
+ isMulti?: boolean;
+
+ }
+
+ export interface ValidationFieldPropsSearch{
+ name: string;
+ type: "Search";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl";
+ option: any[];
+ isMulti?: boolean;
+ searchBy:string;
+ loading?:boolean;
+
+ }
+ export interface ValidationFieldPropsDataRange {
+ name: string;
+ type: "DataRange";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+ Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
+ }
+ export interface ValidationFieldPropsDate {
+ name: string;
+ type: "Date";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+ picker?: "data" | "week" | "month" | "quarter" | "year";
+ Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS" | "YYYY-MM-DD HH:MM:SS";
+
+
+ }
+
+ export interface ValidationFieldPropsTime {
+ name: string;
+ type: "Time";
+ label?: string;
+ placeholder?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+
+ }
+
+ export interface ValidationFieldPropsFile {
+ name: string;
+ type: "File" | "MaltyFile";
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+
+ }
+ export interface ValidationFieldPropsCheckbox {
+ name: string;
+ type: "Checkbox";
+ label?: string;
+ className?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+ Group?: boolean
+
+ }
+ export interface ValidationFieldPropstext {
+ name: string;
+ type?: "text" | "number" | "password" | "TextArea";
+ label?: string;
+ className?: string;
+ placeholder?: string;
+ isDisabled?: boolean;
+ onChange?: (value: any) => void;
+ dir?:'ltr' | "rtl"
+ Group?: boolean
+
+ }
+
+
+ export type ValidationFieldProps = ValidationFieldPropsText| ValidationFieldPropsSelect| ValidationFieldPropsDataRange| ValidationFieldPropsDate| ValidationFieldPropsTime| ValidationFieldPropsFile| ValidationFieldPropsCheckbox| ValidationFieldPropstext | ValidationFieldPropsSearch;
diff --git a/src/Components/order/OrderStatus.tsx b/src/Components/order/OrderStatus.tsx
new file mode 100644
index 0000000..79c46fb
--- /dev/null
+++ b/src/Components/order/OrderStatus.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+
+// for write later
+function OrderStatus() {
+ return (
+ OrderStatus
+ )
+}
+
+export default OrderStatus
\ No newline at end of file
diff --git a/src/Extensions/Editor/HtmlEditor.tsx b/src/Extensions/Editor/HtmlEditor.tsx
new file mode 100644
index 0000000..9d02c9b
--- /dev/null
+++ b/src/Extensions/Editor/HtmlEditor.tsx
@@ -0,0 +1,42 @@
+import React, { FC } from "react";
+import { Editor } from '@tinymce/tinymce-react';
+import { useFormikContext } from "formik";
+
+interface HtmlEditorProps {
+ langCode: number;
+ name: string;
+ editorState: string;
+}
+
+const HtmlEditor: FC = ({ langCode, name, editorState, ...props }) => {
+ const formik = useFormikContext();
+
+ const ar: boolean = langCode === 2;
+
+ return (
+ {
+ formik.setFieldValue(name, newValue)
+ }}
+ />
+ );
+}
+
+export { HtmlEditor };
\ No newline at end of file
diff --git a/src/Extensions/Editor/SingleLangEditor.tsx b/src/Extensions/Editor/SingleLangEditor.tsx
new file mode 100644
index 0000000..c8db2d6
--- /dev/null
+++ b/src/Extensions/Editor/SingleLangEditor.tsx
@@ -0,0 +1,43 @@
+import React, { FC } from "react";
+// import PropTypes from "";
+import { HtmlEditor } from "./HtmlEditor";
+import { useFormikContext } from "formik";
+import { useTranslation } from "react-i18next";
+
+interface SingleLangEditorProps {
+ langCode: number;
+ property: string;
+}
+
+const PROPERTY_TYPES: string[] = [
+ "privacy_description",
+ "conditions_description",
+ "about_us_description",
+ "product_description",
+ "auction_description"
+];
+
+const SingleLangEditor: FC = ({ langCode, property }) => {
+ const formik:any = useFormikContext();
+ const {t} = useTranslation();
+
+ const label = `${t(property)} (${t(`lang_${langCode}`)})`;
+ const fieldName = `translated_fields[${langCode}][${property}]`;
+ return (
+ <>
+ {label}
+
+ >
+ );
+};
+
+// SingleLangEditor.propTypes = {
+// langCode: PropTypes.oneOf([1, 2]).isRequired,
+// property: PropTypes.oneOf(PROPERTY_TYPES).isRequired,
+// };
+
+export default SingleLangEditor;
\ No newline at end of file
diff --git a/src/Extensions/Editor/StatusCard.tsx b/src/Extensions/Editor/StatusCard.tsx
new file mode 100644
index 0000000..25993c4
--- /dev/null
+++ b/src/Extensions/Editor/StatusCard.tsx
@@ -0,0 +1,23 @@
+import React, { FC } from "react";
+import { Card, CardBody, Spinner } from "reactstrap";
+
+interface StatusCardProps {
+ isLoading: boolean;
+ isError: boolean;
+}
+
+const StatusCard= ({ isLoading, isError }:StatusCardProps) => {
+ return (
+
+
+ {isLoading && }
+ {isError && Failed !
}
+
+
+ );
+};
+
+export default StatusCard;
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateAddModal.js b/src/Extensions/FileGenerator/generateAddModal.js
new file mode 100644
index 0000000..88f656f
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateAddModal.js
@@ -0,0 +1,65 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+let FileContiner = `
+
+import React from 'react'
+import LayoutModal from '../../Layout/Dashboard/LayoutModal'
+import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
+import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
+import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useTranslation } from 'react-i18next'
+
+function Add${capitalizeFirstLetter(fileName)}Modal() {
+
+
+ const [t] = useTranslation()
+ const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
+ const handelSubmit = (values:any )=>{
+
+ const dataToSend = getDataToSend(values)
+
+ mutate(dataToSend)
+ // Submit Value
+ }
+ return (
+
+
+
+
+ )
+}
+
+export default Add${capitalizeFirstLetter(fileName)}Modal
+
+`
+fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateApi.js b/src/Extensions/FileGenerator/generateApi.js
new file mode 100644
index 0000000..80d596e
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateApi.js
@@ -0,0 +1,40 @@
+const fs = require('fs');
+
+const fileName = process.argv[2]
+
+
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+
+let FileContiner = `
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation"
+
+ const API = {
+ GET: "/api/admin/${fileName}",
+ ADD: "/api/admin/${fileName}/create",
+ UPDATE: "/api/admin/${fileName}/update",
+ DELETE: "/api/admin/${fileName}/delete",
+ };
+
+ const KEY = "${fileName.toUpperCase()}";
+ export const useGet${capitalizeFirstLetter(fileName)} = (params?:any) => useGetQuery(KEY, API.GET, params);
+ export const useAdd${capitalizeFirstLetter(fileName)} = () => useAddMutation(KEY, API.ADD);
+ export const useUpdate${capitalizeFirstLetter(fileName)} = () => useUpdateMutation(KEY, API.UPDATE);
+ export const useDelete${capitalizeFirstLetter(fileName)} = () =>useDeleteMutation(KEY, API.DELETE);
+`
+fs.writeFileSync('src/api/'+fileName+".ts",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateColumn.js b/src/Extensions/FileGenerator/generateColumn.js
new file mode 100644
index 0000000..c85ad21
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateColumn.js
@@ -0,0 +1,63 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+
+
+let FileContiner = `
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+
+function fnDelete(props :any ){}
+
+const useTableColumns :any = () => {
+ const [t] = useTranslation();
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("email"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.email
+ },
+
+ {
+ name: "#",
+ sortable: false,
+ center: "true",
+ cell: (row) => (
+ row}
+ onView={()=>{}}
+ objectToEdit={row}
+ showEdit={true}
+ // showDelete={false}
+ onDelete={() => fnDelete({ id: row.id })}
+ />
+ ),
+ },
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
+`
+fs.writeFileSync('src/Pages/'+fileName+"/useTableColumns"+".tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
diff --git a/src/Extensions/FileGenerator/generateDashboard.js b/src/Extensions/FileGenerator/generateDashboard.js
new file mode 100644
index 0000000..f291fd9
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateDashboard.js
@@ -0,0 +1,30 @@
+const { exec } = require('child_process');
+
+
+const fileName = process.argv[2]
+
+
+const CreateApi = `node src/Extensions/FileGenerator/generateApi.js ${fileName}`
+const CreatePage = `node src/Extensions/FileGenerator/generatePage.js ${fileName}`
+const CreateColumn = `node src/Extensions/FileGenerator/generateColumn.js ${fileName}`
+const CreateformUtil= `node src/Extensions/FileGenerator/generateformUtils.js ${fileName}`
+const CreateAddModal= `node src/Extensions/FileGenerator/generateAddModal.js ${fileName}`
+const CreateEditModal= `node src/Extensions/FileGenerator/generateEditModal.js ${fileName}`
+const CreateForm= `node src/Extensions/FileGenerator/generateForm.js ${fileName}`
+
+
+
+const RunCommand = async()=>{
+
+ exec(CreatePage)
+ exec(CreateApi)
+ setTimeout(()=>{},100)
+ exec(CreateColumn)
+ exec(CreateformUtil)
+ exec(CreateAddModal)
+ exec(CreateEditModal)
+ exec(CreateForm)
+
+}
+
+RunCommand()
diff --git a/src/Extensions/FileGenerator/generateEditModal.js b/src/Extensions/FileGenerator/generateEditModal.js
new file mode 100644
index 0000000..0171481
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateEditModal.js
@@ -0,0 +1,47 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+let FileContiner = `
+import React from 'react'
+import LayoutModal from '../../Layout/Dashboard/LayoutModal'
+import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
+import { getInitialValues, getValidationSchema } from './formUtil'
+import { usePageState } from '../../lib/state mangment/LayoutPagestate'
+
+function Edit${capitalizeFirstLetter(fileName)}Modal() {
+ const {objectToEdit} = usePageState()
+ return (
+ { }}
+ headerText='Edit Modal'
+ getValidationSchema={getValidationSchema(objectToEdit)}>
+
+
+ )
+}
+
+export default Edit${capitalizeFirstLetter(fileName)}Modal
+`
+fs.writeFileSync('src/Pages/'+fileName+"/"+"Edit"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateForm.js b/src/Extensions/FileGenerator/generateForm.js
new file mode 100644
index 0000000..3558bd6
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateForm.js
@@ -0,0 +1,66 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+let FileContiner = `
+import React from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../Components/ValidationField/ValidationField';
+import { FakeSelectData } from '../../Layout/app/Const';
+import { useFormikContext } from 'formik';
+
+import { DatePicker } from 'antd';
+
+function Form${capitalizeFirstLetter(fileName)}() {
+ const formik = useFormikContext();
+
+
+
+ return (
+
+
+ // name from form utils
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form${capitalizeFirstLetter(fileName)}
+
+
+`
+fs.writeFileSync('src/Pages/'+fileName+'/Form'+ capitalizeFirstLetter(fileName)+".tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateModel.js b/src/Extensions/FileGenerator/generateModel.js
new file mode 100644
index 0000000..442421c
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateModel.js
@@ -0,0 +1,64 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+let FileContiner = `
+
+import React from 'react'
+import LayoutModal from '../../Layout/Dashboard/LayoutModal'
+import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
+import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
+import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useTranslation } from 'react-i18next'
+
+function Add${capitalizeFirstLetter(fileName)}Modal() {
+
+
+ const [t] = useTranslation()
+ const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
+ const handelSubmit = (values:any )=>{
+
+ const dataToSend = getDataToSend(values)
+
+ mutate(dataToSend)
+ // Submit Value
+ }
+ return (
+
+
+
+
+ )
+}
+
+export default Add${capitalizeFirstLetter(fileName)}Modal
+
+`
+fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generatePage.js b/src/Extensions/FileGenerator/generatePage.js
new file mode 100644
index 0000000..f200971
--- /dev/null
+++ b/src/Extensions/FileGenerator/generatePage.js
@@ -0,0 +1,67 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+const folderPath = 'src/Pages/'+fileName;
+
+
+if (!fs.existsSync(folderPath)) {
+ fs.mkdirSync(folderPath, { recursive: true });
+}
+
+let FileContiner = `
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { useGet${capitalizeFirstLetter(fileName)}} from '../../api/${fileName}'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import Edit${capitalizeFirstLetter(fileName)}Modal from './Edit${capitalizeFirstLetter(fileName)}Modal'
+import Add${capitalizeFirstLetter(fileName)}Modal from './Add${capitalizeFirstLetter(fileName)}Modal'
+
+function ${capitalizeFirstLetter(fileName)}Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGet${capitalizeFirstLetter(fileName)}()
+
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ${capitalizeFirstLetter(fileName)}Page
+
+`
+
+fs.writeFileSync('src/Pages/'+fileName+"/"+capitalizeFirstLetter(fileName)+"Page.tsx",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Extensions/FileGenerator/generateformUtils.js b/src/Extensions/FileGenerator/generateformUtils.js
new file mode 100644
index 0000000..f608f43
--- /dev/null
+++ b/src/Extensions/FileGenerator/generateformUtils.js
@@ -0,0 +1,73 @@
+const fs = require('fs');
+
+// Get the file name from the command line arguments
+const fileName = process.argv[2];
+
+// Check if a file name is provided
+if (!fileName) {
+ console.error('Please provide a file name.');
+ process.exit(1);
+}
+
+let FileContiner = `
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+
+interface formUtilCommon {
+ name:string,
+ email:string
+}
+
+interface ObjectToEdit extends formUtilCommon {
+
+ id?:number,
+
+}
+
+interface InitialValues extends ObjectToEdit {
+
+}
+interface ValidateSchema extends formUtilCommon{
+
+}
+
+export const getInitialValues = (objectToEdit: ObjectToEdit | null = null): InitialValues => {
+
+
+ return {
+ id:objectToEdit?.id?? 0 ,
+ name:objectToEdit?.name ?? "",
+ email:objectToEdit?.email?? ""
+ }
+
+
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // validate input
+ return Yup.object().shape({
+ name:Yup.string().required('required'),
+ email:Yup.string().required("required")
+ });
+};
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+`
+fs.writeFileSync('src/Pages/'+fileName+"/"+"formUtil.ts",
+FileContiner
+);
+
+console.log(`File "${fileName}" generated successfully.`);
+
+
+function capitalizeFirstLetter(word) {
+ return (word).charAt(0).toUpperCase() + (word).slice(1);
+}
\ No newline at end of file
diff --git a/src/Hooks/WithDrawer.tsx b/src/Hooks/WithDrawer.tsx
new file mode 100644
index 0000000..4373fe1
--- /dev/null
+++ b/src/Hooks/WithDrawer.tsx
@@ -0,0 +1,45 @@
+import React, { useState, ReactNode } from 'react';
+import type { DrawerProps } from 'antd';
+import { Drawer, Space } from 'antd';
+
+interface WithDrawerProps {
+ button: React.ReactNode;
+ children: ReactNode;
+ title:string;
+ className?:string,
+ iopen?:boolean,
+ setOpen:any
+}
+
+const WithDrawer: React.FC = ({ children,title ="Basic Drawer",className ,setOpen, iopen }) => {
+ const [placement, setPlacement] = useState('right');
+
+
+
+ return (
+ <>
+ setOpen(false)}
+ open={iopen}
+ key={placement}
+ >
+
+ {children}
+
+
+
+ >
+ );
+};
+
+export default WithDrawer;
+
+
+// Open}
+// >
+// {/* Your content goes here */}
+//
\ No newline at end of file
diff --git a/src/Hooks/imageUrlToFile.tsx b/src/Hooks/imageUrlToFile.tsx
new file mode 100644
index 0000000..255055b
--- /dev/null
+++ b/src/Hooks/imageUrlToFile.tsx
@@ -0,0 +1,13 @@
+export async function fetchImage(imageUrl:any) {
+ try {
+ const response = await fetch(imageUrl);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
+ }
+ const blob = await response.blob();
+ return new File([blob], 'image.png', { type: 'image/png' });
+ } catch (error) {
+ console.error('Error fetching image:', error);
+ return null;
+ }
+ }
diff --git a/src/Hooks/isEmpty.tsx b/src/Hooks/isEmpty.tsx
new file mode 100644
index 0000000..e20a5fe
--- /dev/null
+++ b/src/Hooks/isEmpty.tsx
@@ -0,0 +1,3 @@
+export const isEmpty = (Type:any) => {
+ return !Type || (Array.isArray(Type) && Type.length === 0);
+ };
\ No newline at end of file
diff --git a/src/Hooks/useChangeLanguage.tsx b/src/Hooks/useChangeLanguage.tsx
new file mode 100644
index 0000000..588765a
--- /dev/null
+++ b/src/Hooks/useChangeLanguage.tsx
@@ -0,0 +1,75 @@
+import { useEffect } from 'react';
+import { initReactI18next, useTranslation } from 'react-i18next';
+import i18n from 'i18next';
+import translationEN from '../translate/en.json';
+import translationAR from '../translate/ar.json';
+import translationCN from '../translate/cn.json';
+
+const language = localStorage.getItem('language') ?? 'en';
+
+i18n.use(initReactI18next).init({
+ resources: {
+ en: {
+ translation: translationEN
+ },
+ ar: {
+ translation: translationAR
+ },
+ cn: {
+ translation: translationCN
+ }
+ },
+ lng: language,
+ interpolation: {
+ escapeValue: false
+ }
+ });
+
+
+export function useLanguage() {
+ useEffect(() => {
+ changeLanguage(language);
+ }, [language]);
+
+ const changeLanguage = (newLanguage:any) => {
+ i18n.changeLanguage(newLanguage);
+ localStorage.setItem('language', newLanguage);
+ applyLanguageStyles(newLanguage);
+ };
+
+ return { changeLanguage };
+}
+
+function applyLanguageStyles(language:any) {
+ if (language === 'ar') {
+ document.body.setAttribute('dir', 'rtl');
+ document.body.classList.remove('cn');
+ document.body.classList.add('ar');
+ } else if (language === 'en') {
+ document.body.setAttribute('dir', 'ltr');
+ document.body.classList.remove('ar', 'cn');
+ document.body.classList.add('en');
+ } else if (language === 'cn') {
+ document.body.setAttribute('dir', 'ltr');
+ document.body.classList.remove('ar');
+ document.body.classList.add('cn');
+ }
+
+}
+
+export function useLanguageMenu() {
+ const { t } = useTranslation();
+ const { changeLanguage } = useLanguage();
+
+ const languageOptions = [
+ { code: 'ar', icon: '/language/ar.svg', label: t('Arabic') },
+ { code: 'en', icon: '/language/en.svg', label: t('English') },
+ { code: 'cn', icon: '/language/china.svg', label: t('Chinese') }
+ ];
+
+ const handleLanguageChange = (code:any) => {
+ changeLanguage(code);
+ };
+
+ return { languageOptions, handleLanguageChange };
+}
diff --git a/src/Hooks/useEventListener.tsx b/src/Hooks/useEventListener.tsx
new file mode 100644
index 0000000..0c29a4a
--- /dev/null
+++ b/src/Hooks/useEventListener.tsx
@@ -0,0 +1,16 @@
+import { useEffect, useRef } from "react";
+
+export const useEventListener = (eventType:any, callback:any, element = window) => {
+ const callbackRef = useRef(callback);
+
+ useEffect(() => {
+ callbackRef.current = callback;
+ }, [callback]);
+
+ useEffect(() => {
+ const handler = (e:any) => callbackRef.current(e);
+ element.addEventListener(eventType, handler);
+
+ return () => element.removeEventListener(eventType, handler);
+ }, [eventType, element]);
+};
diff --git a/src/Hooks/useFormField.tsx b/src/Hooks/useFormField.tsx
new file mode 100644
index 0000000..57d8cbf
--- /dev/null
+++ b/src/Hooks/useFormField.tsx
@@ -0,0 +1,16 @@
+import { useField, useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+import { Field } from 'formik';
+
+const useFormField = (name: string, props?: any) => {
+ const [field, meta] = useField({ name, ...props });
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const isError = meta.touched && meta.error;
+
+ const errorMsg = meta.error ? t(meta.error.toString()) : '';
+
+ return { Field, field, meta, formik, isError, errorMsg, t };
+};
+
+export default useFormField;
diff --git a/src/Hooks/useFormatToSelect.tsx b/src/Hooks/useFormatToSelect.tsx
new file mode 100644
index 0000000..2c9980d
--- /dev/null
+++ b/src/Hooks/useFormatToSelect.tsx
@@ -0,0 +1,16 @@
+const useFormatToSelect = (Data : any) => {
+ const format = (data :any) => {
+ if (!data) return [];
+ const language = localStorage.getItem("language") ?? "en";
+
+ return data.map((item :any) => ({
+ value: item?.id,
+ label: item?.name[language],
+ }));
+ };
+
+ return format(Data);
+ };
+
+ export default useFormatToSelect;
+
\ No newline at end of file
diff --git a/src/Hooks/useImageError.tsx b/src/Hooks/useImageError.tsx
new file mode 100644
index 0000000..db0d853
--- /dev/null
+++ b/src/Hooks/useImageError.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const useImageError = ({currentTarget}:any) => {
+ const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
+ currentTarget.onerror = null;
+ currentTarget.src=`${ErrorImage}`;
+}
+
+export default useImageError
\ No newline at end of file
diff --git a/src/Hooks/useLoadingState.tsx b/src/Hooks/useLoadingState.tsx
new file mode 100644
index 0000000..dfda80d
--- /dev/null
+++ b/src/Hooks/useLoadingState.tsx
@@ -0,0 +1,21 @@
+import { useState, useEffect } from 'react';
+
+function useLoadingState(initialValue: boolean, duration: number): [boolean, () => void] {
+ const [loading, setLoading] = useState(initialValue);
+
+ useEffect(() => {
+ const timeoutId = setTimeout(() => {
+ setLoading(false);
+ }, duration);
+
+ return () => clearTimeout(timeoutId);
+ }, [duration]);
+
+ const resetLoading = () => {
+ setLoading(true);
+ };
+
+ return [loading, resetLoading];
+}
+
+export default useLoadingState;
diff --git a/src/Hooks/useNavigateOnSuccess.ts b/src/Hooks/useNavigateOnSuccess.ts
new file mode 100644
index 0000000..2b012c3
--- /dev/null
+++ b/src/Hooks/useNavigateOnSuccess.ts
@@ -0,0 +1,20 @@
+import React, { useEffect } from 'react'
+import { useNavigate } from 'react-router-dom'
+
+
+function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess?:any) {
+
+ const navigate = useNavigate()
+ useEffect(()=>{
+
+ if(isSuccess){
+ if (typeof callbackAfterSuccess === 'function') {
+ callbackAfterSuccess()
+ }
+ navigate(to_path )
+ }
+ },[isSuccess])
+
+}
+
+export default useNavigateOnSuccess
\ No newline at end of file
diff --git a/src/Hooks/usePagination.tsx b/src/Hooks/usePagination.tsx
new file mode 100644
index 0000000..5008afe
--- /dev/null
+++ b/src/Hooks/usePagination.tsx
@@ -0,0 +1,38 @@
+import { Pagination } from "antd";
+import { useTranslation } from "react-i18next";
+import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
+
+export const PaginationBody = ({ data }: any) => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const pagination = location?.search || '';
+ const currentPage = parseInt(new URLSearchParams(location.search).get("page") || "1", 10);
+ const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "15", 10);
+
+ const [searchParams] = useSearchParams()
+ const onChange = (page: number, pageSize?: number) => {
+ navigate(`?page=${page}&per_page=${pageSize || data?.per_page}`);
+ };
+
+ const onShowSizeChange = (current: number, pageSize: number) => {
+ navigate(`?page=${current}&per_page=${pageSize}`);
+ };
+ const [t] = useTranslation()
+
+ return (
+ `${t(`Total`)} ${total} ${t(`items`)}`}
+ pageSize={pageSize}
+ pageSizeOptions={[6, 15, 22, 30]}
+ defaultCurrent={currentPage}
+ current={currentPage}
+ onChange={onChange}
+ onShowSizeChange={onShowSizeChange}
+ // showQuickJumper
+ showSizeChanger
+
+ />
+ );
+};
diff --git a/src/Hooks/useWindowSize.tsx b/src/Hooks/useWindowSize.tsx
new file mode 100644
index 0000000..853fdb8
--- /dev/null
+++ b/src/Hooks/useWindowSize.tsx
@@ -0,0 +1,18 @@
+import { useState } from "react";
+import { useEventListener } from "./useEventListener";
+
+export const useWindowSize = () => {
+ const [windowSize, setWindowSize] = useState({
+ width: window.innerWidth,
+ height: window.innerHeight,
+ });
+
+ useEventListener("resize", () => {
+ setWindowSize({
+ width: window.innerWidth,
+ height: window.innerHeight,
+ });
+ });
+
+ return windowSize;
+};
diff --git a/src/Layout/Dashboard/AddButton/AddButton.tsx b/src/Layout/Dashboard/AddButton/AddButton.tsx
new file mode 100644
index 0000000..d6d8cec
--- /dev/null
+++ b/src/Layout/Dashboard/AddButton/AddButton.tsx
@@ -0,0 +1,33 @@
+import React from 'react'
+import './Add_Button.scss'
+import { useTranslation } from 'react-i18next'
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
+
+
+
+const AddButton = (props :any) => {
+ const [t] = useTranslation();
+
+
+ return (
+
+
+
+
+ )
+}
+
+export default AddButton
\ No newline at end of file
diff --git a/src/Layout/Dashboard/AddButton/AddButtonLayout.tsx b/src/Layout/Dashboard/AddButton/AddButtonLayout.tsx
new file mode 100644
index 0000000..d796bae
--- /dev/null
+++ b/src/Layout/Dashboard/AddButton/AddButtonLayout.tsx
@@ -0,0 +1,41 @@
+import React from 'react'
+import './Add_Button.scss'
+import { useTranslation } from 'react-i18next'
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
+
+
+
+const AddButtonLayout = ({haveAddModal}:any) => {
+ const { setIsOpenAddModel , setObjectToEdit } = usePageState()
+ const [t] = useTranslation();
+
+
+ return (
+ {
+ if(haveAddModal){
+
+ setIsOpenAddModel()
+ }
+ setObjectToEdit(null)
+
+ }}>
+
+
+
+ )
+}
+
+export default AddButtonLayout
\ No newline at end of file
diff --git a/src/Layout/Dashboard/AddButton/Add_Button.scss b/src/Layout/Dashboard/AddButton/Add_Button.scss
new file mode 100644
index 0000000..fc7a862
--- /dev/null
+++ b/src/Layout/Dashboard/AddButton/Add_Button.scss
@@ -0,0 +1,77 @@
+.Add_Button{
+ button {
+ border: 2px solid var(--primary);
+ background-color: var(--primary);
+ border-radius: 0.5vw;
+ height: 40px;
+ width: 70px;
+ font-size: 1vw ;
+ display: flex; justify-content: center; align-items: center;
+ box-shadow: 2px 2px 7px 0 var(--primary);
+ }
+ button span {
+ display: flex;
+ align-items: center;
+ color: var(--bg);
+ font-weight: normal;
+ font-size: 14px;
+ }
+
+ button:hover {
+ background-color:var(--primary);
+ }
+}
+
+@media screen and (max-width: 700px) {
+ .Add_Button{
+ button{
+ font-size: 2vw !important;
+ }
+ }
+}
+@media screen and (max-width: 470px) {
+ .Add_Button{
+ button{
+ font-size: 3vw !important;
+ }
+ }
+}
+
+ .Add_Button_notification{
+ button {
+ border: 2px solid var(--primary);
+ background-color: var(--primary);
+ border-radius: 0.5vw;
+ height: 40px;
+ width: 180px;
+ font-size: 1vw ;
+ display: flex; justify-content: center; align-items: center;
+ box-shadow: 2px 2px 7px 0 var(--primary);
+ }
+ button span {
+ display: flex;
+ align-items: center;
+ color: var(--bg);
+ font-weight: normal;
+ font-size: 14px;
+ }
+
+ button:hover {
+ background-color:var(--primary);
+ }
+ }
+
+ @media screen and (max-width: 700px) {
+ .Add_Button_notification{
+ button{
+ font-size: 2vw !important;
+ }
+ }
+ }
+ @media screen and (max-width: 470px) {
+ .Add_Button_notification{
+ button{
+ font-size: 3vw !important;
+ }
+ }
+ }
diff --git a/src/Layout/Dashboard/DashBody.tsx b/src/Layout/Dashboard/DashBody.tsx
new file mode 100644
index 0000000..8f2f449
--- /dev/null
+++ b/src/Layout/Dashboard/DashBody.tsx
@@ -0,0 +1,32 @@
+import { Spinner } from "reactstrap"
+import { QueryStatusEnum } from "../../config/QueryStatus"
+import LoadingPage from "../app/LoadingPage"
+import { useTranslation } from "react-i18next"
+import { BsEmojiFrown } from "react-icons/bs";
+import ErrorPage from "../app/ErrorPage";
+
+
+const DashBody = ({ children , status }: { children: React.ReactNode ,status?:QueryStatusEnum }) => {
+ const {t} = useTranslation();
+
+ // Add You Custom Loadaing Page
+ if(status === QueryStatusEnum.LOADING){
+
+ return
+ }
+
+ // Add Your Custom Error Page
+ if(status === QueryStatusEnum.ERROR){
+ return (
+
+ )
+ }
+
+ return (
+
+ { children }
+
+ )
+}
+
+export default DashBody
diff --git a/src/Layout/Dashboard/DashHeader.tsx b/src/Layout/Dashboard/DashHeader.tsx
new file mode 100644
index 0000000..a71852d
--- /dev/null
+++ b/src/Layout/Dashboard/DashHeader.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+import AddButtonLayout from "./AddButton/AddButtonLayout";
+import { useTranslation } from "react-i18next";
+
+interface DashHeaderProp {
+ title: string;
+ children?: React.ReactNode;
+ showAddButton?: boolean;
+ haveAddModal?:boolean
+}
+const DashHeader = ({
+ children,
+ title,
+ haveAddModal= true ,
+ showAddButton = true,
+}: DashHeaderProp) => {
+ const [t] = useTranslation();
+ return (
+
+
{t(`${title}`)}
+ {children}
+ {showAddButton &&
}
+
+ );
+};
+
+export default DashHeader;
diff --git a/src/Layout/Dashboard/FormPage.tsx b/src/Layout/Dashboard/FormPage.tsx
new file mode 100644
index 0000000..befc606
--- /dev/null
+++ b/src/Layout/Dashboard/FormPage.tsx
@@ -0,0 +1,42 @@
+import { Formik, Form } from 'formik';
+import React, { ReactNode } from 'react';
+import { Button } from "reactstrap";
+import * as Yup from 'yup';
+
+interface FormValues {
+ [key: string]: any;
+}
+
+interface FormPageProps {
+ handleSubmit: (values: any) => void
+ initialValues: FormValues;
+ validationSchema: any;
+ title?: string;
+ children: ReactNode;
+}
+
+const FormPage: React.FC = ({ children, handleSubmit, initialValues, validationSchema, title = "Edit Item" }) => {
+ return (
+ <>
+ {title}
+
+
+ {formik => (
+
+ )}
+
+
+ >
+ );
+};
+
+export default FormPage;
diff --git a/src/Layout/Dashboard/FormikForm.tsx b/src/Layout/Dashboard/FormikForm.tsx
new file mode 100644
index 0000000..9e42628
--- /dev/null
+++ b/src/Layout/Dashboard/FormikForm.tsx
@@ -0,0 +1,67 @@
+import { Formik, Form } from 'formik';
+import React, { ReactNode } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Button } from "reactstrap";
+import * as Yup from 'yup';
+
+interface FormValues {
+ [key: string]: any;
+}
+
+interface FormikFormProps {
+ handleSubmit: (values: any) => void
+ initialValues: FormValues;
+ validationSchema: any;
+ title?: string;
+ children: ReactNode;
+ ButtonName?:string
+}
+const FormikForm: React.FC = ({ children, handleSubmit, initialValues, validationSchema, title = "Add New Item" ,ButtonName="إضافة"}) => {
+ const [t]= useTranslation()
+
+ return (
+ <>
+
+ {formik => (
+
+ )}
+
+ >
+ );
+};
+
+export default FormikForm;
+
+
+
+{/* {
+
+}}
+initialValues={() => {
+ return {
+ id: null,
+ name: "",
+
+ }
+}}
+validationSchema={() => {
+ return Yup.object().shape({
+ name: Yup.string().required('required'),
+
+ });
+}}
+
+>
+
+ */}
\ No newline at end of file
diff --git a/src/Layout/Dashboard/LayoutModal.tsx b/src/Layout/Dashboard/LayoutModal.tsx
new file mode 100644
index 0000000..926e7f0
--- /dev/null
+++ b/src/Layout/Dashboard/LayoutModal.tsx
@@ -0,0 +1,77 @@
+import { Form, Formik } from 'formik'
+import React, { useEffect } from 'react'
+import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'
+import { usePageState } from '../../lib/state mangment/LayoutPagestate'
+import { useTranslation } from 'react-i18next';
+import { LoadingButton } from '../../Components/Ui/LoadingButton';
+import { QueryStatusEnum } from '../../config/QueryStatus';
+
+interface LayoutModalProps {
+ isAddModal: boolean;
+ headerText: string;
+ handleSubmit: (values: any) => void;
+ getInitialValues: any;
+ getValidationSchema: any;
+ children: React.ReactNode;
+ status?:QueryStatusEnum
+}
+function LayoutModal({isAddModal , headerText , handleSubmit =()=>{} , getInitialValues , getValidationSchema,status ,children}:LayoutModalProps) {
+
+
+
+ const {isOpenAddModel ,setIsOpenAddModel , setIsOpenEditModel ,isOpenEditModel , objectToEdit , CloseAllModal} = usePageState(state => state)
+
+
+ useEffect(()=>{
+ if(status === QueryStatusEnum.SUCCESS){
+ CloseAllModal()
+ }
+ },[status , CloseAllModal])
+
+ const [t] = useTranslation()
+ return (
+
+ isAddModal ?setIsOpenAddModel() : setIsOpenEditModel()} >
+ {t(headerText)}
+
+ {
+
+ (( objectToEdit != null && isOpenEditModel) || isOpenAddModel) &&
+
+
+ {(formik) => (
+
+ )}
+
+ }
+
+
+ )
+}
+
+export default LayoutModal
\ No newline at end of file
diff --git a/src/Layout/Dashboard/LyTable.tsx b/src/Layout/Dashboard/LyTable.tsx
new file mode 100644
index 0000000..be96f09
--- /dev/null
+++ b/src/Layout/Dashboard/LyTable.tsx
@@ -0,0 +1,39 @@
+
+import DataTable from 'react-data-table-component';
+import { Card, CardBody, Spinner } from 'reactstrap';
+import { PaginationBody } from '../../Hooks/usePagination';
+import { useTranslation } from 'react-i18next';
+
+const LyTable = (props?: any) => {
+ const {t} = useTranslation();
+
+ return (
+
+
+
+ {t("no_records")}}
+ noHeader
+ pagination
+ progressComponent={}
+
+ {...(props.is_pagination && {
+ paginationServer: true,
+ paginationComponent: () =>
+ })}
+
+ {...props}
+ />
+
+
+
+
+
+ )
+}
+
+
+export default LyTable
\ No newline at end of file
diff --git a/src/Layout/Dashboard/PageStructure.tsx b/src/Layout/Dashboard/PageStructure.tsx
new file mode 100644
index 0000000..570254d
--- /dev/null
+++ b/src/Layout/Dashboard/PageStructure.tsx
@@ -0,0 +1,36 @@
+import React, { FC } from "react";
+import StatusCard from "../../Extensions/Editor/StatusCard";
+import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
+import { useTranslation } from "react-i18next";
+
+interface PageStructureProps {
+ title?: string;
+ isLoading: boolean;
+ isError: boolean;
+ data?: any;
+ children:any
+}
+
+const PageStructure: FC = ({
+ title,
+ isLoading,
+ isError,
+ data,
+ children,
+}) => {
+ const {t} = useTranslation();
+
+ if (!data) return ;
+ return (
+
+ {title && (
+
+ {t(title)}
+
+ )}
+ {children}
+
+ );
+};
+
+export default PageStructure;
\ No newline at end of file
diff --git a/src/Layout/Dashboard/SearchField.tsx b/src/Layout/Dashboard/SearchField.tsx
new file mode 100644
index 0000000..02b03d2
--- /dev/null
+++ b/src/Layout/Dashboard/SearchField.tsx
@@ -0,0 +1,56 @@
+import { Input } from 'antd';
+import { SearchProps } from 'antd/es/input'
+import { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
+const { Search } = Input;
+
+const SearchField = ({ searchBy }: any) => {
+ const navigate = useNavigate()
+ const [searchParams,] = useSearchParams();
+ const location = useLocation()
+ const { t } = useTranslation();
+
+
+ const [searchValue, setSearchValue] = useState(searchParams.get(searchBy ?? "search") || "");
+
+ const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
+ if (value || value !== "") {
+ navigate(`${location?.pathname}?${searchBy ?? "search"}=${value}`);
+ } else {
+ const params = new URLSearchParams(location.search);
+ params.delete(searchBy ?? "search");
+ navigate(`${location.pathname}?${params.toString()}`);
+ }
+ }
+
+ const onChange = (e: any) => {
+ const value = e.target.value
+ setSearchValue(e.target.value);
+ if (value === "") {
+ const params = new URLSearchParams(location.search);
+ params.delete(searchBy ?? "search");
+ navigate(`${location.pathname}?${params.toString()}`);
+ }
+ }
+
+
+ return (
+
+
+
+
+ )
+}
+
+export default SearchField
\ No newline at end of file
diff --git a/src/Layout/Dashboard/SelectField.tsx b/src/Layout/Dashboard/SelectField.tsx
new file mode 100644
index 0000000..a770134
--- /dev/null
+++ b/src/Layout/Dashboard/SelectField.tsx
@@ -0,0 +1,39 @@
+import { Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+const SelectField = ({ selectBy, lebel, option }: any) => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [t] = useTranslation();
+
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location.search);
+ setSearchQuery(searchParams.get('search') || '');
+ }, []);
+
+
+ const handleSelectChange = (value: any) => {
+ if (value) {
+ console.log(`${location.pathname}?${selectBy}=${value}`);
+ navigate(`${location.pathname}?${selectBy}=${value}`);
+ }
+ }
+
+ return (
+
+
+
+ );
+};
+
+export default React.memo(SelectField);
diff --git a/src/Layout/Dashboard/SelectWSearchField.tsx b/src/Layout/Dashboard/SelectWSearchField.tsx
new file mode 100644
index 0000000..bd28411
--- /dev/null
+++ b/src/Layout/Dashboard/SelectWSearchField.tsx
@@ -0,0 +1,52 @@
+import { Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [t] = useTranslation();
+
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location.search);
+ setSearchQuery(searchParams.get('search') || '');
+ }, []);
+
+ const handleSearchChange = (value: any) => {
+ if (value || value !== "") {
+ navigate(`${location.pathname}?${selectBy}=${value}`);
+ } else {
+ const params = new URLSearchParams(location.search);
+ params.delete(selectBy ?? "search");
+ navigate(`${location.pathname}?${params.toString()}`);
+ }
+ };
+
+ const handleSelectChange = (value: any) => {
+ if (value) {
+ console.log(`${location.pathname}?${submiteBy}=${value}`);
+
+ navigate(`${location.pathname}?${submiteBy}=${value}`);
+ }
+}
+
+ return (
+
+
+
+ );
+};
+
+export default React.memo(SelectWSearchField);
diff --git a/src/Layout/Dashboard/ViewPage.tsx b/src/Layout/Dashboard/ViewPage.tsx
new file mode 100644
index 0000000..407862e
--- /dev/null
+++ b/src/Layout/Dashboard/ViewPage.tsx
@@ -0,0 +1,88 @@
+import React from "react";
+import { Card, CardHeader, CardTitle, CardBody, Button } from "reactstrap";
+import { Formik, Form } from "formik";
+import { LoadingButton } from "../../Components/Ui/LoadingButton";
+import ProgressBar from "../../Components/Ui/ProgressBar";
+import { useLocation, useNavigate } from "react-router-dom";
+import { usePageState } from "../../lib/state mangment/LayoutPagestate";
+import { useTranslation } from "react-i18next";
+
+type TViewPage ={
+ children: React.ReactNode,
+ getInitialValues:any,
+ getValidationSchema:any,
+ getDataToSend:any,
+ handleSubmit:any,
+ // BarStatus:any,
+ IsloadingButton:boolean
+}
+
+const ViewPage: React.FC= ({children,getInitialValues, getValidationSchema,handleSubmit,IsloadingButton})=> {
+
+ const {objectToEdit} = usePageState()
+ const {t} = useTranslation();
+ const navigate = useNavigate();
+ // console.log(BarStatus);
+
+ const location = useLocation();
+
+
+
+ const navigateToParent = () => {
+ // // Get the current path
+ // const currentPath = window.location.pathname;
+ // // Find the index of the second '/' in the current path
+ // const secondSlashIndex = currentPath.indexOf('/', 1);
+ // // Extract the parent path
+ // const parentPath = secondSlashIndex !== -1 ? currentPath.substring(0, secondSlashIndex) : currentPath;
+ // // Navigate to the parent path
+ // navigate(parentPath);
+ navigate(-1)
+ };
+
+ return (
+
+
+
+ {t("View_information")}
+
+
+
+
+ {
+
+ {(formik) => (
+
+ )}
+
+ }
+
+
+
+ );
+};
+
+
+export default ViewPage;
diff --git a/src/Layout/Dashboard/useCloseModal.ts b/src/Layout/Dashboard/useCloseModal.ts
new file mode 100644
index 0000000..993685d
--- /dev/null
+++ b/src/Layout/Dashboard/useCloseModal.ts
@@ -0,0 +1,18 @@
+import React, { useEffect } from 'react'
+import { usePageState } from '../../lib/state mangment/LayoutPagestate'
+
+function useCloseModal(statusClose:any) {
+
+ const {CloseAllModal} = usePageState()
+
+ useEffect(()=>{
+
+ if(statusClose){
+ CloseAllModal()
+ }
+ },[statusClose , CloseAllModal])
+
+ return true
+}
+
+export default useCloseModal
\ No newline at end of file
diff --git a/src/Layout/app/Const.tsx b/src/Layout/app/Const.tsx
new file mode 100644
index 0000000..bb3c0cb
--- /dev/null
+++ b/src/Layout/app/Const.tsx
@@ -0,0 +1,48 @@
+export const SideBarLogoUrl = "../Layout/KarimLogo.svg";
+export const UserImageURL = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMsaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4RDI0Q0Q2RjcxQ0ExMUU1QjA2Q0JBMTlGNjJDRUVCMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4RDI0Q0Q3MDcxQ0ExMUU1QjA2Q0JBMTlGNjJDRUVCMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjhEMjRDRDZENzFDQTExRTVCMDZDQkExOUY2MkNFRUIzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhEMjRDRDZFNzFDQTExRTVCMDZDQkExOUY2MkNFRUIzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgB9AH0AwERAAIRAQMRAf/EAHwAAQACAwEBAAAAAAAAAAAAAAABBgMEBQcCAQEAAAAAAAAAAAAAAAAAAAAAEAEAAgECAwQIBAIHCQEAAAAAAQIDEQQhBQYxQVESYXGBkSIyQhOxwVIjoRTRYjNTJBU14XKCkrLiQ4NUFhEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A9cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAJAAAABAJAAAAAABAJAAAAAAAAAAAAABAJAABAJAAAAAAAAAAAAAAAAAAAAAAAAAAABAJAAAAAAAAAAAAAABAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAJAAAAAAAAAAAAAABAJA1AAAAAAAAAAAAAAAAAAAAAAABAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB948OXJPlx0m8z3RGoN/B07zbNETGCaRP65iv4g3cXR29tGuTNjp6I1mfwBsV6M/XutPDSmv5wCf/xeP/65/wCT/uBjt0Zk4+Tc1nw1rMA1s3SXM6fJNMnqnT8dAaG45RzLb/2mC0R+qI1j3wDTmJidJjQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAmImZ0iNZ8IB1Nl05zHc+W00+1jn6r8OHqBYNn0ry/DpbLrntw+bhHD0QDq4dvgw18uLHWkf1YiAZAAAAAAAau55XsNzExlw1mZ+qI0n3wDib7pCsxNtnk0ntjHfs9WoOBu+W73aTpnxTWPHtj3g1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbvLuUbzfX/arpjifiyT2QC2ct6f2WymL6fdzfrt+UA6gAAAAAAAAAAAPnJix5aTTJWLVnhMTxBXOZ9J0ms5NjOlv7qez2SCtZsGXBknHlrNL14TEg+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAd7kvTd9zEZ91rXD21p32BbMWLHixxjx1ilKxpEQD7AAAAAAAAAAAAAABpcy5Ttd/j8uWumSPlyR2wCl8y5Zudhm+3ljWs/JeOyYBpgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs3T/T1ZrTebyuuvxYsM+HdNtf4As4AAAAAAAAAAAAAAAAAMO72mDd4LYc9fNS3vifGAUfnHKMvLtx5Z1thvxxZPGPCfTANAAAAAAAAAAAAAAAAAAAAAAAAAAEAkAAAAAAAAAAAAAFg6b5H9+0bzcV/arOuKs/VMd/qBbQAAAAAAAAAAAAAAAAAAAYN7s8O8218GWNa27J74numAUPmXL82x3VsOSOHbS3daviDVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0OS8stv93FJ4YqfFln0eAL3jx0x4648cRWlY0rEeAPoAAAAAAAAAAAAAAAAAAAAHO51yqm/2sxpEZ6ccd/yBRL0tS81tGlqzpMemAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACa1ta0VrGtrTpER3yC+cj5fGy2NKTH7t/iyT6Z7gdAAAAAAAAAAAAAAAAAAAAAAAFT6r5ZOPNG8x1+C/DJp3T4+0FeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2ultjGffTmvGtMMax4ebuBcwAAAAAAAAAAAAAAAAAAAAAAAYd5tqbnbZMN41reJj2g87zYrYst8duFqTMTHqB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvXT2zjbctx6xHnyfHaY9PYDpgAAAAAAAAAAAAAAAAAAAAAAAAp/VuzjFva56xEVzRx0/VAOEAAAAAAAAAAAAAAAAAAAAAACASACASAAAAAAADY5ft53G9w4YjXz2iJ9QPRK1itYrHZEaR7ASAAAAAAAAAAAAAAAAAAAAAAAADk9T7X73K72iNbYpi8ervBSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdvpLBGTmU5J/wDFSZ9s8AXIAAAAAAAAAAAAAAAAAAAAAAAAAGPcYozYMmKey9Zr74B5vkr5L2r+mZj3AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFp6NxR9vcZe/WtY/EFkAAAAAAAAAAAAAAAAAAAAAAAAAAB57zfFGLmW4pHZF50BqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuPSFJjl17d1sk6eyIB3AAAAAAAAAAAAAAAAAAAAAAAAAAAUbqWs15xm17/LPvgHLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABc+kv9K/8AZb8IB2gAAAAAAAAAAAAAAAAAAAAAAAAAAUjqn/Wcv+7T/pgHJAAAAAAAAAAAAAAAAAAAAAAAAAAABAJAAAABcOkLzPL8ldflyTw9cA7oAAAAAAAAAAAAAAAAAAAAAAAAAAKL1Jebc4z8ddNI90A5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALP0Zl+HcYfVaPwBZgAAAAAAAAAAAAAAAAAAAAAAAAAAeec1y/e5juMnda86A1QAQCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdnpTcfa5n5J7MtZr7uILoAAAAAAAAAAAAAAAAAAAAAAAAADFus0Ydtlyz9FZn3QDzi9vNe1vGZn3ggEAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGbZZ5wbrFmidJpaJ1B6LjvW9K3rOsWiJifWD6AAAAAAAAAAAAAAAAAAAAAAAABxuqd19rls44nS2aYrp6I4yClgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu/TW9jccurWba5MPw2j0dwOsAAAAAAAAAAAAAAAAAAAAAAAACmdVb2M+/wDs1trTDGn/ABT2g4oAAAAAAAAAAAAAAAAAAAAAAAIABIAAIBIAAAAAOv0zvo23MIpedKZo8s+GvcC7AAAAAAAAAAAAAAAAAAAAAAAA1+Ybum02mTPfsrHD0z3A88yZLZMlr2+a0zM+0HyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEzE6xwmOyQXnp/mUbzY1i1tc+L4ckd/okHUAAAAAAAAAAAAAAAAAAAAAABTuqeZ/f3MbbFbXFi+bSeE2/2A4YAAAAAAAAAAAAAAAAAAAAAAAAIBIAAAAAAAAAAANzlXMcmw3dc1eNJ4ZK+NQX7BnxZ8NM2KfNjvGtZB9gAAAAAAAAAAAAAAAAAAAA5XP+b12O28lJ/wARkjSkeEeIKPMzMzM8ZnjMgAAAAAAAAgEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7XT3O52eSNvmt/h7zwmfpme/wBQLnExMRMTrE8YmAAAAAAAAAAAAAAAAAAAAavMuYYdjtrZsk8eylO+0goe93mbebm+fLOtrTwjwjwgGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg6f6h/l4rtN1OuHXSmT9Ovj6AWytq2rFqzFqz2THGASAAAAAAAAAAAAAAAADW5hzHbbHBOXNbj9NI+a0+gFG5nzLPv9xObJwrHClI7KwDUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1+TdQ59jMYssTl2093fX1AuO23W33WKMuC8XpPfH5gygAAAAAAAAAAAAAA5nNue7XYV8v9pnnsxxPZ6wU3e77c7zNOXPbzWnsjuiPCAa4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANjZcw3WzyefBea8fir3T64BcOT87nf/AAXw2x5NNfNETNJ9oOqAAAAAAAAAAACL3ilLXnsrGs6AqvNeqNxbzYdrS2GO/Jbhb2R3Ar172vab3mbWtxm08ZkEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaeAOpy/p7f7zS01+1i/Xbh7oBYtl0zy7b/Fev3r+N+z3A61KUpWK0iK1jsiOEAkAAAAAAAAAAAAGtuuXbLdRMZ8VbTMaebTj7wcHf9IaVm+zvrP93f8AKQV7c7Tcba80zY5paPGAYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdDlvJN7vrVmlfLhmeOW3Zp6PEFq5f0/sNnEW8v3csfXfj7oB0/UAAAAAAAAAAAAAAAAADFuNrt9xSaZscXrPjAK5zTpOYicmxnXjxxW/KQVzLiy4ck48tZpevbWe0HyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6x4smW8Y8dZte3CKwC0co6WpTy5t78V+2MXdHr8QWKtK0rFaRFaxwiI7ASAAAAAAAAAAAAAAAAAAAADT5hyrZ76k1zU+PuyR80e0FP5pyTdbC+to8+GflyR+YOcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADZ2HL9zvs32sFdf1WnsiPSC68q5NtuX4/h+PNMfHkmOM+oG+AAAAAAAAAAAAAAAAAAAAAAACL0pes1vEWrMaTEgqnOumbYtdxsom2Ptti749XoBXgAQCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAb3KuU5+YZorWNMUfPk7oBd9lsdvssEYcFdIj5rd8z4yDYAAAAAAAAAAAAAAAAAAAAAAAAAABXufdO0yxbdbSvly9t8cdk+mAVS1Zraa2jS0cJiQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADf5RynLzDcRWNa4a8cmTwgF5222w7bDXDhr5cdeyPzkGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAHC6h5DXc0tutvGm4rxvWPrj+kFPms1mYmNJjhMAAAAAgEgAAAAAAAAAAAAAAAAAAAAAAA2+Wcuzb/c1w4+Fe29+6IBe9ns8G0wVw4Y0rWOM98z4yDOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACu9R8ijJW2821f3Kxrkxx3x4wCqAAAAAAAAAAAAAAAAAAAAAAAAAAAAybfBl3GamHFHmvedIgF85VyzDsNtXHSNbzxyX75kG6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMRMaTxgFQ6l5LG2v/N4K/s3n9ysdlZn+kHBAAAAAAAAAAAAAAAAAAAAAAAAAA9QLl03yf8AlcP8zmjTPkjhE/TUHbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8ZsVM2K2LJGtLxpaPRIKHzjlmTl+6nHOs4rccVvGAaIAAAAAAAAAAAAAAIBIAIBIAAAAAO101yn+a3H38tf2MU66THC1vAFz7I0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoc55ZTf7S1NIjLXjjtpxifD2goeTHfHe1LxNbVnS0T4wD5BAAJAAAAAAAAAAAAAAAAAAABk223ybjPTDjjW15iIB6DsNnj2e1x7enGKRxnxnvkGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq9V8r8l432P5bzpljwnxBXAQCQAAAAAAAAAAAAAAAQCQAAAWnpPlkRSd9kj4p1ri9XfILIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFutti3OC+DLGtLxpIPPt9tL7TdZMF+2k6RPjHdIMAAAAAAAAAAAAAAAAAAAAAM+x2l93u8e3p23nSZ8I75B6Hgw0w4aYscaUpEVrHqB9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArnV3LvPipvaR8VPhy+qeyQVUAAAAAAAAAAAAAAAAAAAAFn6P2MxGXeWiNJ/bxz3/1pBZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfG4w0zYL4bxrXJWazE+kHnO52+Tb58mHJwvjtNZ9gMYAAAAAAAAAAAAAAAAAAPrHS2S9aV+a0xWPXPAHomx2tdrtMW3idft1iNfGe8GcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT6v2Xk3GPd17MkeS0emvZ/AFeAAAABAJAAAAAAAAAAAAB2Ol9n9/mUZLRE0wR5uPjPCAXUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjzvaRuuW5sekeaI81NfGvEFAAAAAAAAAAAAAAAAAAABc+lNp9nl33Zj4s8+b2RwgHaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmNeAPPucbWdrzHNi00r5vNX1W4g0wAAAAAAAAAAAAAAAAfWPHbJkrjr815iI9oPR9rhrh2+PFXspWI90AyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq3WO10yYdzEcLRNLezjAK2AAAAAAAAAAAAAAAADpdPbf7/NcMTHw0nzz7AXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK6l2/3uVZJiJm2PS8aegFHAAAAAAAAAAAAAAAABY+jcGuXPmn6Yise3iC1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx7nHGXb5Mc9lqzH8Aeb3rNL2rPbWZifYCAAAAAAAAAAAAAAAAXPpPD9vlnnnty3mfdwB2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAefc5w/Z5nuKd3nmY9vEGmAAAAAAAAAAAAAAAD0Dk2L7XK9vX+pEzr6QboAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKX1Zi8nNPNH10ifcDjAAAAAAAAAAAAAAAmtfNaK+M6A9I29Ipt8dYjSK1iP4AyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq3WWPTLt8kR21msyCtgAAAAAAAAAAAAAAy7Snn3WGvjev4g9IiNIiI7IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXusseu2wX74vMe+AVMAAAAAAAAAAAAAAG1yvy/wCY7fzdnnjtB6GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADh9X+X/Lqa9vnjQFOAAAAAAAB//Z"
+export const LoginBg = "../Layout/LoginBg.jpg";
+
+export const BaseURL = "https://64df594871c3335b25827869.mockapi.io/"
+
+
+
+export const convert_data_to_select = (array = []) => {
+ if (typeof array === 'undefined' || !Array.isArray(array)) {
+ throw new Error("Expected an array argument")
+ }
+ let new_array :any = []
+ array.map((e:any) => new_array.push({ label: e?.name, value: e?.id }))
+
+ return new_array
+ }
+
+ export const FakeSelectData :any = [
+ {
+ name : "mhmad",
+ id : 1
+ },
+ {
+ name : "karim",
+ id : 2
+ },
+ {
+ name : "suliman",
+ id : 3
+ },
+ {
+ name : "ibrahim",
+ id : 4
+ },
+]
+
+
+export const hasValue = (fieldValue: any, name: string): boolean => {
+ if (Array.isArray(fieldValue)) {
+ return fieldValue.length > 0;
+ } else if (typeof fieldValue === "object") {
+ return Object.keys(fieldValue).length > 0;
+ } else {
+
+ return !!fieldValue;
+ }
+};
\ No newline at end of file
diff --git a/src/Layout/app/ErrorPage.tsx b/src/Layout/app/ErrorPage.tsx
new file mode 100644
index 0000000..c8ce7f0
--- /dev/null
+++ b/src/Layout/app/ErrorPage.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { Button, Result } from 'antd';
+import { useTranslation } from 'react-i18next'; // Import useTranslation hook
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import { useQueryClient } from 'react-query';
+const ErrorPage: React.FC = () => {
+ const { t } = useTranslation(); // Initialize useTranslation hook
+ const naviagate = useNavigate()
+ const location = useLocation();
+ const queryClient = useQueryClient(); // Initialize useQueryClient hook
+
+
+ const handleRefetch = () => {
+
+ const firstPath = location.pathname.split('/')[1]; // Get the first path segment from the URL
+ console.log(firstPath,"firstPath");
+
+ queryClient.invalidateQueries(firstPath === "/" ? 'home' : firstPath);
+ };
+
+ const handleGoToLogin = () => {
+ naviagate("/")
+ };
+
+ return (
+
+ {t('errorPage.refetch')} {/* Translate button text */}
+ ,
+ ,
+ ]}
+ >
+
+
+ );
+};
+
+export default ErrorPage;
diff --git a/src/Layout/app/Etaxi.tsx b/src/Layout/app/Etaxi.tsx
new file mode 100644
index 0000000..dd85aa4
--- /dev/null
+++ b/src/Layout/app/Etaxi.tsx
@@ -0,0 +1,41 @@
+import React from 'react'
+
+const Etaxi = () => {
+ return (
+
+
+ )
+}
+
+export default Etaxi
\ No newline at end of file
diff --git a/src/Layout/app/Export.tsx b/src/Layout/app/Export.tsx
new file mode 100644
index 0000000..c6dea28
--- /dev/null
+++ b/src/Layout/app/Export.tsx
@@ -0,0 +1,11 @@
+import DashHeader from '../../Layout/Dashboard/DashHeader';
+import LyTable from '../Dashboard/LyTable';
+import LayoutModal from '../Dashboard/LayoutModal'
+
+
+
+export {
+ DashHeader,
+ LyTable,
+ LayoutModal
+}
\ No newline at end of file
diff --git a/src/Layout/app/Header.tsx b/src/Layout/app/Header.tsx
new file mode 100644
index 0000000..5abb651
--- /dev/null
+++ b/src/Layout/app/Header.tsx
@@ -0,0 +1,65 @@
+import React from 'react'
+import { UserImageURL } from './Const'
+import Translate from '../../Components/Utils/Translate'
+import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router-dom';
+import useAuthState from '../../lib/state mangment/AuthState';
+import { GiHamburgerMenu } from 'react-icons/gi';
+import WithDrawer from './WithDrawer';
+import Sidebar from './SideBar';
+import { Dropdown, type MenuProps } from 'antd';
+
+
+const Header = () => {
+
+
+ const [t] = useTranslation();
+ const navigate = useNavigate()
+
+ const { logout , user} = useAuthState()
+ const handelClick = () => {
+ logout()
+ navigate('/auth')
+ }
+
+
+ const items: MenuProps['items'] = [
+ {
+ key: '1',
+ label: (
+ {t("Log Out")}
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+ )
+}
+
+export default Header
\ No newline at end of file
diff --git a/src/Layout/app/KarimLogo.tsx b/src/Layout/app/KarimLogo.tsx
new file mode 100644
index 0000000..6c147ee
--- /dev/null
+++ b/src/Layout/app/KarimLogo.tsx
@@ -0,0 +1,54 @@
+import React from 'react'
+
+const KarimLogo = () => {
+ return (
+<>
+
+>
+ )
+}
+
+export default KarimLogo
\ No newline at end of file
diff --git a/src/Layout/app/Layout.tsx b/src/Layout/app/Layout.tsx
new file mode 100644
index 0000000..ca21133
--- /dev/null
+++ b/src/Layout/app/Layout.tsx
@@ -0,0 +1,39 @@
+import React, { useEffect } from 'react'
+import SideBar from './SideBar'
+import Header from './Header'
+import { useNavigate } from 'react-router-dom'
+import useAuthState from '../../lib/state mangment/AuthState'
+
+const Layout = ({ children }: { children: React.ReactNode }) => {
+
+
+ const navigate = useNavigate()
+ const { isAuthenticated } = useAuthState()
+ useEffect(() => {
+ if (!isAuthenticated) {
+
+ navigate('/auth')
+ }
+ }, [navigate])
+ return (
+ <>
+
+ >
+ )
+}
+
+export default Layout
\ No newline at end of file
diff --git a/src/Layout/app/LoadingPage.tsx b/src/Layout/app/LoadingPage.tsx
new file mode 100644
index 0000000..45d436f
--- /dev/null
+++ b/src/Layout/app/LoadingPage.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import LoadingSpinner from '../../Components/Ui/LoadingSpinner'
+import { Spin } from 'antd'
+
+function LoadingPage() {
+ return (
+
+
+
+ )
+}
+
+export default LoadingPage
\ No newline at end of file
diff --git a/src/Layout/app/NotFoundPage.tsx b/src/Layout/app/NotFoundPage.tsx
new file mode 100644
index 0000000..316349a
--- /dev/null
+++ b/src/Layout/app/NotFoundPage.tsx
@@ -0,0 +1,17 @@
+import React from 'react'
+import { useNavigate } from 'react-router-dom'
+
+function NotFoundPage() {
+ const navigate = useNavigate()
+ return (
+
+
+
+
404
|
This page could not be found
+
+
+
+ )
+}
+
+export default NotFoundPage
\ No newline at end of file
diff --git a/src/Layout/app/SideBar.tsx b/src/Layout/app/SideBar.tsx
new file mode 100644
index 0000000..8016a30
--- /dev/null
+++ b/src/Layout/app/SideBar.tsx
@@ -0,0 +1,116 @@
+import React, { useEffect, useState } from 'react';
+import { FaAngleDown, FaAngleRight } from 'react-icons/fa';
+import { GiHamburgerMenu } from 'react-icons/gi';
+import { Link, useLocation } from 'react-router-dom';
+import { RoutesLinks } from '../../Routes';
+import { useTranslation } from 'react-i18next';
+import KarimLogo from './KarimLogo';
+import { useWindowSize } from '../../Hooks/useWindowSize';
+import Etaxi from './Etaxi';
+import { usePageState } from '../../lib/state mangment/LayoutPagestate';
+
+interface SidebarProps {}
+
+const Sidebar: React.FC = () => {
+ const { pathname } = useLocation();
+ // const {isOpenAddModel ,isOpenEditModel} = usePageState(state => state)
+ // useEffect(() => {
+ // if(isOpenAddModel || isOpenEditModel){
+ // setIsOpenSide(true);
+ // document.getElementById('DashboardLayout_Body')?.classList.add('DashboardLayout_Body_Open');
+ // }
+
+
+
+ // }, [isOpenAddModel ,isOpenEditModel])
+
+
+
+ const [isOpenSide, setIsOpenSide] = useState(false);
+ const [openDropdown, setOpenDropdown] = useState(null);
+ const [t] = useTranslation();
+ const windowSize = useWindowSize()
+
+ const handleHamburgerMenu = () => {
+ setIsOpenSide(true);
+ document.getElementById('DashboardLayout_Body')?.classList.add('DashboardLayout_Body_Open');
+ };
+
+ const handleImg = () => {
+ setIsOpenSide(false);
+ document.getElementById('DashboardLayout_Body')?.classList.remove('DashboardLayout_Body_Open');
+ setOpenDropdown(null);
+ };
+
+ const handleDropdown = (index: number) => {
+ setOpenDropdown((prev) => (prev === index ? null : index));
+ };
+
+ return (
+
+
+
+

+ {/*
*/}
+
+
+
+
+
+
+ {RoutesLinks?.map((item: any, index: number) => {
+ const isActive = pathname === item?.href;
+ const isDropdownOpen = openDropdown === index;
+
+ if (item?.hidden) {
+ return null;
+ }
+
+ if (item?.href ) {
+ return (
+
+ {React.cloneElement(item.icon, { size: 30 })}
+
{t(`${item?.name}`)}
+
+ );
+ } else {
+ return (
+
+ handleDropdown(index)}
+ className={
+ isDropdownOpen
+ ? 'SideBar_Link DropDown DropDown_SideBar_Link Open'
+ : 'SideBar_Link DropDown'
+ }
+ >
+ {React.cloneElement(item.icon, { size: 30 })}
+
{t(`${item?.name}`)}
+
{isDropdownOpen ? : }
+
+ {isDropdownOpen &&
+ item?.children?.map((child: any, childIndex: number) => {
+ if (child?.href) {
+ return (
+
+ {React.cloneElement(child.icon, { size: 30 })}
+ {t(`${child?.name}`)}
+
+ );
+ }
+ return null;
+ })}
+
+ );
+ }
+ })}
+
+
+ );
+};
+
+export default Sidebar;
diff --git a/src/Layout/app/Types.tsx b/src/Layout/app/Types.tsx
new file mode 100644
index 0000000..b8a4dc7
--- /dev/null
+++ b/src/Layout/app/Types.tsx
@@ -0,0 +1,19 @@
+export interface FormTableState {
+ objectToEdit: any[];
+ OpenEdit: boolean;
+ OpenAdd: boolean;
+ }
+
+ export type ValidationFieldProps = {
+ name: string;
+ name2?: string;
+ type: string;
+ placeholder?: string;
+ label?: string;
+ className?: string;
+ option?: any;
+ isMulti?: boolean;
+ Disabled?: boolean;
+ group ? : boolean;
+ dir?:'rtl' | 'ltr'
+ };
\ No newline at end of file
diff --git a/src/Layout/app/WithDrawer.tsx b/src/Layout/app/WithDrawer.tsx
new file mode 100644
index 0000000..2c788a1
--- /dev/null
+++ b/src/Layout/app/WithDrawer.tsx
@@ -0,0 +1,53 @@
+import React, { useState, ReactNode } from 'react';
+import type { DrawerProps } from 'antd';
+import { Drawer, Space } from 'antd';
+
+interface WithDrawerProps {
+ button: React.ReactNode;
+ children: ReactNode;
+ className?:string
+}
+
+const WithDrawer: React.FC = ({ button, children,className }) => {
+ const [open, setOpen] = useState(false);
+ const [placement, setPlacement] = useState('left');
+
+ let What_the_language = localStorage.getItem('language') ?? "en";
+
+
+ return (
+ <>
+
+ {React.cloneElement(button as React.ReactElement, {
+ onClick: () => setOpen(true),
+ })}
+
+ setOpen(false)}
+ open={open}
+ key={What_the_language}
+ width="260"
+
+
+
+ >
+
+ {children}
+
+
+
+ >
+ );
+};
+
+export default WithDrawer;
+
+
+// Open}
+// >
+// {/* Your content goes here */}
+//
\ No newline at end of file
diff --git a/src/Pages/Auth/LoginForm.tsx b/src/Pages/Auth/LoginForm.tsx
new file mode 100644
index 0000000..459917f
--- /dev/null
+++ b/src/Pages/Auth/LoginForm.tsx
@@ -0,0 +1,83 @@
+import React from 'react'
+import { Formik, Form, Field } from 'formik';
+import Translate from '../../Components/Utils/Translate';
+import { useTranslation } from 'react-i18next';
+import { useLoginAdmin } from '../../api/auth';
+
+import * as Yup from "yup";
+import { getInitialValues, getValidationSchema } from './formUtil';
+import { LoadingButton } from '../../Components/Ui/LoadingButton';
+import useNavigateOnSuccess from '../../Hooks/useNavigateOnSuccess';
+import useAuthState from '../../lib/state mangment/AuthState';
+import ValidationField from '../../Components/ValidationField/ValidationField';
+
+const LoginForm = () => {
+ const [t] = useTranslation();
+
+ const {mutate , isLoading , isSuccess, data} = useLoginAdmin()
+ const {login} = useAuthState()
+const OnSuccess = ()=>{
+
+}
+ useNavigateOnSuccess(isSuccess , '/' , ()=>login(data?.data as any ))
+
+ const handelSubmit = (values:any)=>{
+
+ console.log(values);
+ mutate(values)
+ // Implemnt Your Auth Code
+ }
+
+ return (
+
+

+
+
+
+
+
+
+
+
+ )
+}
+
+export default LoginForm
\ No newline at end of file
diff --git a/src/Pages/Auth/Page.tsx b/src/Pages/Auth/Page.tsx
new file mode 100644
index 0000000..fe47926
--- /dev/null
+++ b/src/Pages/Auth/Page.tsx
@@ -0,0 +1,32 @@
+import React, { useEffect } from 'react'
+import LoginForm from './LoginForm';
+import { LoginBg } from '../../Layout/app/Const';
+import useAuthState from '../../lib/state mangment/AuthState';
+import { useNavigate } from 'react-router-dom';
+const Auth = () => {
+
+ const { isAuthenticated } = useAuthState();
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (isAuthenticated) {
+ navigate('/')
+ }
+ }, [])
+
+ return (
+
+
+
+

+
+
+
+
+
+
+
+ )
+}
+
+export default Auth
\ No newline at end of file
diff --git a/src/Pages/Auth/formUtil.ts b/src/Pages/Auth/formUtil.ts
new file mode 100644
index 0000000..1eebde0
--- /dev/null
+++ b/src/Pages/Auth/formUtil.ts
@@ -0,0 +1,36 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+
+interface formUtilCommon {
+ password:string,
+ email:string
+}
+
+
+interface InitialValues extends formUtilCommon {
+
+}
+interface ValidateSchema extends formUtilCommon{
+
+}
+
+export const getInitialValues = (): InitialValues => {
+
+
+ return {
+ password: "",
+ email:""
+ }
+
+
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // validate input
+ return Yup.object().shape({
+ email:Yup.string().required("required"),
+ password:Yup.string().required("required"),
+
+ });
+};
\ No newline at end of file
diff --git a/src/Pages/Categories/Page.tsx b/src/Pages/Categories/Page.tsx
new file mode 100644
index 0000000..a6cc0c8
--- /dev/null
+++ b/src/Pages/Categories/Page.tsx
@@ -0,0 +1,56 @@
+
+import React, { useEffect } from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import { useGetCategories } from '../../api/Categories'
+import SearchField from '../../Layout/Dashboard/SearchField'
+import { usePageState } from '../../lib/state mangment/LayoutPagestate'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,isRefetching ,status } = useGetCategories()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ console.log(objectToEdit,"objectToEdit");
+
+ useEffect(() => {
+ console.log(objectToEdit,"objectToEdit");
+ if(objectToEdit)
+ setObjectToEdit(null)
+
+
+ }, [setObjectToEdit,objectToEdit ,data,isRefetching])
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
navigate('/categories/add')}>
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/Categories/View/Add/AddForm.tsx b/src/Pages/Categories/View/Add/AddForm.tsx
new file mode 100644
index 0000000..36bc68f
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AddForm.tsx
@@ -0,0 +1,35 @@
+
+import React from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../../Components/ValidationField/ValidationField';
+import { useGetCategories } from '../../../../api/Categories';
+import useFormatToSelect from '../../../../Hooks/useFormatToSelect';
+
+function Form() {
+
+ const { data } = useGetCategories()
+ const SelectData = useFormatToSelect(data?.categories)
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/Categories/View/Add/AttributeTab/AttributeTabs.tsx b/src/Pages/Categories/View/Add/AttributeTab/AttributeTabs.tsx
new file mode 100644
index 0000000..bdacfbb
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeTab/AttributeTabs.tsx
@@ -0,0 +1,72 @@
+// AttributeTabs.tsx
+import React from 'react';
+import { Col, Row } from 'reactstrap';
+import { FormItem } from './Field/FormItem';
+import { useFormikContext, FormikValues } from 'formik';
+import File from './Field/File';
+import { useTranslation } from 'react-i18next';
+import { TabsContainer } from '../AttributeValueTab/TabsContainer';
+import SelectField from './Field/Select';
+
+interface AttributeTabsProps {
+ tabKey: string;
+}
+
+export const AttributeTabs: React.FC = ({ tabKey }) => {
+ const { t } = useTranslation();
+ const formikContext = useFormikContext();
+ const { values, handleChange } = formikContext;
+
+ const handleFieldChange = (fieldName: string) => (
+ e: React.ChangeEvent | any
+ ) => {
+ handleChange(`Attribute.${tabKey}.${fieldName}`)(e); // Prepend "Attribute"
+ };
+ const FormikName = (FormikFieldname: any) => values?.Attribute?.[tabKey]?.[FormikFieldname];
+
+
+
+ return (
+ <>
+ {t("Attribute")} {tabKey}
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/Pages/Categories/View/Add/AttributeTab/Field/File.tsx b/src/Pages/Categories/View/Add/AttributeTab/Field/File.tsx
new file mode 100644
index 0000000..cf627c0
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeTab/Field/File.tsx
@@ -0,0 +1,63 @@
+
+
+
+import { Button, Upload, UploadFile } from 'antd'
+import { UploadOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+
+
+const File = ({ tabKey}:any) => {
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const name = `Attribute[${tabKey}].${"main_photo"}`;
+ const imageUrl = formik?.values?.Attribute[tabKey]?.main_photo ? URL.createObjectURL(formik?.values?.Attribute[tabKey]?.main_photo) : "" ;
+
+ const fileList: UploadFile[] = [
+
+ {
+ uid: '-1',
+ name: formik?.values?.Attribute[tabKey]?.main_photo?.name ?? "",
+ status: 'done',
+ url: imageUrl ,
+ thumbUrl: imageUrl ,
+ }
+ ];
+ const FilehandleChange = (value:any) => {
+
+ formik.setFieldValue(name, value.file.originFileObj)
+
+ };
+ const customRequest = async ({ onSuccess}: any) => {
+ onSuccess();
+ };
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+export default File
\ No newline at end of file
diff --git a/src/Pages/Categories/View/Add/AttributeTab/Field/FormItem.tsx b/src/Pages/Categories/View/Add/AttributeTab/Field/FormItem.tsx
new file mode 100644
index 0000000..cc9b2d1
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeTab/Field/FormItem.tsx
@@ -0,0 +1,19 @@
+// FormItem.tsx
+import React from 'react';
+import { Input, Label } from 'reactstrap';
+
+interface FormItemProps {
+ label: string;
+ value: string;
+ onChange: (e: React.ChangeEvent) => void;
+ type?: any
+}
+
+export const FormItem: React.FC = ({ label, value, onChange ,type = "text"}) => {
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/src/Pages/Categories/View/Add/AttributeTab/Field/Select.tsx b/src/Pages/Categories/View/Add/AttributeTab/Field/Select.tsx
new file mode 100644
index 0000000..cc2aecf
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeTab/Field/Select.tsx
@@ -0,0 +1,41 @@
+import { Form, Select } from 'antd'
+import { useFormikContext } from 'formik';
+import React from 'react'
+import { useTranslation } from 'react-i18next';
+
+const SelectField = ({tabKey}: any) => {
+
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const Formikname = `Attribute[${tabKey}].type`;
+ const FormikValue = formik?.values?.Attribute[tabKey]?.["type"];
+
+ const onChange = (value:any) => {
+ formik.setFieldValue(Formikname,value)
+ console.log(value);
+ console.log(formik?.errors,"errors");
+
+
+ }
+ const Data = [{label: "color",value :"color"},{label: "text",value :"text"},{label: "image",value :"image"}]
+ return (
+
+
+
+
+
+ )
+}
+
+export default React.memo(SelectField);
diff --git a/src/Pages/Categories/View/Add/AttributeTab/TabsContainer.tsx b/src/Pages/Categories/View/Add/AttributeTab/TabsContainer.tsx
new file mode 100644
index 0000000..55c1654
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeTab/TabsContainer.tsx
@@ -0,0 +1,145 @@
+// TabsContainer.tsx
+import React, { useEffect, useState } from 'react';
+import { Tabs, Space } from 'antd';
+import { CopyOutlined } from '@ant-design/icons';
+import { FormikValues, useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+import { AttributeTabs as AddAttributeTabs} from '../../Add/AttributeTab/AttributeTabs';
+import { MdOutlineDeleteOutline } from 'react-icons/md';
+
+
+export const TabsContainer: React.FC = () => {
+ const [activeKey, setActiveKey] = useState('1');
+ const { setFieldValue } = useFormikContext();
+ const formikContext = useFormikContext();
+ const { values } = formikContext;
+ const [width, setWidth] = useState(window.innerWidth);
+ const [t] = useTranslation()
+
+
+const initialItemShape: any = {
+ label: `${t(`Attribute`)} 1`,
+ key: '1',
+ closable: true,
+};
+
+
+ const varianCount = values?.Attribute?.slice(1)?.map((item:any,index:any)=>{
+ return {
+ label: `${t(`Attribute`)}`+ `${index+1}`,
+ key: index+1,
+ closable: true,
+ }
+ })?? initialItemShape
+
+ const [items, setItems] = useState(varianCount ?? [initialItemShape]); // Ensure items is always an array
+ useEffect(() => {
+ // Set the active key to the first tab key when the component mounts or items change
+ if (items.length > 0) {
+ setActiveKey(items[0].key);
+ }
+ }, []);
+ const findFirstMissingKey = (itemKeys:string[]) => {
+ const keysAsNumbers = itemKeys.map(Number); // Convert strings to numbers
+ for (let i = 1; i <= keysAsNumbers.length + 1; i++) {
+ if (!keysAsNumbers.includes(i)) {
+ return i;
+ }
+ }
+ };
+ const [nextKey, setNextKey] = useState(items.map((item: any) => `${item.key}`)); // Initialize the key counter to the length of items + 1
+
+
+ useEffect(() => {
+ const keys = items.map((item: any) => `${item.key}`);
+ setNextKey(findFirstMissingKey(keys))
+
+ }, [items]);
+
+
+ const handleAdd = () => {
+
+ const newKey = `${nextKey}`;
+ setItems([...items, { key: newKey, label: `${t(`Attribute`)} ${newKey}`, closable: true ,Add:true}]);
+ setActiveKey(newKey);
+
+ };
+
+ const handleDuplicate = (targetKey: string) => {
+ const targetItem = items.find((item:any) => item.key === targetKey);
+ if (targetItem) {
+ const newKey = `${nextKey}`;
+ const newItem = { ...targetItem, key: newKey, label: `${t(`Attribute`)} ${newKey}`,Add:true };
+ setItems([...items, newItem]);
+ setActiveKey(newKey);
+ setNextKey((prevKey : any) => prevKey + 1);
+
+
+ const originalValues = values?.Attribute?.[targetKey];
+
+ setFieldValue(`Attribute.${newKey}`, {...originalValues,id:null});
+
+ }
+ };
+ const [removedAttribute, setRemovedAttribute] = useState([]);
+ useEffect(() => {
+ setFieldValue(`removedAttribute`, removedAttribute);
+
+ }, [removedAttribute]);
+
+
+ const handleRemove = (targetKey: string) => {
+ const newItems = items.filter((item:any) => item.key !== targetKey);
+ const removedItem = values?.Attribute[targetKey] as any;
+ if (removedItem?.id && removedItem?.id !== null &&removedItem?.id !== 0 ) {
+ setRemovedAttribute((prevRemovedAttribute) => [...prevRemovedAttribute, removedItem.id]);
+ }
+ const newActiveKey = newItems.length ? newItems[newItems.length - 1].key : '1';
+ setItems(newItems);
+ setActiveKey(newActiveKey);
+ setFieldValue(`Attribute.${targetKey}`, undefined);
+
+
+
+};
+
+useEffect(() => {
+ const handleResize = () => {
+ setWidth(window.innerWidth);
+ };
+
+ window.addEventListener('resize', handleResize);
+
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+}, []);
+const tabPosition = width > 1000 ? 'left' : 'top';
+
+ return (
+
+
+
(action === 'add' ? handleAdd() : handleRemove(targetKey))}
+ tabPosition={tabPosition}
+ removeIcon={}
+
+ items={items.map((item: any) => ({
+ label: {t(`${item.label}`)} handleDuplicate(item.key)} />,
+
+ children: ,
+
+ key: item.key,
+ closable: item.closable,
+ }))}
+ >
+
+ { items.length === 0 && (
+
{t("Add New Attribute")}
+ )}
+
+ );
+};
diff --git a/src/Pages/Categories/View/Add/AttributeValueTab/AttributeTabs.tsx b/src/Pages/Categories/View/Add/AttributeValueTab/AttributeTabs.tsx
new file mode 100644
index 0000000..017e015
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeValueTab/AttributeTabs.tsx
@@ -0,0 +1,116 @@
+// AttributeValueTabs.tsx
+import React, { useEffect, useState } from 'react';
+import { Col, Label, Row } from 'reactstrap';
+import { FormItem } from './Field/FormItem';
+import { useFormikContext, FormikValues } from 'formik';
+import File from './Field/File';
+import { useTranslation } from 'react-i18next';
+import { ColorPicker, type ColorPickerProps, type GetProp } from 'antd';
+
+interface AttributeValueTabsProps {
+ tabKey: string;
+ parentKey: string
+}
+
+export const AttributeValueTabs: React.FC = ({ parentKey, tabKey }) => {
+ const { t } = useTranslation();
+ const formikContext = useFormikContext();
+ const { values, handleChange,setFieldValue } = formikContext;
+ const [valuesChanged, setvaluesChanged] = useState(values?.Attribute[parentKey]?.type)
+
+ const handleFieldChange = (fieldName: string) => (
+ e: React.ChangeEvent | any
+ ) => {
+ handleChange(`Attribute.${parentKey}.AttributeValue.${tabKey}.${fieldName}`)(e); // Prepend "AttributeValue"
+ };
+ const FormikName = (FormikFieldname: any) => values?.Attribute?.[parentKey]?.AttributeValue?.[tabKey]?.[FormikFieldname];
+
+ useEffect(() => {
+ setvaluesChanged(values?.Attribute[parentKey]?.type)
+ }, [values?.Attribute[parentKey]?.type])
+
+
+
+ type Color = GetProp;
+ const handelchangeColor = (value: Color, hex: string)=>{
+ console.log(hex,"hex");
+
+ setFieldValue(`Attribute.${parentKey}.AttributeValue.${tabKey}.value_en`,hex)
+ }
+ return (
+ <>
+ {t("Value")} {tabKey}
+
+
+ {valuesChanged !== "color" ?
+ <>
+
+
+ >
+
+
+ :
+
+ {/* */}
+
+
+ ({color.toHexString()})}
+ onChange={handelchangeColor}
+ className=' '
+ format='hex'
+ size='large'
+
+
+ />
+
+
+
+ }
+
+
+
+
+
+ {valuesChanged !== "color" ?
+ <>
+
+
+ >
+
+
+ :
+ <>
+
+
+ >
+ }
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/Pages/Categories/View/Add/AttributeValueTab/Field/File.tsx b/src/Pages/Categories/View/Add/AttributeValueTab/Field/File.tsx
new file mode 100644
index 0000000..6926f69
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeValueTab/Field/File.tsx
@@ -0,0 +1,67 @@
+
+
+
+import { Button, Upload, UploadFile } from 'antd'
+import { UploadOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+import { ImageBaseURL } from '../../../../../../api/config';
+
+
+const FileAttributeaValue = ({ parentKey,tabKey}:any) => {
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const name = `Attribute.[${parentKey}].AttributeValue[${tabKey}].${"image"}`;
+ const fileObject = formik?.values?.Attribute?.[parentKey]?.AttributeValue?.[tabKey]?.["image"];
+
+ const FormikimageUrl = fileObject ? ImageBaseURL + fileObject : '';
+
+ const imageUrl = typeof fileObject === "string" ? (FormikimageUrl)?.replace("public", "/storage") : (fileObject instanceof File) ? URL.createObjectURL(fileObject) : "";
+
+ const fileList: UploadFile[] = [
+
+ {
+ uid: '-1',
+ name: "",
+ status: 'done',
+ url: imageUrl ,
+ thumbUrl: imageUrl ,
+ }
+ ];
+ const FilehandleChange = (value:any) => {
+
+ formik.setFieldValue(name, value.file.originFileObj)
+
+ };
+ const customRequest = async ({ onSuccess}: any) => {
+ onSuccess();
+ };
+ return (
+
+
+
+
+ }>
+ { t("upload_image") }
+
+
+
+
+
+
+
+ )
+}
+
+export default FileAttributeaValue
\ No newline at end of file
diff --git a/src/Pages/Categories/View/Add/AttributeValueTab/Field/FormItem.tsx b/src/Pages/Categories/View/Add/AttributeValueTab/Field/FormItem.tsx
new file mode 100644
index 0000000..91b4c0b
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeValueTab/Field/FormItem.tsx
@@ -0,0 +1,19 @@
+// FormItem.tsx
+import React from 'react';
+import { Input, Label } from 'reactstrap';
+
+interface FormItemProps {
+ label: string;
+ value: string;
+ onChange: (e: React.ChangeEvent) => void;
+ type?: any
+}
+
+export const FormItem: React.FC = ({ label, value, onChange ,type = "text"}) => {
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/src/Pages/Categories/View/Add/AttributeValueTab/Field/Hex.tsx b/src/Pages/Categories/View/Add/AttributeValueTab/Field/Hex.tsx
new file mode 100644
index 0000000..cf2f6f4
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeValueTab/Field/Hex.tsx
@@ -0,0 +1,18 @@
+import { ColorPicker } from 'antd'
+import React from 'react'
+import { Label } from 'reactstrap'
+
+const Hex = ({ label, value, onChange }:any) => {
+ return (
+
+
+
+ )
+}
+
+export default Hex
\ No newline at end of file
diff --git a/src/Pages/Categories/View/Add/AttributeValueTab/TabsContainer.tsx b/src/Pages/Categories/View/Add/AttributeValueTab/TabsContainer.tsx
new file mode 100644
index 0000000..9813c1d
--- /dev/null
+++ b/src/Pages/Categories/View/Add/AttributeValueTab/TabsContainer.tsx
@@ -0,0 +1,132 @@
+// TabsContainer.tsx
+import React, { useEffect, useRef, useState } from 'react';
+import { Tabs, Space } from 'antd';
+import { CopyOutlined } from '@ant-design/icons';
+import { toast } from 'react-toastify';
+import { FormikValues, useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+import { AttributeValueTabs } from './AttributeTabs';
+import { MdOutlineDeleteOutline } from 'react-icons/md';
+
+const { TabPane } = Tabs;
+
+
+export const TabsContainer= ({parentKey}:any) => {
+ const [activeKey, setActiveKey] = useState('1');
+ const [t] = useTranslation()
+
+ const { setFieldValue } = useFormikContext();
+ const formikContext = useFormikContext();
+ const { values } = formikContext;
+
+ const initialItemShape: any = {
+ label: `${t(`Value`)} 1`,
+ key: '1',
+ closable: true,
+ };
+ const varianCount = values?.Attribute[parentKey]?.AttributeValue?.slice(1)?.map((item:any,index:any)=>{
+ return {
+ label: `${t(`Value`)}`+ `${index+1}`,
+ key: `${index+1}`,
+ closable: true,
+ }
+ })?? initialItemShape
+
+ const [items, setItems] = useState(Array.isArray(varianCount) ? varianCount : [initialItemShape]); // Ensure items is always an array
+ useEffect(() => {
+ if (items.length > 0) {
+ setActiveKey(items[0].key);
+ }
+ }, []);
+ const findFirstMissingKey = (itemKeys:string[]) => {
+ const keysAsNumbers = itemKeys.map(Number); // Convert strings to numbers
+ for (let i = 1; i <= keysAsNumbers.length + 1; i++) {
+ if (!keysAsNumbers.includes(i)) {
+ return i;
+ }
+ }
+ };
+ const [nextKey, setNextKey] = useState(items.map((item: any) => `${item.key}`)); // Initialize the key counter to the length of items + 1
+
+
+ useEffect(() => {
+ const keys = items.map((item: any) => `${item.key}`);
+ setNextKey(findFirstMissingKey(keys))
+
+ }, [items]);
+
+ const handleAdd = () => {
+ const newKey = `${nextKey}`;
+ setItems([...items, { key: newKey, label: `${t(`Value`)} ${newKey}`, closable: true }]);
+ setActiveKey(newKey);
+ };
+
+ const handleDuplicate = (targetKey: string) => {
+ const targetItem = items.find((item:any) => item.key === targetKey);
+ if (targetItem) {
+ const newKey = `${nextKey}`;
+ const newItem = { ...targetItem, key: newKey, label: `${t(`Value`)} ${newKey}` };
+ setItems([...items, newItem]);
+ setActiveKey(newKey);
+
+ const originalValues = values?.Attribute[parentKey]?.AttributeValue?.[targetKey];
+ setFieldValue(`Attribute[${parentKey}].AttributeValue[${newKey}]`, {...originalValues,id:null});
+ }
+ };
+
+ const [removedAttributeValue, setremovedAttributeValue] = useState([]);
+
+ useEffect(() => {
+ setFieldValue(`removedAttributeValue[${parentKey}]`, removedAttributeValue);
+
+ }, [removedAttributeValue]);
+
+ const handleRemove = (targetKey: string) => {
+
+ const newItems = items.filter((item:any) => item?.key !== targetKey);
+ console.log(newItems,"newItems",targetKey,"targetKey");
+ const removedItem = values?.Attribute[parentKey]?.AttributeValue[targetKey] as any;
+ if (removedItem?.id && removedItem?.id !== null &&removedItem?.id !== 0 ) {
+ console.log(removedItem?.id,"removedItem?.id");
+ setremovedAttributeValue((prevremovedAttributeValue) => [...prevremovedAttributeValue, removedItem.id]);
+ }
+ const newActiveKey = newItems.length ? newItems[newItems.length - 1].key : '1';
+ setItems(newItems);
+ setActiveKey(newActiveKey);
+ setFieldValue(`Attribute.${parentKey}.AttributeValue.${targetKey}`, undefined);
+};
+
+
+const tabPosition ='top';
+
+ return (
+ (action === 'add' ? handleAdd() : handleRemove(targetKey))}
+ tabPosition={tabPosition}
+ removeIcon={}
+
+
+ >
+ {items.map((item :any) =>{
+ return (
+
+
+ {t(`${item.label}`)}
+ handleDuplicate(item.key)} />
+
+ }
+ closable={item.closable}
+ >
+
+
+ )
+ })}
+
+ );
+};
diff --git a/src/Pages/Categories/View/Add/fn/isvalidation.ts b/src/Pages/Categories/View/Add/fn/isvalidation.ts
new file mode 100644
index 0000000..de38382
--- /dev/null
+++ b/src/Pages/Categories/View/Add/fn/isvalidation.ts
@@ -0,0 +1,87 @@
+import { toast } from 'react-toastify';
+
+
+export const isvalidation = (attributes: any[] , t: any) => {
+ const validationResults: boolean[] = [];
+ let previousNames: { [key: string]: string } = {};
+
+ attributes?.slice(1)?.forEach((item: any, index: number) => {
+ if (item && Object.keys(item).length > 0) {
+ const itemName = t('name');
+ const itemNumber = index + 1;
+
+ const currentItemNames = {
+ name_ar: item.name_ar,
+ name_en: item.name_en,
+ name_cn: item.name_cn,
+ };
+
+ if (
+ previousNames.name_ar === currentItemNames.name_ar ||
+ previousNames.name_en === currentItemNames.name_en ||
+ previousNames.name_cn === currentItemNames.name_cn
+ ) {
+ toast.error(`${itemName} ${t('unique_error_names')}`);
+ validationResults.push(false);
+ }
+
+ previousNames = currentItemNames;
+
+ // Check for other validation rules
+ if (!item?.name_ar || !item?.name_en || !item?.name_cn) {
+ toast.error(`${t('required_name')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (!item?.type) {
+ toast.error(`${t('required_type')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (item?.type === "image") {
+ if (Array.isArray(item.AttributeValue)) {
+ item.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem.image === null) {
+ toast.error(`${t('required_image')} ${index + 1}`);
+ validationResults.push(false);
+ } else if (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de) {
+ toast.error(`${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "color") {
+ if (Array.isArray(item.AttributeValue)) {
+ item.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ const hexColorRegex = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
+ if (attrItem && !hexColorRegex.test(attrItem?.value_en)) {
+ toast.error(`${t('required_color')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "text") {
+ if (Array.isArray(item.AttributeValue)) {
+ item.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem && (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de)) {
+ toast.error(` ${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+
+ return validationResults;
+};
diff --git a/src/Pages/Categories/View/AddPage.tsx b/src/Pages/Categories/View/AddPage.tsx
new file mode 100644
index 0000000..b598e5d
--- /dev/null
+++ b/src/Pages/Categories/View/AddPage.tsx
@@ -0,0 +1,254 @@
+import React, { useEffect, useState } from 'react'
+import { getValidationSchema, getDataToSend ,getInitialValuesForAdd as getInitialValues} from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import { useAddCategories } from '../../../api/Categories';
+import Form from './Add/AddForm';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import { useAddAttribute } from '../../../api/attribute';
+import { TabsContainer } from './Add/AttributeTab/TabsContainer';
+import { useAddAttributeValue } from '../../../api/attributeValue';
+
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { toast } from 'react-toastify';
+
+const AddcategoriesPage = () => {
+
+ const { setObjectToEdit, objectToEdit } = usePageState()
+
+ const {mutate,isLoading:IsloadingButton ,isSuccess,data } = useAddCategories()
+ const {mutateAsync} = useAddAttribute()
+ const {mutate:AddAttributeValue } = useAddAttributeValue()
+
+ const [Attribute , setAttribute] = useState([])
+ const [AttributeValues , setAttributeValues] = useState([])
+
+
+ const handleSubmit = (values:any)=>{
+ console.log(values,"values");
+ function isValid(){
+ values?.Attribute?.slice(1)?.forEach((item:any) => {
+ if (item && Object.keys(item).length > 0){
+ setAttribute((prevAddAttributeValue) => [...prevAddAttributeValue, item]);
+ }
+
+ });
+
+
+ setAttributeValues(values?.Attribute?.slice(1)?.map((item:any)=>{
+ if (item && Object.keys(item).length > 0){
+ return item?.AttributeValue
+ }
+ }))
+ const CategoriesValues = {
+ name: {
+ en:values?.name_en,
+ ar:values?.name_ar,
+ cn:values?.name_cn
+ },
+ parent_id:values?.parent_id,
+ photo:values?.photo,
+ }
+ mutate(CategoriesValues)
+ }
+ const validationResults: boolean[] = [];
+ let previousNames: any = {};
+
+ values?.Attribute?.slice(1)?.forEach((item: any, index: any) => {
+ if (item && Object.keys(item).length > 0) {
+ const itemName = t('name');
+ const itemNumber = index + 1;
+
+ // Check if the names are unique across items
+ const currentItemNames = {
+ name_ar: item?.name_ar,
+ name_en: item?.name_en,
+ name_cn: item?.name_cn,
+ };
+
+ if (
+ previousNames.name_ar === currentItemNames.name_ar ||
+ previousNames.name_en === currentItemNames.name_en ||
+ previousNames.name_cn === currentItemNames.name_cn
+ ) {
+ toast.error(`${itemName} ${t('unique_error_names')}`);
+ validationResults.push(false);
+ }
+
+ previousNames = currentItemNames;
+
+ // Check for other validation rules
+ if (!item?.name_ar || !item?.name_en || !item?.name_cn) {
+ toast.error(`${t('required_name')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (!item?.type) {
+ toast.error(`${t('required_type')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (item?.type === "image") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem.image === null) {
+ toast.error(`${t('required_image')} ${index + 1}`);
+ validationResults.push(false);
+ } else if (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de) {
+ toast.error(`${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "color") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ const hexColorRegex = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
+ if (attrItem && !hexColorRegex.test(attrItem?.value_en)) {
+ toast.error(`${t('required_color')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "text") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem && (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de)) {
+ toast.error(` ${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+
+ if (validationResults.every((result) => result)) {
+ isValid();
+ }
+
+
+
+ }
+ useEffect(()=>{
+
+ if(isSuccess){
+ const categoryId = (data as any )?.id ;
+
+ Attribute?.map((dataToSend:any , index:number)=>{
+
+ const Attribute = dataToSend
+
+ const NewAttribute = {
+ name:{
+ en:Attribute?.name_en,
+ ar:Attribute?.name_ar,
+ de:Attribute?.name_cn
+ },
+ type:Attribute?.type,
+ category_id:categoryId,
+ }
+
+ mutateAsync(NewAttribute).then((data)=>{
+ const AttributeId = (data as any )?.id ;
+ console.log(AttributeValues[0]?.slice(1),"AttributeValues");
+ AttributeValues[index]?.slice(1)?.map((dataToSend:any , index:number)=>{
+
+ const AttributeValue = dataToSend
+ const IMage =
+ (typeof AttributeValue?.image === 'string') ?
+ {
+ copied_image: AttributeValue?.image
+ } :
+ {
+ image: AttributeValue?.image
+ };
+ const NewAttributeValues = {
+ value:{
+ en:AttributeValue?.value_en,
+ ar:AttributeValue?.value_ar,
+ de:AttributeValue?.value_de
+ },
+ ...IMage,
+ attribute_id:AttributeId,
+ }
+ if (NewAttribute.type === "color") {
+ NewAttributeValues["value"] = {
+ en: AttributeValue?.value_en,
+ ar: AttributeValue?.value_en,
+ de: AttributeValue?.value_en
+ }
+ }
+
+ AddAttributeValue(NewAttributeValues)
+
+ })
+ })
+ })
+
+ }
+
+ if(isSuccess){
+ setObjectToEdit([]);
+ }
+ },[isSuccess])
+
+
+ const {t} = useTranslation();
+
+ useNavigateOnSuccess(isSuccess , '/categories' )
+
+ useEffect(() => {
+
+ setObjectToEdit([]);
+
+ }, []);
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+ {/*
{t("attributes")}
*/}
+
+
+
+
+
+
+ {/*
+
+ */}
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddcategoriesPage
+
diff --git a/src/Pages/Categories/View/Atteibute.tsx b/src/Pages/Categories/View/Atteibute.tsx
new file mode 100644
index 0000000..4159e25
--- /dev/null
+++ b/src/Pages/Categories/View/Atteibute.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useGetSingleAttribute } from '../../../api/attribute';
+
+const Attribute = () => {
+ const { values, setFieldValue } = useFormikContext();
+
+ const { data: attributeData } = useGetSingleAttribute({ name: "category_id", id: values?.id });
+
+ const handleAttributeChange = (name: string, value: any) => {
+ setFieldValue(`attribute[${name}]`, value);
+ };
+
+ return (
+ attributeData?.data?.map((item: any) => {
+ const options = item?.attribute_value?.map((attr: any) => ({
+ label: attr?.value,
+ value: attr?.attribute_id
+ }));
+
+ return (
+ handleAttributeChange(item?.name, value)}
+ />
+ );
+ })
+ );
+};
+
+export default Attribute;
diff --git a/src/Pages/Categories/View/EditPage.tsx b/src/Pages/Categories/View/EditPage.tsx
new file mode 100644
index 0000000..92e39a8
--- /dev/null
+++ b/src/Pages/Categories/View/EditPage.tsx
@@ -0,0 +1,535 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValues, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { Spin } from 'antd';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import { useParams } from 'react-router-dom';
+import LoadingPage from '../../../Layout/app/LoadingPage';
+import { useTranslation } from 'react-i18next';
+import { useGetOneCategories, useUpdateCategories } from '../../../api/Categories';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { TabsContainer } from './Add/AttributeTab/TabsContainer';
+import { useAddAttribute, useGetSingleAttribute } from '../../../api/attribute';
+import { useDeleteAttribute, useUpdateAttribute } from '../../../api/attribute';
+import { useAddAttributeValue, useDeleteAttributeValue, useUpdateAttributeValue } from '../../../api/attributeValue';
+import { toast } from 'react-toastify';
+import Form from './Add/AddForm';
+import filterUndefinedAndEmpty from '../../../utils/Array/filterUndefinedAndEmpty';
+
+const EditPage = () => {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const { t } = useTranslation();
+ const { data, isLoading, isRefetching } = useGetOneCategories()
+ const { id } = useParams()
+
+ const { data: Atrribute, isLoading: isLoadingAtrribute, isRefetching: AttributeisRefetching } = useGetSingleAttribute({ name: "category_id", id: id }, {})
+ const { mutate, isSuccess,isLoading:IsloadingButton } = useUpdateCategories()
+ const { mutateAsync } = useAddAttribute()
+ const { mutate: mutateAttributeValue } = useAddAttributeValue()
+ const { mutate: UpdateAttribute } = useUpdateAttribute("put")
+ const { mutate: UpdateAttributeValue } = useUpdateAttributeValue()
+ const { mutate: DeleteAttributeValue } = useDeleteAttributeValue()
+ const { mutate: DeleteAttribute } = useDeleteAttribute()
+
+
+ const [removedAttribute, setremovedAttribute] = useState([])
+ const [removedAttributeValue, setremovedAttributeValue] = useState([])
+
+
+
+ const [AddAttribute, setAddAttribute] = useState([])
+ const [EditAttribute, setEditAttribute] = useState([])
+
+ const [AddAttributeValue, setAddAttributeValue] = useState([])
+ const [EditAttributeValue, setEditAttributeValue] = useState([])
+ const [OldData, setOldData] = useState(Atrribute?.data)
+ const [OldDataValues, setOldDataValues] = useState([])
+
+
+ const handleSubmit = (values: any) => {
+
+ function isValid() {
+ const attributes = values?.Attribute?.slice(1);
+ const flattenedAndFiltered = values?.removedAttributeValue?.flat().filter((value:any) => value !== undefined);
+
+ console.log(flattenedAndFiltered,"flattenedAndFiltered");
+
+ setremovedAttribute(values?.removedAttribute)
+ setremovedAttributeValue(flattenedAndFiltered)
+
+ if (attributes) {
+ attributes.forEach((item: any, index: number) => {
+ if (item?.id && item?.id !== null) {
+ console.log(item,"setEditAttribute");
+
+ setEditAttribute((prevEditAttribute) => [...prevEditAttribute, item]);
+ if (item?.AttributeValue) {
+ item.AttributeValue.slice(1).forEach((attrValueItem: any) => {
+ if (attrValueItem && Object.keys(attrValueItem).length > 0) { // Check if attrValueItem is defined and not empty
+ setEditAttributeValue((prevEditAttributeValue) => [...prevEditAttributeValue, [index, attrValueItem]]);
+ console.log(attrValueItem,"setEditAttributeValue");
+
+ }
+ });
+ }
+ } else {
+ setAddAttribute((prevAddAttribute) => [...prevAddAttribute, item]);
+ console.log(item,"setAddAttribute");
+
+ if (item?.AttributeValue) {
+ item?.AttributeValue?.slice(1)?.forEach((attrValueItem: any) => {
+ if (attrValueItem && Object.keys(attrValueItem)?.length > 0) { // Check if attrValueItem is defined and not empty
+ console.log(attrValueItem,"setAddAttributeValue");
+
+ setAddAttributeValue((prevEditAttributeValue) => [...prevEditAttributeValue, [index, attrValueItem]]);
+ }
+ });
+ }
+ }
+ });
+ }
+
+ const EditedCategory = {
+ name: {
+ en: values?.name_en,
+ ar: values?.name_ar,
+ ce: values?.name_ce
+ },
+ photo: values?.photo,
+ parent_id: values?.parent_id,
+ _method: "PUT"
+ }
+
+ const Imagetype = typeof values?.photo
+ if (Imagetype === "string") {
+ delete EditedCategory['photo']
+ }
+
+
+ mutate(EditedCategory)
+ }
+ const validationResults: boolean[] = [];
+ let previousNames: any = {};
+
+ values?.Attribute?.slice(1)?.forEach((item: any, index: any) => {
+ if (item && Object.keys(item).length > 0) {
+ const itemName = t('name');
+ const itemNumber = index + 1;
+
+ // Check if the names are unique across items
+ const currentItemNames = {
+ name_ar: item?.name_ar,
+ name_en: item?.name_en,
+ name_cn: item?.name_cn,
+ };
+
+ if (
+ previousNames.name_ar === currentItemNames.name_ar ||
+ previousNames.name_en === currentItemNames.name_en ||
+ previousNames.name_cn === currentItemNames.name_cn
+ ) {
+ toast.error(`${itemName} ${t('unique_error_names')}`);
+ validationResults.push(false);
+ }
+
+ previousNames = currentItemNames;
+
+ // Check for other validation rules
+ if (!item?.name_ar || !item?.name_en || !item?.name_cn) {
+ toast.error(`${t('required_name')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (!item?.type) {
+ toast.error(`${t('required_type')} ${itemNumber}`);
+ validationResults.push(false);
+ } else if (item?.type === "image") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem.image === null) {
+ toast.error(`${t('required_image')} ${index + 1}`);
+ validationResults.push(false);
+ } else if (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de) {
+ toast.error(`${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "color") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ const hexColorRegex = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
+ if (attrItem && !hexColorRegex.test(attrItem?.value_en)) {
+ toast.error(`${t('required_color')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else if (item?.type === "text") {
+ if (Array.isArray(item.AttributeValue)) {
+ item?.AttributeValue.slice(1)?.forEach((attrItem: any, index: number) => {
+ if (attrItem && Object.keys(attrItem).length > 0) {
+ if (attrItem && (!attrItem?.value_ar || !attrItem?.value_en || !attrItem?.value_de)) {
+ toast.error(` ${t('required_text')} ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ }
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+
+ if (validationResults.every((result) => result)) {
+ isValid();
+ }
+
+
+ }
+
+
+
+ function filterByIndex(number: number, array: [number, any][]): any[] {
+ return array.filter(([index, _]) => index === number).map(([_, item]) => item);
+ }
+
+
+ useEffect(() => {
+
+ if (isSuccess) {
+
+
+ const categoryId = id;
+ console.log(EditAttribute, "EditAttribute");
+ const filteredEditAttribute = filterUndefinedAndEmpty(EditAttribute);
+
+ filteredEditAttribute?.map((dataToSend: any, index: number) => {
+
+
+ const EditAttribute = dataToSend
+ const foundObject = OldData.find((item: any) => item.id === EditAttribute?.id) as any;
+
+ const UpdatedAttribute = {
+ name: {
+ en: EditAttribute?.name_en,
+ ar: EditAttribute?.name_ar,
+ de: EditAttribute?.name_cn
+ },
+ type: EditAttribute?.type,
+ // category_id:categoryId,
+
+
+ }
+ if (foundObject) {
+ Object.keys(UpdatedAttribute).forEach((propName: any) => {
+ if (foundObject.hasOwnProperty(propName)) {
+ const foundValue = foundObject[propName];
+ //@ts-ignore
+ const editedValue = UpdatedAttribute[propName];
+ if (foundValue === editedValue) {
+ //@ts-ignore
+ delete UpdatedAttribute[propName];
+ }
+ }
+ })
+ if (foundObject.name &&
+ foundObject.name.en === UpdatedAttribute.name.en &&
+ foundObject.name.ar === UpdatedAttribute.name.ar &&
+ foundObject.name.de === UpdatedAttribute.name.de) {
+ //@ts-ignore
+ delete UpdatedAttribute.name;
+ }
+ Object.keys(UpdatedAttribute).forEach((key: any) => {
+ //@ts-ignore
+ if (UpdatedAttribute[key] === undefined) {
+ //@ts-ignore
+ delete UpdatedAttribute[key];
+ }
+ });
+ if (Object.keys(UpdatedAttribute).length > 0) {
+ //@ts-ignore
+ // UpdatedAttribute._method = "PUT";
+ //@ts-ignore
+ UpdatedAttribute.id = foundObject?.id;
+ console.log(UpdatedAttribute, "EditAttribute");
+ UpdateAttribute(UpdatedAttribute)
+
+ }
+
+ }
+ const result: [number, any][] = filterByIndex(index, EditAttributeValue);
+ const filteredresult = filterUndefinedAndEmpty(result);
+ console.log(filteredresult, "EditAttributeValue");
+
+ filteredresult?.map((dataToSend: any) => {
+ if (dataToSend?.id && dataToSend.id !== null) {
+
+ const foundObjectValue = (OldDataValues[index] as any).find((item: any) => item.id === dataToSend?.id) as any;
+
+ const EditAttributeValue = dataToSend
+ console.log(EditAttributeValue);
+
+
+
+ const NewEditAttributeValue = {
+ id: EditAttributeValue?.id,
+ value: {
+ en: EditAttributeValue?.value_en,
+ ar: EditAttributeValue?.value_ar ?? EditAttributeValue?.value_en,
+ de: EditAttributeValue?.value_de ?? EditAttributeValue?.value_en
+ },
+ image: EditAttributeValue?.image,
+ // attribute_id:EditAttribute?.id,
+ // id: EditAttributeValue?.id,
+ }
+
+ if (EditAttribute.type === "color") {
+ NewEditAttributeValue["value"] = {
+ en: EditAttributeValue?.value_en,
+ ar: EditAttributeValue?.value_en,
+ de: EditAttributeValue?.value_en
+ }
+ }
+ if (EditAttributeValue?.main_photo === "string") {
+ delete NewEditAttributeValue['image']
+ }
+
+
+ if (foundObjectValue) {
+
+ Object.keys(NewEditAttributeValue).forEach((propName: any) => {
+ if (foundObjectValue.hasOwnProperty(propName)) {
+ const foundValue = foundObjectValue[propName];
+ //@ts-ignore
+ const editedValue = NewEditAttributeValue[propName];
+ if (foundValue === editedValue) {
+ //@ts-ignore
+ delete NewEditAttributeValue[propName];
+ }
+ }
+ })
+ Object.keys(NewEditAttributeValue).forEach((key: any) => {
+ //@ts-ignore
+ if (NewEditAttributeValue[key] === undefined) {
+ //@ts-ignore
+ delete NewEditAttributeValue[key];
+ }
+ });
+ if (foundObjectValue.value &&
+ foundObjectValue.value.en === NewEditAttributeValue.value.en &&
+ foundObjectValue.value.ar === NewEditAttributeValue.value.ar &&
+ foundObjectValue.value.de === NewEditAttributeValue.value.de) {
+ //@ts-ignore
+ delete NewEditAttributeValue.value;
+ }
+
+ if (Object.keys(NewEditAttributeValue).length > 0) {
+ //@ts-ignore
+ NewEditAttributeValue._method = "PUT";
+ //@ts-ignore
+ NewEditAttributeValue.id = foundObjectValue?.id;
+
+
+ UpdateAttributeValue(NewEditAttributeValue)
+
+ }
+ }
+
+
+
+ }
+ else {
+
+ const EditAttributeValue = dataToSend
+ // const IMage =
+ // (typeof EditAttributeValue?.image === 'string') ?
+ // {
+ // copied_image: EditAttributeValue?.image
+ // } :
+ // {
+ // image: EditAttributeValue?.image
+ // };
+ const NewEditAttributeValue = {
+
+ value: {
+ en: EditAttributeValue?.value_en,
+ ar: EditAttributeValue?.value_ar,
+ de: EditAttributeValue?.value_de
+ },
+ image:EditAttributeValue?.image,
+ attribute_id: EditAttribute?.id,
+ }
+ if (EditAttribute.type === "color") {
+ NewEditAttributeValue["value"] = {
+ en: EditAttributeValue?.value_en,
+ ar: EditAttributeValue?.value_en,
+ de: EditAttributeValue?.value_en
+ }
+ }
+ mutateAttributeValue(NewEditAttributeValue)
+ }
+
+
+
+ })
+
+ }
+
+ )
+ console.log(removedAttribute, "removedAttribute");
+ removedAttribute?.map((item: any) => {
+ DeleteAttribute({ id: item })
+ })
+
+ const filteredAddAttribute = filterUndefinedAndEmpty(AddAttribute);
+ console.log(filteredAddAttribute,"AddAttribute");
+
+ filteredAddAttribute?.map((dataToSend: any, index: number) => {
+
+ const AddAttribute = dataToSend
+
+ const NewAddAttribute = {
+ name: {
+ en: AddAttribute?.name_en,
+ ar: AddAttribute?.name_ar,
+ de: AddAttribute?.name_cn
+ },
+ type: AddAttribute?.type,
+ category_id: categoryId,
+ }
+
+ mutateAsync(NewAddAttribute).then((data: any) => {
+ const AttributeId = (data as any)?.id;
+ // console.log(AttributeValues[0]?.slice(1),"AttributeValues");
+ const result: [number, any][] = filterByIndex(index, AddAttributeValue);
+ console.log(AddAttribute?.AttributeValue,"AddAttribute?.AttributeValue");
+
+ AddAttribute?.AttributeValue?.slice(1)?.map((dataToSend: any, index: number) => {
+
+ const AddAttributeValue = dataToSend
+ // const IMage =
+ // (typeof AddAttributeValue?.image === 'string') ?
+ // {
+ // copied_image: AddAttributeValue?.image
+ // } :
+ // {
+ // image: AddAttributeValue?.image
+ // };
+ const NewAddAttributeValue = {
+ value: {
+ en: AddAttributeValue?.value_en,
+ ar: AddAttributeValue?.value_ar ?? AddAttributeValue?.value_en,
+ de: AddAttributeValue?.value_de ?? AddAttributeValue?.value_en
+ },
+ image:AddAttributeValue?.image,
+ attribute_id: AttributeId,
+ }
+ mutateAttributeValue(NewAddAttributeValue)
+
+ })
+ })
+
+ })
+ console.log(removedAttributeValue, "removedAttributeValue");
+ removedAttributeValue?.map((item: any) => {
+ console.log(item, "item");
+ return DeleteAttributeValue({ id: item })
+ })
+
+
+
+ }
+
+
+ }, [isSuccess])
+
+
+
+
+
+
+
+ useNavigateOnSuccess(isSuccess, '/categories')
+
+
+
+
+
+
+
+
+
+
+
+
+ useEffect(() => {
+
+ setObjectToEdit([data?.category, Atrribute?.data]);
+ setOldData(Atrribute?.data)
+
+ setOldDataValues(Atrribute?.data?.map((item: any) => {
+ return item?.attribute_value
+ }))
+
+ }, [data?.category, Atrribute?.data, isRefetching, AttributeisRefetching, setObjectToEdit,isLoadingAtrribute]);
+
+
+ const getValidationSchema = () => {
+ return null
+ };
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+ if (AttributeisRefetching || isRefetching || isLoadingAtrribute || isLoading || !objectToEdit || (Array.isArray(objectToEdit) && objectToEdit.some(item => item === undefined))) {
+ return
+ }
+
+
+
+ return (
+
+ {objectToEdit && !AttributeisRefetching && data ?
+
+
+
+
{t("BasicInfo")}
+ {/*
{t("attributes")}
*/}
+
+
+
+
+
+
+
+ {/*
+ {Atrribute?.data &&
+
+
+ }
+ */}
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default EditPage
\ No newline at end of file
diff --git a/src/Pages/Categories/formUtil.ts b/src/Pages/Categories/formUtil.ts
new file mode 100644
index 0000000..d01ba53
--- /dev/null
+++ b/src/Pages/Categories/formUtil.ts
@@ -0,0 +1,102 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+import { ImageBaseURL } from "../../api/config";
+import { fetchImage } from "../../Hooks/imageUrlToFile";
+import { useState } from "react";
+
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+
+ const Attribute = objectToEdit[1]?.map((item: any) => {
+ const AttributeValue = item?.attribute_value?.map((item:any)=>(
+ {
+ value_ar: item?.value["ar"],
+ value_en: item?.value["en"],
+ value_cn: item?.value["cn"],
+ id : item?.id,
+ image : item?.image,
+ Edited:false
+
+ } )
+ )
+ return (
+ {
+ name_ar: item?.name["ar"],
+ name_en: item?.name["en"],
+ name_cn: item?.name["cn"],
+ type : item?.type,
+ id : item?.id,
+ icon : item?.icon,
+ category_id : item?.category_id,
+ AttributeValue : [{},...AttributeValue ?? {}],
+ Edited:false
+
+ }
+ )
+ })
+ const imageUrl = ImageBaseURL + objectToEdit[0]?.photo?.replace("public", "/storage")
+
+
+ return {
+ id: objectToEdit[0]?.id ?? 0,
+ name_ar: objectToEdit[0]?.name["ar"],
+ name_en: objectToEdit[0]?.name["en"],
+ name_cn: objectToEdit[0]?.name["de"],
+ parent_id: objectToEdit[0]?.parent_id ?? "",
+ photo: objectToEdit[0]?.photo ?? '',
+ Attribute: [{},...Attribute],
+ removedAttribute:[],
+ removedAttributeValue:[],
+
+
+ };
+};
+export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
+ return {
+ name_ar: '',
+ name_en: '',
+ name_cn: '',
+ parent_id: null,
+ photo: '',
+ Attribute: [{}],
+
+
+ };
+};
+
+
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ name_ar: Yup.string().required('Required'),
+ name_en: Yup.string().required('Required'),
+ name_cn: Yup.string().required('Required'),
+ // parent_id: Yup.string().required('Required'),
+ photo: Yup.string().required('Required'),
+
+ });
+};
+
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data: any) => {
+
+ let new_array = data
+ for (let i = 0; i < data.length; i++) {
+ new_array[i]['status'] = !data[i]['deleted_at'] ? 'available' : 'unavailable'
+ delete new_array[i]['deleted_at']
+ }
+ return new_array
+}
\ No newline at end of file
diff --git a/src/Pages/Categories/useTableColumns.tsx b/src/Pages/Categories/useTableColumns.tsx
new file mode 100644
index 0000000..930c9f0
--- /dev/null
+++ b/src/Pages/Categories/useTableColumns.tsx
@@ -0,0 +1,75 @@
+
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+import ColumnsImage from "../../Components/Columns/ColumnsImage";
+import { useDeleteCategories } from "../../api/Categories";
+import { useNavigate } from "react-router-dom";
+
+
+const useTableColumns :any = () => {
+ const [t] = useTranslation();
+ const deleteMutation = useDeleteCategories()
+ const navigate = useNavigate()
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("name"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.name[language]
+ },
+ {
+ name: t("image"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => {
+ let str = row?.photo;
+ str = str?.replace(`public`, "/storage") ?? "";
+ return
+ }
+
+ },
+ {
+ name: t("parent_id"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.parent_id
+ },
+ {
+ name: t("product_count"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.product_count
+ },
+ {
+ name: "#",
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ navigate(`/categories/${row.id}`) }
+ showView={false}
+ onDelete={() => deleteMutation.mutate({ id: row.id })}
+ />
+ ),
+ },
+
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/Coupon/Page.tsx b/src/Pages/Coupon/Page.tsx
new file mode 100644
index 0000000..43fd85c
--- /dev/null
+++ b/src/Pages/Coupon/Page.tsx
@@ -0,0 +1,47 @@
+
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import { useGetCoupon } from '../../api/Coupon'
+import SearchField from '../../Layout/Dashboard/SearchField'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetCoupon()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
+
navigate('/coupon/add')}>
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/Coupon/View/AddForm.tsx b/src/Pages/Coupon/View/AddForm.tsx
new file mode 100644
index 0000000..7fc08a4
--- /dev/null
+++ b/src/Pages/Coupon/View/AddForm.tsx
@@ -0,0 +1,84 @@
+
+import React, { useEffect } from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useFormikContext } from 'formik';
+
+import { DatePicker, Switch } from 'antd';
+import { useTranslation } from 'react-i18next';
+import { isEmpty } from '../../../Hooks/isEmpty';
+import { useGetCategories } from '../../../api/Categories';
+import useFormatToSelect from '../../../Hooks/useFormatToSelect';
+import { useGetProduct } from '../../../api/product';
+
+function Form() {
+ const { values ,setFieldValue} = useFormikContext();
+ const [t] = useTranslation(); // FLAT coupn not be spacified && flate
+ const coupon_type = [{ lable: "general", value: "general" },{ lable: "specified", value: "specified" }]
+ const coupon_type_discount_flat = [{ lable: "general", value: "general" }]
+
+ const discount_type = [{ lable: "percentage", value: "percentage" },{ lable: "flat", value: "flat" }]
+ const { data: CategoriesData,isLoading:CategoriesLoading} = useGetCategories()
+ const { data: ProductData ,isLoading:ProductLoading} = useGetProduct()
+
+ const SelectCategoriesData = useFormatToSelect(CategoriesData?.categories)
+ const SelectProductData = useFormatToSelect(ProductData?.BaseProducts)
+
+ useEffect(() => {
+
+ if (values?.discount_type === 'flat') {
+ console.log(values?.coupon_type,"values?.coupon_type");
+
+ setFieldValue("coupon_type","general")
+
+ }
+ }, [values?.discount_type])
+
+ const onChange = (checked: boolean) => {
+ console.log(`switch to ${checked}`);
+ setFieldValue("status",checked)
+
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
+
+
diff --git a/src/Pages/Coupon/View/AddPage.tsx b/src/Pages/Coupon/View/AddPage.tsx
new file mode 100644
index 0000000..1e1f3e3
--- /dev/null
+++ b/src/Pages/Coupon/View/AddPage.tsx
@@ -0,0 +1,77 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValuesForAdd as getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { useAddCoupon } from '../../../api/Coupon';
+import Form from './AddForm';
+
+const AddCouponPage = () => {
+
+
+ const { mutate, isLoading:IsloadingButton, isSuccess } = useAddCoupon()
+ const handleSubmit = (values: any) => {
+ const transformedValues = { ...values }; // Create a new object
+
+ // Apply transformations
+ transformedValues.active_at = values.active[0]?.format('YYYY-MM-DD HH:mm:ss.SSS') || '';
+ transformedValues.active_to = values.active[1]?.format('YYYY-MM-DD HH:mm:ss.SSS') || '';
+ transformedValues.status = values.active ? "active" : "inActive";
+
+ const products = Array.isArray(values?.product_attr) ?
+ values.product_attr
+ .map((item: any) => ({ "itemable_type": "product", "itemable_id": item }))
+ .filter((item: any) => item.itemable_id !== "") || [] : [];
+
+ const category = Array.isArray(values?.category_attr) ?
+ values.category_attr
+ .map((item: any) => ({ "itemable_type": "category", "itemable_id": item }))
+ .filter((item: any) => item.itemable_id !== "") || [] : [];
+
+ if (products.length > 0 || category.length > 0) {
+ transformedValues['items'] = [...products, ...category];
+ }
+
+ console.log(transformedValues, "transformedValues");
+ mutate(transformedValues);
+ };
+
+
+ const { t } = useTranslation();
+
+ useNavigateOnSuccess(isSuccess, '/Coupon')
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddCouponPage
\ No newline at end of file
diff --git a/src/Pages/Coupon/View/EditForm.tsx b/src/Pages/Coupon/View/EditForm.tsx
new file mode 100644
index 0000000..090d5f5
--- /dev/null
+++ b/src/Pages/Coupon/View/EditForm.tsx
@@ -0,0 +1,81 @@
+
+import React, { useEffect } from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useFormikContext } from 'formik';
+
+import { DatePicker, Switch } from 'antd';
+import { useTranslation } from 'react-i18next';
+import { isEmpty } from '../../../Hooks/isEmpty';
+import { useGetCategories } from '../../../api/Categories';
+import useFormatToSelect from '../../../Hooks/useFormatToSelect';
+import { useGetProduct } from '../../../api/product';
+
+function Form() {
+ const { values,setFieldValue } = useFormikContext();
+ const [t] = useTranslation(); // FLAT coupn not be spacified && flate
+ const coupon_type = [{ lable: "general", value: "general" },{ lable: "specified", value: "specified" }]
+ const coupon_type_discount_flat = [{ lable: "general", value: "general" }]
+
+ const discount_type = [{ lable: "percentage", value: "percentage" },{ lable: "flat", value: "flat" }]
+ const { data: CategoriesData } = useGetCategories()
+ const { data: ProductData } = useGetProduct()
+
+ const SelectCategoriesData = useFormatToSelect(CategoriesData?.categories)
+ const SelectProductData = useFormatToSelect(ProductData?.BaseProducts)
+ useEffect(() => {
+
+ if (values?.discount_type === 'flat') {
+ console.log(values?.coupon_type,"values?.coupon_type");
+
+ setFieldValue("coupon_type","general")
+
+ }
+ }, [values?.discount_type])
+ const onChange = (checked: boolean) => {
+ console.log(`switch to ${checked}`);
+ setFieldValue("status",checked)
+
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
+
+
diff --git a/src/Pages/Coupon/View/EditPage.tsx b/src/Pages/Coupon/View/EditPage.tsx
new file mode 100644
index 0000000..d808216
--- /dev/null
+++ b/src/Pages/Coupon/View/EditPage.tsx
@@ -0,0 +1,106 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValues, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import { FaSadCry } from 'react-icons/fa'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { Rate, Spin } from 'antd';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import { useParams } from 'react-router-dom';
+import LoadingPage from '../../../Layout/app/LoadingPage';
+import { useTranslation } from 'react-i18next';
+import { BsInfoCircle } from 'react-icons/bs';
+import { useGetOneCoupon, useUpdateCoupon } from '../../../api/Coupon';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import Form from './EditForm';
+
+const EditPage = () => {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const { t } = useTranslation();
+ const { data,isLoading:IsloadingButton,isRefetching } = useGetOneCoupon()
+ const { mutate, isSuccess } = useUpdateCoupon("put")
+ const handleSubmit = (values: any) => {
+ const products = values?.product_attr?.map((item: any) => {
+ const itemableId = typeof item === 'object' ? item.value : item;
+ return { "itemable_type": "product", "itemable_id": itemableId };
+ })?.filter((item: any) => item.itemable_id !== "") || [];
+
+ const category = values?.category_attr?.map((item: any) => {
+ const itemableId = typeof item === 'object' ? item.value : item;
+ return { "itemable_type": "category", "itemable_id": itemableId };
+ })?.filter((item: any) => item.itemable_id !== "") || [];
+
+ // Create an object to hold the values to be mutated
+ const mutationData: any = {};
+
+ if (values?.name !== null) mutationData["name"] = values.name;
+ if (values?.status !== null) mutationData["status"] = values.status ? "active" : "inActive";
+ if (values?.minimum_total_to_order !== null) mutationData["minimum_total_to_order"] = values.minimum_total_to_order;
+ if (values?.maximum_number_of_uses_per_user !== null) mutationData["maximum_number_of_uses_per_user"] = values.maximum_number_of_uses_per_user;
+ if (values?.maximum_number_of_uses !== null) mutationData["maximum_number_of_uses"] = values.maximum_number_of_uses;
+ if (values?.coupon_value !== null) mutationData["coupon_value"] = values.coupon_value;
+ if (values?.coupon_type !== null) mutationData["coupon_type"] = values.coupon_type;
+ if (values?.discount_type !== null) mutationData["discount_type"] = values.discount_type;
+ if (values?.active !== null && values?.active.length === 2) {
+ mutationData["active_at"] = values.active[0].format('YYYY-MM-DD HH:mm:ss.SSS');
+ mutationData["active_to"] = values.active[1].format('YYYY-MM-DD HH:mm:ss.SSS');
+ }
+ if (products.length > 0 || category.length > 0) {
+ mutationData['items'] = [...products, ...category];
+ }
+
+ console.log(mutationData,"mutationData");
+
+ // Perform the mutation with the constructed mutationData object
+ mutate(mutationData);
+}
+
+
+ useNavigateOnSuccess(isSuccess, '/Coupon')
+
+
+ useEffect(() => {
+
+ setObjectToEdit(data?.coupon);
+
+ }, [data?.coupon]);
+
+
+ const getValidationSchema = () => {
+ return null
+ };
+ if ( IsloadingButton || !objectToEdit || isRefetching) {
+ return
+ }
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+ {objectToEdit && data ?
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default EditPage
\ No newline at end of file
diff --git a/src/Pages/Coupon/formUtil.ts b/src/Pages/Coupon/formUtil.ts
new file mode 100644
index 0000000..b6d9c01
--- /dev/null
+++ b/src/Pages/Coupon/formUtil.ts
@@ -0,0 +1,117 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+import moment from 'moment';
+import * as dayjs from 'dayjs'
+
+interface formUtilCommon {
+ number: number,
+ value: number
+}
+
+interface ObjectToEdit extends formUtilCommon {
+
+ id?: number,
+
+}
+
+interface InitialValues extends ObjectToEdit {
+
+}
+interface ValidateSchema extends formUtilCommon {
+
+}
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ //@ts-ignore
+
+ const products = [] as any;
+ const categories = [] as any;
+ const language = localStorage.getItem("language") ?? "en"
+
+ // Map over the items array and push items into the appropriate array
+ objectToEdit?.items?.forEach((item: any) => {
+ if (item?.itemable_type === "product") {
+ products.push({ value: item?.itemable?.id, label: item?.itemable?.name[language] });
+ } else if (item?.itemable_type === "category") {
+ categories.push({ value: item?.itemable?.id, label: item?.itemable?.name[language] });
+ }
+ });
+ return {
+ id: objectToEdit?.id ?? null,
+ name: objectToEdit?.name ,
+ code: objectToEdit?.code ,
+ //@ts-ignore
+ active: objectToEdit?.active_to ? [dayjs(objectToEdit?.active_from), dayjs(objectToEdit?.active_to)] : "",
+ minimum_total_to_order: objectToEdit?.minimum_total_to_order ,
+ maximum_number_of_uses_per_user: objectToEdit?.maximum_number_of_uses_per_user ,
+ maximum_number_of_uses: objectToEdit?.maximum_number_of_uses ,
+
+ coupon_value: objectToEdit?.coupon_value ,
+ coupon_type: objectToEdit?.coupon_type ,
+ discount_type: objectToEdit?.discount_type ,
+ product_attr: products ,
+ category_attr: categories ,
+ status: objectToEdit?.status ?? 0,
+
+
+ };
+};
+
+export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
+ return {
+ // id: '',
+ name: null,
+ code: null,
+ active: null,
+ minimum_total_to_order: null,
+ maximum_number_of_uses_per_user: null,
+ maximum_number_of_uses: null,
+ coupon_value: null,
+ coupon_type: null,
+ discount_type: null,
+ product_attr: null,
+ category_attr: null,
+ status: 0,
+
+
+
+ };
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ name: Yup.string().required('Required'),
+ code: Yup.string().required('Required'),
+ coupon_value: Yup.string().required('Required'),
+ active: Yup.mixed().required('Required'),
+ discount_type: Yup.string().required('Required'),
+ coupon_type: Yup.string().required('Required'),
+ minimum_total_to_order: Yup.number().required('Required'),
+ maximum_number_of_uses: Yup.number().required('Required'),
+ maximum_number_of_uses_per_user: Yup.number().required('Required'),
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data: any) => {
+
+ let new_array = data
+ for (let i = 0; i < data.length; i++) {
+ new_array[i]['status'] = !data[i]['deleted_at'] ? 'available' : 'unavailable'
+ delete new_array[i]['deleted_at']
+ }
+ return new_array
+}
\ No newline at end of file
diff --git a/src/Pages/Coupon/useTableColumns.tsx b/src/Pages/Coupon/useTableColumns.tsx
new file mode 100644
index 0000000..37652a7
--- /dev/null
+++ b/src/Pages/Coupon/useTableColumns.tsx
@@ -0,0 +1,83 @@
+
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+import ColumnsImage from "../../Components/Columns/ColumnsImage";
+import { useNavigate } from "react-router-dom";
+import { useDeleteCoupon } from "../../api/Coupon";
+import { MdEmail } from "react-icons/md";
+import { useModalState } from "../../zustand/Modal";
+
+function fnDelete(props :any ){}
+
+const useTableColumns :any = () => {
+ const [t] = useTranslation();
+ const deleteMutation = useDeleteCoupon()
+ const navigate = useNavigate()
+ const {setIsOpen} = useModalState(state=>state)
+ return useMemo(
+ () => [
+
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("name"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.name
+ },
+ {
+ name: t("discount_type"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.discount_type
+ },
+ {
+ name: t("coupon_type"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.coupon_type
+ },
+ {
+ name: t("code"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.code
+ },
+ {
+ name: t("coupon_value"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => {
+ const rowvalue = row?.discount_type === "percentage" ? "%" : ""
+ return (
+ rowvalue + row?.coupon_value
+ )
+ }
+ },
+ {
+ name: "#",
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ navigate(`/coupon/${row.id}`) }
+ showView={false}
+ onDelete={() => deleteMutation.mutate({ id: row.id })}
+ >
+
+ ),
+ },
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/Home/Chart.tsx b/src/Pages/Home/Chart.tsx
new file mode 100644
index 0000000..df36400
--- /dev/null
+++ b/src/Pages/Home/Chart.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+//@ts-ignore
+import { BarChart } from '@mui/x-charts/BarChart';
+import { axisClasses } from '@mui/x-charts';
+import { useTranslation } from 'react-i18next';
+
+const chartSetting = {
+ yAxis: [
+ {
+ label: 'rainfall (mm)',
+ },
+ ],
+ sx: {
+ [`.${axisClasses.left} .${axisClasses.label}`]: {
+ transform: 'translate(-20px, 0)',
+ },
+ },
+};
+
+
+const BarsDataset = ({dataMonth}:any) => {
+
+
+ const [t] = useTranslation()
+ const dataset = dataMonth;
+
+
+ const series = [
+ { dataKey: 'users', label: t('users'), valueFormatter: (value: number) => `${value}` },
+ { dataKey: 'orders', label: t('orders'), valueFormatter: (value: number) => `${value}` }
+ ];
+
+
+ if(!dataMonth){
+ return <>
+
+ >;
+ }
+ return (
+
+
+
+
+ );
+};
+
+export default BarsDataset;
diff --git a/src/Pages/Home/HomePage.tsx b/src/Pages/Home/HomePage.tsx
new file mode 100644
index 0000000..6cd3365
--- /dev/null
+++ b/src/Pages/Home/HomePage.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+import StatisticsCard from "../../Components/Ui/StaticsCard/StaticCard";
+import { FaFirstOrder, FaProductHunt, FaUser } from "react-icons/fa";
+import { useTranslation } from "react-i18next";
+import { Col, Row } from "reactstrap";
+import Chart from "./Chart";
+import { useGetHome } from "../../api/home";
+import { Spin } from "antd";
+
+export default function HomePage() {
+ const { t } = useTranslation();
+
+ const {data,isLoading} = useGetHome()
+
+
+ const cardsData = [
+ {
+ icon: ,
+ count: data?.userCount, // Example count
+ pathWhenClick: '/users',
+ titleKey: "userCount",
+ contentKey: "user_in_your_Application"
+ },
+ {
+ icon: ,
+ count: data?.productCount, // Example count
+ pathWhenClick: "/products",
+ titleKey: "productCount",
+ contentKey: "Product_Count_in_your_Application"
+ },
+ {
+ icon: ,
+ count: data?.orderCount, // Example count
+ pathWhenClick: "/order",
+ titleKey: "orderCount",
+ contentKey: "order_count_in_your_Application"
+ }
+ ];
+
+ if(isLoading){
+ return
+ }
+ return (
+ <>
+
+ {cardsData.map((card, index) => (
+
+
+
+
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/Pages/Notifcation/Page.tsx b/src/Pages/Notifcation/Page.tsx
new file mode 100644
index 0000000..4132b0f
--- /dev/null
+++ b/src/Pages/Notifcation/Page.tsx
@@ -0,0 +1,46 @@
+
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import SearchField from '../../Layout/Dashboard/SearchField'
+import { useGetNotification } from '../../api/notification'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetNotification()
+ const [t] = useTranslation()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
+
navigate('/notification/add')}>
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/Notifcation/View/AddForm.tsx b/src/Pages/Notifcation/View/AddForm.tsx
new file mode 100644
index 0000000..464d4b3
--- /dev/null
+++ b/src/Pages/Notifcation/View/AddForm.tsx
@@ -0,0 +1,84 @@
+
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useGetUsers } from '../../../api/users';
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+import { useEffect } from 'react';
+
+function Form() {
+ const type = [{ lable: "other", value: "other" }, { lable: "product", value: "product" }, { lable: "order", value: "order" }]
+
+ const { data: user } = useGetUsers()
+ const [t]=useTranslation()
+ const useFormatToSelect = (Data : any) => {
+ const format = (data :any) => {
+ if (!data) return [];
+ return data.map((item :any) => ({
+ value: item?.id,
+ label: item?.name,
+ }));
+ };
+
+ return format(Data);
+ };
+ const SelectData = useFormatToSelect(user?.data)
+
+ // const selectAllOption = [{ label: t("Select All"), value: "*" }];
+ const selectAllOption = { label: "Select All", value: "*" };
+
+ const optionsWithSelectAll = [selectAllOption, ...SelectData];
+ const { values,setFieldValue} = useFormikContext();
+
+ const hasAsterisk = values?.user_ids.some((item:any) => typeof item === 'string' && item.includes('*'));
+ const option = hasAsterisk ? [selectAllOption] : optionsWithSelectAll;
+
+ useEffect(() => {
+ if(hasAsterisk){
+ setFieldValue("user_ids",["*"])
+
+ }
+ }, [hasAsterisk])
+
+
+ useEffect(() => {
+ console.log(values?.user_ids,"user_ids");
+
+ }, [values.user_ids])
+
+
+ return (
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
+
+
diff --git a/src/Pages/Notifcation/View/AddPage.tsx b/src/Pages/Notifcation/View/AddPage.tsx
new file mode 100644
index 0000000..9bd2b98
--- /dev/null
+++ b/src/Pages/Notifcation/View/AddPage.tsx
@@ -0,0 +1,86 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValuesForAdd as getInitialValues, getValidationSchema, getDataToSend } from './formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { useAddUsers } from '../../../api/users';
+import Form from './AddForm';
+import { useParams } from 'react-router-dom';
+import { useAddNotification } from '../../../api/notification';
+
+const AddPage = () => {
+
+
+ const { mutate, isLoading:IsloadingButton, isSuccess } = useAddNotification()
+ const {id} = useParams()
+ const handleSubmit = (values: any) => {
+ const hasAsterisk = values?.user_ids.some((item:any) => typeof item === 'string' && item.includes('*'));
+
+ const userIds = hasAsterisk ? { notifyAll: 1 } : { user_ids: values?.user_ids };
+
+ const body = {
+ en: values.body_en,
+ ar: values.body_ar,
+ de: values.body_ce
+ };
+
+ const meta = {};
+
+ const title = {
+ en: values.title_en,
+ ar: values.title_ar,
+ de: values.title_cn
+ };
+
+ const dataToSend = {
+ body: body,
+ meta: meta,
+ title: title,
+ type: "other",
+ ...userIds
+ };
+
+ console.log(dataToSend?.notifyAll);
+
+ mutate(dataToSend);
+ };
+
+
+ const { t } = useTranslation();
+
+ useNavigateOnSuccess(isSuccess, '/notification')
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddPage
\ No newline at end of file
diff --git a/src/Pages/Notifcation/View/formUtil.ts b/src/Pages/Notifcation/View/formUtil.ts
new file mode 100644
index 0000000..23ab30a
--- /dev/null
+++ b/src/Pages/Notifcation/View/formUtil.ts
@@ -0,0 +1,72 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../../api/helper/buildFormData";
+import moment from 'moment';
+import * as dayjs from 'dayjs'
+
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ //@ts-ignore
+ return {
+ id: objectToEdit?.id ,
+ name: objectToEdit?.name ,
+ email: objectToEdit?.email ,
+ type: objectToEdit?.type ,
+ avatar: objectToEdit?.avatar ,
+
+ };
+};
+export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
+ return {
+ body_en: "" ,
+ body_ar: "" ,
+ body_ce: "" ,
+ meta: "" ,
+ title_en: "" ,
+ title_ar: "" ,
+ title_cn: "" ,
+ user_ids:[],
+ type: "" ,
+
+ };
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ body_en: Yup.string().required('Required'),
+ body_ar: Yup.string().required('Required'),
+ body_ce: Yup.string().required('Required'),
+ user_ids: Yup.array().min(1, 'At least one user must be selected').required('Required'),
+
+ title_en: Yup.string().required('Required'),
+ title_ar: Yup.string().required('Required'),
+ title_cn: Yup.string().required('Required'),
+ // type: Yup.string().required('Required'),
+
+
+
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data: any) => {
+
+ let new_array = data
+ for (let i = 0; i < data.length; i++) {
+ new_array[i]['status'] = !data[i]['deleted_at'] ? 'available' : 'unavailable'
+ delete new_array[i]['deleted_at']
+ }
+ return new_array
+}
\ No newline at end of file
diff --git a/src/Pages/Notifcation/useTableColumns.tsx b/src/Pages/Notifcation/useTableColumns.tsx
new file mode 100644
index 0000000..93c89f8
--- /dev/null
+++ b/src/Pages/Notifcation/useTableColumns.tsx
@@ -0,0 +1,72 @@
+
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+import ColumnsImage from "../../Components/Columns/ColumnsImage";
+import { useNavigate } from "react-router-dom";
+import { useDeleteNotification } from "../../api/notification";
+import { Switch } from "antd";
+import { MdEmail } from "react-icons/md";
+import { useModalState } from "../../zustand/Modal";
+
+
+const useTableColumns: any = () => {
+ const [t] = useTranslation();
+ const deleteMutation = useDeleteNotification()
+ const navigate = useNavigate()
+
+ const { setIsOpen } = useModalState(state => state)
+
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("name"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.user?.name
+ },
+ {
+ name: t("title"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.title[language]
+ },
+ {
+ name: t("body"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.body[language]
+ },
+
+ // {
+ // name: "#",
+ // sortable: false,
+ // center: true,
+ // cell: (row: any) => (
+ // navigate(`/notification/${row.id}`)}
+ // showView={false}
+ // onDelete={() => deleteMutation.mutate({ id: row.id })}
+ // >
+
+ //
+ // ),
+ // },
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/Products/ProductsPage.tsx b/src/Pages/Products/ProductsPage.tsx
new file mode 100644
index 0000000..98d05aa
--- /dev/null
+++ b/src/Pages/Products/ProductsPage.tsx
@@ -0,0 +1,58 @@
+
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useGetProduct } from '../../api/product'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import SearchField from '../../Layout/Dashboard/SearchField'
+import SelectField from '../../Layout/Dashboard/SelectWSearchField'
+import { useGetCategories } from '../../api/Categories'
+
+function ProductsPage() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetProduct()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+ const { data:Categories } = useGetCategories()
+ const language = localStorage.getItem("language") ?? "en"
+
+ const SelectData = Categories?.categories?.map((item:any)=>(
+ {
+ label : item?.name[language],
+ value : item?.id
+ }
+ ))
+
+
+ return (
+
+
+
+
+
+
+
+
navigate('/products/add')}>
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ProductsPage
+
diff --git a/src/Pages/Products/View/AddPage.tsx b/src/Pages/Products/View/AddPage.tsx
new file mode 100644
index 0000000..dd0e534
--- /dev/null
+++ b/src/Pages/Products/View/AddPage.tsx
@@ -0,0 +1,130 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValuesAdd as getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import BasicInfo from './BasicInfo';
+import { useTranslation } from 'react-i18next';
+import { BsInfoCircle } from 'react-icons/bs';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { useAddProduct, useAddProductVariation } from '../../../api/product';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+// import AttributeInfo from './AttributeInfo';
+
+const AddProductPage = () => {
+
+
+ const { mutateAsync, isLoading:IsloadingButton, data, isSuccess } = useAddProduct()
+ const { mutate: AddVariation } = useAddProductVariation()
+
+ const [IsValed, setIsValed] = useState(false)
+
+ const [Varibletaps, setVaribleTaps] = useState([])
+
+ const handleSubmit = (values: any) => {
+
+ console.log(values, "values");
+
+
+ const new_category = {
+ name: {
+ en: values?.name_en,
+ ar: values?.name_ar,
+ cn: values?.name_cn
+ },
+ category_id: values?.category_id,
+
+ }
+
+ mutateAsync(new_category).then((data)=>{
+ const baseProductId = (data as any )?.id
+ const info = values?.variable?.[0]?.info;
+ const convertedArray = info?.reduce((acc: any, obj: any) => {
+ acc[obj.Description] = obj.key;
+ return acc;
+ }, {});
+ const jsonString = Object.keys(convertedArray).length === 0 ? null : JSON.stringify(convertedArray);
+ console.log(jsonString, "jsonString");
+
+ const FileimagesArray = values?.variable?.images?.filter((item: any) => typeof item !== 'string') || [];
+ const images = { images: FileimagesArray };
+ const main_photo = (typeof values?.main_photo === 'string') ? {} : { main_photo: values?.main_photo };
+
+ const Newproduct = {
+ name: {
+ en: values?.name_en,
+ ar: values?.name_ar,
+ cn: values?.name_cn
+ },
+ description: {
+ en: values?.description_en,
+ ar: values?.description_ar,
+ cn: values?.description_cn
+ },
+ info: jsonString,
+ price: values?.price,
+ base_product_id: baseProductId,
+ ...images,
+ ...main_photo,
+
+
+ };
+ console.log(Newproduct);
+ AddVariation(Newproduct);
+
+
+
+ })
+
+
+ }
+
+
+
+ const { setObjectToEdit } = usePageState()
+
+ useEffect(() => {
+ setObjectToEdit([]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const { t } = useTranslation();
+
+ useNavigateOnSuccess(isSuccess, '/products')
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("Base_info")}
+
+ {/*
{t("VarianInfo")}
*/}
+
+
+
+
+
+
+ {/*
+
+ */}
+
+
+
+
+
+
+ )
+
+}
+
+export default AddProductPage
+
diff --git a/src/Pages/Products/View/Addfn/AddNewVariation.ts b/src/Pages/Products/View/Addfn/AddNewVariation.ts
new file mode 100644
index 0000000..2ed7e94
--- /dev/null
+++ b/src/Pages/Products/View/Addfn/AddNewVariation.ts
@@ -0,0 +1,83 @@
+// Function to process variable taps when success is true
+export function AddNewVariation(Varibletaps: any[], data: any, AddVariation: any) {
+ const baseProductId = data?.id;
+ Varibletaps?.forEach((dataToSend: any) => {
+ const varible = dataToSend;
+ const info = varible?.info ?? [];
+ const convertedArray = info?.reduce((acc: any, obj: any) => {
+ acc[obj.Description] = obj.key;
+ return acc;
+ }, {});
+ const jsonString = Object.keys(convertedArray).length === 0 ? null : JSON.stringify(convertedArray);
+
+
+ const StringimagesArray = varible?.images?.filter((item: any) => typeof item === 'string') || [];
+ const FileimagesArray = varible?.images?.filter((item: any) => typeof item !== 'string') || [];
+
+ const images = { images: FileimagesArray };
+ console.log(FileimagesArray,"FileimagesArray");
+
+ const main_photo = (typeof varible?.main_photo === 'string') ? {} : { main_photo: varible?.main_photo };
+
+ // const copied_assets = [
+ // ...(typeof varible?.main_photo === 'string' ? [{ main_photo: varible?.main_photo }] : []),
+ // [StringimagesArray]
+ // ];
+
+ const filteredAttributes = varible.attribute?.filter(Boolean); // Filter out undefined values
+
+ const mappedAttributes = filteredAttributes?.map((item: any) => {
+ return {
+ attribute_value_id: item?.value,
+ attribute_id: item?.id
+ }
+ });
+
+ console.log(mappedAttributes, "mappedAttributes");
+
+ // Flatten the array of arrays
+ // const flattenedAttributes = mappedAttributes?.filter(Boolean)?.flat();
+
+ // const arrayOfObjects = flattenedAttributes?.map((subArray: any, index: any) => {
+ // console.log(subArray, "subArray");
+ // console.log(index, "index");
+
+ // return {
+ // ...subArray,
+ // };
+ // });
+
+ // console.log(arrayOfObjects, "arrayOfObjects");
+
+
+ const Newproduct = {
+ name: {
+ en: varible?.name_en,
+ ar: varible?.name_ar,
+ de: varible?.name_cn
+ },
+ description: {
+ en: varible?.description_en,
+ ar: varible?.description_ar,
+ de: varible?.description_cn
+ },
+ // quantity: varible?.quantity,
+ info: jsonString,
+ price: varible?.price,
+ // product_attributes: varible?.attribute?.map((item: any, index: any) => {
+ // return item?.map((subitem:any,index:any)=>{
+ // return { attribute_value_id: subitem?.value, attribute_id: subitem?.id }
+ // })
+ // }),
+ product_attributes:mappedAttributes,
+ base_product_id: baseProductId,
+ ...images,
+ ...main_photo,
+ // ...copied_assets,
+
+
+ };
+ console.log(Newproduct);
+ AddVariation(Newproduct);
+ });
+}
diff --git a/src/Pages/Products/View/Addfn/isvalidation.ts b/src/Pages/Products/View/Addfn/isvalidation.ts
new file mode 100644
index 0000000..1bbabb5
--- /dev/null
+++ b/src/Pages/Products/View/Addfn/isvalidation.ts
@@ -0,0 +1,27 @@
+import { toast } from "react-toastify";
+
+// Function to validate variable items
+export function isvalidation(variableItems: any[], t: any) {
+ const validationResults: boolean[] = [];
+ variableItems.slice(1).forEach((item: any, index: any) => {
+ if (item && Object.keys(item).length > 0) { // Check if item is defined and not empty
+ if (!item?.name_ar || !item?.name_en || !item?.name_cn) {
+ toast.error(t('required_name') + ` ${index + 1}`);
+ validationResults.push(false);
+ } else if (!item?.description_ar || !item?.description_en || !item?.description_cn) {
+ toast.error(t('required_description') + ` ${index + 1}`);
+ validationResults.push(false);
+ } else if (item.main_photo === null || !item?.main_photo) {
+ toast.error(t('required_main_photo') + ` ${index + 1}`);
+ validationResults.push(false);
+ } else if (item.price === null || !item?.price) {
+ toast.error(t('required_price') + ` ${index + 1}`);
+ validationResults.push(false);
+ } else {
+ validationResults.push(true);
+ }
+ }
+ });
+ return validationResults;
+ }
+
\ No newline at end of file
diff --git a/src/Pages/Products/View/BasicInfo.tsx b/src/Pages/Products/View/BasicInfo.tsx
new file mode 100644
index 0000000..4e9f915
--- /dev/null
+++ b/src/Pages/Products/View/BasicInfo.tsx
@@ -0,0 +1,73 @@
+import React, { useEffect, useState } from 'react'
+import { Col, Row } from 'reactstrap'
+import ValidationField from '../../../Components/ValidationField/ValidationField'
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+import { toast } from 'react-toastify';
+import { useGetCategories } from '../../../api/Categories';
+import useFormatToSelect from '../../../Hooks/useFormatToSelect';
+import { useParams } from 'react-router-dom';
+import Atteibute from '../View/FormikTab/Field/Atteibute';
+import ObjectField from './FormikTab/Field/Object';
+import MaltyFile from './FormikTab/Field/FileImage';
+
+const BasicInfo = ({ setIsValed, IsValed }: any) => {
+ const [t] = useTranslation();
+ const formikContext = useFormikContext();
+ const { values, isValid ,errors} = formikContext;
+ const { data } = useGetCategories()
+ const language = localStorage.getItem("language") ?? "en"
+ const {id}=useParams()
+
+ const SelectData = data?.categories?.map((item:any)=>(
+ {
+ label : item?.name[language],
+ value : item?.id
+ }
+ ))
+ useEffect(() => {
+ // const { name_ar, name_en, name_cn, main_photo, category_id } = values as any;
+
+ // if (name_ar && name_en && name_cn && main_photo && category_id && IsValed === false) {
+ // // toast.success(t("view_information_filed_fill_sucsessfully"));
+ // setIsValed(true)
+ // } else {
+ // // console.log(isValid, "isValid");
+ // }
+ console.log(errors,'errors');
+
+ }, [values]);
+
+
+ return (
+
+
+
+
+
+
+ {!id &&
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default BasicInfo
\ No newline at end of file
diff --git a/src/Pages/Products/View/EditPage.tsx b/src/Pages/Products/View/EditPage.tsx
new file mode 100644
index 0000000..4444feb
--- /dev/null
+++ b/src/Pages/Products/View/EditPage.tsx
@@ -0,0 +1,460 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValues, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import { useParams } from 'react-router-dom';
+import LoadingPage from '../../../Layout/app/LoadingPage';
+import { useTranslation } from 'react-i18next';
+import { BsInfoCircle } from 'react-icons/bs';
+import { useAddProductVariation, useDeleteProductVariation, useGetOneProduct, useUpdateProduct, useUpdateProductVariation } from '../../../api/product';
+import { Spin } from 'antd';
+import BasicInfo from './BasicInfo';
+import { TabsContainer } from './FormikTab/TabsContainer';
+import { isvalidation } from './Addfn/isvalidation';
+import filterUndefinedAndEmpty from '../../../utils/Array/filterUndefinedAndEmpty';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+
+const ViewProduct = () => {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const { t } = useTranslation();
+ const { id } = useParams()
+ const { data, isLoading, isRefetching } = useGetOneProduct({ id: id })
+
+ const [OldData, setOldData] = useState(data?.data?.products)
+ const { mutate, isSuccess, isLoading: IsloadingButton } = useUpdateProduct('put')
+ const { mutate: UpdateVariation } = useUpdateProductVariation()
+ const { mutate: AddVariation } = useAddProductVariation()
+
+
+ const { mutate: DeleteVariation } = useDeleteProductVariation()
+
+ const [removedVariant, setremovedVariant] = useState([])
+
+ const [Variant, setVariant] = useState([])
+
+
+ const handleSubmit = (values: any) => {
+ // Update removedVarianteValues state
+ // console.log(values?.removedVariant, "values?.removedVariant");
+ console.log(values, "values");
+
+
+ // return ;
+
+ // function isValid() {
+ // setremovedVariant(values?.removedVariant);
+ // const variables = values?.variable?.slice(1);
+ // if (variables) {
+ // variables.forEach((item: any, index: number) => {
+ // setVariant((prevEditAttribute: any) => [...prevEditAttribute, item]);
+ // })
+ // };
+
+ // }
+ // const validationResults = isvalidation(values.variable, t);
+ // if (validationResults.every((result) => result)) {
+ // isValid();
+ // }
+
+ const newBaseProduct = {
+ name: {
+ en: values?.name_en,
+ ar: values?.name_ar,
+ cn: values?.name_cn
+ },
+ category_id: values?.category_id
+ };
+
+
+
+ mutate(newBaseProduct);
+
+ if(values?.product_id){
+
+ const info = values?.variable?.info;
+ const convertedArray = Array.isArray(info) ? info.reduce((acc: any, obj: any) => {
+ acc[obj.Description] = obj.key;
+ return acc;
+ }, {}) : {};
+
+ const jsonString = Object.keys(convertedArray).length === 0 ? null : JSON.stringify(convertedArray);
+
+ const FileimagesArray = values?.variable?.images?.filter((item: any) => typeof item !== 'string') || [];
+ console.log(values?.variable?.images,"values?.variable?.images");
+ const main_photo = (typeof values?.main_photo === 'string') ? {} : { main_photo: values?.main_photo };
+ const images = values?.variable?.images?.length > 0 && values?.variable?.images?.map((item: any) => {
+ console.log("Processing item:", item);
+ if (item?.id) {
+ return item.id;
+
+ } else {
+ return item; // Assume the item is a primitive value and return it directly
+ }
+ });
+ console.log(images,"images");
+
+ console.clear()
+ console.log(images,"images");
+
+ const Product = {
+ id:values?.product_id,
+ name: {
+ en: values?.name_en,
+ ar: values?.name_ar,
+ cn: values?.name_cn
+ },
+ description: {
+ en: values?.description_en,
+ ar: values?.description_ar,
+ cn: values?.description_cn
+ },
+ price: values?.price,
+ info: jsonString,
+ images:images ?? "",
+ main_photo:main_photo,
+ _method:"PUT"
+ }
+ UpdateVariation(Product)
+
+ }
+
+ };
+
+
+
+
+ function arraysAreEqual(array1: any, array2: any) {
+ // Sort both arrays
+ const sortedArray1 = array1.slice().sort();
+ const sortedArray2 = array2.slice().sort();
+
+ // Compare the sorted arrays
+ return JSON.stringify(sortedArray1) === JSON.stringify(sortedArray2);
+ }
+
+
+
+ useEffect(() => {
+ if (isSuccess) {
+ const filtered_Variant = filterUndefinedAndEmpty(Variant);
+
+ // eslint-disable-next-line array-callback-return
+ filtered_Variant?.map((dataToSend: any, index: number) => {
+ console.log(dataToSend, "dataToSend");
+ const varible = dataToSend
+ if (varible?.id && varible?.id !== null && varible?.id !== 0) {
+ const foundObject = OldData.find((item: any) => item.id === varible?.id) as any;
+ const formattedOldInfoData = foundObject?.info ? Object.entries(foundObject?.info).map(([key, value], index) => ({
+ [`Description`]: key,
+ [`key`]: value,
+ })) : [];
+ // const info = varible?.info
+ const info = varible?.info ?? [];
+ const convertedArray = info?.reduce((acc: any, obj: any) => {
+ acc[obj.Description] = obj.key;
+ return acc;
+ }, {});
+ const jsonString = Object.keys(convertedArray).length === 0 ? null : JSON.stringify(convertedArray);
+
+ const images_product = foundObject?.images?.map((item: any) => {
+ return item?.path
+ })
+ console.log(varible?.images,"varible?.images");
+
+ const array1 = images_product;
+ const array2 = varible?.images;
+ const Oldimages = isthereequal(array2 || [], foundObject.images || []);
+ const finalImageData= removeStrings(array2 || [])
+ const isEqual_images = array1.length === array2.length && array1.every((value: any, index: any) => value === array2[index]);
+ // const Newimages = varible?.images?.map((item: any) => {
+ // const value = typeof item === 'object' ? item.url : item;
+ // return [value];
+ // });
+
+
+ console.log("Arrays are equal:", isEqual_images);
+ console.log("finalImageData", finalImageData);
+ console.log("Oldimages", Oldimages);
+ const filteredOldimages = Oldimages.filter(item => item !== undefined);
+
+
+ const images = isEqual_images ? { } : { images:[...finalImageData,...filteredOldimages] }
+
+
+
+ const Editedproduct = {
+ name: {
+ en: varible?.name_en,
+ ar: varible?.name_ar,
+ de: varible?.name_cn
+ },
+ description: {
+ en: varible?.description_en,
+ ar: varible?.description_ar,
+ de: varible?.description_cn
+ },
+ // quantity: varible?.quantity,
+ main_photo: varible?.main_photo,
+ ...images,
+ info: jsonString,
+ price: varible?.price,
+
+
+ // product_attributes: varible?.attribute?.map((item: any, index: any) => {
+ // return { attribute_value_id: item?.value, attribute_id: item?.id }
+ // }),
+
+ } as any
+
+
+
+ if (foundObject) {
+ Object.keys(Editedproduct).forEach((propName: any) => {
+ if (foundObject.hasOwnProperty(propName)) {
+ const foundValue = foundObject[propName];
+ //@ts-ignore
+ const editedValue = Editedproduct[propName];
+ if (foundValue === editedValue) {
+ //@ts-ignore
+ delete Editedproduct[propName];
+ }
+ }
+
+ });
+ if (foundObject.name &&
+ foundObject.name.en === Editedproduct.name.en &&
+ foundObject.name.ar === Editedproduct.name.ar &&
+ foundObject.name.de === Editedproduct.name.de) {
+ //@ts-ignore
+ delete Editedproduct.name;
+ }
+
+ if (foundObject.description &&
+ foundObject.description.en === Editedproduct.description.en &&
+ foundObject.description.ar === Editedproduct.description.ar &&
+ foundObject.description.de === Editedproduct.description.de) {
+ //@ts-ignore
+ delete Editedproduct.description;
+ }
+
+ Object.keys(Editedproduct).forEach((key: any) => {
+ // Check if the property value is undefined
+ //@ts-ignore
+
+ if (Editedproduct[key] === undefined) {
+ // Remove the property
+ //@ts-ignore
+ delete Editedproduct[key];
+ }
+ });
+ console.log(foundObject.info, "foundObject.info");
+
+ const isEqual = arraysAreEqual(formattedOldInfoData, varible?.info);
+ console.log(isEqual, "isEqual");
+
+ if (isEqual) {
+ //@ts-ignore
+ delete Editedproduct?.info;
+ }
+
+
+ if (Object.keys(Editedproduct).length > 0) {
+ //@ts-ignore
+ Editedproduct._method = "PUT";
+ //@ts-ignore
+ Editedproduct.id = foundObject?.id;
+ console.log(Editedproduct, "Editedproduct");
+ UpdateVariation(Editedproduct)
+
+ }
+
+ }
+
+
+ } else {
+ const info = varible?.info ?? [];
+ const convertedArray = info?.reduce((acc: any, obj: any) => {
+ acc[obj.Description] = obj.key;
+ return acc;
+ }, {});
+ const jsonString = Object.keys(convertedArray).length === 0 ? null : JSON.stringify(convertedArray);
+
+
+ const StringimagesArray = varible?.images?.filter((item: any) => typeof item === 'string') || [];
+ const FileimagesArray = varible?.images?.filter((item: any) => typeof item !== 'string') || [];
+
+ console.log(FileimagesArray,"FileimagesArray");
+ console.log(StringimagesArray,"StringimagesArray");
+
+ const images = { images: FileimagesArray };
+
+ const main_photo = (typeof varible?.main_photo === 'string') ? {} : { main_photo: varible?.main_photo };
+
+ const copied_assets = {
+ ...(typeof varible?.main_photo === 'string' ? { main_photo: varible?.main_photo } : {}),
+ ...(StringimagesArray.length > 0 ? {images:StringimagesArray} : {})
+
+ }
+ const copied_assets_arry = Object.keys(copied_assets).length !== 0 ? { copied_assets: copied_assets } : {};
+
+
+ console.log(images,"images");
+ console.log(main_photo,"main_photo");
+ console.log(copied_assets,"copied_assets");
+
+
+
+
+ const filteredAttributes = varible.attribute?.filter(Boolean); // Filter out undefined values
+ console.log(filteredAttributes,"filteredAttributes");
+
+ const mappedAttributes = filteredAttributes?.map((item: any) => {
+ return {
+ attribute_value_id: item?.value,
+ attribute_id: item?.id
+ }
+ });
+
+console.log(mappedAttributes, "mappedAttributes");
+
+// // Flatten the array of arrays
+// const flattenedAttributes = mappedAttributes?.filter(Boolean)?.flat();
+
+// const arrayOfObjects = flattenedAttributes?.map((subArray: any, index: any) => {
+// console.log(subArray, "subArray");
+// console.log(index, "index");
+
+// return {
+// ...subArray,
+// };
+// });
+
+
+
+ const Newproduct = {
+ name: {
+ en: varible?.name_en,
+ ar: varible?.name_ar,
+ de: varible?.name_cn
+ },
+ description: {
+ en: varible?.description_en,
+ ar: varible?.description_ar,
+ de: varible?.description_cn
+ },
+ // quantity: varible?.quantity,
+
+ info: jsonString,
+ price: varible?.price,
+ base_product_id: id,
+ product_attributes: mappedAttributes,
+
+ ...images,
+ ...main_photo,
+ ...copied_assets_arry,
+
+
+
+
+
+
+ }
+
+
+ AddVariation(Newproduct)
+ }
+
+ })
+
+ console.log(removedVariant);
+ const filtered_removedVariant = filterUndefinedAndEmpty(removedVariant);
+ console.log(filtered_removedVariant);
+
+ removedVariant?.map((item: any) => {
+ return DeleteVariation({ id: item })
+ })
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isSuccess])
+
+
+
+ useNavigateOnSuccess(isSuccess, '/products')
+
+
+ useEffect(() => {
+ // refetch()
+ setObjectToEdit(data?.data);
+ setOldData(data?.data?.products)
+ }, [data, data?.data?.products, data?.data, id, isRefetching, setObjectToEdit]);
+
+ const getValidationSchema = () => {
+ return null
+ };
+ if (isRefetching || isLoading) {
+ return
+ }
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, IsloadingButton };
+
+
+ return (
+
+ {objectToEdit && data?.data && objectToEdit ?
+
+
+
+
{t("Base_info")}
+
+ {/*
{t("VarianInfo")}
*/}
+
+
+
+
+
+
+ {/*
+ {isLoading && objectToEdit && isRefetching ? : }
+ */}
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default ViewProduct
+
+
+
+function isthereequal(arr1: any[], arr2: any[]): any[] {
+ console.log("arr1:", arr1);
+ console.log("arr2:", arr2);
+
+ const equalItems = arr2
+ .filter(item2 => arr1.some(item1 => item1 === item2?.path))
+ .map(item => item.id);
+
+ console.log("Equal items found in arr2:", equalItems);
+
+ return equalItems;
+}
+
+function removeDuplicates(arr1: any[], arr2: any[]): any[] {
+ const combinedArray = [...arr1, ...arr2];
+ const uniqueArray = combinedArray.filter((item, index) => {
+ return combinedArray.indexOf(item) === index;
+ });
+ return uniqueArray;
+}
+
+function removeStrings(arr: any[]): any[] {
+ return arr.filter(item => typeof item !== 'string');
+}
diff --git a/src/Pages/Products/View/FormikTab/Field/Atteibute.tsx b/src/Pages/Products/View/FormikTab/Field/Atteibute.tsx
new file mode 100644
index 0000000..57483f6
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/Field/Atteibute.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+import { useGetSingleAttribute } from '../../../../../api/attribute';
+import { Select } from 'antd';
+import { useTranslation } from 'react-i18next';
+
+const Attribute = ({ tabKey }: any) => {
+ const { values, setFieldValue } = useFormikContext();
+ const language = localStorage.getItem("language") ?? "en";
+ const [t] = useTranslation()
+ const { data: attributeData } = useGetSingleAttribute({ name: "category_id", id: values?.category_id });
+
+
+
+ const handleAttributeChange = ( value: any, option: any) => {
+ if (option) {
+ console.log(option, "option");
+ setFieldValue(`variable[${tabKey}].attribute[${option.id}]`, option);
+ } else {
+ console.error("Option is undefined");
+ }
+ };
+
+
+ return (
+ attributeData?.data?.map((item: any) => {
+ const uniqueOptionsMap = new Map();
+ item?.attribute_value.forEach((attr: any) => {
+ if (!uniqueOptionsMap.has(attr.value[language])) {
+ uniqueOptionsMap.set(attr.value[language], { attribute_id: attr.id, id: attr.attribute_id });
+ }
+ });
+
+ const options = Array?.from(uniqueOptionsMap?.entries())?.map(([label, value]) => ({
+ label,
+ value: value.attribute_id, // Change value to attribute_id
+ id: value.id, // Include id in the option
+ })) as any;
+
+ if (options.length === 0) {
+ return <>>; // Don't render anything if options are empty
+ }
+ // console.log(options[0].id,"options");
+ // console.log(values?.variable[tabKey]?.attribute,"values?.[options[0].id");
+
+ return (
+
+
+
+
+
+
+ );
+ })
+ );
+};
+
+export default Attribute;
diff --git a/src/Pages/Products/View/FormikTab/Field/File.tsx b/src/Pages/Products/View/FormikTab/Field/File.tsx
new file mode 100644
index 0000000..7bce823
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/Field/File.tsx
@@ -0,0 +1,66 @@
+
+
+
+import { Button, Upload, UploadFile } from 'antd'
+import { UploadOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+import { ImageBaseURL } from '../../../../../api/config';
+
+
+const FileProduct = ({ tabKey}:any) => {
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const name = `variable[${tabKey}].${"main_photo"}`;
+ let FormikName = formik?.values?.variable[tabKey]?.main_photo as any;
+ const FormikimageUrl = FormikName ? ImageBaseURL + FormikName : '';
+
+ const imageUrl = typeof FormikName === "string" ? (FormikimageUrl)?.replace("public", "/storage") : (FormikName instanceof File) ? URL.createObjectURL(FormikName) : "";
+
+ const fileList: UploadFile[] = [
+
+ {
+ uid: '-1',
+ name: '',
+ status: 'done',
+ url: imageUrl,
+ thumbUrl: imageUrl
+ }
+ ];
+ const FilehandleChange = (value:any) => {
+
+ formik.setFieldValue(name, value.file.originFileObj)
+
+ };
+ const customRequest = async ({ onSuccess}: any) => {
+ onSuccess();
+ };
+ return (
+
+
+
+
+ }>
+ { t("upload_image") }
+
+
+
+
+
+
+
+ )
+}
+
+export default FileProduct
\ No newline at end of file
diff --git a/src/Pages/Products/View/FormikTab/Field/FileImage.tsx b/src/Pages/Products/View/FormikTab/Field/FileImage.tsx
new file mode 100644
index 0000000..5398bdb
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/Field/FileImage.tsx
@@ -0,0 +1,65 @@
+import { Button, Upload } from 'antd';
+import { UploadOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { useFormikContext } from 'formik';
+import { ImageBaseURL } from '../../../../../api/config';
+
+const MaltyFile = ({ tabKey }: any) => {
+ const { t } = useTranslation();
+ const formik = useFormikContext();
+ const name = `variable.${"images"}`;
+
+ // Construct imageUrl and fileList for each file
+ const files = formik.values?.variable?.images || [];
+ console.log(formik.values?.variable);
+
+ const fileItems = files.map((file: any, index: number) => {
+ console.log(typeof file);
+
+ const FormikimageUrl = file ? ImageBaseURL + file?.path : '';
+ const imageUrl = typeof file === "object" ? (FormikimageUrl)?.replace("public", "/storage") : (file instanceof File) ? URL.createObjectURL(file) : "";
+ console.log(imageUrl);
+
+ return {
+ uid: `-${index}`,
+ name: file?.name || '',
+ status: 'done',
+ url: file,
+ thumbUrl: imageUrl
+ };
+ });
+
+ const FilehandleChange = ({ fileList }: { fileList: any }) => {
+ console.log(fileList);
+
+ formik.setFieldValue(
+ name,
+ fileList.map((file: any) => (file?.originFileObj ? file.originFileObj : file.url))
+ ); };
+
+ const customRequest = async ({ onSuccess }: any) => {
+ onSuccess();
+ };
+
+ return (
+
+
+
+ }>
+ {t("upload_image")}
+
+
+
+ );
+}
+
+export default MaltyFile
\ No newline at end of file
diff --git a/src/Pages/Products/View/FormikTab/Field/Object.tsx b/src/Pages/Products/View/FormikTab/Field/Object.tsx
new file mode 100644
index 0000000..477198b
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/Field/Object.tsx
@@ -0,0 +1,85 @@
+import React, { useEffect, useState } from 'react';
+import { CloseOutlined } from '@ant-design/icons';
+import { Button, Form, Input, Space, Typography } from 'antd';
+import { useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+
+const ObjectField = ({ tabKey }: any) => {
+ const [form] = Form.useForm();
+ const formik = useFormikContext();
+ const [FieldItems, setFieldItems] = useState([]);
+ const [t] = useTranslation();
+ const initialValues = formik?.values?.variable?.info;
+
+ console.log(initialValues);
+
+ const handleChange = (index: number, fieldKey: string, value: string) => {
+ const updatedItems = [...FieldItems];
+ updatedItems[index][fieldKey] = value;
+ setFieldItems(updatedItems);
+ formik.setFieldValue(`variable.${tabKey}.info`, updatedItems);
+ console.log(updatedItems,"updatedItems");
+
+ };
+
+ useEffect(() => {
+ if (initialValues) {
+ setFieldItems([...initialValues]);
+ } else {
+ setFieldItems([{ Description: '', key: '' }]);
+ }
+ }, []); // Update when tabKey or info[tabKey] changes
+
+ const removeItem = (index: number) => {
+ const updatedItems = [...FieldItems];
+ updatedItems.splice(index, 1);
+ setFieldItems(updatedItems);
+ formik.setFieldValue(`variable.info`, updatedItems);
+ };
+
+ useEffect(() => {
+ // console.log(initialValues);
+ console.log(initialValues,"initialValues");
+
+ }, [initialValues])
+
+ return (
+
+ );
+};
+
+export default ObjectField;
diff --git a/src/Pages/Products/View/FormikTab/FormItem.tsx b/src/Pages/Products/View/FormikTab/FormItem.tsx
new file mode 100644
index 0000000..91b4c0b
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/FormItem.tsx
@@ -0,0 +1,19 @@
+// FormItem.tsx
+import React from 'react';
+import { Input, Label } from 'reactstrap';
+
+interface FormItemProps {
+ label: string;
+ value: string;
+ onChange: (e: React.ChangeEvent) => void;
+ type?: any
+}
+
+export const FormItem: React.FC = ({ label, value, onChange ,type = "text"}) => {
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/src/Pages/Products/View/FormikTab/TabsContainer.tsx b/src/Pages/Products/View/FormikTab/TabsContainer.tsx
new file mode 100644
index 0000000..8d10bbf
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/TabsContainer.tsx
@@ -0,0 +1,161 @@
+// TabsContainer.tsx
+import React, { useEffect, useState } from 'react';
+import { Tabs, Space } from 'antd';
+import { CopyOutlined } from '@ant-design/icons';
+import { toast } from 'react-toastify';
+import { FormikValues, useFormikContext } from 'formik';
+import { useTranslation } from 'react-i18next';
+import { useProductVarianState } from '../../../../lib/state mangment/Pages/Products';
+import { VariableTabs } from './VariableTabs';
+import { log } from 'console';
+import { MdOutlineDeleteOutline } from 'react-icons/md';
+
+const { TabPane } = Tabs;
+
+
+export const TabsContainer: React.FC = () => {
+ const [activeKey, setActiveKey] = useState('1');
+ const [t] = useTranslation()
+ const initialItemShape: any = {
+ label: `${t(`variant`)} 1`,
+ key: '1',
+ closable: true,
+ };
+ const formikContext = useFormikContext();
+ const { setFieldValue } = useFormikContext();
+ const { values, handleChange } = formikContext;
+ const varianCount = values?.variable?.slice(1)?.map((item: any, index: any) => {
+ return {
+ label: `${t(`variant`)}` + `${index + 1}`,
+ key: index + 1,
+ closable: true,
+ }
+ }) ?? initialItemShape
+ const [items, setItems] = useState(varianCount ?? [initialItemShape]); // Ensure items is always an array
+ const [width, setWidth] = useState(window.innerWidth);
+
+ const findFirstMissingKey = (itemKeys: string[]) => {
+ const keysAsNumbers = itemKeys.map(Number); // Convert strings to numbers
+ for (let i = 1; i <= keysAsNumbers.length + 1; i++) {
+ if (!keysAsNumbers.includes(i)) {
+ return i;
+ }
+ }
+ };
+ const [nextKey, setNextKey] = useState(items?.map((item: any) => `${item.key}`)); // Initialize the key counter to the length of items + 1
+ useEffect(() => {
+ // Set the active key to the first tab key when the component mounts or items change
+ if (items.length > 0) {
+ setActiveKey(items[0].key);
+ }
+ }, []);
+
+ useEffect(() => {
+ const keys = items.map((item: any) => `${item.key}`);
+ setNextKey(findFirstMissingKey(keys))
+
+ }, [items]);
+
+
+ const handleAdd = () => {
+ const newKey = `${nextKey}`;
+ setItems([...items, { key: newKey, label: `${t(`variant`)} ${newKey}`, closable: true }]);
+ setActiveKey(newKey);
+ };
+
+ const handleDuplicate = (targetKey: string) => {
+ const targetItem = items.find((item: any) => item.key === targetKey);
+ if (targetItem) {
+ const newKey = `${nextKey}`;
+ const newItem = { ...targetItem, key: newKey, label: `${t(`variant`)} ${newKey}` };
+ setItems([...items, newItem]);
+ setActiveKey(newKey);
+
+ const originalValues = values?.variable?.[targetKey];
+ console.log(originalValues?.id,'originalValues?.id');
+
+ if(originalValues?.id === null){
+ console.log(originalValues?.id,'`1`');
+
+ setFieldValue(`variable.${newKey}`, { ...originalValues, id: 0 });
+
+ }else{
+ console.log(originalValues?.id,'2');
+
+ setFieldValue(`variable.${newKey}`, { ...originalValues, id: null });
+
+ }
+
+ const originalInfo = values?.info?.[targetKey];
+
+ setFieldValue(`info.${newKey}`, originalInfo);
+
+ const originalvariable = values?.variable[targetKey]?.variable;
+
+ setFieldValue(`variable.${newKey}.variable`, originalvariable);
+
+ }
+ };
+
+ const [removedVariant, setremovedVariant] = useState([]);
+ useEffect(() => {
+ setFieldValue(`removedVariant`, removedVariant);
+
+ }, [removedVariant]);
+ const handleRemove = (targetKey: string) => {
+ const newItems = items.filter((item: any) => item.key !== targetKey);
+ const removedItem = values?.variable[targetKey] as any;
+ if (removedItem?.id) {
+ setremovedVariant((prevRemovedvariable) => [...prevRemovedvariable, removedItem.id]);
+ }
+ const newActiveKey = newItems.length ? newItems[newItems.length - 1].key : '1';
+ setItems(newItems);
+ setActiveKey(newActiveKey);
+ setFieldValue(`variable.${targetKey}`, undefined);
+ setFieldValue(`info.${targetKey}`, undefined);
+
+
+ };
+
+ useEffect(() => {
+ const handleResize = () => {
+ setWidth(window.innerWidth);
+ };
+
+ window.addEventListener('resize', handleResize);
+
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+ const tabPosition = width > 1000 ? 'left' : 'top';
+
+ return (
+ <>
+ (action === 'add' ? handleAdd() : handleRemove(targetKey))}
+ tabPosition={tabPosition}
+ removeIcon={}
+
+ items={
+ items.map((item: any) => ({
+ label: {t(`${item.label}`)} handleDuplicate(item.key)} />,
+ children: ,
+ key: item.key,
+ closable: item.closable,
+ }))
+ }
+
+ >
+
+
+ {items.length === 0 && (
+ {t("Add New Variant")}
+
+ )}
+ >
+ );
+};
diff --git a/src/Pages/Products/View/FormikTab/VariableTabs.tsx b/src/Pages/Products/View/FormikTab/VariableTabs.tsx
new file mode 100644
index 0000000..6f2fead
--- /dev/null
+++ b/src/Pages/Products/View/FormikTab/VariableTabs.tsx
@@ -0,0 +1,99 @@
+// VariableTabs.tsx
+import React from 'react';
+import { Col, Row } from 'reactstrap';
+import { FormItem } from './FormItem';
+import { useFormikContext, FormikValues } from 'formik';
+import File from './Field/File';
+import MaltyFile from './Field/FileImage';
+import ObjectField from './Field/Object';
+import Atteibute from './Field/Atteibute';
+
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+
+interface VariableTabsProps {
+ tabKey: string;
+}
+
+export const VariableTabs: React.FC = ({ tabKey }) => {
+ const { t } = useTranslation();
+ const formikContext = useFormikContext();
+ const { values, handleChange } = formikContext;
+ // const {id}= useParams()
+
+ const handleFieldChange = (fieldName: string) => (
+ e: React.ChangeEvent | any
+ ) => {
+ handleChange(`variable.${tabKey}.${fieldName}`)(e); // Prepend "variable"
+ };
+ const FormikName = (FormikFieldname: any) => values?.variable?.[tabKey]?.[FormikFieldname];
+
+
+
+ return (
+ <>
+ {t("variables")} {tabKey}
+
+
+
+
+
+
+ {/* */}
+
+ {(values?.category_id && (!values?.variable?.[tabKey]?.id || values?.variable?.[tabKey]?.id == null ) ) &&
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/Pages/Products/formUtil.ts b/src/Pages/Products/formUtil.ts
new file mode 100644
index 0000000..77f649b
--- /dev/null
+++ b/src/Pages/Products/formUtil.ts
@@ -0,0 +1,128 @@
+
+import * as Yup from "yup";
+import { ImageBaseURL } from "../../api/config";
+
+
+
+export const getInitialValues = (objectToEdit: any) => {
+ if (!objectToEdit || !objectToEdit?.products) {
+ return {};
+ }
+ const products = objectToEdit?.products?.map((item: any) => {
+ // console.log(item,"item");
+
+
+ const formattedData = item?.info ? Object.entries(item?.info).map(([key, value], index) => ({
+ [`Description`]: key,
+ [`key`]: value,
+ })) : [];
+
+ // console.log(formattedData,"formattedData");
+ const images_product = item?.images?.map((item:any)=>{
+ return item?.path
+ })
+
+
+ return ({
+
+ name_ar: item?.name["ar"],
+ name_en: item?.name["en"],
+ name_cn: item?.name["cn"],
+ description_ar: item?.description["ar"],
+ description_en: item?.description["en"],
+ description_cn: item?.description["cn"],
+ images: images_product,
+ main_photo: item?.main_photo,
+ price: item?.price,
+ // quantity: item?.quantity,
+ // products_attributes: item?.products_attributes,
+ id:item?.id,
+ info:formattedData
+
+ })
+ })
+
+ console.log(objectToEdit);
+
+
+ const images_product = objectToEdit?.product?.images?.map((item:any)=>{
+ return item?.path
+ })
+
+ const formattedData = objectToEdit?.product?.info ? Object.entries(objectToEdit?.product?.info).map(([key, value], index) => ({
+ [`Description`]: key,
+ [`key`]: value,
+ })) : [];
+ console.log(formattedData);
+
+ return {
+ id:objectToEdit?.id,
+ name: objectToEdit?.name ?? "",
+ name_ar: objectToEdit?.name?.ar ?? '',
+ name_en: objectToEdit?.name?.en ?? '',
+ name_cn: objectToEdit?.name?.cn ?? '',
+ description_ar: objectToEdit?.product?.description?.ar ?? '',
+ description_en: objectToEdit?.product?.description?.en ?? '',
+ description_cn: objectToEdit?.product?.description?.cn ?? '',
+ // attribute: objectToEdit?.attribute ?? "",
+ category_id: objectToEdit?.category?.id ?? "",
+ price: objectToEdit?.product?.price ?? "",
+ product_id:objectToEdit?.product?.id,
+ main_photo: objectToEdit?.product?.main_photo,
+ variable:{ info:formattedData, images: objectToEdit?.product?.images, },
+
+ // variable: [{}, ...products],
+ // info: [undefined, ...formattedData] ?? [],
+ removedVariant:[],
+ }
+};
+
+export const getInitialValuesAdd = (objectToEdit: any | null = null) => {
+
+ return {
+ id: "",
+ name: "",
+ name_ar:'',
+ name_en: '',
+ name_cn: '',
+ description_ar:'',
+ description_en: '',
+ description_cn: '',
+ price: "",
+ main_photo:'',
+ images:'',
+
+ category_id: "",
+ variable: [],
+ info: [],
+
+ }
+};
+
+export const getValidationSchema = (editMode: boolean = false) => {
+ // validate input
+ return Yup.object().shape({
+ name_ar: Yup.string().required('Required'),
+ name_en: Yup.string().required('Required'),
+ name_cn: Yup.string().required('Required'),
+ description_ar: Yup.string().required('Required'),
+ description_en: Yup.string().required('Required'),
+ description_cn: Yup.string().required('Required'),
+ category_id: Yup.string().required('Required'),
+ price: Yup.number().required('Required').typeError("must be number"),
+ main_photo: Yup.string().required('Required'),
+ // images: Yup.array().required('Required'),
+
+
+
+
+
+ });
+};
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+ return data;
+};
+
diff --git a/src/Pages/Products/useTableColumns.tsx b/src/Pages/Products/useTableColumns.tsx
new file mode 100644
index 0000000..d6af11a
--- /dev/null
+++ b/src/Pages/Products/useTableColumns.tsx
@@ -0,0 +1,89 @@
+
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+import { HovarableImage } from "../../Components/Ui";
+import { BaseURL } from "../../api/config";
+import { ToggleStatus } from "../../Components/Ui/ToggleStatus";
+import ColumnsImage from "../../Components/Columns/ColumnsImage";
+import LoadingSpinner from "../../Components/Ui/LoadingSpinner";
+import { Switch } from "antd";
+import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
+// import { useDeleteProduct, useUpdateProductStatus } from "../../api/owner_products";
+import { useNavigate } from "react-router-dom";
+import { useDeleteProduct, useUpdateProductStatus } from "../../api/product";
+
+
+const useTableColumns :any = () => {
+ const [t] = useTranslation();
+ const toggleMutation = useUpdateProductStatus();
+ const deleteMutation = useDeleteProduct();
+
+ const navigate = useNavigate()
+ const handleChange = (row:any)=> {
+ const status = row?.favorite ;
+ toggleMutation.mutate({id:row?.id,new_status:status})
+
+ }
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useMemo(
+ () => [
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("name"),
+ sortable: false,
+ center: true,
+ selector:(row:any) => row?.name?.[language],
+
+ },
+ {
+ name: t("product_count"),
+ sortable: false,
+ center: true,
+ selector:(row:any) => row?.product_count,
+
+ },
+
+ {
+ name: t("category"),
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ row?.category?.name?.[language]
+ ),
+ },
+
+ // {
+ // name: t("favorite"),
+ // sortable: false,
+ // center: true,
+ // cell: (row:any) => (
+ //
+ // ),
+ // },
+ {
+ name: "#",
+ sortable: false,
+ center: "true",
+ cell: (row:any) => (
+ navigate('/products/'+row.id)}
+ objectToEdit={row}
+ showEdit={true}
+ showView={false}
+ onDelete={() => deleteMutation.mutate({ id: row.id })}
+ />
+ ),
+ },
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/Slider/Page.tsx b/src/Pages/Slider/Page.tsx
new file mode 100644
index 0000000..9f1c3b3
--- /dev/null
+++ b/src/Pages/Slider/Page.tsx
@@ -0,0 +1,47 @@
+
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import { useGetSlider } from '../../api/Slider'
+import SearchField from '../../Layout/Dashboard/SearchField'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetSlider()
+ const [t] = useTranslation()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
navigate('/slider/add')}>
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/Slider/View/AddForm.tsx b/src/Pages/Slider/View/AddForm.tsx
new file mode 100644
index 0000000..8f3f82e
--- /dev/null
+++ b/src/Pages/Slider/View/AddForm.tsx
@@ -0,0 +1,37 @@
+
+import React from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useFormikContext } from 'formik';
+
+import { DatePicker } from 'antd';
+import { useTranslation } from 'react-i18next';
+
+function Form() {
+ const formik = useFormikContext();
+ const [t] = useTranslation();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/Slider/View/AddPage.tsx b/src/Pages/Slider/View/AddPage.tsx
new file mode 100644
index 0000000..a602d62
--- /dev/null
+++ b/src/Pages/Slider/View/AddPage.tsx
@@ -0,0 +1,65 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import { BsInfoCircle } from 'react-icons/bs';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { useAddSlider } from '../../../api/Slider';
+import Form from './AddForm';
+
+const AddSliderPage = () => {
+
+
+ const {mutate , isLoading:IsloadingButton , isSuccess} = useAddSlider()
+ const handleSubmit = (values:any)=>{
+
+ mutate({
+ title: {
+ en: values?.title_en,
+ ar: values?.title_ar,
+ cn: values?.title_cn
+ },
+ image:values?.image
+
+ })
+
+
+ }
+ const {t} = useTranslation();
+
+ useNavigateOnSuccess(isSuccess , '/Slider' )
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddSliderPage
\ No newline at end of file
diff --git a/src/Pages/Slider/View/EditForm.tsx b/src/Pages/Slider/View/EditForm.tsx
new file mode 100644
index 0000000..2b1b6ad
--- /dev/null
+++ b/src/Pages/Slider/View/EditForm.tsx
@@ -0,0 +1,39 @@
+
+import React from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useFormikContext } from 'formik';
+
+import { DatePicker } from 'antd';
+import { useTranslation } from 'react-i18next';
+import { useGetCategories } from '../../../api/Categories';
+
+function Form() {
+ const formik = useFormikContext();
+ const [t] = useTranslation()
+ const {data} = useGetCategories()
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/Slider/View/EditPage.tsx b/src/Pages/Slider/View/EditPage.tsx
new file mode 100644
index 0000000..4940b32
--- /dev/null
+++ b/src/Pages/Slider/View/EditPage.tsx
@@ -0,0 +1,89 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValues, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import { FaSadCry } from 'react-icons/fa'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { Rate } from 'antd';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import { useParams } from 'react-router-dom';
+import LoadingPage from '../../../Layout/app/LoadingPage';
+import { useTranslation } from 'react-i18next';
+import { BsInfoCircle } from 'react-icons/bs';
+import { useGetOneSlider, useUpdateSlider } from '../../../api/Slider';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import Form from './EditForm';
+
+const EditPage = () => {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const {t} = useTranslation();
+ const { data } = useGetOneSlider()
+ const {mutate ,isSuccess,isLoading:IsloadingButton} = useUpdateSlider()
+ const FormatedData = data?.data ;
+ const handleSubmit = (values:any)=>{
+
+ const newData = {} as any;
+
+ for (const key in FormatedData) {
+ if (values[key] !== FormatedData[key]) {
+ newData[key] = values[key];
+ }
+
+ }
+
+ return mutate({
+ title: {
+ en: values?.title_en,
+ ar: values?.title_ar,
+ cn: values?.title_cn
+ },
+ image:values?.image
+
+ })
+ }
+
+ useNavigateOnSuccess(isSuccess , '/Slider')
+
+
+ useEffect(() => {
+ console.log(data);
+
+ setObjectToEdit(data?.category);
+
+ }, [data]);
+
+
+ const getValidationSchema = () => {
+ return null
+ };
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+ {objectToEdit && data ?
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default EditPage
\ No newline at end of file
diff --git a/src/Pages/Slider/formUtil.ts b/src/Pages/Slider/formUtil.ts
new file mode 100644
index 0000000..bc9c19d
--- /dev/null
+++ b/src/Pages/Slider/formUtil.ts
@@ -0,0 +1,67 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+
+interface formUtilCommon {
+ number:number,
+ value:number
+}
+
+interface ObjectToEdit extends formUtilCommon {
+
+ id?:number,
+
+}
+
+interface InitialValues extends ObjectToEdit {
+
+}
+interface ValidateSchema extends formUtilCommon{
+
+}
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ console.log(objectToEdit,"objectToEdit");
+ return {
+ id: objectToEdit?.id ?? 0,
+ title_en:objectToEdit?.title_en ?? "",
+ title_ar:objectToEdit?.title_ar ?? "",
+ title_cn:objectToEdit?.title_cn ?? "",
+
+ image: objectToEdit?.image ?? '',
+
+ };
+};
+
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ title_en: Yup.string().required('Required'),
+ title_ar: Yup.string().required('Required'),
+ title_cn: Yup.string().required('Required'),
+
+ image: Yup.string().required('Required'),
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data:any)=>{
+
+ let new_array = data
+ for(let i =0 ; i {
+ const [t] = useTranslation();
+ const deleteMutation = useDeleteSlider()
+ const navigate = useNavigate()
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("title"),
+ sortable: false,
+ center: "true",
+ cell: (row:any) => row?.title[language] ?? row?.title
+ },
+ {
+ name: t("image"),
+ sortable: false,
+ center: "true",
+
+ cell: (row:any) => {
+ let str = row?.image;
+ str = str?.replace(`public`, "/storage") ?? "";
+ return
+ }
+ },
+ {
+ name: "#",
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ navigate(`/slider/${row.id}`) }
+ showView={false}
+ showEdit={false}
+ onDelete={() => deleteMutation.mutate({ id: row.id })}
+ />
+ ),
+ },
+ // {
+ // name: t("status"),
+ // sortable: false,
+ // center: "true",
+ // cell: (row:any) => {
+
+ // return(
+ //
+ // {
+ // !row.deleted_at ? t('available') : t('unavailable')
+
+ // }
+ //
+ // )
+ // }
+ // },
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/SupportMessages/Page.tsx b/src/Pages/SupportMessages/Page.tsx
new file mode 100644
index 0000000..e2639a0
--- /dev/null
+++ b/src/Pages/SupportMessages/Page.tsx
@@ -0,0 +1,45 @@
+
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+import { useNavigate } from 'react-router-dom'
+import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
+import { useGetSupportMessages } from '../../api/supportmessages'
+import SearchField from '../../Layout/Dashboard/SearchField'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetSupportMessages()
+ const navigate = useNavigate()
+ const totalRows = data?.meta?.total;
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+ {/*
navigate('/support_messages/add')}> */}
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/SupportMessages/View/AddForm.tsx b/src/Pages/SupportMessages/View/AddForm.tsx
new file mode 100644
index 0000000..a1817d7
--- /dev/null
+++ b/src/Pages/SupportMessages/View/AddForm.tsx
@@ -0,0 +1,50 @@
+
+import React, { useEffect } from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { useGetUsers } from '../../../api/users';
+import { useFormikContext } from 'formik';
+
+function Form() {
+ const useFormatToSelect = (Data : any) => {
+ const format = (data :any) => {
+ if (!data) return [];
+ return data.map((item :any) => ({
+ value: item?.id,
+ label: item?.name,
+ }));
+ };
+
+ return format(Data);
+ };
+ const { data: user } = useGetUsers()
+
+ const SelectData = useFormatToSelect(user?.data)
+
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/SupportMessages/View/AddPage.tsx b/src/Pages/SupportMessages/View/AddPage.tsx
new file mode 100644
index 0000000..cdb7258
--- /dev/null
+++ b/src/Pages/SupportMessages/View/AddPage.tsx
@@ -0,0 +1,56 @@
+import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import { useAddSupportMessages } from '../../../api/supportmessages';
+import Form from './AddForm';
+
+const AddSupportMessagesPage = () => {
+
+
+ const {mutate , isLoading:IsloadingButton , isSuccess} = useAddSupportMessages()
+ const handleSubmit = (values:any)=>{
+ console.log(values,"values");
+
+ mutate(values)
+
+
+ }
+ const {t} = useTranslation();
+
+ useNavigateOnSuccess(isSuccess , '/SupportMessages' )
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddSupportMessagesPage
\ No newline at end of file
diff --git a/src/Pages/SupportMessages/formUtil.ts b/src/Pages/SupportMessages/formUtil.ts
new file mode 100644
index 0000000..8eb1c46
--- /dev/null
+++ b/src/Pages/SupportMessages/formUtil.ts
@@ -0,0 +1,46 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ console.log(objectToEdit,"objectToEdit");
+ return {
+ whatsApp: "",
+ subject: "",
+ message: "",
+ id:null,
+ };
+};
+
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ whatsApp: Yup.string().required('Required'),
+ subject: Yup.string().required('Required'),
+ message: Yup.string().required('Required'),
+ id: Yup.string().required('Required'),
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data:any)=>{
+
+ let new_array = data
+ for(let i =0 ; i {
+ const [t] = useTranslation();
+ const deleteMutation = useDeleteSupportMessages()
+ const navigate = useNavigate()
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("email"),
+ center: true,
+ selector: (row: any) => row.email, // Specify selector function for sorting
+ },
+
+ {
+ name: t("whatsApp"),
+ center: true,
+ selector: (row: any) => row.whatsApp, // Specify selector function for sorting
+ },
+
+ {
+ name: t("subject"),
+ center: true,
+ selector: (row: any) => row.subject, // Specify selector function for sorting
+ },
+ {
+ name: t("message"),
+ center: true,
+ selector: (row: any) => row.message, // Specify selector function for sorting
+ },
+ {
+ name: "#",
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ navigate(`/support_messages/${row.id}`) }
+ showView={false}
+ showEdit={false}
+ onDelete={() => deleteMutation.mutate({ id: row.id })}
+ />
+ ),
+ },
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/Users/Page.tsx b/src/Pages/Users/Page.tsx
new file mode 100644
index 0000000..08018c6
--- /dev/null
+++ b/src/Pages/Users/Page.tsx
@@ -0,0 +1,56 @@
+
+import React from 'react'
+import DashBody from '../../Layout/Dashboard/DashBody'
+import DashHeader from '../../Layout/Dashboard/DashHeader'
+import LyTable from '../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../config/QueryStatus'
+
+import { useGetUsers } from '../../api/users'
+import SearchField from '../../Layout/Dashboard/SearchField'
+import SelectField from '../../Layout/Dashboard/SelectField'
+import AddButtonNotification from './ui/AddButtonNotification'
+
+function Page() {
+
+ const column =useTableColumns()
+ const {data ,status } = useGetUsers()
+
+ const totalRows = data?.meta?.total;
+
+
+ const typeData = [{label:"admin",value:"admin"},{value:"user",label:"user"}]
+ const statusData = [{label:"active",value:"active"},{label:"inActive",value:"inActive"}]
+
+
+ return (
+ // Pass Status to Layout
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
diff --git a/src/Pages/Users/SendNotifcation/View/AddForm.tsx b/src/Pages/Users/SendNotifcation/View/AddForm.tsx
new file mode 100644
index 0000000..e5482a9
--- /dev/null
+++ b/src/Pages/Users/SendNotifcation/View/AddForm.tsx
@@ -0,0 +1,43 @@
+
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../../Components/ValidationField/ValidationField';
+
+function Form() {
+ // const type = [{ lable: "other", value: "other" },{ lable: "product", value: "product"},{ lable: "order", value: "order" }]
+
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
+
+
diff --git a/src/Pages/Users/SendNotifcation/View/AddPage.tsx b/src/Pages/Users/SendNotifcation/View/AddPage.tsx
new file mode 100644
index 0000000..e1645bc
--- /dev/null
+++ b/src/Pages/Users/SendNotifcation/View/AddPage.tsx
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from 'react'
+import { getInitialValuesForAdd as getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../../Layout/Dashboard/ViewPage';
+import { useTranslation } from 'react-i18next';
+import useNavigateOnSuccess from '../../../../Hooks/useNavigateOnSuccess';
+import { useAddUsers } from '../../../../api/users';
+import Form from './AddForm';
+import { useParams } from 'react-router-dom';
+import { useAddNotification } from '../../../../api/notification';
+
+const AddPage = () => {
+
+
+ const { mutate, isLoading:IsloadingButton, isSuccess } = useAddNotification()
+ const {id} = useParams()
+ const handleSubmit = (values: any) => {
+
+ const body = {
+ en: values.body_en,
+ ar: values.body_ar,
+ de: values.body_ce
+ }
+ const meta = {}
+ const title = {
+ en: values.title_en,
+ ar: values.title_ar,
+ de: values.title_cn
+ }
+ const dataToSend = {
+ body: body,
+ meta: meta,
+ title:title,
+ user_ids: [id],
+ type: "other",
+
+ };
+
+
+ mutate(dataToSend);
+ };
+
+ const { t } = useTranslation();
+
+ useNavigateOnSuccess(isSuccess, '/users')
+
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+}
+
+export default AddPage
\ No newline at end of file
diff --git a/src/Pages/Users/SendNotifcation/formUtil.ts b/src/Pages/Users/SendNotifcation/formUtil.ts
new file mode 100644
index 0000000..4480aa5
--- /dev/null
+++ b/src/Pages/Users/SendNotifcation/formUtil.ts
@@ -0,0 +1,73 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../../api/helper/buildFormData";
+import moment from 'moment';
+import * as dayjs from 'dayjs'
+
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ //@ts-ignore
+ return {
+ id: objectToEdit?.id ,
+ name: objectToEdit?.name ,
+ email: objectToEdit?.email ,
+ type: objectToEdit?.type ,
+ avatar: objectToEdit?.avatar ,
+
+ };
+};
+
+export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
+ return {
+ body_en: "" ,
+ body_ar: "" ,
+ body_ce: "" ,
+ // meta: "" ,
+ title_en: "" ,
+ title_ar: "" ,
+ title_cn: "" ,
+ user_ids:[],
+ // type: "" ,
+
+ };
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+ body_en: Yup.string().required('Required'),
+ body_ar: Yup.string().required('Required'),
+ body_ce: Yup.string().required('Required'),
+ // meta: Yup.string().required('Required'),
+
+ title_en: Yup.string().required('Required'),
+ title_ar: Yup.string().required('Required'),
+ title_cn: Yup.string().required('Required'),
+ // type: Yup.string().required('Required'),
+
+
+
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data: any) => {
+
+ let new_array = data
+ for (let i = 0; i < data.length; i++) {
+ new_array[i]['status'] = !data[i]['deleted_at'] ? 'available' : 'unavailable'
+ delete new_array[i]['deleted_at']
+ }
+ return new_array
+}
\ No newline at end of file
diff --git a/src/Pages/Users/View/EditForm.tsx b/src/Pages/Users/View/EditForm.tsx
new file mode 100644
index 0000000..07060c0
--- /dev/null
+++ b/src/Pages/Users/View/EditForm.tsx
@@ -0,0 +1,30 @@
+
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+
+function Form() {
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
+
+
diff --git a/src/Pages/Users/View/EditPage.tsx b/src/Pages/Users/View/EditPage.tsx
new file mode 100644
index 0000000..e6d8022
--- /dev/null
+++ b/src/Pages/Users/View/EditPage.tsx
@@ -0,0 +1,67 @@
+import React, { useEffect } from 'react'
+import { getInitialValues, getDataToSend } from '../formUtil'
+import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
+import 'react-tabs/style/react-tabs.css';
+import { MdLanguage } from 'react-icons/md'
+import ViewPage from '../../../Layout/Dashboard/ViewPage';
+import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
+import LoadingPage from '../../../Layout/app/LoadingPage';
+import { useTranslation } from 'react-i18next';
+import { useGetOneUser, useUpdateAdmin } from '../../../api/users';
+import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
+import Form from './EditForm';
+
+const EditPage = () => {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const {t} = useTranslation();
+ const { data } = useGetOneUser()
+ const {mutate ,isSuccess,isLoading:IsloadingButton} = useUpdateAdmin()
+ const handleSubmit = (values:any)=>{
+
+ return mutate(values);
+ }
+
+ useNavigateOnSuccess(isSuccess , '/users')
+
+
+ useEffect(() => {
+ console.log(data);
+
+ setObjectToEdit(data?.data);
+
+ }, [data]);
+
+
+ const getValidationSchema = () => {
+ return null
+ };
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+
+
+ return (
+
+ {objectToEdit && data ?
+
+
+
+
{t("BasicInfo")}
+
+
+
+
+
+
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default EditPage
\ No newline at end of file
diff --git a/src/Pages/Users/formUtil.ts b/src/Pages/Users/formUtil.ts
new file mode 100644
index 0000000..7d5e227
--- /dev/null
+++ b/src/Pages/Users/formUtil.ts
@@ -0,0 +1,52 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../api/helper/buildFormData";
+
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ //@ts-ignore
+ return {
+ // id: objectToEdit?.id ,
+ new_password: objectToEdit?.password ,
+ old_password: objectToEdit?.old_password ,
+
+
+ };
+};
+
+export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
+ return {
+ password: null ,
+
+
+ };
+};
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data: any) => {
+
+ let new_array = data
+ for (let i = 0; i < data.length; i++) {
+ new_array[i]['status'] = !data[i]['deleted_at'] ? 'available' : 'unavailable'
+ delete new_array[i]['deleted_at']
+ }
+ return new_array
+}
\ No newline at end of file
diff --git a/src/Pages/Users/ui/AddButtonNotification.tsx b/src/Pages/Users/ui/AddButtonNotification.tsx
new file mode 100644
index 0000000..0f8f917
--- /dev/null
+++ b/src/Pages/Users/ui/AddButtonNotification.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router-dom'
+
+const AddButtonNotification = () => {
+ const navigate = useNavigate()
+ const [t] = useTranslation()
+ return (
+navigate('/notification/add')} >
+
+
)
+}
+
+export default AddButtonNotification
\ No newline at end of file
diff --git a/src/Pages/Users/useTableColumns.tsx b/src/Pages/Users/useTableColumns.tsx
new file mode 100644
index 0000000..fb4bda8
--- /dev/null
+++ b/src/Pages/Users/useTableColumns.tsx
@@ -0,0 +1,110 @@
+
+import React, { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Actions from "../../Components/Ui/tables/Actions";
+import ColumnsImage from "../../Components/Columns/ColumnsImage";
+import { useNavigate } from "react-router-dom";
+import { useUpdateUsers } from "../../api/users";
+import { Switch } from "antd";
+import { MdEmail } from "react-icons/md";
+import { toast } from "react-toastify";
+
+
+const useTableColumns: any = () => {
+ const [t] = useTranslation();
+ const navigate = useNavigate()
+
+ const { mutate: update_status } = useUpdateUsers("put")
+ const onChange = (checked: boolean, row: any) => {
+ console.log(`switch to ${checked}`)
+ if(row.type === "user"){
+
+ update_status({
+ id: row.id,
+ status: checked ? "active" : "suspended"
+
+ })
+ }else{
+ toast.error("sorry_only_user_can_change_his_status")
+ }
+
+
+ };
+
+
+ return useMemo(
+ () => [
+
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ },
+ {
+ name: t("avatar"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => {
+ let str = row?.avatar;
+ str = str?.replace(`public`, "/storage") ?? "";
+ return
+ }
+
+ },
+ {
+ name: t("name"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.name
+ },
+ {
+ name: t("email"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.email
+ },
+ {
+ name: t("type"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => row?.type
+ },
+ {
+ name: t("status"),
+ sortable: false,
+ center: "true",
+ cell: (row: any) => {
+ let status = row?.status;
+ return onChange(value, row)} />;
+
+ }
+
+ },
+
+ {
+ name: "#",
+ sortable: false,
+ center: true,
+ cell: (row: any) => (
+ navigate(`/admin/${row.id}`)}
+ showView={false}
+ // onDelete={() => deleteMutation.mutate({ id: row.id })}
+ showDelete={false}
+ >
+ navigate(`/users/${row.id}`)} className="cursor-pointer m-2" size={25} />
+
+
+ ),
+ },
+
+ ],
+ [t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/order/Edit/EditForm.tsx b/src/Pages/order/Edit/EditForm.tsx
new file mode 100644
index 0000000..8588700
--- /dev/null
+++ b/src/Pages/order/Edit/EditForm.tsx
@@ -0,0 +1,46 @@
+
+import React from 'react'
+import { Col, Row } from 'reactstrap';
+import ValidationField from '../../../Components/ValidationField/ValidationField';
+import { FaCheck, FaTimes, FaClock, FaBan } from 'react-icons/fa';
+import { useTranslation } from 'react-i18next';
+
+
+function Form() {
+
+
+ const [t] = useTranslation()
+ // 'pending_approve', 'approved', 'rejected', 'pending_cancellation', 'cancelled'
+ const stateSelect = [
+ { label: {t("Pending Approve")}
, value: "pending_approve" },
+ { label: {t("Approved")}
, value: "approved" },
+ { label: {t("Rejected")}
, value: "rejected" },
+ { label: {t("Pending Cancellation")}
, value: "pending_cancellation" }
+ ];
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/order/Edit/formUtil.ts b/src/Pages/order/Edit/formUtil.ts
new file mode 100644
index 0000000..770178a
--- /dev/null
+++ b/src/Pages/order/Edit/formUtil.ts
@@ -0,0 +1,47 @@
+
+import * as Yup from "yup";
+import { buildFormData } from "../../../api/helper/buildFormData";
+import * as dayjs from 'dayjs'
+
+
+export const getInitialValues = (objectToEdit: any | null = null): any => {
+ // console.log(objectToEdit,"objectToEdit");
+ return {
+ id: objectToEdit?.id ?? 0,
+ state:objectToEdit?.state ?? "",
+ admin_note: objectToEdit?.admin_note ?? '',
+ //@ts-ignore
+ deliviration_estimated_time: dayjs(objectToEdit?.deliviration_estimated_time,"YYYY-MM-DD") ??"",
+ delivery_link:objectToEdit?.delivery_link ??"",
+
+ };
+};
+
+
+export const getValidationSchema = (editMode: boolean = false): Yup.Schema => {
+ // Validate input
+ return Yup.object().shape({
+
+ });
+};
+
+
+
+export const getDataToSend = (values: any): FormData => {
+ const data = { ...values };
+
+
+ const formData = new FormData();
+ buildFormData(formData, data);
+ return formData;
+};
+
+export const ChangeDataToPrint = (data:any)=>{
+
+ let new_array = data
+ for(let i =0 ; i {
+ const { setObjectToEdit, objectToEdit } = usePageState()
+ const {t} = useTranslation();
+ const {mutate ,isSuccess,isLoading:IsloadingButton} = useUpdateOrder("put")
+ const { id } = useParams();
+ const { data, isLoading } = useGetOneOrder({id: id })
+ const handleSubmit = (values:any) => {
+ // Create a new object without empty string values
+ const cleanedValues = {} as any;
+ for (let key in values) {
+ if (values[key] !== "") {
+ cleanedValues[key] = values[key];
+ }
+ }
+
+ // Optionally format the deliviration_estimated_time if it exists
+ if (cleanedValues.deliviration_estimated_time) {
+ cleanedValues.deliviration_estimated_time = cleanedValues.deliviration_estimated_time.format('YYYY-MM-DD HH:mm:ss');
+ }
+
+ if(cleanedValues.deliviration_estimated_time === "Invalid Date"){
+ delete cleanedValues["deliviration_estimated_time"]
+ }
+
+ // Log the formatted deliviration_estimated_time
+ console.log(cleanedValues.deliviration_estimated_time);
+
+ // Mutate with the cleaned values
+ return mutate(cleanedValues);
+}
+
+
+ useNavigateOnSuccess(isSuccess , '/order')
+
+
+ useEffect(() => {
+
+ setObjectToEdit(data?.data);
+
+ }, [data]);
+
+
+ const getValidationSchema = () => {
+ return null
+ };
+
+
+ const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit,IsloadingButton };
+ if ( isLoading || !objectToEdit) {
+ return
+ }
+
+
+
+
+ return (
+
+ {objectToEdit && data?.data ?
+
+
+
+
{t("EditDetails")}
+
{t("BasicInfo")}
+
{t("OrderItems")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ :
}
+
+
+
+ )
+
+}
+
+export default EditPage
\ No newline at end of file
diff --git a/src/Pages/order/OrderPage.tsx b/src/Pages/order/OrderPage.tsx
new file mode 100644
index 0000000..153fd1c
--- /dev/null
+++ b/src/Pages/order/OrderPage.tsx
@@ -0,0 +1,126 @@
+import React from "react";
+import useTableColumns from "./useTableColumns";
+import DashBody from "../../Layout/Dashboard/DashBody";
+import DashHeader from "../../Layout/Dashboard/DashHeader";
+import { QueryStatusEnum } from "../../config/QueryStatus";
+import LyTable from "../../Layout/Dashboard/LyTable";
+import { useGetOrder } from "../../api/order";
+import SelectField from "./ui/CustomSelectField";
+import { useGetCoupon } from "../../api/Coupon";
+import SelectWSearchField from "./ui/CustomSelectWSearchField";
+// import SearchField from "../../Components/ValidationField/View/SearchField";
+import CustomDateRange from "./ui/CustomDateRange";
+import { FaCheck, FaTimes, FaClock, FaBan } from 'react-icons/fa';
+import { Button } from "antd";
+import { useLocation, useNavigate } from "react-router-dom";
+import { useOrderFillterState } from "../../zustand/OrderFillter";
+import CustomSearchField from "./ui/CustomSearchField";
+import CustomNumber from "./ui/CustomNumber";
+import { useTranslation } from "react-i18next";
+
+const OrderPage = () => {
+
+ const { data, status } = useGetOrder();
+
+ const totalRows = data?.meta?.total;
+
+ const columns = useTableColumns();
+
+ const [t] = useTranslation()
+ // 'pending_approve', 'approved', 'rejected', 'pending_cancellation', 'cancelled'
+ const stateSelect = [
+ { label: {t("Pending Approve")}
, value: "pending_approve" },
+ { label: {t("Approved")}
, value: "approved" },
+ { label: {t("Rejected")}
, value: "rejected" },
+ { label: {t("Pending Cancellation")}
, value: "pending_cancellation" }
+ ];
+ const { data: Coupon } = useGetCoupon()
+
+
+ const SelectData = Coupon?.coupons?.map((item: any) => (
+ {
+ label: item?.name,
+ value: item?.id
+ }
+ ))
+ const location = useLocation();
+ const navigate = useNavigate();
+ const { username, coupon, state, fromDate, toDate, totalFrom, totalTo, reset } = useOrderFillterState();
+
+
+ const handleSubmit = () => {
+ let queryParams = [];
+ console.log(username);
+
+ if (username !== null && username!== "" ) {
+ queryParams.push(`username=${username}`);
+ }
+ if (coupon !== null) {
+ queryParams.push(`coupon=${coupon}`);
+ }
+ if (state !== null) {
+ queryParams.push(`state=${state}`);
+ }
+ if (fromDate !== null) {
+ queryParams.push(`createdFrom=${fromDate}`);
+ }
+ if (toDate !== null) {
+ queryParams.push(`createdTo=${toDate}`);
+ }
+ if (totalFrom !== null) {
+ queryParams.push(`totalFrom=${totalFrom}`);
+ }
+ if (totalTo !== null) {
+ queryParams.push(`totalTo=${totalTo}`);
+ }
+
+ const queryString = queryParams.join('&');
+ const newPathname = `${location.pathname}${queryString ? '?' : ''}${queryString}`;
+
+ navigate(newPathname);
+ };
+
+
+ const handelReset = () => {
+
+ navigate(`${location.pathname}`);
+ reset()
+
+
+ }
+
+
+ return (
+
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default OrderPage;
diff --git a/src/Pages/order/Products/ProductsPage.tsx b/src/Pages/order/Products/ProductsPage.tsx
new file mode 100644
index 0000000..fe0746d
--- /dev/null
+++ b/src/Pages/order/Products/ProductsPage.tsx
@@ -0,0 +1,44 @@
+
+import DashBody from '../../../Layout/Dashboard/DashBody'
+import DashHeader from '../../../Layout/Dashboard/DashHeader'
+import LyTable from '../../../Layout/Dashboard/LyTable'
+import useTableColumns from './useTableColumns'
+import { QueryStatusEnum } from '../../../config/QueryStatus'
+import { useParams } from 'react-router-dom'
+import { useGetOneOrder } from '../../../api/order'
+import { Spin } from 'antd'
+
+function ProductsPage() {
+
+ const column =useTableColumns()
+ const {id} = useParams()
+ const { data, status,isLoading } = useGetOneOrder({id: id })
+ const order = data?.data || {};
+ const items = order?.products ;
+ if(isLoading){
+ return
+ }
+
+ console.log(items,"items");
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ProductsPage
+
diff --git a/src/Pages/order/Products/useTableColumns.tsx b/src/Pages/order/Products/useTableColumns.tsx
new file mode 100644
index 0000000..f7e83ac
--- /dev/null
+++ b/src/Pages/order/Products/useTableColumns.tsx
@@ -0,0 +1,49 @@
+
+import { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+
+
+const useTableColumns :any = () => {
+ const [t] = useTranslation();
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useMemo(
+ () => [
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ }, {
+ name: t("name"),
+ sortable: false,
+ center: true,
+ selector:(row:any) => row?.name?.[language],
+
+ },
+ {
+ name: t("quantity"),
+ sortable: false,
+ center: true,
+ selector:(row:any) => row?.quantity,
+
+ },
+
+ {
+ name: t("category"),
+ sortable: false,
+ center: true,
+ cell: (row:any) => (
+ row?.category?.name?.[language]
+ ),
+ },
+
+
+
+ ],
+ [language, t]
+ );
+};
+
+export default useTableColumns;
+
diff --git a/src/Pages/order/View/ViewForm.tsx b/src/Pages/order/View/ViewForm.tsx
new file mode 100644
index 0000000..b9d7a31
--- /dev/null
+++ b/src/Pages/order/View/ViewForm.tsx
@@ -0,0 +1,52 @@
+
+import React from 'react'
+import { useTranslation } from 'react-i18next';
+import { Col, Row } from 'reactstrap';
+import { useGetOneOrder } from '../../../api/order';
+import { useParams } from 'react-router-dom';
+import { ChangeformatDate } from '../../../utils/Date/ChangeFormat';
+import { Spin } from 'antd';
+
+
+function Form() {
+ const [t] = useTranslation()
+ const { id } = useParams()
+ const { data, isLoading } = useGetOneOrder({ id: id })
+ const order = data?.data || {};
+ console.log(order,"order");
+
+
+ if (isLoading) {
+ return
+ }
+
+ return (
+ <>
+
+
+ {t("customer_name")}{" : "}{order.user?.name??"null"}
+ {t("customer_phone_number")}{" : "}{order.user?.phone??"null"}
+ {t("order_created_at")}{" : "}{ChangeformatDate(order.created_at)}
+ {t("address")}{" : "}{order?.form?.address}
+ {t("country")}{" : "}{order?.form?.country}
+ {t("note")}{" : "}{order?.form?.note}
+
+
+
+ {t("sub_total")}{" : "}{order?.total}
+ {t("delivery_fee")}{" : "}{0}
+ {t("overall_total")}{" : "}{order?.total}
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default Form
+
+
diff --git a/src/Pages/order/ui/CustomDateRange.tsx b/src/Pages/order/ui/CustomDateRange.tsx
new file mode 100644
index 0000000..eedf3b6
--- /dev/null
+++ b/src/Pages/order/ui/CustomDateRange.tsx
@@ -0,0 +1,33 @@
+import React from 'react'
+import { DatePicker } from 'antd'
+import { useLocation, useNavigate } from 'react-router-dom';
+import { useOrderFillterState } from '../../../zustand/OrderFillter';
+import { useTranslation } from 'react-i18next';
+const { RangePicker } = DatePicker;
+
+const CustomDateRange = () => {
+ const dateFormat = 'YYYY-MM-DD';
+ const { toDate,fromDate,setFromDate,setToDate } = useOrderFillterState(); // Moved hook call inside the functional component
+ const [t] = useTranslation();
+
+ const onCalendarChange = (value: any) => {
+ const FromData = value[0]?.format(dateFormat)
+ const ToData = value[1]?.format(dateFormat)
+
+ setFromDate(FromData)
+ setToDate(ToData)
+
+ };
+ return (
+
+ )
+}
+
+export default CustomDateRange
\ No newline at end of file
diff --git a/src/Pages/order/ui/CustomNumber.tsx b/src/Pages/order/ui/CustomNumber.tsx
new file mode 100644
index 0000000..412eb49
--- /dev/null
+++ b/src/Pages/order/ui/CustomNumber.tsx
@@ -0,0 +1,36 @@
+import { InputNumber } from 'antd';
+import { useState, useEffect } from 'react';
+import { useOrderFillterState } from '../../../zustand/OrderFillter';
+import { useTranslation } from 'react-i18next';
+
+const CustomNumber = () => {
+ const [valueFrom, setValueFrom] = useState(null);
+ const [valueTo, setValueTo] = useState(null);
+ const { totalFrom, setTotalFrom, totalTo, setTotalTo } = useOrderFillterState();
+
+ // Set initial state based on totalFrom and totalTo
+
+ const onChangeFrom = (value: number | null) => {
+ setValueFrom(value);
+ setTotalFrom(value);
+ };
+
+ const onChangeTo = (value: number | null) => {
+ setValueTo(value);
+ setTotalTo(value);
+ };
+ const [t] = useTranslation()
+
+ return (
+
+
+
+
+ );
+};
+
+export default CustomNumber;
diff --git a/src/Pages/order/ui/CustomSearchField.tsx b/src/Pages/order/ui/CustomSearchField.tsx
new file mode 100644
index 0000000..e46bd8c
--- /dev/null
+++ b/src/Pages/order/ui/CustomSearchField.tsx
@@ -0,0 +1,50 @@
+import { Input } from 'antd';
+import { SearchProps } from 'antd/es/input'
+import { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSearchParams } from 'react-router-dom';
+import { useOrderFillterState } from '../../../zustand/OrderFillter';
+const { Search } = Input;
+
+const CustomSearchField = ({ searchBy }: any) => {
+ const { t } = useTranslation();
+ const {setUsername,username} = useOrderFillterState((state) => state)
+
+
+ const [searchParams,] = useSearchParams();
+ const [searchValue, setSearchValue] = useState(searchParams.get(searchBy ) || "");
+
+ const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
+ if (value || value !== "") {
+ setUsername(value)
+ } else {
+ setUsername(null)
+ }
+ }
+
+ const onChange = (e: any) => {
+ const value = e.target.value
+ setSearchValue(value);
+ setUsername(value)
+ }
+
+
+ return (
+
+
+
+
+ )
+}
+
+export default CustomSearchField
\ No newline at end of file
diff --git a/src/Pages/order/ui/CustomSelectField.tsx b/src/Pages/order/ui/CustomSelectField.tsx
new file mode 100644
index 0000000..313933e
--- /dev/null
+++ b/src/Pages/order/ui/CustomSelectField.tsx
@@ -0,0 +1,43 @@
+import { Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { useOrderFillterState } from '../../../zustand/OrderFillter';
+
+const SelectField = ({ selectBy, lebel, option }: any) => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [t] = useTranslation();
+ const {setstate,state} = useOrderFillterState((state) => state)
+
+
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location.search);
+ setSearchQuery(searchParams.get( selectBy) || '');
+ }, []);
+
+
+ const handleSelectChange = (value: any) => {
+ if (value) {
+ console.log(`${location.pathname}?${selectBy}=${value}`);
+ // navigate(`${location.pathname}?${selectBy}=${value}`);
+ setstate(value)
+ }
+ }
+
+ return (
+
+
+
+ );
+};
+
+export default React.memo(SelectField);
diff --git a/src/Pages/order/ui/CustomSelectWSearchField.tsx b/src/Pages/order/ui/CustomSelectWSearchField.tsx
new file mode 100644
index 0000000..16a46ee
--- /dev/null
+++ b/src/Pages/order/ui/CustomSelectWSearchField.tsx
@@ -0,0 +1,55 @@
+import { Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { useOrderFillterState } from '../../../zustand/OrderFillter';
+
+const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [t] = useTranslation();
+ const { coupon, setCoupon } = useOrderFillterState(); // Moved hook call inside the functional component
+
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location.search);
+ setSearchQuery(searchParams.get(submiteBy) || '');
+ }, []);
+
+ const handleSearchChange = (value: any) => {
+ if (value || value !== "") {
+ navigate(`${location.pathname}?${selectBy}=${value}`);
+ } else {
+ const params = new URLSearchParams(location.search);
+ params.delete(selectBy ?? "search");
+ navigate(`${location.pathname}?${params.toString()}`);
+ }
+ };
+
+ const handleSelectChange = (value: any) => {
+ if (value) {
+ setCoupon(value)
+ // navigate(`${location.pathname}?${submiteBy}=${value}`);
+ }
+}
+
+ return (
+
+
+
+ );
+};
+
+export default React.memo(SelectWSearchField);
diff --git a/src/Pages/order/useTableColumns.tsx b/src/Pages/order/useTableColumns.tsx
new file mode 100644
index 0000000..1e29200
--- /dev/null
+++ b/src/Pages/order/useTableColumns.tsx
@@ -0,0 +1,112 @@
+import { useTranslation } from "react-i18next";
+import { useDeleteOrder } from "../../api/order";
+import Actions from "../../Components/Ui/tables/Actions";
+import { useNavigate } from "react-router-dom";
+import { FaCheck, FaTimes, FaClock, FaBan } from 'react-icons/fa';
+
+const useTableColumns = () => {
+ const [t] = useTranslation();
+ const navigate = useNavigate()
+ const deleteMutation = useDeleteOrder()
+ const language = localStorage.getItem("language") ?? "en"
+
+
+ let column = [
+ {
+ name: t("id"),
+ sortable: true, // Enable sorting for id column
+ center: true,
+ selector: (row: any) => row.id, // Specify selector function for sorting
+ },
+ {
+ name: t("name"),
+ sortable: false,
+ center:true,
+ selector:(row:any) => row?.user?.name ?? "null",
+
+ },
+ {
+ name: t("email"),
+ sortable: false,
+ center:true,
+ selector:(row:any) => row?.user?.email?? "null",
+
+ },
+
+ {
+ name: t("status"),
+ center: true,
+ cell: (row: any) => {
+ let status = row?.state;
+ let icon;
+
+ // Assigning an icon component based on the status
+ switch (status) {
+ case 'pending_approve':
+ icon = ;
+ break;
+ case 'approved':
+ icon = ;
+ break;
+ case 'rejected':
+ icon = ;
+ break;
+ case 'pending_cancellation':
+ icon = ;
+ break;
+ default:
+ icon = null;
+ }
+
+ // Rendering the status with its corresponding icon
+ return (
+
+ {icon} {status}
+
+ );
+ }
+ },
+ {
+ name: t("total"),
+ center: true,
+ cell:(row:any)=>{
+
+ return (row?.total)
+ }
+ },
+ {
+ name: "#",
+ center: true,
+ cell: (row:any) =>
+
+
+ deleteMutation.mutate({order_id:row.id })}
+ showEdit={false}
+ showView={true}
+ // onView={()=>navigate(`/order/view/${row?.id}` )}
+ onView={()=>navigate(`/order/${row?.id}` )}
+
+ />
+
+
+
+
+ },
+
+
+
+
+ ]
+
+
+
+
+
+ return column
+
+};
+
+export default useTableColumns;
diff --git a/src/ProviderContainer.tsx b/src/ProviderContainer.tsx
new file mode 100644
index 0000000..c30c964
--- /dev/null
+++ b/src/ProviderContainer.tsx
@@ -0,0 +1,24 @@
+import React, { ReactNode } from 'react'
+import QueryProvider from './lib/ReactQueryProvider'
+import { BrowserRouter } from 'react-router-dom'
+import ToastProvider from './lib/ToastProvider'
+import { createBrowserHistory } from 'history'
+
+type ProviderContainerProps = {
+ children:ReactNode
+
+}
+export let history = createBrowserHistory()
+function ProviderContainer({children}:ProviderContainerProps) {
+ return (
+
+
+
+ {children}
+
+
+
+ )
+}
+
+export default ProviderContainer
\ No newline at end of file
diff --git a/src/Routes.tsx b/src/Routes.tsx
new file mode 100644
index 0000000..cbfbbb7
--- /dev/null
+++ b/src/Routes.tsx
@@ -0,0 +1,204 @@
+import { ReactNode } from "react";
+
+// Icons Import
+import { FaCartArrowDown, FaHome, FaProductHunt, FaRegImages, FaUser } from "react-icons/fa"
+import { BiSolidCategory } from "react-icons/bi";
+import { BiSolidCoupon } from "react-icons/bi";
+
+
+// Pages Import
+import HomePage from "./Pages/Home/HomePage";
+
+import CategoriesPage from "./Pages/Categories/Page";
+import AddCategoriesPage from "./Pages/Categories/View/AddPage";
+import EditCategories from "./Pages/Categories/View/EditPage";
+
+
+import ProductsPage from "./Pages/Products/ProductsPage";
+import AddProductPage from "./Pages/Products/View/AddPage";
+import EditProduct from "./Pages/Products/View/EditPage";
+import OrderPage from "./Pages/order/OrderPage";
+import EditOrder from "./Pages/order/EditPage";
+
+
+import CouponPage from "./Pages/Coupon/Page";
+import AddCouponPage from "./Pages/Coupon/View/AddPage";
+import EditCoupon from "./Pages/Coupon/View/EditPage";
+
+import SliderPage from "./Pages/Slider/Page";
+import AddSliderPage from "./Pages/Slider/View/AddPage";
+import EditSlider from "./Pages/Slider/View/EditPage";
+
+
+
+import SupportMessagesPage from "./Pages/SupportMessages/Page";
+import AddSupportMessagesPage from "./Pages/SupportMessages/View/AddPage";
+// import EditSupportMessages from "./Pages/SupportMessages/View/EditPage";
+
+import UsersPage from "./Pages/Users/Page";
+import EditUsersPage from "./Pages/Users/View/EditPage";
+
+import AddUsersNotifactionPage from "./Pages/Users/SendNotifcation/View/AddPage";
+
+// import NotificationPage from "./Pages/Notifcation/Page";
+import AddNotificationPage from "./Pages/Notifcation/View/AddPage";
+// import { MdEmail } from "react-icons/md";
+import { MessageOutlined } from "@ant-design/icons";
+
+
+
+interface RoutesLinksType {
+ name?: string,
+ href?: string,
+ element?: ReactNode,
+ icon?: any,
+ Viewelement?: ReactNode,
+ Viewhref?: string
+ children?: any
+ hidden?: boolean
+}
+export const RoutesLinks: RoutesLinksType[] = [
+
+ {
+ name: "Home",
+ element: ,
+ icon: ,
+ href: "/",
+ },
+
+ {
+ name: "Categories",
+ element: ,
+ icon:
+ ,
+ href: "/categories",
+ },
+ {
+ href: "/categories/:id",
+ element: ,
+ hidden:true
+ },
+ {
+ href: "/categories/add",
+ element: ,
+ hidden:true
+ },
+
+ {
+ name: "Products",
+ element: ,
+ icon: ,
+ href: "/products",
+ },
+ {
+ name: "add_products",
+ element: ,
+ href: "/products/add",
+ hidden:true
+ },
+ {
+ name: "edit_products",
+ element: ,
+ href: "/products/:id",
+ hidden:true
+
+ },
+
+ {
+ name: "Order",
+ element: ,
+ icon: ,
+ href: "/order",
+ },
+
+
+ {
+ name: "edit_order",
+ element: ,
+ href: "/order/:id",
+ hidden:true
+
+ },
+
+ {
+ name: "Coupon",
+ element: ,
+ icon: ,
+ href: "/coupon",
+ },
+ {
+ href: "/coupon/:id",
+ element: ,
+ hidden:true
+ },
+ {
+ href: "/coupon/add",
+ element: ,
+ hidden:true
+ },
+
+ {
+ name: "Slider",
+ element: ,
+ icon: ,
+ href: "/slider",
+ },
+ {
+ href: "/slider/:id",
+ element: ,
+ hidden:true
+ },
+ {
+ href: "/slider/add",
+ element: ,
+ hidden:true
+ },
+
+ {
+ name: "users",
+ element: ,
+ icon: ,
+ href: "/users",
+ },
+ {
+ href: "/admin/:id",
+ element: ,
+ hidden:true
+ },
+
+ {
+ href: "/users/:id",
+ element: ,
+ hidden:true
+ }
+
+ ,
+ // {
+ // name: "notification",
+ // element: ,
+ // icon: ,
+ // href: "/notification",
+ // },
+ {
+ href: "/notification/add",
+ element: ,
+ hidden:true
+ },
+ {
+ name: "SupportMessages",
+ element: ,
+ icon: ,
+ href: "/support_messages",
+ },
+ // {
+ // href: "/support_messages/:id",
+ // element: ,
+ // hidden:true
+ // },
+ {
+ href: "/support_messages/add",
+ element: ,
+ hidden:true
+ },
+
+]
\ No newline at end of file
diff --git a/src/Styles/AppStyle/App.scss b/src/Styles/AppStyle/App.scss
new file mode 100644
index 0000000..d9efda0
--- /dev/null
+++ b/src/Styles/AppStyle/App.scss
@@ -0,0 +1,93 @@
+@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
+
+
+html,body {
+ background: var(--bg2);
+ font-family: 'Poppins', sans-serif;
+
+}
+
+*{
+ padding: 0;
+ margin: 0;
+}
+
+a {
+ color: inherit;
+ font-weight: bold;
+ text-decoration: none !important;
+ cursor: pointer;
+
+}
+
+button,
+svg {
+ cursor: pointer;
+}
+
+
+
+
+
+
+.not_foound_page{
+ background: black;
+ height: 100vh;
+ display: flex;justify-content: center;align-items:center;
+
+
+ p{
+ color: white;
+ display: inline;
+
+ }
+ h6{
+ display: inline;
+ margin-inline:20px;
+ height: 140px !important;
+
+ width: 20px;
+ }
+ .container-not-found{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ div{
+ display: flex;justify-content: center;align-items: center;
+
+ }
+ button{
+ border: none;
+ outline: none;
+ padding: 8px;
+ border-radius: 10px;
+
+ margin-inline:30px;
+ }
+ }
+}
+
+.lock_icon{
+ color: var(--primary);
+ margin-left: 7px;
+}
+
+
+.primary {
+ color: var(--primary) !important;
+}
+
+
+.bg-primary {
+ background-color: var(--primary) !important;
+}
+.secondary {
+ color: var(--secondary) !important;
+}
+
+
+.bg-secondary {
+ background-color: var(--secondary) !important;
+}
+
+
diff --git a/src/Styles/AppStyle/Import.scss b/src/Styles/AppStyle/Import.scss
new file mode 100644
index 0000000..36643f3
--- /dev/null
+++ b/src/Styles/AppStyle/Import.scss
@@ -0,0 +1,22 @@
+
+@import './Varibils.scss';
+@import './Mixing.scss';
+@import './App.scss';
+@import '../Layout/Header.scss';
+@import '../Layout/Layout.scss';
+@import '../Layout/SideBar.scss';
+@import '../Auth/Auth.scss';
+@import '../Layout/Table.scss';
+
+@import '../component/printButton.scss';
+@import '../component/DarkStyle.scss';
+@import '../component/ErrorPage.scss';
+@import '../component/Tab_Info.scss';
+@import '../component/DriverInfoSocket.scss';
+@import '../component/radio.scss';
+
+@import '../Layout/FillterSection.scss';
+
+
+
+
diff --git a/src/Styles/AppStyle/Mixing.scss b/src/Styles/AppStyle/Mixing.scss
new file mode 100644
index 0000000..5641f40
--- /dev/null
+++ b/src/Styles/AppStyle/Mixing.scss
@@ -0,0 +1,21 @@
+
+@mixin Shadow {
+ box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
+
+}
+
+
+@mixin Flex {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+}
+
+
+
+
+@mixin GlassModeCover{
+ background-color: rgba(16 18 27 / 40%);
+ backdrop-filter: blur(24px);
+}
\ No newline at end of file
diff --git a/src/Styles/AppStyle/Varibils.scss b/src/Styles/AppStyle/Varibils.scss
new file mode 100644
index 0000000..a9aaec8
--- /dev/null
+++ b/src/Styles/AppStyle/Varibils.scss
@@ -0,0 +1,36 @@
+:root {
+ --primary:red ;
+ --secondary : #565656;
+ --text: #565656;
+ --bg: #ffffff;
+ --bg2: #f8f8f8;
+ --shadow: rgba(0, 0, 0, 0.15);
+ --gray : rgb(207, 210, 214);
+ --linear : linear-gradient(118deg, #2D9CDB, #2D9CDB)
+}
+
+:root:has(.dark) {
+ --primary: #fcb04f ;
+ --secondary: #304a7e;
+ --text: #ffffff;
+ --bg: #1f1f1f;
+ --bg2: #2c2c2c;
+ --shadow: rgba(255, 255, 255, 0.15);
+ --gray: #a0aec0;
+ --linear : linear-gradient(118deg, #fcb04f, #f78c3f)
+
+
+}
+
+:root:has(.glass) {
+ --primary:white;
+ --secondary : #ffd12e;
+ --text: white;
+ --bg: ;
+ --bg2: ;
+ --shadow: rgba(0, 0, 0, 0.15);
+ --gray : rgb(207, 210, 214);
+ --linear :
+
+ }
+
diff --git a/src/Styles/Auth/Auth.scss b/src/Styles/Auth/Auth.scss
new file mode 100644
index 0000000..f2c8028
--- /dev/null
+++ b/src/Styles/Auth/Auth.scss
@@ -0,0 +1,156 @@
+.Auth{
+ // background: url('../../../public/Layout/LoginBg.jpg');
+
+ .In_Auth{
+ background-color: var(--bg);
+ display: flex;
+ justify-content: center; align-items: center;
+ gap: 30px;
+ height: 80vh;
+
+ }
+ .LoginForm{
+ display: flex; flex-direction: column;
+
+ .Login_Nav{
+ display: flex; justify-content: space-between ; align-items: center;
+ margin-bottom: 30px;
+ }
+ }
+ .Left_Col,.Right_Col{
+ padding-inline: 30px;
+ display: flex;
+ justify-content: center; align-items: center;
+ height: 100%;
+ width: 30vw;
+ }
+ .Left_Col{
+ background-color: #edf4f4;
+ }
+
+ .img-fluid{
+ width: 30vw;
+ height: 100%;
+ }
+ .Login_H4{
+ width: 90%;
+ font-size: 16px;
+ }
+ .Reserved{
+ transform:translatex(15px) translatey(44px);
+ }
+ .LoginForm{
+ .Logo{
+ display: none;
+ }
+ }
+ @media screen and (max-width: 800px) {
+ .Left_Col{
+ display: none;
+ }
+
+ .LoginForm{
+ .Logo{
+ // position: absolute;
+ display: block;
+ width: 150px;
+ transform: translate(80px ,-40px);
+
+ }
+ }
+ .Login_H4{
+ text-align: center ;
+ }
+ .Right_Col{
+ padding-inline: 30px;
+ display: flex;
+ justify-content: center; align-items: center;
+ width: 400px;
+ height: fit-content;
+ }
+ .In_Auth{
+
+ height: 550px !important;
+
+ }
+
+
+ }
+
+ @media screen and (max-width: 400px) {
+ .LoginForm{
+ .Logo{
+ // position: absolute;
+ display: block;
+ width: 120px;
+ transform: translate(50px ,-20px);
+
+ }
+ }
+ .Right_Col{
+ padding-inline: 30px;
+ display: flex;
+ justify-content: center; align-items: center;
+ width: 300px !important;
+ }
+ }
+
+
+}
+
+.Logo{
+ width: 100%;
+}
+
+.dark{
+ .Left_Col{
+ background-color: #343434;
+ }
+ .Login_H4, .Reserved{
+ color: #fff;
+ }
+ .Login_Nav{
+ h5{
+ color: #fff;
+ }
+ }
+}
+.glass{
+ .Left_Col{
+ background-color: inherit;
+ }
+ .Login_H4, .Reserved{
+ color: #000;
+ }
+ .Login_Nav{
+ h5{
+ color: #000;
+ }
+ }
+}
+
+
+.ar{
+ .LoginForm{
+ .Logo{
+ transform: translate(-90px ,-40px);
+ }
+ }
+}
+
+
+@media screen and (max-width:400px) {
+
+ .ar{
+ .LoginForm{
+ .Logo{
+ transform: translate(-70px ,-30px) !important;
+
+ }
+ }
+ }
+}
+
+.logo_link{
+ display: none !important;
+}
\ No newline at end of file
diff --git a/src/Styles/Home/Auth.scss b/src/Styles/Home/Auth.scss
new file mode 100644
index 0000000..bc8b1e6
--- /dev/null
+++ b/src/Styles/Home/Auth.scss
@@ -0,0 +1,16 @@
+// Auth
+// In_Auth
+// Left_Auth
+// Right_Auth
+
+.Auth{
+ height: 100vh;
+ max-width: 100vw;
+ display: flex; justify-content: center; align-items: center;
+ flex-direction: column;
+
+ .In_Auth{
+ width: 70vw;
+
+ }
+}
\ No newline at end of file
diff --git a/src/Styles/Layout/FillterSection.scss b/src/Styles/Layout/FillterSection.scss
new file mode 100644
index 0000000..aa7e1ee
--- /dev/null
+++ b/src/Styles/Layout/FillterSection.scss
@@ -0,0 +1,18 @@
+.FillterSection{
+
+ display: flex;
+ gap: 20px;
+ flex-wrap: wrap;
+ margin-bottom: 20px;
+ align-items: center;
+ flex-direction: row-reverse;
+ .SelectField{
+ width: 150px;
+ }
+}
+
+@media screen and (max-width: 600px) {
+ .SelectField{
+ display: none;
+ }
+ }
\ No newline at end of file
diff --git a/src/Styles/Layout/Header.scss b/src/Styles/Layout/Header.scss
new file mode 100644
index 0000000..84cd0b3
--- /dev/null
+++ b/src/Styles/Layout/Header.scss
@@ -0,0 +1,120 @@
+.Header{
+ width: 100%;
+ height: 65px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 30px;
+ background: var(--bg);
+ margin-top: 20px;
+ @include Shadow;
+ .Header_Left{
+ @include Flex;
+ gap: 5px;
+ border-radius: 5px;
+ padding-inline: 5px;
+ color: var(--primary);
+ font-weight: bolder;
+
+ }
+ svg{
+ margin-inline:10px;
+ }
+}
+.Header_Menu{
+
+ z-index: 9999999999999;
+
+}
+.szh-menu-button{
+ color: var(--text);
+ background: var(--bg);
+ border: none; outline: none;
+ padding: .6vw;
+ border-radius: 5px;
+}
+
+.szh-menu__item{
+ color: var(--text);
+ background: var(--bg);
+ &:hover{
+ background: var(--bg2);
+ }
+}
+.Header_Right{
+ padding-inline: 5px;
+ direction: ltr;
+ @include Flex ;
+ .Setting{
+
+ margin-inline: 1vw;
+ svg{
+ fill: var(--primary);
+ background: var(--bg2);
+ padding: 0.6vw;
+ border-radius: 5px;
+ }
+ }
+ .User_Pro{
+ margin-inline: 1vw;
+ @include Flex ;
+ gap: .5vw;
+ }
+}
+
+.szh-menu {
+ min-width: auto !important;
+}
+.UNK_User{
+ border-radius: 50%;
+ height: 40px;
+ aspect-ratio: auto 40 / 40;
+ width: 40px;
+
+}
+.User_info{
+ text-align: right;
+ display: flex;justify-content: center;align-items: flex-end; flex-direction: column;
+ text-align: start;
+ width: 100%;
+ h6,p{
+ width: 100%;
+ margin-bottom: 0;
+ font-size: .7rem;
+ }
+ h6{
+ text-align: start !important;
+ }
+ p{
+ opacity: .8;
+ }
+}
+.Translate{
+ display: flex; justify-content: center; align-items: center; margin-inline: 10px;
+ p{
+ opacity: .7;
+ margin-bottom: 0;
+ }
+}
+
+.Header_Left{
+ >*{
+ display: none;
+ }
+}
+@media screen and (max-width: 700px) {
+
+.Header_Left{
+ >*{
+ display: block;
+ }
+}
+}
+
+.ant-dropdown-link{
+ all: unset;
+}
+.MenuPropsItem{
+ display: flex;
+ gap: 10px;
+}
\ No newline at end of file
diff --git a/src/Styles/Layout/Layout.scss b/src/Styles/Layout/Layout.scss
new file mode 100644
index 0000000..9a63f31
--- /dev/null
+++ b/src/Styles/Layout/Layout.scss
@@ -0,0 +1,548 @@
+
+///// Layout
+.DashboardLayout {
+ color: var(--text); background: var(--bg2);
+ width: 100%;
+ background-size: cover;
+ background-position: center;
+ min-height: 100vh;
+ display: flex; justify-content: center; align-items: center;
+ .DashboardLayout_Cover{
+ min-height: 100vh;
+ width: 100vw;
+ border-radius: 20px;
+
+ }
+
+ .DashboardLayout_Body{
+ position: absolute;
+ left: 290px;
+ width: calc(98% - 290px);
+
+ }
+ .DashboardLayout_Body_Open{
+ left: 140px;
+ width: calc(98% - 140px);
+ transition: 1s ease-in-out;
+
+
+ }
+
+
+}
+///// Arabic Mood
+.ar{
+ .DashboardLayout_Body{
+ right: 290px;
+
+ }
+ .DashboardLayout_Body_Open{
+ right: 140px;
+ width: calc(96% - 140px);
+ transition: 1s ease-in-out;
+
+
+ }
+}
+
+
+
+.card {
+background: var(--bg);
+ @include Shadow;
+ outline: none !important;
+ border: none !important;
+}
+.Model_Button{
+ background: var(--primary);
+ outline: none;
+ border: none;
+}
+.SweetAlert{
+ .btn-primary{
+ background: var(--primary);
+ outline: none;
+ border: none;
+ box-shadow: none !important;
+ }
+}
+.TableActions{
+ svg{
+ fill: var(--primary);
+ }
+}
+.form-control:focus{
+ box-shadow: 0 0 0 0.2rem rgba(0, 0, 0, 0.2) !important;
+ border: var(--primary);
+}
+.Page_Header{
+ display: flex; justify-content: space-between;
+ margin-bottom: 20px;
+ // align-items: center ;
+
+ h1{
+ font-size: 30px;
+ font-weight: 600;
+
+ }
+}
+.PrimaryColor{
+ margin-inline: 10px;
+}
+
+.react-toggle-track{
+ background: red ;
+}
+
+///// spinner
+.jwoUqQ{
+ color: var(--primary) !important;
+}
+.Auth{
+ background-color: var(--bg2);
+ margin: auto;
+ width:100%;
+ display: flex; justify-content: center; align-items: center;
+ height: 100vh;
+}
+.Translate,.Theme{
+ img{
+ margin-inline: 6px;
+ }
+}
+.szh-menu{
+ background-color: var(--bg);
+}
+
+.modal-content{
+ background-color: var(--bg) !important;
+}
+.btn-primary{
+ background: var(--primary);
+ border-color: var(--primary) !important;
+ &:hover{
+ background: var(--primary);
+
+ }
+}
+.ModalHeader{
+ color: var(--text);
+}
+ .css-b62m3t-container .react-select__control{
+ background-color:var(--bg);
+ }
+
+ .react-select__control--is-disabled{
+ background-color:var(--bg) !important;
+ }
+
+
+
+
+
+
+ .ViewPage{
+ .card-header{
+ padding: 25px 25px 0 25px;
+ background: var(--bg) !important;
+ color: var(--text);
+ border-bottom: none !important;
+ display: flex; justify-content: space-between;
+ .card-title{
+ font-size: 2vw;
+ }
+ button{
+ background: var(--primary) ;
+ color: var(--bg);
+ font-weight: bold;
+ outline: none;
+ border: none;
+ min-width: 100px;
+ max-height: 40px;
+ }
+ }
+ .react-tabs__tab-list{
+ z-index: 1;
+ display: flex;
+ }
+ .react-tabs__tab{
+
+ flex: 1;
+ color: var(--text);
+
+ }
+ .react-tabs__tab--selected {
+ border-top: none; border-left: none; border-right: none;
+ border-bottom: 4px solid var(--primary);
+ color: var(--primary);
+ background: var(--bg2);
+
+ }
+
+ }
+
+
+ .react-tabs__tab:focus{
+ // background-color: red !important;
+
+ all: unset;
+ border-top: none; border-left: none; border-right: none;
+ border-bottom: 4px solid var(--primary);
+ color: var(--primary);
+ z-index: 99999;
+ background: var(--bg2);
+ min-width: 100px;
+
+ flex: 1;
+ color: var(--text);
+ max-height: 40px;
+
+ }
+
+
+ .ant-input-group-wrapper .ant-input-wrapper .ant-input-affix-wrapper{
+ background-color:var(--bg) !important;
+ color: var(--text) !important;
+}
+
+/* Input */
+.ant-input-wrapper .ant-input-affix-wrapper input[type=text]{
+ background-color:var(--bg) !important;
+ color: var(--text) !important;
+ &::placeholder{
+ color: var(--text) !important;
+ }
+}
+
+/* Warning */
+#root div .warning{
+ color:var(--secondary);
+}
+
+/* Content Division */
+#root .title-section p{
+ color:var(--secondary);
+}
+
+
+
+
+
+/* Iwjdpv */
+#root .card .sc-iwjdpV{
+ background-color:var(--bg2) !important;
+ color:var(--text) !important;
+}
+
+.Card{
+ padding: 30px;
+ @include Shadow;
+ background: var(--bg);
+ border-radius: 20px;
+}
+.SingleInfo{
+
+ svg{
+ color: green !important;
+ }
+}
+.ResposiveTabs{
+ padding-block: 20px;
+ min-height: 500px;
+}
+/* Ant tabs tab active */
+.ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab-active{
+ display:inline-block;
+ transform:translatex(0px) translatey(0px) !important;
+ }
+
+ /* Ant tabs content holder */
+ .VarianInfo .ant-tabs-left .ant-tabs-content-holder{
+ transform:translatex(0px) translatey(0px);
+
+ }
+ .ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab{
+align-self: center !important;
+padding: 10px 40px;
+
+ }
+
+ .VarianInfo{
+ padding: 10px 20px;
+ }
+
+
+ .react-tabs__tab-panel--selected .mt-4 .VarianInfo .ant-tabs-editable .ant-tabs-content-holder .ant-tabs-content .ant-tabs-tabpane .row .col .ant-upload-wrapper .ant-upload-select .ant-upload .ant-btn{
+ width:100% !important;
+ }
+ .tabstext{
+ margin-bottom: 10px;
+ }
+
+ .SellectTab{
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ }
+ .warning {
+ color: var(--primary) !important;
+ margin-bottom: 10px;
+ height: 4vw;
+ width: 4vw;
+ }
+ .css-1u0lry5-MuiChartsLegend-root {
+ direction: ltr;
+ }
+
+
+ ////// Tabs
+ .ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab{
+ display:flex;
+ color:var(--subtext) !important;
+ }
+
+ /* Ant tabs tab */
+ .ant-tabs-nav-list .ant-tabs-tab .ant-tabs-tab-btn{
+ color:var(--text) !important;
+ }
+
+ /* Ant tabs tab remove */
+ .ant-tabs-nav-list .ant-tabs-tab .ant-tabs-tab-remove{
+ color:var(--subtext) !important;
+ }
+ /* Ant tabs nav add */
+.ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-nav-add{
+ padding-top:5px;
+ padding-bottom:5px;
+ color:var(--primary) !important;
+
+ }
+
+ .ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab-active{
+
+.ant-tabs-tab-btn{
+ color:var(--text) !important;
+ font-weight: bold;
+ background: var(--bg);
+
+}
+.ant-tabs-tab-remove{
+ color:var(--text) !important;
+ }
+ }
+
+
+ .BarChart{
+ width: 100%;
+ height: 30vw
+ ;
+ }
+
+ @media (max-width: 800px) {
+ .BarChart{
+ width: 100%;
+ height: 50vw
+ ;
+ }
+ /* Text */
+.MuiChartsLegend-root .MuiChartsLegend-series text{
+ font-size:2vw !important;
+ }
+ .MuiChartsLegend-root .MuiChartsLegend-series .MuiChartsLegend-mark{
+ width:2vw;
+ height:2vw;
+ }
+ #root .DashboardLayout .DashboardLayout_Cover #DashboardLayout_Body .Layout_Children .row-cols-1 .BarChart .css-l0h214-MuiResponsiveChart-container .css-bd9tpx-MuiChartsSurface-root .MuiChartsLegend-root .MuiChartsLegend-series text{
+ transform: translatey(-2px) !important;
+ }
+
+
+ }
+ @media (max-width: 600px) {
+ .BarChart{
+ display:none;
+ }
+
+ .Page_Header{
+ flex-wrap: wrap;
+ }
+
+
+ }
+
+ /* Ant input affix wrapper */
+.ant-input-group-wrapper .ant-input-wrapper .ant-input-affix-wrapper{
+ height:40px;
+ }
+
+ /* Ant input search button */
+ .ant-input-wrapper .ant-input-group-addon .ant-input-search-button{
+ height:40px;
+ }
+
+
+
+
+
+
+
+ @media screen and (max-width: 600px) {
+ .SearchField,.SelectWSearchField{
+ display: none;
+ }
+}
+
+
+.AddNewTabText{
+ text-wrap: nowrap !important;
+ margin-left: 60px;
+ transform: translateY(-30px);
+}
+ @media screen and (max-width: 1000px) {
+
+.AddNewTabText{
+ transform: translateY(-50px);
+
+
+ }
+}
+.ar{
+ .AddNewTabText{
+ text-wrap: nowrap !important;
+ margin-right: 60px;
+ transform: translateY(-30px);
+ }
+ @media screen and (max-width: 1000px) {
+
+ .AddNewTabText{
+ transform: translateY(-50px);
+
+
+ }
+ }
+}
+/* Dynamic form complex */
+#dynamic_form_complex{
+ margin-bottom:5px;
+ }
+
+ /* Ant space gap col small */
+ #dynamic_form_complex .ant-space-gap-col-small{
+ margin-bottom:-9px !important;
+ transform:translatex(0px) translatey(0px);
+ }
+
+ /* Ant space item */
+ #dynamic_form_complex .ant-space-gap-col-small .ant-space-item{
+ margin-bottom:6px;
+ }
+
+ /* Span Tag */
+ #dynamic_form_complex .ant-space-item span{
+ transform: translatex(-13px) translatey(-12px);
+ }
+
+ #dynamic_form_complex .ant-space-gap-col-small {
+ width: 100%;
+ }
+ .Information{
+ margin-block: 20px;
+ }
+ .ant-form{
+ overflow-x: hidden;
+ }
+// .ant-tabs{
+// min-height: 200px;
+// }
+.gomecards{
+ min-height: 220px;
+}
+
+.ValidationFiledCusom{
+ display: flex;
+ flex-direction: column;
+ >*{
+ width: 100%;
+
+ }
+}
+
+
+// .withfillter{
+// display: flex;
+// flex-direction: column;
+// align-items: flex-end;
+// gap: 10px;
+// >div{
+// display: flex;
+// gap: 30px;
+// }
+// }
+.SelectWSearchField{
+ width: 200px;
+}
+.ant-drawer .ant-drawer-body{
+ padding: 0;
+}
+.RightSide{
+ flex-wrap: wrap;
+ justify-content: flex-end;
+}
+.orderStatus{
+ display: flex;
+ gap: 5px;
+ padding-inline: 10px;
+ text-wrap: nowrap;
+ white-space: nowrap;
+ width: 300px;
+ align-items: center;
+ justify-content: center;
+ svg{
+ color: var(--primary);
+ }
+
+}
+.orderStatus_select{
+ display: flex;
+ align-items: center;
+ gap: 5px;
+
+ svg{
+ color: var(--primary);
+ }
+
+}
+.CustomDateRange{
+ width: 240px;
+}
+.OrderDetails{
+ padding: 20px 4vw ;
+}
+.CustomNumber{
+ display: flex;
+ gap: 20px;
+}
+.SliderDataRange{
+ display: flex;
+ // flex-direction: column;
+ // justify-content: center;
+ // align-items: center;
+ // background: var(--bg);
+ // gap: 20px;
+ // width: 300px;
+ padding-inline: 20px;
+ h1{
+ font-size: 18px;
+ text-wrap: nowrap;
+ white-space: nowrap;
+ }
+
+}
+
+
+.ar{
+ .ant-tabs >.ant-tabs-nav{
+ direction: ltr !important;
+
+ }
+}
\ No newline at end of file
diff --git a/src/Styles/Layout/SideBar.scss b/src/Styles/Layout/SideBar.scss
new file mode 100644
index 0000000..15fd911
--- /dev/null
+++ b/src/Styles/Layout/SideBar.scss
@@ -0,0 +1,354 @@
+
+
+.SideBar {
+ display: flex;
+ flex-direction: column;
+ width: 260px;
+ height: 100%;
+ position: fixed;
+ background: var(--bg);
+ transition: .3s ease-in-out;
+@include Shadow;
+overflow-y: auto;
+overflow-x: hidden;
+&::-webkit-scrollbar {
+ width: 7px;
+ cursor: pointer;
+
+}
+
+&::-webkit-scrollbar-thumb {
+ background-color: transparent;
+ border-radius: 10px;
+ cursor: pointer;
+}
+&:hover{
+ &::-webkit-scrollbar {
+ width: 7px;
+}
+
+&::-webkit-scrollbar-thumb {
+ background-color: var(--primary);
+ border-radius: 10px;
+}
+}
+
+}
+
+.SideBar_Top {
+ @include Flex;
+ margin-block: 20px;
+ color: var(--primary);
+ z-index: 9999999;
+ img{
+ margin-inline: 10px;
+ width: 60px;
+ }
+ .HamburgerMenu{
+ z-index: 999999999999999;
+ height: 30px;
+ width: 30px;
+ svg{
+ font-size: 20px;
+ }
+ }
+}
+
+.SideBar_Links {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-inline: 15px;
+
+}
+
+.SideBar_Link {
+
+ color: var(--text);
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ transition: .3s ease-in-out all;
+ margin-bottom: 5px;
+ width: 90%;
+ height: 45px;
+ border-radius: 10px;
+ margin-bottom: 10px;
+ font-weight: bold;
+ margin-left: 15px;
+ text-wrap: nowrap;
+
+
+ &:hover{
+ background: var(--primary);
+ color: var(--bg);
+ border-radius: 5px;
+ box-shadow: 2px 2px 7px 0 var(--shadow);
+ cursor: pointer;
+ transform: scale(1.01);
+ @include Shadow;
+
+ svg{
+
+ // background-color: var(--secondary);
+ color: var(--bg);
+
+ }
+ }
+ .DropDown_Svg svg{
+ color:var(--text);
+}
+
+
+
+ svg {
+ padding: 5px;
+ border-radius: 10px;
+ // background-color: var(--bg2);
+ color: var(--secondary);
+ margin-left: 10px;
+
+ font-size: 30px;
+
+ &:hover{
+ background-color: var(--primary);
+ color: var(--bg);
+ }
+ }
+
+
+}
+.ar{
+ .SideBar_Link{
+ margin-right: 15px !important;
+ margin-left: 0px !important;
+ svg {
+
+ margin-right: 10px;
+ margin-left: 0px !important;
+
+
+ }
+
+
+ }
+}
+
+.SideBar_Links_Header{
+
+ margin-block: 10px;
+ font-size: 12px;
+ background: var(--bg2);
+ height: 50px;
+ @include Flex;
+
+ width: 100%;
+}
+.Active_SideBar_Link{
+ background: var(--linear);
+ color: var(--bg);
+ border-radius: 5px;
+ box-shadow: 2px 2px 7px 0 var(--shadow);
+
+ svg {
+ background-color: transparent;
+ color: var(--bg);
+
+ }
+}
+.glass{
+ .Active_SideBar_Link{
+ background: var(--linear);
+ color: var(--bg);
+ border-radius: 5px;
+ box-shadow: 2px 2px 7px 0 transparent;
+ background-color: rgba(16 18 27 / 1%);
+ backdrop-filter: blur(24px);
+ }
+ .SideBar_Link {
+ &:hover {
+ background-color: rgba(16 18 27 / 40%);
+ backdrop-filter: blur(24px);
+ }
+ &:hover{
+ background: transparent;
+ color: var(--bg);
+ border-radius: 5px;
+ box-shadow: 1px 2px 7px 0 transparent;
+ cursor: pointer;
+ transform: scale(1.01);
+ svg{
+
+ background-color:transparent;
+ color: var(--text);
+
+ }
+ }
+ }
+}
+
+
+.DropDown_Text{
+ width: 100px;
+// text-overflow: ellipsis;
+// overflow: hidden;
+ margin-right: 30px;
+}
+
+
+.SideBar_Open{
+ width: 90px;
+ .Active_SideBar_Link,.SideBar_Link:hover{
+ width: 60%;
+ }
+ .Link_Text,.DropDown_Text,.DropDown_Svg{
+ display: none;
+ }
+
+ .HamburgerMenu{
+ display: none;
+ }
+ .SideBar_Top{
+ cursor: pointer;
+ }
+ }
+ .DropDown_SideBar_Link{
+ background: var(--bg2);
+ }
+
+
+
+ .KarimLogo{
+ width: 50px;
+ margin-inline: 70px;
+
+ .cls-1, .cls-2 {
+ fill:var(--primary) !important;
+ }
+ }
+
+
+
+
+
+ .SideBar_Open{
+.Etaxi{
+ width: 60px;
+ height: 30px;
+ margin-inline: 40px;
+ }
+ }
+ .noOpen{
+.Etaxi{
+ width: 90px;
+ height:30px;
+ margin-inline: 40px;
+ }
+ }
+
+@media screen and (max-width: 700px) {
+ .out_Sidebar{
+.SideBar_Open,.noOpen{
+ display: none;
+ // transform: translateX(-100px);
+}
+ }
+
+.DashboardLayout_Body_Open,.DashboardLayout .DashboardLayout_Body{
+ left: 30px !important;
+ width : calc(100% - 60px) !important;
+}
+.ar{
+ .DashboardLayout_Body{
+ right: 30px !important;
+
+ }
+ .DashboardLayout_Body_Open{
+ right: 30px !important;
+ width: calc(96% - 140px);
+ transition: 1s ease-in-out;
+
+
+ }
+}
+
+}
+
+
+
+
+.ant-drawer-left .ant-drawer-wrapper-body .ant-drawer-header{
+ display:none;
+}
+
+/* Ant drawer body */
+.ant-drawer-left .ant-drawer-wrapper-body .ant-drawer-body{
+ padding-left:0px;
+ padding-right:0px;
+ padding-top:0px;
+ padding-bottom:0px;
+ background: var(--bg);
+}
+
+
+.ant-drawer-left{
+
+ .SideBar_Top{
+ display: none;
+ }
+ .ant-drawer-body div .RoutesLinks{
+ margin-top:27px;
+}
+}
+
+
+.ant-drawer-left .ant-drawer-content{
+
+ @include GlassModeCover;
+ // @include GlassModeBG;
+ color: var(--primary);
+ overflow-x: hidden;
+
+
+}
+
+
+
+
+
+
+.ant-drawer-right .ant-drawer-wrapper-body .ant-drawer-header{
+ display:none;
+}
+
+/* Ant drawer body */
+.ant-drawer-right .ant-drawer-wrapper-body .ant-drawer-body{
+ padding-left:0px;
+ padding-right:0px;
+ padding-top:0px;
+ padding-bottom:0px;
+ background: var(--bg);
+}
+
+
+.ant-drawer-right{
+
+ .SideBar_Top{
+ display: none;
+ }
+ .ant-drawer-body div .RoutesLinks{
+ margin-top:27px;
+}
+}
+
+
+.ant-drawer-right .ant-drawer-content{
+
+ @include GlassModeCover;
+ // @include GlassModeBG;
+ color: var(--primary);
+ overflow-x: hidden;
+
+
+}
diff --git a/src/Styles/Layout/Table.scss b/src/Styles/Layout/Table.scss
new file mode 100644
index 0000000..5bff747
--- /dev/null
+++ b/src/Styles/Layout/Table.scss
@@ -0,0 +1,101 @@
+ .sc-hKwDye,.card-body div nav{
+ background-color:var(--bg) !important;
+ color: var(--text) !important;
+ }
+ .glass{
+ /* Hmcd */
+ .sc-dkPtRN .sc-hKwDye .sc-crHmcD{
+ background-color:transparent;
+ }
+
+ /* Ptrn */
+ .sc-bdvvtL .sc-gsDKAQ .sc-dkPtRN{
+ background-color:transparent;
+ }
+
+ /* Card */
+ #root .card{
+ transform:translatex(0px) translatey(0px);
+ }
+
+ /* Bdvvtl */
+ .sc-dlVxhl .sc-fKVqWL .sc-bdvvtL{
+ background-color:transparent;
+ }
+
+
+ /* Row 12 */
+ .rdt_TableRow{
+ background-color:transparent !important;
+ }
+ //// table
+ .sc-fKVqWL .sc-bdvvtL .sc-iwjdpV{
+ background-color:transparent;
+ color:var(--text);
+}
+
+
+ }
+
+
+ :where(.css-dev-only-do-not-override-1adbn6x).ant-pagination .ant-pagination-total-text,
+ a:not([href]):not([class]), a:not([href]):not([class]):hover,
+ .dLwgoF svg
+ {
+ color: var(--text);
+ }
+ :where(.css-dev-only-do-not-override-1adbn6x).ant-pagination .ant-pagination-item-active a{
+ color: var(--primary) !important;
+ }
+
+
+ .modal .modal-dialog .modal-content{
+ z-index: 999999999 !important;
+ }
+
+
+ .ImageWname{
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+
+ }
+
+/* Ant form item control */
+#dynamic_form_complex div .ant-form-item-control{
+ transform:translatex(0px) translatey(0px);
+ min-width:93%;
+ }
+
+ /* Ant form item row */
+ #dynamic_form_complex div .ant-form-item-row{
+ transform:translatex(0px) translatey(0px);
+ overflow:hidden;
+ }
+
+ /* Ant space item */
+ #dynamic_form_complex div .ant-space-item{
+ min-width:47%;
+ }
+
+
+
+
+ .ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab{
+ direction: ltr;
+ }
+
+
+ .ar{
+ .VarianInfo .ant-tabs-editable .ant-tabs-nav{
+ margin-left:12px;
+ }
+ }
+
+
+ .react-tabs .react-tabs__tab-panel--selected .mt-4 .d-flex .ant-tabs-left{
+ width:100% !important;
+ }
+ :where(.css-dev-only-do-not-override-6j9yrn).ant-tabs {
+ width:100% !important;
+ }
\ No newline at end of file
diff --git a/src/Styles/component/DarkStyle.scss b/src/Styles/component/DarkStyle.scss
new file mode 100644
index 0000000..07e412e
--- /dev/null
+++ b/src/Styles/component/DarkStyle.scss
@@ -0,0 +1,115 @@
+
+.dark{
+ .modal_info{
+ color: #fff !important;
+}
+.modal-title{
+ color: #fff !important;
+}
+
+// pagination
+ .ant-pagination-item-container .ant-pagination-item-ellipsis{
+ color: #fff !important;
+ }
+ .ant-pagination{
+ color: #fff;
+ }
+ .anticon{
+ color: #fff;
+ }
+ .ant-pagination-item-active{
+ background: #ffffffd6;
+ border: 1px solid var(--primary);
+ a{
+ color: black !important;
+ }
+ }
+// Add Account Page
+ .add_account_title{
+ color: #fff;
+ }
+// INformation pages
+ .Information_title{
+ color: #fff;
+ }
+//karim field image
+ .ant-upload-list .ant-upload-list-item .ant-upload-list-item-name{
+ color: #fff !important;
+ }
+ .anticon svg{
+ color: #fff !important;
+ }
+//LyTable Arrow
+ .gPLhoV{
+ svg{
+ display: none;
+ }
+ }
+// single order
+ .Single_order_title, .Single_order_body{
+ color: #fff !important;
+ }
+// change password
+ .change_password_body{
+ color: #fff !important;
+ }
+ .dark_mode_white_color{
+ color: #fff !important;
+ }
+}
+
+
+
+
+
+
+
+
+//galss
+.glass{
+ //LyTable Arrow
+ .gPLhoV{
+ svg{
+ display: none;
+ }
+ }
+ // add account
+ .add_account_title{
+ color: #fff;
+ }
+ //information
+ .Information_title{
+ color: #fff;
+ }
+ //paginate
+ .ant-pagination{
+ color: #fff;
+ }
+ .anticon{
+ color: #fff;
+ }
+ .ant-pagination-item-active{
+ background: #ffffffd6;
+ border: 1px solid var(--primary);
+ a{
+ color: black !important;
+ }
+ }
+ //error page
+ .error_show{
+ margin-top: 3vw !important;
+ }
+//chart
+ .apexcharts-canvas {
+ // color: #edf4f4 !important;
+ background: #ffffff;
+ }
+// single order
+ .Single_order_title, .Single_order_body{
+ color: #fff !important;
+ }
+// change password
+ .change_password_body,.change_password_header{
+ color: #fff !important;
+ }
+}
\ No newline at end of file
diff --git a/src/Styles/component/DriverInfoSocket.scss b/src/Styles/component/DriverInfoSocket.scss
new file mode 100644
index 0000000..94b2e14
--- /dev/null
+++ b/src/Styles/component/DriverInfoSocket.scss
@@ -0,0 +1,102 @@
+.driverInfo_socket_container{
+ background: #fff;
+ width: 100%;
+ height: 100vh;
+ .image_container{
+ display: flex;justify-content: center;
+ .driver_track_image{
+ border: 4px solid var(--secondary);
+ display: flex;justify-content: center;align-items: center;
+ border-radius: 50% !important;
+ margin: auto;
+ margin-top: 6px;
+ width: 70px;
+ height: 70px;
+ object-fit: contain;
+ }
+ }
+
+ .info_container{
+ border: 3px solid var(--secondary);
+ width: 90%;
+ border-radius: 30px;
+ background: #fff;
+ margin-top: .7vw;
+ padding: 10px 10px;
+ display: flex; flex-direction: column;
+ margin-inline:auto ;
+ h4{
+ text-align: center;
+ }
+ .icon_info{
+ color: var(--primary);
+ width: 10%;
+ }
+ .main_info_cont{
+ display: flex;justify-content: center;align-items: center;
+ border-bottom: 1px solid var(--secondary);
+ .driver_info{
+ width: 90%;
+ margin-inline: auto;
+ text-transform: capitalize;
+ color: #000;
+ font-size: 14px;
+ font-weight: 600;
+ display: flex; justify-content: space-between;align-items: center;
+ margin-bottom: .5vw;
+ .driver_respons{
+ padding-top: 5px;
+ margin-left: 1vw;
+ font-size: 11px;
+ font-weight: 400;
+ color: var(--primary);
+ }
+ }
+ }
+
+ }
+ .phone_container{
+ border: 3px solid var(--secondary);
+ display: flex;align-items: center;justify-content: center;
+ background: #fff;
+ padding: 20px 10px;
+ width: 90%;height: 25px;
+ border-radius: 30px;
+ margin-inline:auto;
+ margin-top: 1vw;
+ svg{
+ font-size: 28px;
+ margin: 0 40px 0 10px;
+ }
+ .driver_respons{
+ font-size: 28px;
+ }
+ }
+}
+
+@media screen and (max-width: 360px) {
+ .phone_container{
+ svg{
+ font-size: 17px !important;
+ margin-left: 10px !important;
+ }
+ .driver_respons{
+ font-size: 17px !important;
+ }
+ }
+}
+
+.dark{
+ .driverInfo_socket_container{
+ background: var(--bg) !important;
+ .info_container{
+ background: #000 !important;
+ .driver_info{
+ color: #fff !important;
+ }
+ }
+ .phone_container{
+ background: #000 !important;
+ }
+ }
+}
diff --git a/src/Styles/component/ErrorPage.scss b/src/Styles/component/ErrorPage.scss
new file mode 100644
index 0000000..75da78c
--- /dev/null
+++ b/src/Styles/component/ErrorPage.scss
@@ -0,0 +1,21 @@
+.error_show{
+ display: flex;justify-content: center;align-items: center; flex-direction: column;
+ // background: #000;
+ height: 40vh;
+ .error_icon{
+ text-align: center;
+ color: var(--secondary);
+ font-size: 7vw;
+ }
+ .error_text{
+ text-align: center;
+ color: var(--primary);
+ font-size: 4vw;
+ }
+}
+
+.socket_debug_error{
+ font-size: 30px ;
+ font-weight: 600;
+ color: var(--secondary);
+}
\ No newline at end of file
diff --git a/src/Styles/component/Tab_Info.scss b/src/Styles/component/Tab_Info.scss
new file mode 100644
index 0000000..7b2b1b2
--- /dev/null
+++ b/src/Styles/component/Tab_Info.scss
@@ -0,0 +1,14 @@
+
+.Tab_Info_Container{
+ display: flex;justify-content: start;align-items: center;
+ .Tab_Info{
+ font-size: 1.4vw;
+ padding-left: .5vw;
+ margin-bottom: 0;
+ }
+ .SignleDriverInfoIcon{
+ svg{
+ width: 90%;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Styles/component/printButton.scss b/src/Styles/component/printButton.scss
new file mode 100644
index 0000000..3f8c242
--- /dev/null
+++ b/src/Styles/component/printButton.scss
@@ -0,0 +1,16 @@
+.print_button{
+ border: 2px solid var(--primary);
+ background-color: var(--primary);
+ border-radius: 0.5vw;
+ padding:.61vw 1.6vw;
+ font-size: 1vw;
+ display: flex; justify-content: center; align-items: center;
+ box-shadow: 2px 2px 7px 0 var(--primary);
+ margin-right: -770px !important;
+ text-transform: capitalize;
+ font-weight: 700;
+ &:hover{
+ background-color:var(--primary);
+ border: 2px solid var(--primary);
+ }
+}
\ No newline at end of file
diff --git a/src/Styles/component/radio.scss b/src/Styles/component/radio.scss
new file mode 100644
index 0000000..b5b90cb
--- /dev/null
+++ b/src/Styles/component/radio.scss
@@ -0,0 +1,79 @@
+.ant-radio-button-wrapper{
+ span{
+ position: absolute !important;
+ top: -6px;
+ left: 6px;
+ font-size: 8.5px !important;
+ color: #000 !important;
+ }
+}
+
+
+
+.ant-radio-button-wrapper {
+ // margin-left: 10px;
+ width: 50px !important;
+ height: 20px !important;
+}
+
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:first-child{
+ border-radius: 5px 0px 0px 0px !important;
+ margin-left: 10px;
+}
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:nth-child(2){
+ border-radius: 0px 5px 0px 0px !important;
+}
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:last-child{
+ border-radius: 0px 0px 5px 5px !important;
+ width: 100px !important;
+ border: 1px solid #d9d9d9;
+ margin-left: 10px;
+ span{
+ font-size: 10.5px !important;
+ left: 30px !important;
+ }
+}
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):first-child{
+ background: #04ff00 !important;
+ border: 1px solid #000;
+ span{
+ color: #fff !important;
+ }
+}
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):nth-child(2){
+ background: #bcbcbc !important;
+ border: 1px solid #000;
+ span{
+ color: #fff !important;
+ }
+}
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):last-child{
+ background: #e20000 !important;
+ border: 1px solid #000 ;
+ span{
+ color: #fff !important;
+ }
+}
+
+
+:where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:not(:first-child)::before {
+ display: none !important;
+}
+
+
+
+.ar{
+ :where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:first-child{
+ border-radius: 0px 5px 0px 0px !important;
+ margin-left: 0px;
+ }
+ :where(.css-dev-only-do-not-override-6j9yrn).ant-radio-button-wrapper:nth-child(2){
+ border-radius: 5px 0px 0px 0px !important;
+ }
+}
\ No newline at end of file
diff --git a/src/api/Categories.ts b/src/api/Categories.ts
new file mode 100644
index 0000000..8341899
--- /dev/null
+++ b/src/api/Categories.ts
@@ -0,0 +1,26 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationPost from "./helper/useUpdateMutationPut";
+
+const API = {
+ ADD: `category`,
+ GET_ALL: `category`,
+ DELETE: `category`,
+ UPDATE: `category`,
+
+};
+const KEY = "categories"
+
+
+export const useGetCategories = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneCategories = () => useGetOneQuery(KEY, API.GET_ALL);
+
+export const useAddCategories = () => useAddMutation(KEY, API.ADD);
+export const useUpdateCategories = (method?:any) => useUpdateMutationPost(KEY, API.UPDATE,true,method);
+
+export const useDeleteCategories = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/Coupon.ts b/src/api/Coupon.ts
new file mode 100644
index 0000000..96399e7
--- /dev/null
+++ b/src/api/Coupon.ts
@@ -0,0 +1,25 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+
+const API = {
+ ADD: `coupon`,
+ GET_ALL: `coupon`,
+ DELETE: `coupon`,
+ UPDATE: `coupon`,
+
+};
+const KEY = "coupon"
+
+
+export const useGetCoupon = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneCoupon = () => useGetOneQuery(KEY, API.GET_ALL);
+
+export const useAddCoupon = () => useAddMutation(KEY, API.ADD);
+export const useUpdateCoupon = (method:any) => useUpdateMutation(KEY, API.UPDATE,true,method);
+
+export const useDeleteCoupon = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/Slider.ts b/src/api/Slider.ts
new file mode 100644
index 0000000..3b8f9e9
--- /dev/null
+++ b/src/api/Slider.ts
@@ -0,0 +1,25 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+
+const API = {
+ ADD: `slider`,
+ GET_ALL: `slider`,
+ DELETE: `slider`,
+ UPDATE: `slider`,
+
+};
+const KEY = "slider"
+
+
+export const useGetSlider = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneSlider = () => useGetOneQuery(KEY, API.GET_ALL);
+
+export const useAddSlider = () => useAddMutation(KEY, API.ADD);
+export const useUpdateSlider = () => useUpdateMutation(KEY, API.UPDATE);
+
+export const useDeleteSlider = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/attribute.ts b/src/api/attribute.ts
new file mode 100644
index 0000000..079ce97
--- /dev/null
+++ b/src/api/attribute.ts
@@ -0,0 +1,29 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetSingleQuery from "./helper/useGetSingleQuery";
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationById from "./helper/useUpdateMutationById";
+
+const API = {
+ ADD: `attribute`,
+ GET_ALL: `attribute`,
+
+ DELETE: `attribute`,
+ UPDATE: `attribute`,
+
+};
+const KEY = "attribute"
+const KEYS =['attribute', 'category']
+
+export const useGetAttribute = (params?:any) => useGetQueryPagination(KEYS, API.GET_ALL,params);
+// export const useGetOneAttribute = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+export const useGetSingleAttribute = (params?:any,options?:any) => useGetSingleQuery(KEY, API.GET_ALL,params,options);
+
+export const useAddAttribute = () => useAddMutation(KEY, API.ADD);
+
+export const useUpdateAttribute = (method:any) => useUpdateMutationById(KEY, API.UPDATE,false,method);
+export const useUpdateAttributeStatus = () => useUpdateMutation(KEY, API.UPDATE,false);
+
+export const useDeleteAttribute = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/attributeValue.ts b/src/api/attributeValue.ts
new file mode 100644
index 0000000..6f2a4e2
--- /dev/null
+++ b/src/api/attributeValue.ts
@@ -0,0 +1,31 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useGetSingleQuery from "./helper/useGetSingleQuery";
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationById from "./helper/useUpdateMutationById";
+
+const API = {
+ ADD: `attributeValue`,
+ GET_ALL: `attributeValue`,
+
+ DELETE: `attributeValue`,
+ UPDATE: `attributeValue`,
+
+};
+const KEY = "attributeValue"
+
+
+export const useGetAttributeValue = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneAttributeValue = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+export const useGetSingleAttributeValue = (params?:any) => useGetSingleQuery(KEY, API.GET_ALL,params);
+
+export const useAddAttributeValue = () => useAddMutation(KEY, API.ADD);
+
+export const useUpdateAttributeValue = () => useUpdateMutationById(KEY, API.UPDATE);
+export const useUpdateAttributeValueStatus = () => useUpdateMutation(KEY, API.UPDATE);
+
+export const useDeleteAttributeValue = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/auth.ts b/src/api/auth.ts
new file mode 100644
index 0000000..001260e
--- /dev/null
+++ b/src/api/auth.ts
@@ -0,0 +1,11 @@
+import useAddMutation from "./helper/useAddMutation";
+
+
+
+
+const KEY = "AUTH"
+const API = {
+ LOGIN: `admin/login`,
+ LOGOUT: `/api/admin/logout`,
+ };
+export const useLoginAdmin = ()=>useAddMutation(KEY , API.LOGIN,"login_successful")
\ No newline at end of file
diff --git a/src/api/baseSubCategory.ts b/src/api/baseSubCategory.ts
new file mode 100644
index 0000000..8341899
--- /dev/null
+++ b/src/api/baseSubCategory.ts
@@ -0,0 +1,26 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationPost from "./helper/useUpdateMutationPut";
+
+const API = {
+ ADD: `category`,
+ GET_ALL: `category`,
+ DELETE: `category`,
+ UPDATE: `category`,
+
+};
+const KEY = "categories"
+
+
+export const useGetCategories = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneCategories = () => useGetOneQuery(KEY, API.GET_ALL);
+
+export const useAddCategories = () => useAddMutation(KEY, API.ADD);
+export const useUpdateCategories = (method?:any) => useUpdateMutationPost(KEY, API.UPDATE,true,method);
+
+export const useDeleteCategories = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/config.ts b/src/api/config.ts
new file mode 100644
index 0000000..568e610
--- /dev/null
+++ b/src/api/config.ts
@@ -0,0 +1,19 @@
+
+// export const BaseURL = `https://etaxiapi.rayantaxi.com/`;
+// export const BaseURL = `https://etaxi.Point.net/`;
+export const BaseURL = `https://back-demo.dmmobiles.com/api/`;
+export const ImageBaseURL = `https://back-demo.dmmobiles.com`;
+
+
+// export const BaseURL = `http://192.168.1.112:8000/api/`;
+// export const ImageBaseURL = `http://192.168.1.112:8000`;
+
+export const BaseURL_IMAGE = BaseURL.slice(0,-1);
+
+
+const PROJECT_NAME = "Hijab"
+
+export const TOKEN_KEY = PROJECT_NAME + "_TOKEN"
+
+export const USER_KEY = PROJECT_NAME + "_USER"
+
diff --git a/src/api/helper/AxiosBuilder.ts b/src/api/helper/AxiosBuilder.ts
new file mode 100644
index 0000000..3f19067
--- /dev/null
+++ b/src/api/helper/AxiosBuilder.ts
@@ -0,0 +1,49 @@
+import axios, { AxiosInstance, AxiosRequestConfig ,ResponseType } from 'axios';
+
+
+class AxiosBuilder {
+ private baseURL: string = '';
+ private headers: Record = {};
+ private timeout: number = 60000; // Request failed with 60 second
+ private withCreds: boolean = false;
+ private responseType: ResponseType = 'json';
+ // Custom Another Props with Your Position
+
+
+ withBaseURL(baseURL: string): AxiosBuilder {
+ this.baseURL = baseURL;
+ return this;
+ }
+
+ withHeaders(headers: Record): AxiosBuilder {
+ this.headers = headers;
+ return this;
+ }
+
+ withTimeout(timeout: number): AxiosBuilder {
+ this.timeout = timeout;
+ return this;
+ }
+
+
+ withResponseType(responseType: ResponseType): AxiosBuilder {
+ this.responseType = responseType;
+ return this;
+ }
+
+
+ build(): AxiosInstance {
+ const config: AxiosRequestConfig = {
+ baseURL: this.baseURL,
+ headers: this.headers,
+ timeout: this.timeout,
+ responseType:this.responseType
+ };
+
+ return axios.create(config);
+ }
+ }
+
+
+export default AxiosBuilder
+
diff --git a/src/api/helper/Get.tsx b/src/api/helper/Get.tsx
new file mode 100644
index 0000000..9a97c04
--- /dev/null
+++ b/src/api/helper/Get.tsx
@@ -0,0 +1,18 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+
+function useGetQuery(key: string, url: string , params:any) {
+ const axios = useAxios();
+
+ return useQuery(key, async () => {
+ const response = await axios.get( params ? url+params: url , params);
+ return response.data.data;
+ }, {
+ onError: (error) => {
+ console.error('An error occurred:', error);
+ },
+ refetchOnWindowFocus: false,
+ });
+}
+
+export default useGetQuery;
diff --git a/src/api/helper/buildFormData.ts b/src/api/helper/buildFormData.ts
new file mode 100644
index 0000000..eb1f736
--- /dev/null
+++ b/src/api/helper/buildFormData.ts
@@ -0,0 +1,27 @@
+
+
+export const buildFormData = (
+ formData: FormData,
+ data: any,
+ parentKey?: string
+ ): void => {
+ if (
+ data &&
+ typeof data === "object" &&
+ !(data instanceof Date) &&
+ !(data instanceof File)
+ ) {
+ Object.keys(data).forEach((key) => {
+ buildFormData(
+ formData,
+ data[key],
+ parentKey ? `${parentKey}[${key}]` : key
+ );
+ });
+ } else {
+ const value = data == null ? "" : data;
+
+ formData.append(parentKey as string, value);
+ }
+ };
+
\ No newline at end of file
diff --git a/src/api/helper/ueGetPagination.tsx b/src/api/helper/ueGetPagination.tsx
new file mode 100644
index 0000000..f570e1c
--- /dev/null
+++ b/src/api/helper/ueGetPagination.tsx
@@ -0,0 +1,44 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+import { useLocation, useNavigate } from 'react-router-dom';
+import useAuthState from '../../lib/state mangment/AuthState';
+
+export default function useGetQueryPagination(KEY: string | string[], Api: string, params: any = {}, options: any = {}, dontSearchBy?: string) {
+ const axios = useAxios();
+ const location = useLocation();
+ let pagination = location?.search || '';
+ const language = localStorage.getItem("language") ?? "en"
+
+ const { logout } = useAuthState();
+ const navigate = useNavigate();
+
+ if (dontSearchBy && pagination.includes(dontSearchBy)) {
+ const searchParams = new URLSearchParams(pagination);
+ searchParams.delete(dontSearchBy);
+ pagination = searchParams.toString();
+ }
+ if (pagination && !pagination.startsWith('?')) {
+ pagination = '?' + pagination;
+ }
+
+ // Check if pagination exists and append it to the API endpoint
+ const paginationParams = pagination ? pagination + '&orderById=desc' : '?orderById=desc';
+ const apiUrl = Api + paginationParams;
+
+ return useQuery(
+ [Array.isArray(KEY) ? KEY.join(',') : KEY, pagination,language], async () => {
+ const response = await axios.get(apiUrl, { params });
+ return response.data;
+ },
+ {
+ onError: (error: any) => {
+ if (error?.response?.status === 401 || error?.response?.status === 403) {
+ logout();
+ navigate("/auth");
+ }
+ },
+ refetchOnWindowFocus: false,
+ ...options
+ }
+ );
+}
diff --git a/src/api/helper/useAddMutation.ts b/src/api/helper/useAddMutation.ts
new file mode 100644
index 0000000..88ffb5d
--- /dev/null
+++ b/src/api/helper/useAddMutation.ts
@@ -0,0 +1,39 @@
+import { useMutation, useQueryClient, UseMutationResult } from 'react-query';
+import { toast } from 'react-toastify';
+import useAxios from './useAxios';
+import { useTranslation } from 'react-i18next';
+
+type AxiosResponse = {
+ message: string;
+ data:any ,
+ success:true
+};
+
+function useAddMutation(key: string, url: string,message?:string): UseMutationResult {
+ const axios = useAxios();
+ const [t] = useTranslation();
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ async (dataToSend) => {
+ const { data } = await axios.post(url, dataToSend,{
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ } );
+ return data;
+ },
+ {
+ onSuccess: (data) => {
+ queryClient.invalidateQueries([key]);
+ toast.success(data.message || t(message??"") || t("added_successful"));
+ },
+ onError: (error:any) => {
+ const message = error?.response?.data?.message || t("failed_to_add_data");
+ toast.error(message);
+ }
+ }
+ );
+}
+
+export default useAddMutation;
diff --git a/src/api/helper/useAddMutationJson.ts b/src/api/helper/useAddMutationJson.ts
new file mode 100644
index 0000000..8e9864b
--- /dev/null
+++ b/src/api/helper/useAddMutationJson.ts
@@ -0,0 +1,35 @@
+import { useMutation, useQueryClient, UseMutationResult } from 'react-query';
+import { toast } from 'react-toastify';
+import useAxios from './useAxios';
+import { useTranslation } from 'react-i18next';
+
+type AxiosResponse = {
+ message: string;
+ data:any ,
+ success:true
+};
+
+function useAddMutationJson(key: string, url: string): UseMutationResult {
+ const axios = useAxios();
+ const [t] = useTranslation();
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ async (dataToSend) => {
+ const { data } = await axios.post(url, dataToSend);
+ return data;
+ },
+ {
+ onSuccess: (data) => {
+ queryClient.invalidateQueries([key]);
+ toast.success(data.message || t("added_successful"));
+ },
+ onError: (error:any) => {
+ const message = error?.response?.data?.message || t("failed_to_add_data");
+ toast.error(message);
+ }
+ }
+ );
+}
+
+export default useAddMutationJson;
diff --git a/src/api/helper/useAxios.ts b/src/api/helper/useAxios.ts
new file mode 100644
index 0000000..7cdd7db
--- /dev/null
+++ b/src/api/helper/useAxios.ts
@@ -0,0 +1,25 @@
+import { BaseURL } from '../config'
+import useAuthState from '../../lib/state mangment/AuthState'
+import AxiosBuilder from './AxiosBuilder'
+
+function useAxios() {
+ const {isAuthenticated , token}= useAuthState()
+
+ const buildAxios = new AxiosBuilder().
+ withBaseURL(BaseURL)
+ .withResponseType('json')
+ .withTimeout(120000)
+ .withHeaders({"Content-Type" :"application/json"})
+
+
+ if(isAuthenticated){
+
+ buildAxios.withHeaders({ Authorization: 'Bearer '+ token })
+ }
+
+ return (
+ buildAxios.build()
+ )
+}
+
+export default useAxios
\ No newline at end of file
diff --git a/src/api/helper/useDeleteMutation.ts b/src/api/helper/useDeleteMutation.ts
new file mode 100644
index 0000000..87e84ca
--- /dev/null
+++ b/src/api/helper/useDeleteMutation.ts
@@ -0,0 +1,35 @@
+import { useMutation, useQueryClient, UseMutationResult } from 'react-query';
+import { toast } from 'react-toastify';
+import useAxios from './useAxios';
+import { useTranslation } from 'react-i18next';
+
+type AxiosResponse = {
+ message: string;
+ // Add other properties as needed
+};
+
+function useDeleteMutation(key:any , url: string): UseMutationResult {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const {t} = useTranslation();
+
+ return useMutation(
+ async ({dataToSend,id}:any) => {
+ const { data } = await axios.delete(url+"/"+id );
+ return {...data, id,dataToSend};
+ },
+ {
+ onSuccess: (data) => {
+ queryClient.invalidateQueries(key);
+ toast.success(t('deleted_successfully'));
+ },
+ onError: (error:any) => {
+ const message = error?.response?.data?.message || t("failed_to_add_data");
+ toast.error(message);
+ }
+ },
+
+ );
+}
+
+export default useDeleteMutation;
diff --git a/src/api/helper/useGetOneQuery.ts b/src/api/helper/useGetOneQuery.ts
new file mode 100644
index 0000000..c0719f6
--- /dev/null
+++ b/src/api/helper/useGetOneQuery.ts
@@ -0,0 +1,39 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+import useAuthState from '../../lib/state mangment/AuthState';
+import { useNavigate, useParams } from 'react-router-dom';
+
+function useGetOneQuery(key: string, url: string , params:any={},options:any={}) {
+ const axios = useAxios();
+ const {logout} = useAuthState()
+ const language = localStorage.getItem("language") ?? "en"
+ const navigate = useNavigate()
+ const {id} = useParams()
+
+ return useQuery(
+ [id, key,language],
+ async () => {
+ const response = await axios.get(url+"/"+ id+`?lang=${language}`);
+ return response.data;
+ },
+
+
+ {
+ onError: (error:any) => {
+ if(error.response.status == 401 || error.response.status == 403){
+ logout()
+ navigate("/auth")
+
+ }
+
+ },
+ cacheTime: 0, // Set cacheTime to 0 to disable caching
+ refetchOnWindowFocus: false,
+
+ ...options
+
+ }
+ );
+}
+
+export default useGetOneQuery;
diff --git a/src/api/helper/useGetQuery.ts b/src/api/helper/useGetQuery.ts
new file mode 100644
index 0000000..4c689fd
--- /dev/null
+++ b/src/api/helper/useGetQuery.ts
@@ -0,0 +1,41 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+import useAuthState from '../../lib/state mangment/AuthState';
+import { useNavigate } from 'react-router-dom';
+
+function useGetQuery(KEY: string | string[], url: string , params:any={},options:any={}) {
+ const axios = useAxios();
+ const {logout} = useAuthState()
+ const navigate = useNavigate()
+ const language = localStorage.getItem("language") ?? "en"
+
+ return useQuery(
+ params ? [Array.isArray(KEY) ? KEY.join(',') : KEY, params,language] : [Array.isArray(KEY) ? KEY.join(',') : KEY,language],
+ async () => {
+ const response = await axios.get(url , {params});
+ return response.data;
+ },
+
+
+ {
+ onError: (error:any) => {
+ console.log('====================================');
+ console.log(error.response.status);
+ console.log('====================================');
+ if(error.response.status == 401 || error.response.status == 403){
+ logout()
+ navigate("/auth")
+
+
+ }
+
+ },
+ refetchOnWindowFocus: false,
+
+ ...options
+
+ }
+ );
+}
+
+export default useGetQuery;
diff --git a/src/api/helper/useGetSingleQuery.ts b/src/api/helper/useGetSingleQuery.ts
new file mode 100644
index 0000000..e930dc7
--- /dev/null
+++ b/src/api/helper/useGetSingleQuery.ts
@@ -0,0 +1,38 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+import useAuthState from '../../lib/state mangment/AuthState';
+import { useNavigate, useParams } from 'react-router-dom';
+
+function useGetSingleQuery(key: string, url: string , params:any={},options:any={}) {
+ const axios = useAxios();
+ const {logout} = useAuthState()
+ const language = localStorage.getItem("language") ?? "en"
+ const navigate = useNavigate()
+ const {id} = useParams()
+
+ return useQuery(
+ [id, key,params?.id],
+ async () => {
+ const response = await axios.get(url+"?"+params?.name+"="+params?.id+`?lang=${language}`);
+ return response.data;
+ },
+
+
+ {
+ onError: (error:any) => {
+ if(error.response.status == 401 || error.response.status == 403){
+ logout()
+ navigate("/auth")
+
+ }
+
+ },
+ refetchOnWindowFocus: false,
+
+ ...options
+
+ }
+ );
+}
+
+export default useGetSingleQuery;
diff --git a/src/api/helper/useGetWithFillter.ts b/src/api/helper/useGetWithFillter.ts
new file mode 100644
index 0000000..448e548
--- /dev/null
+++ b/src/api/helper/useGetWithFillter.ts
@@ -0,0 +1,22 @@
+import { useQuery } from 'react-query';
+import useAxios from './useAxios';
+
+export default function useGetQueryPagination(KEY: string | string[], Api: string, options: any = {}) {
+ const axios = useAxios();
+ const pagination = options;
+
+ return useQuery(
+ [ KEY], async () => {
+ const response = await axios.get(Api + pagination );
+ return response.data;
+ },
+ {
+ onError: (error:any) => {
+
+ },
+
+
+ }
+ );
+}
+
diff --git a/src/api/helper/useToggleStatus.ts b/src/api/helper/useToggleStatus.ts
new file mode 100644
index 0000000..0ef86bb
--- /dev/null
+++ b/src/api/helper/useToggleStatus.ts
@@ -0,0 +1,32 @@
+import { useQueryClient, useMutation } from "react-query";
+import { toast } from "react-toastify";
+import { useTranslation } from "react-i18next";
+import useAxios from "./useAxios";
+
+export const useToggleStatus = (key:any, url:any, object_id:any) => {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const [t] = useTranslation();
+
+ return useMutation(
+ async ({ id, new_status }:any) => {
+ const { data } = await axios.post(url, {
+ [object_id]: id,
+ new_status,
+ });
+ return { ...data, id, new_status };
+ },
+ {
+ onSuccess: ({ message, id, new_status }) => {
+ toast.success(message || t("toggle_success"));
+
+ queryClient.invalidateQueries([key]);
+ },
+ onError: (err:any) => {
+ const message = err?.response?.data?.message || t("toggle_failed");
+ toast.error(message);
+ // validateSession(err.response);
+ },
+ }
+ );
+};
diff --git a/src/api/helper/useUpdateMutation.ts b/src/api/helper/useUpdateMutation.ts
new file mode 100644
index 0000000..29b258f
--- /dev/null
+++ b/src/api/helper/useUpdateMutation.ts
@@ -0,0 +1,58 @@
+import { useQueryClient, useMutation, UseMutationResult } from "react-query";
+import { toast } from "react-toastify";
+import useAxios from "./useAxios";
+import { useTranslation } from "react-i18next";
+import { useParams } from "react-router-dom";
+
+type AxiosResponse = {
+ message: string;
+ // Add other properties as needed
+};
+
+const useUpdateMutation = (
+ key: string,
+ url: string,
+ toastMessage: boolean = false,
+ method?:string
+): UseMutationResult => {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const [t] = useTranslation();
+ const {id}= useParams()
+
+ return useMutation(
+ async (dataToSend) => {
+ if(method === "put"){
+ const { data } = await axios.put(url+"/"+id, dataToSend );
+ return data;
+ }else{
+ const { data } = await axios.post(url+"/"+id, dataToSend,{
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+ return data;
+ }
+
+
+
+ },
+ {
+ onSuccess: (data) => {
+ if (toastMessage) {
+ toast.success(data.message || t("updated_successfully"));
+ }
+ queryClient.invalidateQueries([key]);
+ },
+ onError: (err:any) => {
+ const message = err?.response?.data?.message || t("failed_to_update_data");
+ toast.error(message);
+
+
+ // validateSession(err.response);
+ },
+ }
+ );
+};
+
+export default useUpdateMutation;
diff --git a/src/api/helper/useUpdateMutationById.ts b/src/api/helper/useUpdateMutationById.ts
new file mode 100644
index 0000000..1d5ed72
--- /dev/null
+++ b/src/api/helper/useUpdateMutationById.ts
@@ -0,0 +1,57 @@
+import { useQueryClient, useMutation, UseMutationResult } from "react-query";
+import { toast } from "react-toastify";
+import useAxios from "./useAxios";
+import { useTranslation } from "react-i18next";
+import { useParams } from "react-router-dom";
+
+type AxiosResponse = {
+ message: string;
+ // Add other properties as needed
+};
+
+const useUpdateMutationById = (
+ key: string,
+ url: string,
+ toastMessage: boolean = false,
+ method?:string
+): UseMutationResult => {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const [t] = useTranslation();
+
+ return useMutation(
+ async (dataToSend:any) => {
+ if(method === "put"){
+ const { data } = await axios.put(url+"/"+dataToSend?.id, dataToSend );
+ return data;
+ }else{
+ const { data } = await axios.post(url+"/"+dataToSend?.id, dataToSend,{
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+ return data;
+ }
+
+
+
+ },
+ {
+ onSuccess: (data) => {
+ if (toastMessage) {
+ toast.success(data.message || t("updated_successfully"));
+ }
+ queryClient.invalidateQueries([key]);
+ },
+ onError: (err:any) => {
+ const message = err?.response?.data?.message || t("failed_to_update_data");
+ toast.error(message);
+
+
+ // validateSession(err.response);
+ },
+ }
+ );
+};
+
+export default useUpdateMutationById;
diff --git a/src/api/helper/useUpdateMutationPut.ts b/src/api/helper/useUpdateMutationPut.ts
new file mode 100644
index 0000000..bac1190
--- /dev/null
+++ b/src/api/helper/useUpdateMutationPut.ts
@@ -0,0 +1,52 @@
+import { useQueryClient, useMutation, UseMutationResult } from "react-query";
+import { toast } from "react-toastify";
+import useAxios from "./useAxios";
+import { useTranslation } from "react-i18next";
+import { useParams } from "react-router-dom";
+
+type AxiosResponse = {
+ message: string;
+ // Add other properties as needed
+};
+
+const useUpdateMutationPost = (
+ key: string,
+ url: string,
+ toastMessage: boolean = true,
+ method?:string
+): UseMutationResult => {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const [t] = useTranslation();
+ const {id}= useParams()
+
+ return useMutation(
+ async (dataToSend) => {
+
+ const { data } = await axios.post(url+"/"+id, dataToSend,{
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+ return data;
+
+ },
+ {
+ onSuccess: (data) => {
+ if (toastMessage) {
+ toast.success(data.message || t("updated_successfully"));
+ }
+ queryClient.invalidateQueries([key]);
+ },
+ onError: (err:any) => {
+ const message = err?.response?.data?.message || t("failed_to_update_data");
+ toast.error(message);
+
+
+ // validateSession(err.response);
+ },
+ }
+ );
+};
+
+export default useUpdateMutationPost;
diff --git a/src/api/helper/useUploadWithProgress.ts b/src/api/helper/useUploadWithProgress.ts
new file mode 100644
index 0000000..00db02a
--- /dev/null
+++ b/src/api/helper/useUploadWithProgress.ts
@@ -0,0 +1,75 @@
+import { useState } from "react";
+import { useQueryClient, useMutation, MutationFunction } from "react-query";
+import { toast } from "react-toastify";
+import useAxios from "./useAxios";
+import { AxiosResponse } from "axios";
+import { useTranslation } from "react-i18next";
+import { QueryStatusEnum } from "../../config/QueryStatus";
+// import { validateSession } from "./validateSession";
+
+interface UploadWithProgressReturnType {
+ percentCompleted: number;
+ mutate: MutationFunction;
+ isLoading: boolean;
+ isError: boolean;
+ error: unknown;
+ isSuccess:boolean,
+ value:any,
+ status : QueryStatusEnum
+}
+
+export const useUploadWithProgress = (
+ key: string,
+ url: string
+): UploadWithProgressReturnType => {
+ const axios = useAxios();
+ const queryClient = useQueryClient();
+ const [percentCompleted, setPercentCompleted] = useState(0.0);
+ const {t} = useTranslation();
+ const mutation = useMutation<
+ AxiosResponse,
+ unknown,
+ any,
+ {
+ onSuccess: (data: AxiosResponse) => void;
+ onError: (error: unknown) => void;
+ }
+ >(
+ async (dataToSend) => {
+ setPercentCompleted(0.0);
+ const { data } = await axios.post(url, dataToSend, {
+ onUploadProgress: (event:any) => {
+ console.log();
+
+ if (event?.event?.lengthComputable) {
+ setPercentCompleted(Math.round((event.loaded * 100) / event.total));
+ }
+ },
+ });
+ return data;
+ },
+ {
+ onSuccess: ({ data }) => {
+ toast.success(data.message || t("_messages.success.upload"));
+ queryClient.invalidateQueries([key]);
+ },
+ onError: (err:any) => {
+ const message =
+ err?.response?.data?.message || t("_messages.error.upload");
+ toast.error(message);
+ // validateSession(err.response);
+ },
+ }
+ );
+
+ return {
+ percentCompleted,
+ value:percentCompleted,
+ mutate: mutation.mutate as MutationFunction,
+ isLoading: mutation.isLoading,
+ isError: mutation.isError,
+ error: mutation.error,
+ isSuccess :mutation.isSuccess,
+ status : mutation.status as QueryStatusEnum
+ };
+};
\ No newline at end of file
diff --git a/src/api/home.ts b/src/api/home.ts
new file mode 100644
index 0000000..df80374
--- /dev/null
+++ b/src/api/home.ts
@@ -0,0 +1,14 @@
+
+import useGetQuery from "./helper/useGetQuery"
+
+const API = {
+ ADD: `home/overview`,
+ GET_ALL: `home/overview`,
+ DELETE: `home/overview`,
+ UPDATE: `home/overview`,
+
+};
+const KEY = ["home","order","users","categories"]
+
+
+export const useGetHome = (params?:any) => useGetQuery(KEY, API.GET_ALL,params);
\ No newline at end of file
diff --git a/src/api/notification.ts b/src/api/notification.ts
new file mode 100644
index 0000000..1ccd2e3
--- /dev/null
+++ b/src/api/notification.ts
@@ -0,0 +1,23 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+
+const API = {
+ ADD: `notification`,
+ GET_ALL: `notification`,
+ DELETE: `notification`,
+
+};
+const KEY = "notification"
+
+
+export const useGetNotification = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOnenotification = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+
+export const useAddNotification = () => useAddMutation(KEY, API.ADD);
+
+export const useDeleteNotification = () =>useDeleteMutation(KEY, API.DELETE);
+
+
diff --git a/src/api/order.ts b/src/api/order.ts
new file mode 100644
index 0000000..b9083fa
--- /dev/null
+++ b/src/api/order.ts
@@ -0,0 +1,32 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+
+const API = {
+ ADD: `order`,
+ GET_ALL: `order`,
+ DELETE: `order`,
+ UPDATE: `order`,
+
+};
+const KEY = "order"
+
+
+export const useGetOrder = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params,{},"coupon");
+export const useGetOneOrder = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+
+export const useAddOrder = () => useAddMutation(KEY, API.ADD);
+export const useUpdateOrder = (method?:any) => useUpdateMutation(KEY, API.UPDATE,true,method);
+
+export const useDeleteOrder = () =>useDeleteMutation(KEY, API.DELETE);
+
+
+export const useAcceptOrder = ()=> useAddMutation(KEY, API.ADD)
+export const useCancelOrder = ()=> useAddMutation(KEY, API.ADD)
+export const useDeliverOrder = ()=> useAddMutation(KEY, API.ADD)
+export const useDeliveredOrder = ()=> useAddMutation(KEY, API.ADD)
+
diff --git a/src/api/product.ts b/src/api/product.ts
new file mode 100644
index 0000000..593d2b4
--- /dev/null
+++ b/src/api/product.ts
@@ -0,0 +1,38 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useGetQuery from "./helper/useGetQuery"
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationById from "./helper/useUpdateMutationById";
+
+const API = {
+ ADD: `baseProduct`,
+ GET_ALL: `baseProduct`,
+ DELETE: `baseProduct`,
+ UPDATE: `baseProduct`,
+ ADD_VAR:"product",
+ UPDATE_VAR:"product",
+ DELETE_VAR:"product"
+
+
+};
+const KEY = "products"
+// const ONEKEY = "Product"
+
+
+export const useGetProduct = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params,{},"category");
+export const useGetOneProduct = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+
+export const useAddProduct = () => useAddMutation(KEY, API.ADD);
+export const useAddProductVariation = () => useAddMutation(KEY, API.ADD_VAR);
+
+export const useUpdateProduct = (method:any) => useUpdateMutation(KEY, API.UPDATE,true,method);
+export const useUpdateProductVariation = (method?:any) => useUpdateMutationById(KEY, API.UPDATE_VAR,false,method);
+
+export const useUpdateProductStatus = () => useUpdateMutation(KEY, API.UPDATE);
+
+export const useDeleteProduct = () =>useDeleteMutation(KEY, API.DELETE);
+export const useDeleteProductVariation = () =>useDeleteMutation(KEY, API.DELETE_VAR);
+
diff --git a/src/api/supportmessages.ts b/src/api/supportmessages.ts
new file mode 100644
index 0000000..412ea52
--- /dev/null
+++ b/src/api/supportmessages.ts
@@ -0,0 +1,24 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useUpdateMutationById from "./helper/useUpdateMutationById";
+
+const API = {
+ ADD: `SupportMessages`,
+ GET_ALL: `supportMessages`,
+ DELETE: `SupportMessages`,
+ UPDATE: `SupportMessages`,
+
+};
+const KEY = "support_messages"
+
+
+export const useGetSupportMessages = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneSupportMessages = () => useGetOneQuery(KEY, API.GET_ALL);
+
+export const useAddSupportMessages = () => useAddMutation(KEY, API.ADD);
+export const useUpdateSupportMessages = () => useUpdateMutationById(KEY, API.UPDATE);
+
+export const useDeleteSupportMessages = () =>useDeleteMutation(KEY, API.DELETE);
diff --git a/src/api/users.ts b/src/api/users.ts
new file mode 100644
index 0000000..d477a1f
--- /dev/null
+++ b/src/api/users.ts
@@ -0,0 +1,32 @@
+
+import useGetQueryPagination from "./helper/ueGetPagination";
+import useAddMutation from "./helper/useAddMutation"
+import useDeleteMutation from "./helper/useDeleteMutation"
+import useGetOneQuery from "./helper/useGetOneQuery";
+import useUpdateMutation from "./helper/useUpdateMutation";
+import useUpdateMutationById from "./helper/useUpdateMutationById";
+
+const API = {
+ ADD: `user`,
+ // GET_ALL: `user?notOfType=admin`,
+ GET_ALL: `user`,
+
+ DELETE: `user`,
+ UPDATE: `user/updateStatus`,
+ UPDATE_ADMIN: `user/changeAdminPassword`,
+
+
+};
+const KEY = "users"
+
+
+export const useGetUsers = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
+export const useGetOneUser = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
+
+export const useAddUsers = () => useAddMutation(KEY, API.ADD);
+export const useUpdateUsers = (method?:string) => useUpdateMutationById(KEY, API.UPDATE,true,method);
+export const useUpdateAdmin = () => useAddMutation(KEY, API.UPDATE_ADMIN);
+
+export const useDeleteUsers = () =>useDeleteMutation(KEY, API.DELETE);
+
+
diff --git a/src/asset/logo/MainLogo.png b/src/asset/logo/MainLogo.png
new file mode 100644
index 0000000..84d4dcb
Binary files /dev/null and b/src/asset/logo/MainLogo.png differ
diff --git a/src/config/AppKey.ts b/src/config/AppKey.ts
new file mode 100644
index 0000000..5b81dac
--- /dev/null
+++ b/src/config/AppKey.ts
@@ -0,0 +1,12 @@
+
+
+
+const PROJECT_NAME = "Hijab_DAHBOARD"
+
+export const TOKEN_KEY = PROJECT_NAME + "_TOKEN"
+export const TOKEN_KEY_SOCKET = PROJECT_NAME + "_SOKCET_TOKEN"
+
+export const USER_KEY = PROJECT_NAME + "_USER"
+
+
+/// 1825|laravel_sanctum_RpRqXDf1jg1Jgb0VjnJcUzPpSmX46PD7h8jB8ag372d0778a
diff --git a/src/config/QueryStatus.ts b/src/config/QueryStatus.ts
new file mode 100644
index 0000000..2351251
--- /dev/null
+++ b/src/config/QueryStatus.ts
@@ -0,0 +1,8 @@
+
+
+export enum QueryStatusEnum {
+ LOADING="loading",
+ ERROR ="error",
+ SUCCESS="success",
+ IDLE ="idle"
+}
diff --git a/src/config/RoleConfige.ts b/src/config/RoleConfige.ts
new file mode 100644
index 0000000..b445160
--- /dev/null
+++ b/src/config/RoleConfige.ts
@@ -0,0 +1,7 @@
+export const VIEWER: string = "viewer";
+export const VENDOR: string = "vendor";
+export const ADMIN: string = "admin";
+export const SUPER_ADMIN: string = "Super Admin";
+export const CLIENT: string = "client";
+
+export const Roles: string[] = [VIEWER, VENDOR, ADMIN, SUPER_ADMIN];
\ No newline at end of file
diff --git a/src/enums/ChartTypeEnum.ts b/src/enums/ChartTypeEnum.ts
new file mode 100644
index 0000000..79a6c3e
--- /dev/null
+++ b/src/enums/ChartTypeEnum.ts
@@ -0,0 +1,14 @@
+export enum ChartTypeEnum {
+ LINE = "line",
+ AREA = "area",
+ BAR = "bar",
+ RADIAL = "radialBar",
+ PIE = "pie",
+ DONUT = "donut",
+ SCATTER = "scatter",
+ BUBBLE = "bubble",
+ HEATMAP = "heatmap",
+ CANDLESTICK = "candlestick",
+ }
+
+
\ No newline at end of file
diff --git a/src/enums/SocketEventEnum.ts b/src/enums/SocketEventEnum.ts
new file mode 100644
index 0000000..bcded09
--- /dev/null
+++ b/src/enums/SocketEventEnum.ts
@@ -0,0 +1,15 @@
+
+
+
+export enum SocketEventLisntEnum {
+
+ SOCKET_DEBUG = 'dashboard_debug' ,
+ SOCKET_NEW_USER = 'new_driver_come_online' ,
+ SOCKET_UPDATE_USER = 'update_driver_online' ,
+ SOCKET_REMOVE_USER = 'remove_driver_from_online' ,
+
+ SOCKET_NEW_ORDER = 'new_order_come_pendding' ,
+ SOCKET_ACCEPTE_ORDER = 'order_accepted_by_driver' ,
+ SOCKET_START_ORDER = 'order_start' ,
+
+}
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
new file mode 100644
index 0000000..28bfafa
--- /dev/null
+++ b/src/index.tsx
@@ -0,0 +1,14 @@
+import App from './App';
+import 'bootstrap/dist/css/bootstrap.min.css';
+import './Styles/AppStyle/Import.scss'
+import { createRoot } from "react-dom/client";
+import ProviderContainer from './ProviderContainer';
+
+const root = createRoot(document.getElementById("root") as HTMLElement);
+root.render(
+
+
+
+
+
+);
\ No newline at end of file
diff --git a/src/lib/ReactQueryProvider.tsx b/src/lib/ReactQueryProvider.tsx
new file mode 100644
index 0000000..4b56401
--- /dev/null
+++ b/src/lib/ReactQueryProvider.tsx
@@ -0,0 +1,17 @@
+import React from 'react'
+import { QueryClient, QueryClientProvider } from 'react-query'
+
+function QueryProvider({ children }: any) {
+ const queryClient = new QueryClient()
+
+ return (
+
+ {children}
+
+
+ )
+}
+
+export default QueryProvider
+
+
diff --git a/src/lib/SocketProvider.tsx b/src/lib/SocketProvider.tsx
new file mode 100644
index 0000000..aa00e9d
--- /dev/null
+++ b/src/lib/SocketProvider.tsx
@@ -0,0 +1,37 @@
+
+import { Socket, io } from 'socket.io-client';
+import { TOKEN_KEY_SOCKET } from '../config/AppKey';
+
+
+export const BASE_URL_SOCKET = 'http://192.168.1.14:8001/';
+var socket :Socket | null = null ;
+
+
+function InitSocket(){
+
+
+
+ if (!socket){
+ socket = io(BASE_URL_SOCKET , {
+ transports:['websocket'],
+ autoConnect:true,
+ query:{
+ token:localStorage.getItem(TOKEN_KEY_SOCKET),
+
+ }
+ });
+ }
+}
+
+export const disconnectSocket = ()=>{
+
+ socket?.disconnect();
+ socket = null;
+}
+export const getScoket = ()=>{
+
+ InitSocket();
+ return socket;
+}
+
+
diff --git a/src/lib/ToastProvider.tsx b/src/lib/ToastProvider.tsx
new file mode 100644
index 0000000..b2e9c27
--- /dev/null
+++ b/src/lib/ToastProvider.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import { ToastContainer } from 'react-toastify'
+import 'react-toastify/dist/ReactToastify.css';
+
+function ToastProvider({ children }: any) {
+ let What_the_language = localStorage.getItem('language') ?? "en";
+
+ return (
+ <>
+
+ {children}
+ >
+ )
+}
+
+export default ToastProvider
\ No newline at end of file
diff --git a/src/lib/state mangment/AuthState.ts b/src/lib/state mangment/AuthState.ts
new file mode 100644
index 0000000..b2bd147
--- /dev/null
+++ b/src/lib/state mangment/AuthState.ts
@@ -0,0 +1,51 @@
+import {create} from 'zustand';
+import { TOKEN_KEY, TOKEN_KEY_SOCKET, USER_KEY } from '../../config/AppKey';
+
+interface LoginResponse {
+ token:string ,
+ "admin": any,
+ token_node:string
+}
+
+interface AuthStore {
+ user: any | null |undefined;
+ token:string |null | undefined;
+ isAuthenticated: boolean;
+ login: (userData: LoginResponse) => Promise;
+ logout: () => Promise;
+}
+
+const useAuthState = create((set) => {
+
+ const storedUser :any = localStorage.getItem(USER_KEY) ;
+
+ const storedToken = localStorage.getItem(TOKEN_KEY);
+ const initialUser = (storedUser && storedUser !== 'undefined') ? JSON.parse(storedUser) : null;
+
+ return {
+ user: initialUser,
+ isAuthenticated: !!storedToken,
+ token:storedToken,
+ login: async (userData) => {
+ console.log(userData);
+ localStorage.setItem(TOKEN_KEY , userData.token)
+
+ localStorage.setItem(USER_KEY , JSON.stringify(userData.admin))
+
+ set((state)=>({user:userData.admin , isAuthenticated:true , token:userData.token}))
+
+ },
+ logout: async () => {
+
+ localStorage.removeItem(TOKEN_KEY )
+ localStorage.removeItem(TOKEN_KEY_SOCKET )
+
+ localStorage.removeItem(USER_KEY )
+ set((state)=>({user:null , isAuthenticated:false , token:null}))
+
+
+ },
+ };
+});
+
+export default useAuthState;
diff --git a/src/lib/state mangment/LayoutPagestate.ts b/src/lib/state mangment/LayoutPagestate.ts
new file mode 100644
index 0000000..db1c4b5
--- /dev/null
+++ b/src/lib/state mangment/LayoutPagestate.ts
@@ -0,0 +1,29 @@
+import {create} from 'zustand'
+
+interface ModelState {
+ isOpenAddModel: boolean;
+ isOpenEditModel: boolean;
+ objectToEdit: any;
+ isThemChanged:any;
+ setThemChange :()=> void ;
+ setIsOpenAddModel: () => void;
+ setIsOpenEditModel: () => void;
+ CloseAllModal: () => void;
+ setObjectToEdit: (data: any) => void;
+}
+
+export const usePageState = create((set) => ({
+ isOpenAddModel: false,
+ isOpenEditModel: false,
+ objectToEdit: null,
+ isThemChanged:false,
+ setThemChange: () =>
+ set((state) => ({ isThemChanged: !state.isThemChanged })),
+ setIsOpenAddModel: () =>
+ set((state) => ({ isOpenAddModel: !state.isOpenAddModel })),
+ setIsOpenEditModel: () =>
+ set((state) => ({ isOpenEditModel: !state.isOpenEditModel })),
+ CloseAllModal: () =>
+ set((state) => ({ isOpenAddModel: false, isOpenEditModel: false })),
+ setObjectToEdit: (data) => set(() => ({ objectToEdit: data })),
+}));
diff --git a/src/lib/state mangment/Pages/Products.ts b/src/lib/state mangment/Pages/Products.ts
new file mode 100644
index 0000000..4679b15
--- /dev/null
+++ b/src/lib/state mangment/Pages/Products.ts
@@ -0,0 +1,15 @@
+import {create} from 'zustand'
+
+interface ProductState {
+ TapItems:any;
+ setTapItems :any ;
+ TapItemValues:any;
+ setTapItemValues :any ;
+}
+
+export const useProductVarianState = create((set) => ({
+ TapItems:[],
+ setTapItems: (data : any) => set(() => ({ TapItems: data })),
+ TapItemValues:[],
+ setTapItemValues: (data : any) => set(() => ({ TapItemValues: data })),
+}));
diff --git a/src/lib/state mangment/driver&customer/ModelState.tsx b/src/lib/state mangment/driver&customer/ModelState.tsx
new file mode 100644
index 0000000..4a4ee9c
--- /dev/null
+++ b/src/lib/state mangment/driver&customer/ModelState.tsx
@@ -0,0 +1,33 @@
+import {create} from 'zustand'
+
+interface ModalState {
+
+ isOpenBlock:boolean ,
+ isOpenGift:boolean ,
+ isOpenUnBlock:boolean ,
+
+ setIsopenBlock :() => void ,
+ setIsopenUnBlock :() => void ,
+ setIsopenGift :() => void ,
+
+ setObjectId :(data:number) => void ,
+ objectID:number
+
+}
+
+export const useCommonModelState = create((set) => ({
+
+ isOpenBlock:false,
+ isOpenGift:false,
+ isOpenUnBlock:false,
+
+ setIsopenBlock: () =>
+ set((state) => ({ isOpenBlock: !state.isOpenBlock })),
+ setIsopenUnBlock: () =>
+ set((state) => ({ isOpenUnBlock: !state.isOpenUnBlock })),
+ setIsopenGift: () =>
+ set((state) => ({ isOpenGift: !state.isOpenGift })),
+ setObjectId: (data:number) =>
+ set((state) => ({ objectID: data })),
+ objectID:0
+}));
diff --git a/src/translate/ar.json b/src/translate/ar.json
new file mode 100644
index 0000000..5764c22
--- /dev/null
+++ b/src/translate/ar.json
@@ -0,0 +1,191 @@
+{
+ "Ar": "عربي",
+ "En": "انكليزي",
+ "Arabic": "عربي",
+ "English": "إنجليزي",
+ "Chinese" : "الصينية",
+ "Login": "تسجيل الدخول",
+ "Welcome back, please login to your account.": "مرحبًا بك مرة أخرى ، يرجى تسجيل الدخول إلى حسابك.",
+ "Username": "اسم المستخدم",
+ "Password": "كلمة المرور",
+ "Sign in": "تسجيل الدخول",
+ "Point © 2022 | All Rights Reserved": "Point © 2022 |كل الحقوق محفوظة",
+ "unknown": "مجهول",
+ "super admin": "مشرف سوبر",
+ "admin": "مسؤل",
+ "Home": "الصفحة الرئيسية",
+ "example": "مثال",
+ "Log Out": "تسجيل خروج",
+ "Example": "مثال",
+ "Add": "اضافة",
+ "username": "اسم ",
+ "password": "كلمة المرور ",
+ "name": "اسم",
+ "email": "الحساب",
+ "cancel": "الغاء",
+ "edit": "تعديل",
+ "light": "وضع النهاري",
+ "dark": "وضع اليلي",
+ "Categories": "الفئات",
+ "Products": "المنتجات",
+ "Order": "الطلب",
+ "Coupon": "القسيمة",
+ "Slider": "الشريحة",
+ "Product_in_your_Application": "المنتج في تطبيقك",
+ "categories_in_your_Application": "الفئات في تطبيقك",
+ "Order_in_your_Application": "الطلب في تطبيقك",
+ "You_have": "لديك",
+ "January": "يناير",
+ "February": "فبراير",
+ "March": "مارس",
+ "April": "أبريل",
+ "London": "لندن",
+ "Paris": "باريس",
+ "New York": "نيويورك",
+ "Seoul": "سيول",
+ "image": "صورة",
+ "parent_id": "الأب",
+ "product_count": "عدد المنتجات",
+ "basicinfo": "معلومات أساسية",
+ "view_information": "عرض المعلومات",
+ "back": "العودة",
+ "price": "السعر",
+ "description": "الوصف",
+ "favorite": "المفضلة",
+ "main_photo": "الصورة الرئيسية",
+ "category_id": " الفئة",
+ "order_code": "رمز الطلب",
+ "status": "الحالة",
+ "total": "المجموع",
+ "order_id": " الطلب",
+ "customer_name": "اسم العميل",
+ "customer_phone_number": "رقم هاتف العميل",
+ "order_created_at": "تم إنشاء الطلب في",
+ "address": "عنوان",
+ "country": "بلد",
+ "note": "ملاحظة",
+ "categories": "الفئات",
+ "coupon": "القسيمة",
+ "orders": "الطلبات",
+ "products": "المنتجات",
+ "slider": "الشريحة",
+ "discount_type": "نوع الخصم",
+ "coupon_type": "نوع الكوبون",
+ "code": "الكود",
+ "coupon_value": "قيمة الكوبون",
+ "view_information_filed_fill_sucsessfully": "تم ملء حقل معلومات العرض بنجاح",
+ "category": "الفئة",
+ "View_information": "عرض المعلومات",
+ "VarianInfo": "معلومات المتغير",
+ "Base_info": "المعلومات الأساسية",
+ "name_ar": "الاسم بالعربية",
+ "name_en": "الاسم بالإنجليزية",
+ "name_cn": "الاسم بالصينية",
+ "description_ar": "الوصف بالعربية",
+ "description_en": "الوصف بالإنجليزية",
+ "description_cn": "الوصف بالصينية",
+ "upload_image": "تحميل الصورة",
+ "photo": "الصورة",
+ "admin_note": "ملاحظة الإدارة",
+ "state": "الحالة",
+ "title": "العنوان",
+ "totals": "المجموعات",
+ "delivery_fee": "رسوم التوصيل",
+ "overall_total": "المجموع الكلي",
+ "sub_total": "المجموع الفرعي",
+ "quantity": "الكمية",
+ "active_from_to": "النشاط من/إلى",
+ "maximum_number_of_uses": "الحد الأقصى لعدد الاستخدامات",
+ "minimum_total_to_order": "الحد الأدنى للإجمالي للطلب",
+ "maximum_number_of_uses_per_user": "الحد الأقصى لعدد الاستخدامات لكل مستخدم",
+ "product_item": "عنصر المنتج",
+ "categories_item": " عنصر الفئات",
+ "variables": "المتغيرات",
+ "Information": "المعلومات",
+ "key": "المفتاح",
+ "Description": "الوصف",
+ "Add Another Item": "إضافة عنصر آخر",
+ "images": "الصور",
+ "no_records": "لا توجد سجلات",
+ "Total": "المجموع",
+ "items": "عناصر",
+ "search": "بحث",
+ "required_name": "جميع حقول الاسم مطلوبة. في السمة",
+ "required_description": "جميع حقول الوصف مطلوبة. في السمة",
+ "required_main_photo": "حقل الصورة الرئيسية مطلوب في السمة",
+ "required_price": "حقل السعر مطلوب في السمة",
+ "required_type": "حقل النوع مطلوب. في السمة",
+ "required_image": "حقل الصورة مطلوب في القيمة",
+ "required_color": "يجب أن يكون حقل اللون قيمة هكس",
+ "required_text": "جميع حقول القيمة مطلوبة.",
+ "BasicInfo": "المعلومات الأساسية",
+ "attributes": "السمات",
+ "Add New Attribute": "إضافة سمة جديدة",
+ "Attribute": "السمة",
+ "Value": "القيمة",
+ "Add New Variant": "إضافة متغير جديد",
+ "variant": "متغير",
+ "unique_error_names": "اسم فريد لكل سمة مطلوب",
+ "deliviration_estimated_time": "الوقت المقدر للتسليم",
+ "delivery_link": "رابط التسليم",
+ "delete_are_you_sure": "هل أنت متأكد أنك تريد الحذف؟",
+ "yes_delete_it": "نعم، احذفه",
+ "notification": "إشعار",
+ "users": "المستخدمون",
+ "body": "جسم",
+ "body_en": "الجسم (الإنجليزية)",
+ "body_ar": "الجسم (العربية)",
+ "body_ce": "الجسم (الصينية)",
+ "title_en": "العنوان (الإنجليزية)",
+ "title_ar": "العنوان (العربية)",
+ "title_cn": "العنوان (الصينية)",
+ "avatar": "الصورة الرمزية",
+ "added_successful": "تمت الإضافة بنجاح",
+ "failed_to_add_data": "فشلت عملية الإضافة",
+ "deleted_successfully": "تم الحذف بنجاح",
+ "updated_successfully": "تم التحديث بنجاح",
+ "Product_Count_in_your_Application": "عدد المنتجات في تطبيقك",
+ "productCount": "عدد المنتجات",
+ "user_in_your_Application": "المستخدمون في تطبيقك",
+ "userCount": "عدد المستخدمين",
+ "orderCount": "عدد الطلبات",
+ "order_count_in_your_Application": "عدد الطلبات في تطبيقك",
+ "month": "شهر",
+ "sorry_only_user_can_change_his_status": "عذرًا، فقط المستخدم يمكنه تغيير حالته.",
+ "create_notification": "إنشاء إشعار",
+ "SupportMessages": "رسائل الدعم",
+ "whatsApp": "واتساب",
+ "subject": "الموضوع",
+ "message": "الرسالة",
+ "EditDetails": "تعديل التفاصيل",
+ "OrderItems": "عناصر الطلب",
+ "reset": "إعادة تعيين",
+ "submit": "إرسال",
+ "errorPage": {
+ "networkError": "خطأ في الشبكة",
+ "checkAndModify": "يرجى التحقق من الشبكة الخاصة بك وإعادة تحميل الصفحة.",
+ "refetch": "إعادة تحميل الصفحة",
+ "goToHome": "الانتقال إلى الصفحة الرئيسية"
+ },
+ "value_en": "القيمة (الإنجليزية)",
+ "value_ar": "القيمة (العربية)",
+ "value_de": "القيمة (الصينية)",
+ "type": "النوع",
+ "id": "المعرف",
+ "submite": "تقديم",
+ "PriceFrom": "السعر من",
+ "PriceTo": "السعر إلى",
+ "Pending Approve": "قيد الموافقة",
+ "Approved": "تمت الموافقة عليه",
+ "Rejected": "مرفوض",
+ "Pending Cancellation": "في انتظار الإلغاء",
+ "user_ids": "معرفات المستخدمين",
+ "user": "مستخدم",
+ "login_successful": "تسجيل الدخول ناجح",
+ "DateFrom": "تاريخ البدء",
+ "DateTo": "تاريخ الانتهاء"
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/translate/cn.json b/src/translate/cn.json
new file mode 100644
index 0000000..cbed66f
--- /dev/null
+++ b/src/translate/cn.json
@@ -0,0 +1,190 @@
+{
+ "Ar": "阿拉伯语",
+ "En": "英语",
+ "Arabic": "阿拉伯语",
+ "English": "英语",
+ "Chinese": "中文",
+ "Login": "登录",
+ "Welcome back, please login to your account.": "欢迎回来,请登录您的账户。",
+ "Username": "用户名",
+ "Password": "密码",
+ "Sign in": "登录",
+ "Point © 2022 | All Rights Reserved": "版权 © 2022 | 保留所有权利",
+ "unknown": "未知",
+ "super admin": "超级管理员",
+ "Home": "首页",
+ "example": "示例",
+ "Log Out": "登出",
+ "Example": "示例",
+ "Add": "添加",
+ "edit": "编辑",
+ "ligth": "浅色",
+ "dark": "深色",
+ "Categories": "分类",
+ "Products": "产品",
+ "Order": "订单",
+ "Coupon": "优惠券",
+ "Slider": "轮播图",
+ "Product_in_your_Application": "应用中的产品",
+ "categories_in_your_Application": "应用中的分类",
+ "Order_in_your_Application": "应用中的订单",
+ "You_have": "你有",
+ "January": "一月",
+ "February": "二月",
+ "March": "三月",
+ "April": "四月",
+ "London": "伦敦",
+ "Paris": "巴黎",
+ "New York": "纽约",
+ "Seoul": "首尔",
+ "name": "名称",
+ "image": "图片",
+ "parent_id": "父级",
+ "product_count": "产品数量",
+ "basicinfo": "基本信息",
+ "view_information": "查看信息",
+ "back": "返回",
+ "price": "价格",
+ "description": "描述",
+ "favorite": "收藏",
+ "main_photo": "主照片",
+ "category_id": "分类",
+ "order_code": "订单代码",
+ "email": "电子邮件",
+ "status": "状态",
+ "total": "总计",
+ "order_id": "订单",
+ "customer_name": "客户姓名",
+ "customer_phone_number": "客户电话号码",
+ "order_created_at": "订单创建于",
+ "address": "地址",
+ "country": "国家",
+ "note": "备注",
+ "categories": "分类",
+ "coupon": "优惠券",
+ "orders": "订单",
+ "products": "产品",
+ "slider": "轮播图",
+ "discount_type": "折扣类型",
+ "coupon_type": "优惠券类型",
+ "code": "代码",
+ "coupon_value": "优惠券价值",
+ "view_information_filed_fill_sucsessfully": "查看信息字段填写成功",
+ "category": "分类",
+ "View_information": "查看信息",
+ "VarianInfo": "变体信息",
+ "Base_info": "基本信息",
+ "name_ar": "名称(阿拉伯语)",
+ "name_en": "名称(英语)",
+ "name_cn": "名称(中文)",
+ "description_ar": "描述(阿拉伯语)",
+ "description_en": "描述(英语)",
+ "description_cn": "描述(中文)",
+ "upload_image": "上传图片",
+ "images": "图片",
+ "photo": "照片",
+ "admin_note": "管理员备注",
+ "state": "状态",
+ "title": "标题",
+ "totals": "总计",
+ "delivery_fee": "配送费",
+ "overall_total": "总计",
+ "sub_total": "小计",
+ "quantity": "数量",
+ "active_from_to": "活动时间",
+ "maximum_number_of_uses": "最大使用次数",
+ "minimum_total_to_order": "最低订购总额",
+ "maximum_number_of_uses_per_user": "每用户最大使用次数",
+ "product_item": "产品项",
+ "categories_item": "分类项",
+ "variables": "变量",
+ "Information": "信息",
+ "key": "键",
+ "Description": "描述",
+ "Add Another Item": "添加另一项",
+ "no_records": "没有记录",
+ "Total": "总计",
+ "items": "项目",
+ "search": "搜索",
+ "required_name": "所有名称字段为必填。属性中",
+ "required_description": "所有描述字段为必填。属性中",
+ "required_main_photo": "主照片字段为必填。属性中",
+ "required_price": "价格字段为必填。属性中",
+ "required_type": "类型字段为必填。属性中",
+ "required_image": "图片字段为必填。值中",
+ "required_color": "颜色字段必须是十六进制值",
+ "required_text": "所有值字段为必填。",
+ "BasicInfo": "基本信息",
+ "attributes": "属性",
+ "Add New Attribute": "添加新属性",
+ "Attribute": "属性",
+ "Value": "值",
+ "Items": "项目",
+ "Search": "搜索",
+ "Basicinfo": "基本信息",
+ "Attributes": "属性",
+ "Add New Variant": "添加新变体",
+ "variant": "变体",
+ "unique_error_names": "每个属性需要唯一名称",
+ "deliviration_estimated_time": "预计送达时间",
+ "delivery_link": "配送链接",
+ "delete_are_you_sure": "确定要删除吗?",
+ "yes_delete_it": "是的,删除它",
+ "cancel": "取消",
+ "required_error": "必填错误",
+ "notification": "通知",
+ "users": "用户",
+ "body": "正文",
+ "body_en": "正文(英语)",
+ "body_ar": "正文(阿拉伯语)",
+ "body_ce": "正文(中文)",
+ "title_en": "标题(英语)",
+ "title_ar": "标题(阿拉伯语)",
+ "title_cn": "标题(中文)",
+ "avatar": "头像",
+ "added_successful": "添加成功",
+ "failed_to_add_data": "添加数据失败",
+ "deleted_successfully": "删除成功",
+ "updated_successfully": "更新成功",
+ "Product_Count_in_your_Application": "应用中的产品数量",
+ "productCount": "产品数量",
+ "user_in_your_Application": "应用中的用户",
+ "userCount": "用户数量",
+ "orderCount": "订单数量",
+ "order_count_in_your_Application": "应用中的订单数量",
+ "month": "月份",
+ "sorry_only_user_can_change_his_status": "抱歉,只有用户可以更改其状态。",
+ "create_notification": "创建通知",
+ "SupportMessages": "支持消息",
+ "whatsApp": "WhatsApp",
+ "subject": "主题",
+ "message": "信息",
+ "EditDetails": "编辑详情",
+ "OrderItems": "订单项",
+ "reset": "重置",
+ "submit": "提交",
+ "errorPage": {
+ "networkError": "网络错误",
+ "checkAndModify": "请检查您的网络并重新加载页面。",
+ "refetch": "重新加载页面",
+ "goToHome": "回到首页"
+ },
+ "value_en": "值(英语)",
+ "value_ar": "值(阿拉伯语)",
+ "value_de": "值(中文)",
+ "type": "类型",
+ "id": "ID",
+ "submite": "提交",
+ "PriceFrom": "价格从",
+ "PriceTo": "价格到",
+ "Pending Approve": "待批准",
+ "Approved": "已批准",
+ "Rejected": "已拒绝",
+ "Pending Cancellation": "待取消",
+ "user_ids": "用户 ID",
+ "user": "用户",
+ "login_successful": "登录成功",
+ "DateFrom": "日期从",
+ "DateTo": "日期到"
+ }
+
\ No newline at end of file
diff --git a/src/translate/en.json b/src/translate/en.json
new file mode 100644
index 0000000..b6228ff
--- /dev/null
+++ b/src/translate/en.json
@@ -0,0 +1,191 @@
+{
+ "Ar": "Ar",
+ "En": "En",
+ "Arabic": "Arabic",
+ "English": "English",
+ "Chinese": "Chinese",
+ "Login": "Login",
+ "Welcome back, please login to your account.": "Welcome back, please login to your account.",
+ "Username": "Username",
+ "Password": "Password",
+ "Sign in": "Sign in",
+ "Point © 2022 | All Rights Reserved": "Point © 2022 | All Rights Reserved",
+ "unknown": "Unknown",
+ "super admin": "Super admin",
+ "Home": "Home",
+ "example": "Example",
+ "Log Out": "Log Out",
+ "Example": "Example",
+ "Add": "Add",
+ "edit": "Edit",
+ "ligth": "Light",
+ "dark": "Dark",
+ "Categories": "Categories",
+ "Products": "Products",
+ "Order": "Order",
+ "Coupon": "Coupon",
+ "Slider": "Slider",
+ "Product_in_your_Application": "Product in your Application",
+ "categories_in_your_Application": "Categories in your Application",
+ "Order_in_your_Application": "Order in your Application",
+ "You_have": "You have",
+ "January": "January",
+ "February": "February",
+ "March": "March",
+ "April": "April",
+ "London": "London",
+ "Paris": "Paris",
+ "New York": "New York",
+ "Seoul": "Seoul",
+ "name": "Name",
+ "image": "Image",
+ "parent_id": "Parent",
+ "product_count": "Product count",
+ "basicinfo": "Basic info",
+ "view_information": "View information",
+ "back": "Back",
+ "price": "Price",
+ "description": "Description",
+ "favorite": "Favorite",
+ "main_photo": "Main photo",
+ "category_id": "Category",
+ "order_code": "Order code",
+ "email": "Email",
+ "status": "Status",
+ "total": "Total",
+ "order_id": "Order",
+ "customer_name": "Customer name",
+ "customer_phone_number": "Customer phone number",
+ "order_created_at": "Order created at",
+ "address": "Address",
+ "country": "Country",
+ "note": "Note",
+ "categories": "Categories",
+ "coupon": "Coupon",
+ "orders": "Orders",
+ "products": "Products",
+ "slider": "Slider",
+ "discount_type": "Discount Type",
+ "coupon_type": "Coupon Type",
+ "code": "Code",
+ "coupon_value": "Coupon Value",
+ "view_information_filed_fill_sucsessfully": "View information field filled successfully",
+ "category": "Category",
+ "View_information": "View Information",
+ "VarianInfo": "Variant Information",
+ "Base_info": "Base Information",
+ "name_ar": "Name (Arabic)",
+ "name_en": "Name (English)",
+ "name_cn": "Name (Chinese)",
+ "description_ar": "Description (Arabic)",
+ "description_en": "Description (English)",
+ "description_cn": "Description (Chinese)",
+ "upload_image": "Upload Image",
+ "images": "Images",
+ "photo": "Photo",
+ "admin_note": "Admin Note",
+ "state": "State",
+ "title": "Title",
+ "totals": "Totals",
+ "delivery_fee": "Delivery Fee",
+ "overall_total": "Overall Total",
+ "sub_total": "Sub Total",
+ "quantity": "Quantity",
+ "active_from_to": "Active From/To",
+ "maximum_number_of_uses": "Maximum Number of Uses",
+ "minimum_total_to_order": "Minimum Total to Order",
+ "maximum_number_of_uses_per_user": "Maximum Number of Uses Per User",
+ "product_item": "Product Item",
+ "categories_item": "Categories Item",
+ "variables": "Variables",
+ "Information": "Information",
+ "key": "Key",
+ "Description": "Description",
+ "Add Another Item": "Add Another Item",
+ "no_records": "No records",
+ "Total": "Total",
+ "items": "Items",
+ "search": "Search",
+ "required_name": "All Name Fields Are Required. in attribute",
+ "required_description": "All Description Fields Are Required. in attribute",
+ "required_main_photo": "Main Photo Field Is Required in attribute",
+ "required_price": "Price Field Is Required in attribute",
+ "required_type": "Type Field Is Required. in attribute",
+ "required_image": "Image Field Is Required in Value",
+ "required_color": "Color Field Must be a Hex value",
+ "required_text": "All value Fields Are Required.",
+ "BasicInfo": "Basic Info",
+ "attributes": "Attributes",
+ "Add New Attribute": "Add New Attribute",
+ "Attribute": "Attribute",
+ "Value": "Value",
+ "Items": "Items",
+ "Search": "Search",
+ "Basicinfo": "Basic Info",
+ "Attributes": "Attributes",
+ "Add New Variant": "Add New Variant",
+ "variant": "Variant",
+ "unique_error_names": "Unique name for each attribute is required",
+ "deliviration_estimated_time": "Delivery Estimated Time",
+ "delivery_link": "Delivery Link",
+ "delete_are_you_sure": "Are you sure you want to delete?",
+ "yes_delete_it": "Yes, delete it",
+ "cancel": "Cancel",
+ "required_error":"required_error",
+ "notification": "Notification",
+ "users": "Users",
+ "body": "Body",
+ "body_en": "Body (English)",
+ "body_ar": "Body (Arabic)",
+ "body_ce": "Body (Chinese)",
+ "title_en": "Title (English)",
+ "title_ar": "Title (Arabic)",
+ "title_cn": "Title (Chinese)",
+ "avatar": "Avatar",
+ "added_successful": "Added successful",
+ "failed_to_add_data": "Failed to add data",
+ "deleted_successfully": "Deleted successfully",
+ "updated_successfully": "updated successfully",
+ "Product_Count_in_your_Application": "Number of Products in Your Application",
+ "productCount": "Count of Products",
+ "user_in_your_Application": "Users in Your Application",
+ "userCount": "Count of Users",
+ "orderCount": "Count of Orders",
+ "order_count_in_your_Application": "Number of Orders in Your Application",
+ "month": "Month",
+ "sorry_only_user_can_change_his_status": "Sorry, only the user can change their status.",
+ "create_notification": "Create Notification",
+ "SupportMessages": "Support Messages",
+ "whatsApp": "WhatsApp",
+ "subject": "Subject",
+ "message": "Message",
+ "EditDetails": "Edit Details",
+ "OrderItems": "Order Items",
+ "reset": "Reset",
+ "submit": "Submit",
+ "errorPage": {
+ "networkError": "Network Error",
+ "checkAndModify": "Please check your network and refetch the page.",
+ "refetch": "Refetch Page",
+ "goToHome": "Go to Home"
+ }
+,
+"value_en": "Value (English)",
+"value_ar": "Value (Arabic)",
+"value_de": "Value (Chinese)",
+"type": "Type",
+"id": "ID",
+"submite": "Submit",
+"PriceFrom": "Price From",
+"PriceTo": "Price To",
+"Pending Approve": "Pending Approve",
+"Approved": "Approved",
+"Rejected": "Rejected",
+"Pending Cancellation": "Pending Cancellation",
+"user_ids": "User IDs",
+ "user": "User",
+ "login_successful": "Login successful",
+ "DateFrom": "Date From",
+ "DateTo": "Date To"
+
+}
\ No newline at end of file
diff --git a/src/translate/text b/src/translate/text
new file mode 100644
index 0000000..928285b
--- /dev/null
+++ b/src/translate/text
@@ -0,0 +1,11 @@
+value_en
+value_ar
+value_de
+type
+id
+
+submite
+PriceFrom
+PriceTo
+PriceFrom
+PriceTo
\ No newline at end of file
diff --git a/src/types/SocketEvent.ts b/src/types/SocketEvent.ts
new file mode 100644
index 0000000..55b613f
--- /dev/null
+++ b/src/types/SocketEvent.ts
@@ -0,0 +1,8 @@
+
+
+export interface SocketDashboardDebugDataEvent {
+ event :string ,
+ socket_id :string ,
+ room ?: string | null ,
+ data : any
+}
\ No newline at end of file
diff --git a/src/types/User.ts b/src/types/User.ts
new file mode 100644
index 0000000..e8c3788
--- /dev/null
+++ b/src/types/User.ts
@@ -0,0 +1,21 @@
+
+export interface IUser{
+ "id": number ,
+ "full_name": string ,
+ "email": string ,
+ "phone": string ,
+ "is_verified": number,
+ "created_at": string ,
+ "updated_at": string,
+ "stripe_id": any,
+ "pm_type": any,
+ "pm_last_four": any,
+ "trial_ends_at": any,
+ "user_uid": any,
+ "sms_code": any,
+ "messageID": any,
+ "insurance_amount": number ,
+ "insurance_payment_id": any,
+ "role_type": string
+
+}
\ No newline at end of file
diff --git a/src/utils/Array/ArrayToObjectFormik.ts b/src/utils/Array/ArrayToObjectFormik.ts
new file mode 100644
index 0000000..5c11d90
--- /dev/null
+++ b/src/utils/Array/ArrayToObjectFormik.ts
@@ -0,0 +1,26 @@
+export function objectToArray(obj:any) {
+ // Initialize an empty array to store the result
+ const result = [] as any ;
+
+ // Iterate over the keys of the object
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ // Extract the index from the key
+ const index = parseInt(key.split('.')[0]);
+
+ // Get the attribute name from the key
+ const attributeName = key.split('.')[1];
+
+ // If the index does not exist in the result array, create a new object
+ if (!result[index]) {
+ result[index] = {};
+ }
+
+
+ // Set the attribute value in the result object
+ result[index][attributeName] = obj[key];
+ }
+ }
+
+ return result;
+ }
\ No newline at end of file
diff --git a/src/utils/Array/changeShapeInfo.tsx b/src/utils/Array/changeShapeInfo.tsx
new file mode 100644
index 0000000..0908eb2
--- /dev/null
+++ b/src/utils/Array/changeShapeInfo.tsx
@@ -0,0 +1,14 @@
+export function changeShapeInfo(originalObject: any) {
+ const transformedObject: any = {};
+
+ for (const key in originalObject) {
+ if (originalObject.hasOwnProperty(key)) {
+ const index = key.split('.')[0]; // Extract index from key
+ const attribute = key.split('.')[1]; // Extract attribute from key
+
+ transformedObject[originalObject[`${index}.key`]] = originalObject[`${index}.Description`];
+ }
+ }
+
+ return transformedObject
+}
\ No newline at end of file
diff --git a/src/utils/Array/filterUndefinedAndEmpty.ts b/src/utils/Array/filterUndefinedAndEmpty.ts
new file mode 100644
index 0000000..c0982ce
--- /dev/null
+++ b/src/utils/Array/filterUndefinedAndEmpty.ts
@@ -0,0 +1,5 @@
+const filterUndefinedAndEmpty = (array:any) => {
+ return array?.filter((data:any) => data !== undefined && Object.keys(data).length !== 0);
+ };
+
+ export default filterUndefinedAndEmpty;
\ No newline at end of file
diff --git a/src/utils/Array/getInfoKeyAndDescriptions.ts b/src/utils/Array/getInfoKeyAndDescriptions.ts
new file mode 100644
index 0000000..8ff163f
--- /dev/null
+++ b/src/utils/Array/getInfoKeyAndDescriptions.ts
@@ -0,0 +1,12 @@
+export function convertToObject(obj:any) {
+ const newObj = {} as any;
+ for (const key in obj) {
+ if (key.includes('.Key')) {
+ const newKey = obj[key];
+ const valueKey = key.replace('.Key', '.Description');
+ const value = obj[valueKey];
+ newObj[newKey] = value;
+ }
+ }
+ return newObj;
+}
\ No newline at end of file
diff --git a/src/utils/Date/ChangeFormat.ts b/src/utils/Date/ChangeFormat.ts
new file mode 100644
index 0000000..311775f
--- /dev/null
+++ b/src/utils/Date/ChangeFormat.ts
@@ -0,0 +1,5 @@
+export function ChangeformatDate(dateString:string) {
+ const dateObject = new Date(dateString);
+ const formattedDate = `${dateObject.toLocaleDateString()} ${dateObject.toLocaleTimeString()}`;
+ return formattedDate;
+}
\ No newline at end of file
diff --git a/src/utils/colors/getPrimaryColor.ts b/src/utils/colors/getPrimaryColor.ts
new file mode 100644
index 0000000..4f86548
--- /dev/null
+++ b/src/utils/colors/getPrimaryColor.ts
@@ -0,0 +1,3 @@
+
+
+export const getPrimaryColor = ()=>getComputedStyle(document.querySelector(':root')as any )?.getPropertyValue('--primary')
diff --git a/src/utils/language/mapTranslatedProperties.tsx b/src/utils/language/mapTranslatedProperties.tsx
new file mode 100644
index 0000000..e91b645
--- /dev/null
+++ b/src/utils/language/mapTranslatedProperties.tsx
@@ -0,0 +1,27 @@
+export const mapTranslatedProperties = (
+ arrayOfDetails :any,
+ properties:any ,
+ language_id : '1'|'2' |1 |2
+ ) => {
+ if (!arrayOfDetails || !properties || !language_id) return "";
+ if (Array.isArray(arrayOfDetails) && arrayOfDetails.length === 0) return "";
+
+ const target = arrayOfDetails.find(
+ (item:any) => item.language_id === language_id
+ );
+ if (!target) {
+ return "";
+ }
+
+ if (!Array.isArray(properties)) {
+ return target[properties];
+ }
+
+ // [prop1, prop2, prop3, ....] is passed
+ const ret:any = {};
+ properties.forEach((prop:string) => {
+ ret[prop] = target[prop];
+ });
+ return ret;
+ };
+
\ No newline at end of file
diff --git a/src/zustand/Modal.ts b/src/zustand/Modal.ts
new file mode 100644
index 0000000..97cd522
--- /dev/null
+++ b/src/zustand/Modal.ts
@@ -0,0 +1,15 @@
+import {create} from 'zustand'
+
+interface ModalState {
+ isOpen: boolean;
+ setIsOpen: (value:boolean) => void;
+
+}
+
+
+export const useModalState = create((set) => ({
+ isOpen: false,
+ setIsOpen: () =>
+ set((state) => ({ isOpen: !state.isOpen })),
+
+}));
diff --git a/src/zustand/OrderFillter.ts b/src/zustand/OrderFillter.ts
new file mode 100644
index 0000000..6c6e6bf
--- /dev/null
+++ b/src/zustand/OrderFillter.ts
@@ -0,0 +1,50 @@
+import { create } from 'zustand';
+
+interface ModalState {
+ username: string | null;
+ coupon: string | null;
+ state: string | null;
+ fromDate: Date | null;
+ toDate: Date | null;
+ totalFrom: number | null;
+ totalTo: number | null;
+ setUsername: (value: string | null) => void;
+ setCoupon: (value: string | null) => void;
+ setstate: (value: string | null) => void;
+ setFromDate: (value: Date | null) => void;
+ setToDate: (value: Date | null) => void;
+ setTotalFrom: (value: number | null) => void;
+ setTotalTo: (value: number | null) => void;
+ reset: () => void;
+}
+
+export const useOrderFillterState = create((set) => {
+
+ return {
+ username: null,
+ coupon: null,
+ state: null,
+ fromDate: null,
+ toDate: null,
+ totalFrom: null,
+ totalTo: null,
+ setUsername: (value) => set((state) => ({ ...state, username: value })),
+ setCoupon: (value) => set((state) => ({ ...state, coupon: value })),
+ setstate: (value) => set((state) => ({ ...state, state: value })),
+ setFromDate: (value) => set((state) => ({ ...state, fromDate: value })),
+ setToDate: (value) => set((state) => ({ ...state, toDate: value })),
+ setTotalFrom: (value) => set((state) => ({ ...state, totalFrom: value })),
+ setTotalTo: (value) => set((state) => ({ ...state, totalTo: value })),
+ reset: () => {
+ set({
+ username: null,
+ coupon: null,
+ fromDate: null,
+ toDate: null,
+ totalFrom: null,
+ totalTo: null,
+ state:null
+ });
+ },
+ };
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..a273b0c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "src"
+ ]
+}