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..ebc5ec0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cSpell.words": [ + "aldeen", + "antd", + "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/index.html b/index.html new file mode 100644 index 0000000..8168fa3 --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + + E-Menu - App + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..d244094 --- /dev/null +++ b/package.json @@ -0,0 +1,72 @@ +{ + "name": "my-app", + "version": "0.1.0", + "private": true, + "dependencies": { + "@ant-design/icons": "^4.8.3", + "@types/node": "^16.18.97", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "antd": "^5.17.4", + "apexcharts": "^3.49.1", + "axios": "^1.7.2", + "bootstrap": "^5.3.3", + "chart.js": "^4.4.3", + "dayjs": "^1.11.11", + "formik": "^2.4.6", + "i18next": "^23.11.5", + "leaflet": "^1.9.4", + "react": "^18.3.1", + "react-apexcharts": "^1.4.1", + "react-bootstrap": "^2.10.2", + "react-bootstrap-sweetalert": "^5.2.0", + "react-confirm-alert": "^3.0.6", + "react-data-table-component": "^7.6.2", + "react-dom": "^18.3.1", + "react-i18next": "^13.5.0", + "react-icons": "^4.12.0", + "react-leaflet": "^4.2.1", + "react-number-format": "^5.4.0", + "react-player": "^2.16.0", + "react-query": "^3.39.3", + "react-router-dom": "^6.23.1", + "react-select": "^5.8.0", + "react-tabs": "^6.0.2", + "react-toastify": "^9.1.3", + "reactstrap": "^9.2.2", + "sass": "^1.77.4", + "typescript": "^4.9.5", + "web-vitals": "^2.1.4", + "xlsx": "^0.18.5", + "yup": "^1.4.0", + "zustand": "^4.5.2" + }, + "scripts": { + "start": "vite", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "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/leaflet": "^1.9.12", + "@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/Layout/images.png b/public/Layout/images.png new file mode 100644 index 0000000..a283724 Binary files /dev/null and b/public/Layout/images.png differ diff --git a/public/Logo.png b/public/Logo.png new file mode 100644 index 0000000..56736b1 Binary files /dev/null and b/public/Logo.png differ 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/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/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/public/test.mp4 b/public/test.mp4 new file mode 100644 index 0000000..ed139d6 Binary files /dev/null and b/public/test.mp4 differ 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..815ed9c --- /dev/null +++ b/src/Components/Columns/ColumnsImage.tsx @@ -0,0 +1,85 @@ +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; + + const handleError = useImageError; + // or you can download flipped and rotated image + // https://codesandbox.io/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 ( +
+ {props.alt} + + {props.alt} + +
+ ); +}; + +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 ( +
+ + {value}% + +
+ ); +}; + + +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..cce85cc --- /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/statemangment/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..f12aa9d --- /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/statemangment/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: blockvalue }); + } + }; + + 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..ace3d1d --- /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/statemangment/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 && enterCodesvalue) { + Mutation.mutate({ type, id: objectID, value: enterCodesvalue }); + } + }; + + 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..41cd9f3 --- /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/statemangment/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 ( +
+ + {(What_the_Theme === "light") ? + <> + + {t("light")} + + + : (What_the_Theme === "dark") ? + <> + + {t("dark")} + + : + <> + + {t("glass")} + + } + } transition> + changeTheme('light')}> + + {t("light")} + + changeTheme('dark')}> + + {t("dark")} + changeTheme('glass')}> + + {t("glass")} + + +
+ ); +} 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..85a8e3c --- /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/statemangment/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..53a29d4 --- /dev/null +++ b/src/Components/ValidationField/Ui/KarimSpinner.tsx @@ -0,0 +1,24 @@ +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..9dc4900 --- /dev/null +++ b/src/Components/ValidationField/Ui/SearchBar.scss @@ -0,0 +1,37 @@ +.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: 0.4; + } + + .icon { + position: absolute; + left: 1rem; + fill: var(--subtext); + width: 1rem; + height: 1rem; + } +} diff --git a/src/Components/ValidationField/Ui/SearchBar.tsx b/src/Components/ValidationField/Ui/SearchBar.tsx new file mode 100644 index 0000000..46d2566 --- /dev/null +++ b/src/Components/ValidationField/Ui/SearchBar.tsx @@ -0,0 +1,45 @@ +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}`, { replace: true }); + }; + + return ( +
+
+ + +
+
+ ); +}; + +export default SearchBar; diff --git a/src/Components/ValidationField/ValidationField.tsx b/src/Components/ValidationField/ValidationField.tsx new file mode 100644 index 0000000..7d3cc79 --- /dev/null +++ b/src/Components/ValidationField/ValidationField.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import "./utils/ValidationField.scss"; +import { + Date, + Time, + File, + DataRange, + SelectField, + Default, + CheckboxField, + MaltyFile, + SearchField, + TextField, + DropFile, +} from "./View"; +import { ValidationFieldProps, ValidationFieldType } from "./utils/types"; +import LocalSearchField from "./View/LocalSearch"; +import NumberFormate from "./View/NumberFormate"; + +const components: { [key: string]: React.FC } = { + Select: SelectField, + Search: SearchField, + LocalSearch: LocalSearchField, + DataRange: DataRange, + TextArea: TextField, + Date: Date, + Time: Time, + File: File, + DropFile: DropFile, + MaltyFile: MaltyFile, + Checkbox: CheckboxField, + NumberFormate: NumberFormate, +}; + +const ValidationField: React.FC = React.memo( + ({ type, ...otherProps }) => { + const Component = components[type as ValidationFieldType]; + + if (!Component) { + return ; + } + return ; + }, +); + +export default ValidationField; diff --git a/src/Components/ValidationField/View/CheckboxField.tsx b/src/Components/ValidationField/View/CheckboxField.tsx new file mode 100644 index 0000000..2550354 --- /dev/null +++ b/src/Components/ValidationField/View/CheckboxField.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { Checkbox, Form } from "antd"; + +const CheckboxField = ({ + name, + label, + isDisabled, + onChange, + Group, + className, + props, + no_label, + label_icon, +}: 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; diff --git a/src/Components/ValidationField/View/DataRange.tsx b/src/Components/ValidationField/View/DataRange.tsx new file mode 100644 index 0000000..17723a3 --- /dev/null +++ b/src/Components/ValidationField/View/DataRange.tsx @@ -0,0 +1,64 @@ +import { Form, DatePicker } from "antd"; + +import React from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { MdOutlineEdit } from "react-icons/md"; + +const { RangePicker } = DatePicker; + +const DataRange = ({ + name, + label, + Format, + props, + onChange, + isDisabled, + placeholder, + className, + no_label, + label_icon, +}: any) => { + const { errorMsg, isError, t, formik } = useFormField(name, props); + const onCalendarChange = (value: any) => { + formik.setFieldValue(name, value); + }; + return ( +
+ {no_label ? ( + + ) : label_icon ? ( +
+ + +
+ ) : ( + + )} + + + + +
+ ); +}; + +export default DataRange; diff --git a/src/Components/ValidationField/View/Date.tsx b/src/Components/ValidationField/View/Date.tsx new file mode 100644 index 0000000..a285abc --- /dev/null +++ b/src/Components/ValidationField/View/Date.tsx @@ -0,0 +1,69 @@ +import { Form, DatePicker } from "antd"; +import React from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { MdOutlineEdit } from "react-icons/md"; +import dayjs from "dayjs"; + +const Date = ({ + name, + label, + picker = "date", + isDisabled, + props, + onChange, + placeholder, + className, + no_label, + label_icon, +}: any) => { + const { errorMsg, isError, t, formik } = useFormField(name, props); + + const FormikValue = formik.values[name]; + const onCalendarChange = (value: any) => { + formik.setFieldValue(name, value); + // console.log(value,"value "); + }; + + const Formater = "YYYY/MM/DD"; + return ( +
+ {no_label ? ( + + ) : label_icon ? ( +
+ + +
+ ) : ( + + )} + + + + {/* */} + +
+ ); +}; + +export default Date; diff --git a/src/Components/ValidationField/View/Default.tsx b/src/Components/ValidationField/View/Default.tsx new file mode 100644 index 0000000..d14e28a --- /dev/null +++ b/src/Components/ValidationField/View/Default.tsx @@ -0,0 +1,62 @@ +import { Form, Input } from "antd"; +import React from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { MdOutlineEdit } from "react-icons/md"; +import { Field } from "formik"; + +const Default = ({ + name, + label, + placeholder, + isDisabled, + onChange, + type, + no_label, + label_icon, + ...props +}: any) => { + const { errorMsg, isError, t } = useFormField(name, props); + + return ( +
+ {no_label ? ( + + ) : label_icon ? ( +
+ + +
+ ) : ( + + )} + + + + +
+ ); +}; + +export default React.memo(Default); diff --git a/src/Components/ValidationField/View/DropFile.tsx.tsx b/src/Components/ValidationField/View/DropFile.tsx.tsx new file mode 100644 index 0000000..3a86955 --- /dev/null +++ b/src/Components/ValidationField/View/DropFile.tsx.tsx @@ -0,0 +1,93 @@ +import React, { useEffect, useState } from "react"; +import { LoadingOutlined, PlusOutlined } from "@ant-design/icons"; +import { message, Upload } from "antd"; +import type { GetProp, UploadProps } from "antd"; +import useFormField from "../../../Hooks/useFormField"; +import { ImageBaseURL } from "../../../api/config"; + +type FileType = Parameters>[0]; + +const DropFile = ({ + name, + label, + onChange, + isDisabled, + placholder, + className, + props, + no_label, + label_icon, +}: any) => { + const { formik, t, isError } = useFormField(name, props); + let FormikName = formik?.values[name]; + + const FormikValue = + typeof FormikName === "string" + ? ImageBaseURL + FormikName + : FormikName instanceof File + ? URL.createObjectURL(FormikName) + : ""; + + const [imageUrl, setImageUrl] = useState(FormikValue ?? ""); + + useEffect(() => { + setImageUrl(FormikName); + }, [FormikName]); + + const getBase64 = (img: FileType, callback: (url: string) => void) => { + const reader = new FileReader(); + reader.addEventListener("load", () => callback(reader.result as string)); + reader.readAsDataURL(img); + }; + + const handleChange: UploadProps["onChange"] = (info) => { + if (info.file.status === "done") { + getBase64(info.file.originFileObj as FileType, (url) => { + setImageUrl(url); + }); + } + formik.setFieldValue(name, info.file.originFileObj); + }; + const customRequest = async ({ onSuccess }: any) => { + onSuccess(); + }; + const uploadButton = ( + + ); + + return ( +
+ + + {imageUrl ? ( + + ) : ( + uploadButton + )} + +
+ ); +}; + +export default DropFile; diff --git a/src/Components/ValidationField/View/File.tsx b/src/Components/ValidationField/View/File.tsx new file mode 100644 index 0000000..de0538d --- /dev/null +++ b/src/Components/ValidationField/View/File.tsx @@ -0,0 +1,80 @@ +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, + acceptedFileType, + props, +}: any) => { + const { formik, t, isError } = useFormField(name, props); + let imageUrl = formik?.values?.[name] ?? null; + // console.log(ImageBaseURL + imageUrl); + + const fileList: UploadFile[] = imageUrl + ? [ + typeof imageUrl === "string" + ? { + uid: "-1", + name: "", + status: "done", + url: ImageBaseURL + imageUrl, + thumbUrl: ImageBaseURL + imageUrl, + } + : { + uid: imageUrl.uid || "-1", + name: imageUrl.name || "", + status: "done", + originFileObj: imageUrl, + }, + ] + : []; + // console.log(1); + + const FilehandleChange = (value: any) => { + // console.log(value,"filevalue"); + if (value.fileList.length === 0) { + formik.setFieldValue(name, null); + } else { + formik.setFieldValue(name, value?.file?.originFileObj); + } + }; + const customRequest = async ({ onSuccess, no_label, label_icon }: any) => { + onSuccess(); + }; + return ( +
+ + + + +
{isError ? "required" : ""}
+
+
+ ); +}; + +export default File; diff --git a/src/Components/ValidationField/View/LocalSearch.tsx b/src/Components/ValidationField/View/LocalSearch.tsx new file mode 100644 index 0000000..382f570 --- /dev/null +++ b/src/Components/ValidationField/View/LocalSearch.tsx @@ -0,0 +1,93 @@ +import { Form, Select } from "antd"; +import React, { useState } from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { MdOutlineEdit } from "react-icons/md"; +import { translateOptions } from "../utils/translatedOptions"; + +const LocalSelectField = ({ + name, + label, + placeholder, + isDisabled, + option, + isMulti, + onChange, + className, + props, + no_label, + label_icon, +}: any) => { + const { errorMsg, isError, t, formik } = useFormField(name, props); + + // State to manage the search input value + const [searchValue, setSearchValue] = useState(""); + + const handleSearch = ( + input: string, + option: { value: string; label: React.ReactNode | undefined }, + ) => + option?.label?.toString().toLowerCase().includes(toLowerCase()) || + option?.value?.toString().toLowerCase().includes(toLowerCase()); + + const SelectableChange = (value: { + value: string; + label: React.ReactNode; + }) => { + formik.setFieldValue(name, value); + }; + + const handleSelectChange = (value: any) => { + formik.setFieldValue(name, value); + if (onChange) onChange(value); + }; + + const handleSearchChange = (input: string) => { + setSearchValue(input); // Update the search input value + }; + + return ( +
+ {no_label ? ( + + ) : label_icon ? ( +
+ + +
+ ) : ( + + )} + + + +
+ ); +}; + +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..880a9ed --- /dev/null +++ b/src/Components/ValidationField/View/SelectField.tsx @@ -0,0 +1,72 @@ +import { Form, Select } from "antd"; +import React from "react"; +import useFormField from "../../../Hooks/useFormField"; +import { MdOutlineEdit } from "react-icons/md"; +import { translateOptions } from "../utils/translatedOptions"; + +const SelectField = ({ + name, + label, + placeholder, + isDisabled, + option, + isMulti, + onChange, + className, + props, + no_label, + label_icon, +}: any) => { + const { errorMsg, isError, t, formik } = useFormField(name, props); + const SelectableChange = (value: { + value: string; + label: React.ReactNode; + }) => { + formik.setFieldValue(name, value); + }; + // console.log(name,"Select"); + + return ( +
+ {no_label ? ( + + ) : label_icon ? ( +
+ + +
+ ) : ( + + )} + + + +
+ ); +}; + +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 ( +
+ handleChangeKey(e, index)} + value={group.key} + size="large" + style={{ width: "100%" }} + allowClear + disabled={only_value} + /> + handleChangeValue(e, index)} + value={group.value} + size="large" + style={{ width: "100%" }} + allowClear + + /> +
+ ))} + {!isStatic && +
+ + +
+ } + + + ); +}; + +export default FieldGroup; diff --git a/src/Pages/Setting/Map.tsx b/src/Pages/Setting/Map.tsx new file mode 100644 index 0000000..86febcb --- /dev/null +++ b/src/Pages/Setting/Map.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import 'leaflet/dist/leaflet.css'; + +const Map = ({lat,long}:any) => { + return ( + + + + ); + +} + +export default Map \ No newline at end of file diff --git a/src/Pages/Setting/Page.tsx b/src/Pages/Setting/Page.tsx new file mode 100644 index 0000000..51de6c1 --- /dev/null +++ b/src/Pages/Setting/Page.tsx @@ -0,0 +1,98 @@ +import { Image, Spin } from "antd"; +import React from "react"; +import SimpleMap from "./Map"; +import { useGetSetting } from "../../api/GuestApi"; +import { TOKEN_KEY } from "../../api/config"; +import useAuthState from "../../lib/statemangment/AuthState"; +import ReactPlayer from 'react-player' +import { FaEdit } from "react-icons/fa"; +import { useNavigate } from "react-router-dom"; + +const Page = () => { + const navigate= useNavigate() + const fake = "900310ff-610f-11ee-9fbc-00505649cf62"; + const { data, isLoading } = useGetSetting({ + show: fake, + }); + console.log(data, "data"); + const { + logo, + name, + address, + contact_info, + slogan, + attributes, + images, + video, + id + + } = data?.data ?? []; + const fakeIMage = [1,1,1,1,1,1] + const handel_navigate = (id:number)=>{ + console.log(id); + + + navigate(`/Setting/${id}`) + } + if(isLoading){ + return + } + return ( +
+
+ +
+

{name}

+
+
+ {Object.entries(slogan).map(([key, value]:any, index) => { + console.log(key, value); + return ( +
+ {key} : {value} +
+ ); + })} + + +
+
phone_number : {contact_info?.phone_number}
+
email : {contact_info?.email}
+ {Object.entries(attributes).map(([key, value]:any, index) => { + console.log(key, value); + return ( +
+ {key} : {value} +
+ ); + })} +
+
+ {images?.map((item:any,index:number)=>{ + return ( + + ) + })} +
+ {video && + + + } + +
+ handel_navigate(id)} size={30}/> +
+
+ ); +}; + +export default Page; diff --git a/src/Pages/Setting/View/EditForm.tsx b/src/Pages/Setting/View/EditForm.tsx new file mode 100644 index 0000000..637410b --- /dev/null +++ b/src/Pages/Setting/View/EditForm.tsx @@ -0,0 +1,46 @@ + +import React, { useEffect } from 'react' +import { Col, Row } from 'reactstrap'; +import ValidationField from '../../../Components/ValidationField/ValidationField'; +import { useGetCategories } from '../../../api/Categories'; +import useFormatToSelect from '../../../Hooks/useFormatToSelect'; +import FieldGroup from '../Groups/FieldGroup'; +function Form() { + const {data:Categories} = useGetCategories() + const SelectCategoriesData = useFormatToSelect(Categories?.data) + + + return ( + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + ) +} + +export default Form + + + + diff --git a/src/Pages/Setting/View/EditPage.tsx b/src/Pages/Setting/View/EditPage.tsx new file mode 100644 index 0000000..d890444 --- /dev/null +++ b/src/Pages/Setting/View/EditPage.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from 'react' +import { getInitialValues } from '../formUtil' +import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs' +import { MdLanguage } from 'react-icons/md' +import ViewPage from '../../../Layout/Dashboard/ViewPage'; +import { Rate, Spin } from 'antd'; +import { usePageState } from '../../../lib/statemangment/LayoutPagestate'; +import LoadingPage from '../../../Layout/app/LoadingPage'; +import { useTranslation } from 'react-i18next'; +import { useGetSetting, useUpdateSetting } from '../../../api/setting'; +import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess'; +import Form from './EditForm'; +import { useParams } from 'react-router-dom'; +import { convertArrayToJsonString } from '../../../utils/Array/convertArrayToJsonString'; + +const EditPage = () => { + const { setObjectToEdit, objectToEdit } = usePageState() + const { t } = useTranslation(); + const {id} = useParams() + + const { data,isRefetching } = useGetSetting({ + show:id + }) + const { mutate, isSuccess,isLoading:IsloadingButton } = useUpdateSetting() + + const handleSubmit = (values:any) => { + const dataToSend = {...values}; + + const slogan = convertArrayToJsonString(values?.slogan); + const phone_number = values?.phone_number; + const email = values?.email; + const contact_info = convertArrayToJsonString([{key: 'phone_number', value:phone_number},{key: 'email', value:email}]) + + dataToSend["slogan"] = slogan; + dataToSend["contact_info"] = contact_info; + + + // mutate({...values, _method : "put"}); + console.log(dataToSend,"dataToSend"); + + +} + + + useNavigateOnSuccess(isSuccess, '/Setting') + + + useEffect(() => { + + setObjectToEdit(data?.data); + + }, [data]); + + + const getValidationSchema = () => { + return null + }; + if ( !objectToEdit || isRefetching) { + return + } + + + const ViewProps = { getInitialValues, getValidationSchema, handleSubmit,IsloadingButton }; + + + return ( +
+ {objectToEdit && data ? + + + +
{t("BasicInfo")}
+ +
+ +
+
+ + +
+
+ : } + + +
+ ) + +} + +export default EditPage \ No newline at end of file diff --git a/src/Pages/Setting/formUtil.ts b/src/Pages/Setting/formUtil.ts new file mode 100644 index 0000000..b1e2709 --- /dev/null +++ b/src/Pages/Setting/formUtil.ts @@ -0,0 +1,37 @@ + +import * as Yup from "yup"; +import { KeyAndValue, objectToArray } from "../../utils/object/objectToArray"; +import { Restaurant } from "../../types/item"; + + +export const getInitialValues = (objectToEdit: Restaurant ): any => { + + + const slogan: KeyAndValue[] = objectToArray(objectToEdit?.slogan); + const contact_info: KeyAndValue[] = objectToArray(objectToEdit?.contact_info); + const categories = objectToEdit?.categories?.map((item:any)=>(item.id)) || null; + + return { + id: objectToEdit?.id ?? null, + name: objectToEdit?.name ?? null, + images: objectToEdit?.images ?? null, + logo: objectToEdit?.logo ?? null, + + address: objectToEdit?.address ?? null, + phone_number:contact_info?.[0]?.value ?? null, + email:contact_info?.[1]?.value ?? null, + categories:categories, + slogan: slogan ?? null, + video: objectToEdit?.video ?? null, + attributes: objectToEdit?.attributes ?? null, + }; +}; + +export const getValidationSchema = (editMode: boolean = false): Yup.Schema => { + // Validate input + return Yup.object().shape({ + name: Yup.string().required('Required'), + + }); +}; + diff --git a/src/ProviderContainer.tsx b/src/ProviderContainer.tsx new file mode 100644 index 0000000..8deda4c --- /dev/null +++ b/src/ProviderContainer.tsx @@ -0,0 +1,22 @@ +import React, { ReactNode } from 'react' +import QueryProvider from './lib/ReactQueryProvider' +import { BrowserRouter } from 'react-router-dom' +import ToastProvider from './lib/ToastProvider' + +type ProviderContainerProps = { + children:ReactNode + +} +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..8e1e7e2 --- /dev/null +++ b/src/Routes.tsx @@ -0,0 +1,147 @@ +import { ReactNode } from "react"; + +// Icons Import +import { FaHome } from "react-icons/fa" +import { BiCategory, BiSolidCoupon, BiSolidOffer } from "react-icons/bi"; + + +// Pages Import + + +import CategoriesPage from "./Pages/Categories/Page"; +import AddCategoriesPage from "./Pages/Categories/View/AddPage"; +import EditCategories from "./Pages/Categories/View/EditPage"; + + +import RestaurantPage from "./Pages/Restaurant/Page"; +import AddRestaurantPage from "./Pages/Restaurant/View/AddPage"; +import EditRestaurant from "./Pages/Restaurant/View/EditPage"; + + +import MealPage from "./Pages/Meal/Page"; +import AddMealPage from "./Pages/Meal/View/AddPage"; +import EditMeal from "./Pages/Meal/View/EditPage"; + + +import OfferPage from "./Pages/Offer/Page"; +import AddOfferPage from "./Pages/Offer/View/AddPage"; +import EditOffer from "./Pages/Offer/View/EditPage"; + +import SettingPage from './Pages/Setting/Page' +import SettingEditPage from './Pages/Setting/View/EditPage' + + +import { SettingFilled } from "@ant-design/icons"; +import { GiMeal } from "react-icons/gi"; + + +import Home from './Pages/Home/Page' + + + + +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: "Restaurant", + element: , + icon: , + href: "/Restaurant", + }, + { + href: "/Restaurant/:id", + element: , + hidden:true + }, + { + href: "/Restaurant/add", + element: , + hidden:true + }, + + + { + name: "Categories", + element: , + icon: , + href: "/Categories", + }, + { + href: "/Categories/:id", + element: , + hidden:true + }, + { + href: "/Categories/add", + element: , + hidden:true + }, + { + name: "Meal", + element: , + icon: , + href: "/Meal", + }, + { + href: "/Meal/:id", + element: , + hidden:true + }, + { + href: "/Meal/add", + element: , + hidden:true + }, + + { + name: "Offer", + element: , + icon: , + href: "/Offer", + }, + { + href: "/Offer/:id", + element: , + hidden:true + }, + { + href: "/Offer/add", + element: , + hidden:true + }, + + { + name: "Setting", + element: , + icon: , + href: "/Setting", + }, + + { + name: "SettingEdit", + element: , + href: "/Setting/:id", + 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..eed8ba0 --- /dev/null +++ b/src/Styles/AppStyle/Import.scss @@ -0,0 +1,23 @@ + +@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'; + +@import '../Home/Setting.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..38d9750 --- /dev/null +++ b/src/Styles/AppStyle/Varibils.scss @@ -0,0 +1,36 @@ +:root { + --primary:black ; + --secondary : #2D9CDB; + --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/Home/Setting.scss b/src/Styles/Home/Setting.scss new file mode 100644 index 0000000..7c3f073 --- /dev/null +++ b/src/Styles/Home/Setting.scss @@ -0,0 +1,37 @@ +.Personal_Image{ + border-radius: 50%; +} +.SettingPage{ + background: var(--bg); + @include Shadow; + border-radius: 40px; + padding: 20px 10%; + display: flex; + flex-direction: column; + gap: 20px; + position: relative; + +} +.SettingPage_header{ + display: flex; + gap: 20px; +} +.leaflet-control-attribution { + display: none !important; +} +.Personal_Images{ + display: flex; + gap: 30px; + flex-wrap: wrap; + + +} + +.EditButton{ + position: absolute; + right: 40px; + top: 40px; + svg{ + color: var(--primary); + } +} \ 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..df29ed9 --- /dev/null +++ b/src/api/Categories.ts @@ -0,0 +1,22 @@ + +import useGetQuery from "./helper/useGetQuery"; +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `category`, + GET_ALL: `category`, + DELETE: `category`, + UPDATE: `category`, + +}; +const KEY = "categories" + + +export const useGetCategories = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params); + +export const useAddCategories = () => useAddMutation(KEY, API.ADD); +export const useUpdateCategories = (method?:any) => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteCategories = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/GuestApi.ts b/src/api/GuestApi.ts new file mode 100644 index 0000000..9f10b1a --- /dev/null +++ b/src/api/GuestApi.ts @@ -0,0 +1,22 @@ + +import useGetQuery from "./helper/useGetQuery"; +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `guest/restaurant`, + GET_ALL: `guest/restaurant`, + DELETE: `guest/restaurant`, + UPDATE: `guest/restaurant`, + +}; +const KEY = "setting" + + +export const useGetSetting = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params); + +export const useAddSetting = () => useAddMutation(KEY, API.ADD); +export const useUpdateSetting = (method?:any) => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteSetting = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/Meal.ts b/src/api/Meal.ts new file mode 100644 index 0000000..ac97da0 --- /dev/null +++ b/src/api/Meal.ts @@ -0,0 +1,22 @@ + +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useGetQuery from "./helper/useGetQuery"; +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `meal`, + GET_ALL: `meal`, + DELETE: `meal`, + UPDATE: `meal`, + +}; +const KEY = "Meal" + + +export const useGetMeal = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params,option); + +export const useAddMeal = () => useAddMutation(KEY, API.ADD); +export const useUpdateMeal = () => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteMeal = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/Offer.ts b/src/api/Offer.ts new file mode 100644 index 0000000..f159365 --- /dev/null +++ b/src/api/Offer.ts @@ -0,0 +1,22 @@ + +import useGetQuery from "./helper/useGetQuery"; +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `offer`, + GET_ALL: `offer`, + DELETE: `offer`, + UPDATE: `offer`, + +}; +const KEY = "Offer" + + +export const useGetOffer = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params); + +export const useAddOffer = () => useAddMutation(KEY, API.ADD); +export const useUpdateOffer = (method?:any) => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteOffer = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/Restaurant.ts b/src/api/Restaurant.ts new file mode 100644 index 0000000..3e7413f --- /dev/null +++ b/src/api/Restaurant.ts @@ -0,0 +1,22 @@ + +import useGetQuery from "./helper/useGetQuery"; +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `restaurant`, + GET_ALL: `restaurant`, + DELETE: `restaurant`, + UPDATE: `restaurant`, + +}; +const KEY = "Restaurant" + + +export const useGetRestaurant = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params); + +export const useAddRestaurant = () => useAddMutation(KEY, API.ADD); +export const useUpdateRestaurant = (method?:any) => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteRestaurant = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 0000000..f9df5ba --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,10 @@ +import useAddMutation from "./helper/useAddMutation"; + + + + +const KEY = "AUTH" +const API = { + LOGIN: `login`, + }; +export const useLoginAdmin = ()=>useAddMutation(KEY , API.LOGIN,"login_successful") \ No newline at end of file diff --git a/src/api/config.ts b/src/api/config.ts new file mode 100644 index 0000000..8b71874 --- /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://e-menus-back-dev.point-dev.net/api/`; +export const ImageBaseURL = `https://e-menus-back-dev.point-dev.net/`; + + +// export const BaseURL = `http://192.168.1.14:8000/`; +export const BaseURL_IMAGE = BaseURL.slice(0,-1); + + +const PROJECT_NAME = "E-Menu" + +export const TOKEN_KEY = PROJECT_NAME + "_TOKEN" + +export const USER_KEY = PROJECT_NAME + "_USER" + + +export const HEADER_KEY = "X-Custom-Query-Key"; diff --git a/src/api/helper/AxiosBuilder.ts b/src/api/helper/AxiosBuilder.ts new file mode 100644 index 0000000..d8cd905 --- /dev/null +++ b/src/api/helper/AxiosBuilder.ts @@ -0,0 +1,43 @@ +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/useAddMutation.ts b/src/api/helper/useAddMutation.ts new file mode 100644 index 0000000..adaccc7 --- /dev/null +++ b/src/api/helper/useAddMutation.ts @@ -0,0 +1,29 @@ +import { useMutation, UseMutationResult } from "react-query"; +import useAxios from "./useAxios"; +import { HEADER_KEY } from "../config"; +import { AxiosResponse } from "../../types/Axios"; + +function useAddMutation( + key: string, + url: string, + message?: string, + +): UseMutationResult { + const axios = useAxios(); + return useMutation( + async (dataToSend) => { + + const { data } = await axios.post(url, dataToSend, { + headers: { + "Content-Type": "multipart/form-data", + [HEADER_KEY]: key, + }, + }); + return data; + + }, + + ); +} + +export default useAddMutation; diff --git a/src/api/helper/useAxios.ts b/src/api/helper/useAxios.ts new file mode 100644 index 0000000..d7d2b80 --- /dev/null +++ b/src/api/helper/useAxios.ts @@ -0,0 +1,82 @@ + +import { BaseURL, HEADER_KEY } from "../config"; +import AxiosBuilder from "./AxiosBuilder"; +import { useTranslation } from "react-i18next"; +import { toast } from "react-toastify"; +import { useQueryClient } from "react-query"; +import { useNavigate } from "react-router-dom"; +import useAuthState from "../../lib/statemangment/AuthState"; +import { useValidationState } from "../../Components/ValidationField/utils/ValidationState"; +import { AxiosQueryEnum, AxiosStatusEnum } from "../../enums/Axios"; +import { usePageState } from "../../lib/statemangment/LayoutPagestate"; + +function useAxios() { + const { isAuthenticated, token } = useAuthState(); + + const { setValidation } = useValidationState((state) => state); + const {setObjectToEdit} = usePageState() + const [t] = useTranslation(); + const queryClient = useQueryClient(); + + const { logout } = useAuthState(); + const navigate = useNavigate(); + + const buildAxios = new AxiosBuilder() + .withBaseURL(BaseURL) + .withResponseType("json") + .withTimeout(120000); + + if (isAuthenticated) { + buildAxios.withHeaders({ + Authorization: "Bearer " + token, + }); + } + + const build_Axios = buildAxios.build(); + + build_Axios.interceptors.response.use( + function (response: any) { + const responseMsg = response?.data?.message; + const method = response.config.method; + + const key = response.config.headers[HEADER_KEY]; + const ResponseMessage = + responseMsg || t("validation.the_possess_done_successful"); + if (method !== AxiosQueryEnum?.GET) { + queryClient.invalidateQueries(key); + toast.success(ResponseMessage); + setValidation([{}]); + setObjectToEdit([]) + } + + return response; + }, + function (error) { + const status = error?.request?.status; + const errorMsg = error?.response?.data?.message; + const errorField = error?.response?.data; + const method = error.config.method; + + if (status === AxiosStatusEnum.VALIDATION) { + setValidation(errorMsg ?? errorField); + const errorMessage = errorMsg || t("validation.some_thing_went_wrong"); + toast.error(errorMessage); + } + if (status === AxiosStatusEnum.AUTHENTICATED) { + logout(); + navigate("/auth"); + } + + if (method !== AxiosQueryEnum?.GET) { + const errorMessage = errorMsg || t("validation.some_thing_went_wrong"); + toast.error(errorMessage); + return Promise.reject(error); + } + }, + ); + return build_Axios; + + // return buildAxios.build(); +} + +export default useAxios; diff --git a/src/api/helper/useDeleteMutation.ts b/src/api/helper/useDeleteMutation.ts new file mode 100644 index 0000000..c5fb446 --- /dev/null +++ b/src/api/helper/useDeleteMutation.ts @@ -0,0 +1,28 @@ +import { useMutation, UseMutationResult } from "react-query"; +import useAxios from "./useAxios"; +import { HEADER_KEY } from "../config"; +import { AxiosResponse } from "../../types/Axios"; + +type DataToSend = { + id: number | string; +}; + +function useDeleteMutation( + key: any, + url: string, + message?: string, +): UseMutationResult { + const axios = useAxios(); + return useMutation( + async (dataToSend) => { + const { data } = await axios.delete(url + `/` + dataToSend?.id, { + headers: { + [HEADER_KEY]: key, + }, + }); + return data; + }, + ); +} + +export default useDeleteMutation; diff --git a/src/api/helper/useGetQuery.ts b/src/api/helper/useGetQuery.ts new file mode 100644 index 0000000..e466018 --- /dev/null +++ b/src/api/helper/useGetQuery.ts @@ -0,0 +1,39 @@ +import { useQuery } from "react-query"; +import useAxios from "./useAxios"; +import { useLocation } from "react-router-dom"; +import { PaginationParams } from "../utils/PaginationParams"; +import { filterParams } from "../utils/filterParams"; + +function useGetQuery( + KEY: string | string[], + url: string, + params: any = {}, + options: any = {}, +) { + const axios = useAxios(); + + const { show, pagination, ...remainingParams } = params; + + const location = useLocation(); + + const { page, per_page } = PaginationParams(location); + + const param_to_send = pagination + ? { ...remainingParams, page: page, per_page: per_page } + : { ...remainingParams }; + + const filteredParams = filterParams(param_to_send); + + return useQuery( + [KEY, filteredParams, show], + async () => { + const response = await axios.get(url + (show ? `/${show}` : ""), { + params: filteredParams, + }); + return response?.data ?? []; + }, + options, + ); +} + +export default useGetQuery; diff --git a/src/api/helper/useUpdateMutation.ts b/src/api/helper/useUpdateMutation.ts new file mode 100644 index 0000000..195c32f --- /dev/null +++ b/src/api/helper/useUpdateMutation.ts @@ -0,0 +1,36 @@ +import { useMutation, UseMutationResult } from "react-query"; +import useAxios from "./useAxios"; +import { HEADER_KEY } from "../config"; +import { AxiosResponse } from "../../types/Axios"; + +const useUpdateMutation = ( + key: string, + url: string, + message?: string, +): UseMutationResult => { + const axios = useAxios(); + + return useMutation(async (dataToSend) => { + let request = null; + let id = null; + + if (dataToSend instanceof FormData) { + dataToSend.append("_method", "PUT"); + request = dataToSend; + id = dataToSend.get("id"); + } else { + request = { ...dataToSend, _method: "PUT" }; + id = dataToSend?.id; + } + + const { data } = await axios.post(url + `/` + id, request, { + headers: { + "Content-Type": "multipart/form-data", + [HEADER_KEY]: key, + }, + }); + return data; + }); +}; + +export default useUpdateMutation; diff --git a/src/api/setting.ts b/src/api/setting.ts new file mode 100644 index 0000000..1ac545f --- /dev/null +++ b/src/api/setting.ts @@ -0,0 +1,22 @@ + +import useGetQuery from "./helper/useGetQuery"; +import useAddMutation from "./helper/useAddMutation" +import useDeleteMutation from "./helper/useDeleteMutation" +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + ADD: `restaurant`, + GET_ALL: `restaurant`, + DELETE: `restaurant`, + UPDATE: `restaurant`, + +}; +const KEY = "restaurant" + + +export const useGetSetting = (params?:any,option?:any) => useGetQuery(KEY, API.GET_ALL,params); + +export const useAddSetting = () => useAddMutation(KEY, API.ADD); +export const useUpdateSetting = (method?:any) => useUpdateMutation(KEY, API.UPDATE); + +export const useDeleteSetting = () =>useDeleteMutation(KEY, API.DELETE); diff --git a/src/api/utils/PaginationParams.ts b/src/api/utils/PaginationParams.ts new file mode 100644 index 0000000..6b1f83a --- /dev/null +++ b/src/api/utils/PaginationParams.ts @@ -0,0 +1,7 @@ +export function PaginationParams(location: any) { + const searchParams = new URLSearchParams(location?.search); + return { + page: searchParams.get("page") || "", + per_page: searchParams.get("per_page") || "", + }; +} diff --git a/src/api/utils/filterParams.ts b/src/api/utils/filterParams.ts new file mode 100644 index 0000000..1381ae3 --- /dev/null +++ b/src/api/utils/filterParams.ts @@ -0,0 +1,5 @@ +export function filterParams(params: any) { + return Object.fromEntries( + Object.entries(params ?? {}).filter(([_, value]) => value !== ""), + ); +} diff --git a/src/api/utils/useSearchQuery.ts b/src/api/utils/useSearchQuery.ts new file mode 100644 index 0000000..2f2f298 --- /dev/null +++ b/src/api/utils/useSearchQuery.ts @@ -0,0 +1,16 @@ +import { useState, useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +function useSearchQuery(paramName: string): [string, (value: string) => void] { + const location = useLocation(); + const [queryValue, setQueryValue] = useState(""); + + useEffect(() => { + const searchParams = new URLSearchParams(location.search); + setQueryValue(searchParams.get(paramName) || ""); + }, [location, paramName]); + + return [queryValue, setQueryValue]; +} + +export default useSearchQuery; 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..81f1533 --- /dev/null +++ b/src/config/AppKey.ts @@ -0,0 +1,12 @@ + + + +const PROJECT_NAME = "E-Menu_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/Axios.ts b/src/enums/Axios.ts new file mode 100644 index 0000000..2f966fd --- /dev/null +++ b/src/enums/Axios.ts @@ -0,0 +1,10 @@ +export enum AxiosQueryEnum { + GET = "get", + POST = "post", + DELETE = "delete", +} + +export enum AxiosStatusEnum { + VALIDATION = 422, + AUTHENTICATED = 401, +} 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/QueryStatus.ts b/src/enums/QueryStatus.ts new file mode 100644 index 0000000..31cbdef --- /dev/null +++ b/src/enums/QueryStatus.ts @@ -0,0 +1,5 @@ +export enum QueryStatusEnum { + LOADING = "loading", + ERROR = "error", + SUCCESS = "success", +} 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/enums/Validation.ts b/src/enums/Validation.ts new file mode 100644 index 0000000..8dcca3d --- /dev/null +++ b/src/enums/Validation.ts @@ -0,0 +1,7 @@ +export enum Payments { + MAX_VALUE = 900000, +} + +export enum Exam { + MAX_VALUE = 10000, +} diff --git a/src/enums/params.ts b/src/enums/params.ts new file mode 100644 index 0000000..9a68120 --- /dev/null +++ b/src/enums/params.ts @@ -0,0 +1,8 @@ +export enum ParamsEnum { + STUDENT_ID = "student_id", + COURSE_ID = "course_id", + EDUCATION_CLASS_ID = "edu_class_id", + SUBJECT_ID = "subject_id", + BRANCH_ID = "branch_id", + CYCLE_ID = "cycle_id", +} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..15a09ab --- /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..4320c63 --- /dev/null +++ b/src/lib/ReactQueryProvider.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { QueryClient, QueryClientProvider } from "react-query"; + +function QueryProvider({ children }: any) { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, + }); + + return ( + {children} + ); +} + +export default QueryProvider; 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/statemangment/AuthState.ts b/src/lib/statemangment/AuthState.ts new file mode 100644 index 0000000..b2bd147 --- /dev/null +++ b/src/lib/statemangment/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/statemangment/LayoutPagestate.ts b/src/lib/statemangment/LayoutPagestate.ts new file mode 100644 index 0000000..db1c4b5 --- /dev/null +++ b/src/lib/statemangment/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/statemangment/Pages/Products.ts b/src/lib/statemangment/Pages/Products.ts new file mode 100644 index 0000000..4679b15 --- /dev/null +++ b/src/lib/statemangment/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/statemangment/driver&customer/ModelState.tsx b/src/lib/statemangment/driver&customer/ModelState.tsx new file mode 100644 index 0000000..4a4ee9c --- /dev/null +++ b/src/lib/statemangment/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..e7f7553 --- /dev/null +++ b/src/translate/ar.json @@ -0,0 +1,191 @@ +{ + "Ar": "عربي", + "En": "انكليزي", + "Arabic": "عربي", + "English": "إنجليزي", + "German" : "ألمانية", + "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_de": "الاسم بالألمانية", + "description_ar": "الوصف بالعربية", + "description_en": "الوصف بالإنجليزية", + "description_de": "الوصف بالألمانية", + "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_de": "الجسم (الألمانية)", + "title_en": "العنوان (الإنجليزية)", + "title_ar": "العنوان (العربية)", + "title_de": "العنوان (الألمانية)", + "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/de.json b/src/translate/de.json new file mode 100644 index 0000000..618b8e7 --- /dev/null +++ b/src/translate/de.json @@ -0,0 +1,191 @@ +{ + "Ar": "Ar", + "En": "En", + "Arabic": "Arabisch", + "English": "Englisch", + "German": "Deutsch", + "Login": "Anmelden", + "Welcome back, please login to your account.": "Willkommen zurück, bitte melden Sie sich bei Ihrem Konto an.", + "Username": "Benutzername", + "Password": "Passwort", + "Sign in": "Einloggen", + "Point © 2022 | All Rights Reserved": "Point © 2022 | Alle Rechte vorbehalten", + "unknown": "unbekannt", + "super admin": "Superadmin", + "Home": "Startseite", + "example": "Beispiel", + "Log Out": "Abmelden", + "Example": "Beispiel", + "Add": "Hinzufügen", + "edit": "Bearbeiten", + "ligth": "Licht", + "dark": "Dunkel", + "Categories": "Kategorien", + "Products": "Produkte", + "Order": "Bestellung", + "Coupon": "Gutschein", + "Slider": "Schieberegler", + "Product_in_your_Application": "Produkt in Ihrer Anwendung", + "categories_in_your_Application": "Kategorien in Ihrer Anwendung", + "Order_in_your_Application": "Bestellung in Ihrer Anwendung", + "You_have": "Sie haben", + "January": "Januar", + "February": "Februar", + "March": "März", + "April": "April", + "London": "London", + "Paris": "Paris", + "New York": "New York", + "Seoul": "Seoul", + "name": "name", + "image": "bild", + "parent_id": "übergeordnete", + "product_count": "produktanzahl", + "basicinfo": "grundinfo", + "view_information": "information anzeigen", + "back": "zurück", + "price": "preis", + "description": "beschreibung", + "favorite": "favorit", + "main_photo": "hauptfoto", + "category_id": "kategorie", + "order_code": "bestellcode", + "email": "email", + "status": "status", + "total": "gesamt", + "order_id": "bestell", + "customer_name": "kundenname", + "customer_phone_number": "kunden telefonnummer", + "order_created_at": "bestellung erstellt am", + "address": "adresse", + "country": "land", + "note": "anmerkung", + "categories": "kategorien", + "coupon": "gutschein", + "orders": "bestellungen", + "products": "produkte", + "slider": "schieberegler", + "discount_type": "Rabatttyp", + "coupon_type": "Gutscheintyp", + "code": "Code", + "coupon_value": "Gutscheinwert", + "view_information_filed_fill_sucsessfully": "Anzeigeinformationsfeld erfolgreich ausgefüllt", + "category": "Kategorie", + "View_information": "Information anzeigen", + "VarianInfo": "Varianteninformation", + "Base_info": "Basisinformationen", + "name_ar": "Name (Arabisch)", + "name_en": "Name (Englisch)", + "name_de": "Name (Deutsch)", + "description_ar": "Beschreibung (Arabisch)", + "description_en": "Beschreibung (Englisch)", + "description_de": "Beschreibung (Deutsch)", + "upload_image": "Bild hochladen", + "images": "Bilder", + "photo": "Foto", + "admin_note": "Admin-Hinweis", + "state": "Status", + "title": "Titel", + "totals": "Summen", + "delivery_fee": "Liefergebühr", + "overall_total": "Gesamtsumme", + "sub_total": "Teilsumme", + "quantity": "Menge", + "active_from_to": "Aktiv von/bis", + "maximum_number_of_uses": "Maximale Anzahl von Verwendungen", + "minimum_total_to_order": "Mindestgesamtbestellung", + "maximum_number_of_uses_per_user": "Maximale Anzahl von Verwendungen pro Benutzer", + "product_item": "Produktartikel", + "categories_item": "der Kategorienartikel", + "variables": "Variablen", + "Information": "Informationen", + "key": "Schlüssel", + "Description": "Beschreibung", + "Add Another Item": "Weiteres Element hinzufügen", + "no_records": "Keine Datensätze", + "Total": "Insgesamt", + "items": "Artikel", + "search": "Suche", + "required_name": "Alle Namensfelder sind erforderlich. in Attribut", + "required_description": "Alle Beschreibungsfelder sind erforderlich. in Attribut", + "required_main_photo": "Hauptfoto-Feld ist in Attribut erforderlich", + "required_price": "Preisfeld ist in Attribut erforderlich", + "required_type": "Feldtyp ist erforderlich. in Attribut", + "required_image": "Bildfeld ist in Wert erforderlich", + "required_color": "Farbfeld muss ein Hex-Wert sein", + "required_text": "Alle Wertfelder sind erforderlich.", + "BasicInfo": "Grundlegende Informationen", + "attributes": "Attribute", + "Add New Attribute": "Neues Attribut hinzufügen", + "Attribute": "Attribut", + "Value": "Wert", + "Add New Variant": "Neue Variante hinzufügen", + "variant": "Variante", + "unique_error_names": "Einzigartiger Name für jede Eigenschaft ist erforderlich", + "deliviration_estimated_time": "Voraussichtliche Lieferzeit", + "delivery_link": "Lieferlink", + "delete_are_you_sure": "Möchten Sie wirklich löschen?", + "yes_delete_it": "Ja, löschen", + "cancel": "Abbrechen", + "notification": "Benachrichtigung", + "users": "Benutzer", + "body": "Körper", + "body_en": "Körper (Englisch)", + "body_ar": "Körper (Arabisch)", + "body_de": "Körper (Deutsch)", + "title_en": "Titel (Englisch)", + "title_ar": "Titel (Arabisch)", + "title_de": "Titel (Deutsch)", + "avatar": "Avatar", + "added_successful": "Erfolgreich hinzugefügt", + "failed_to_add_data": "Daten konnten nicht hinzugefügt werden", + "deleted_successfully": "Erfolgreich gelöscht", + "updated_successfully": "Erfolgreich aktualisiert", + "Product_Count_in_your_Application": "Anzahl der Produkte in Ihrer Anwendung", + "productCount": "Anzahl der Produkte", + "user_in_your_Application": "Benutzer in Ihrer Anwendung", + "userCount": "Anzahl der Benutzer", + "orderCount": "Anzahl der Bestellungen", + "order_count_in_your_Application": "Anzahl der Bestellungen in Ihrer Anwendung", + "month": "Monat", + "sorry_only_user_can_change_his_status": "Entschuldigung, nur der Benutzer kann seinen Status ändern.", + "create_notification": "Benachrichtigung erstellen", + "SupportMessages": "Support-Nachrichten", + "whatsApp": "WhatsApp", + "subject": "Betreff", + "message": "Nachricht", + "EditDetails": "Details bearbeiten", + "OrderItems": "Bestellpositionen", + "reset": "Zurücksetzen", + "submit": "Absenden", + "errorPage": { + "networkError": "Netzwerkfehler", + "checkAndModify": "Bitte überprüfen Sie Ihr Netzwerk und laden Sie die Seite erneut.", + "refetch": "Seite neu laden", + "goToHome": "Zum Startseite" + } +, +"value_en": "Wert (Englisch)", +"value_ar": "Wert (Arabisch)", +"value_de": "Wert (Deutsch)", +"type": "Typ", +"id": "ID", +"submite": "Einreichen", +"PriceFrom": "Preis ab", +"PriceTo": "Preis bis", +"Pending Approve": "Ausstehende Genehmigung", +"Approved": "Genehmigt", +"Rejected": "Abgelehnt", +"Pending Cancellation": "Ausstehende Stornierung", +"user_ids": "Benutzer-IDs", +"user": "Benutzer", +"login_successful": "Anmeldung erfolgreich", + + "DateFrom": "Datum von", + "DateTo": "Datum bis" + + + + + +} \ No newline at end of file diff --git a/src/translate/en.json b/src/translate/en.json new file mode 100644 index 0000000..f473476 --- /dev/null +++ b/src/translate/en.json @@ -0,0 +1,191 @@ +{ + "Ar": "Ar", + "En": "En", + "Arabic": "Arabic", + "English": "English", + "German": "German", + "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_de": "Name (German)", + "description_ar": "Description (Arabic)", + "description_en": "Description (English)", + "description_de": "Description (German)", + "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_de": "Body (German)", + "title_en": "Title (English)", + "title_ar": "Title (Arabic)", + "title_de": "Title (German)", + "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 (German)", +"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/Axios.ts b/src/types/Axios.ts new file mode 100644 index 0000000..c59cb16 --- /dev/null +++ b/src/types/Axios.ts @@ -0,0 +1,3 @@ +export type AxiosResponse = { + message?: string; +}; 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/types/item.ts b/src/types/item.ts new file mode 100644 index 0000000..07860c8 --- /dev/null +++ b/src/types/item.ts @@ -0,0 +1,49 @@ +export interface Restaurant { + id: number; + name: string; + address: string; + logo: string; + slogan: { + text: string; + }; + contact_info: { + phone_number: string; + email: string; + }; + attributes: { + attribute: string; + }; + video: string; + design_system: { + primary_color: string; + secondry_color: string; + }; + location: { + location: string; + }; + images: any[]; // Assuming the images array contains objects, replace 'any' with appropriate type if known + categories: Category[]; + offers: Offer[]; + uuid: string; + created_at: string; + updated_at: string; +} + +interface Category { + id: number; + name: string; + image: string; + restaurant_id: number; + meals_count: number; + uuid: string; +} + +interface Offer { + id: number; + name: string; + description: string; + image: string; + price: number; + restaurant_id: number; + uuid: string; +} 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/Equal.ts b/src/utils/Array/Equal.ts new file mode 100644 index 0000000..e69de29 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/convertArrayToJsonString.ts b/src/utils/Array/convertArrayToJsonString.ts new file mode 100644 index 0000000..d6b40af --- /dev/null +++ b/src/utils/Array/convertArrayToJsonString.ts @@ -0,0 +1,13 @@ +interface KeyValue { + key: string; + value: any; + } + + export function convertArrayToJsonString(array: KeyValue[]): string { + const jsonObject: { [key: string]: any } = {}; + array.forEach(item => { + jsonObject[item.key] = item.value; + }); + return JSON.stringify(jsonObject); + } + \ 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/Array/isItArrayOfString.ts b/src/utils/Array/isItArrayOfString.ts new file mode 100644 index 0000000..caa7aaf --- /dev/null +++ b/src/utils/Array/isItArrayOfString.ts @@ -0,0 +1,12 @@ +export const isItArrayOfString = (data:any[])=>{ + if(data?.length < 1){ + return true + } + + const filteredArray = data?.filter((item:any)=>{ return typeof item !== "string"}) + if(filteredArray?.length < 1){ + return true + } + return false; + + } \ 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/utils/object/objectToArray.ts b/src/utils/object/objectToArray.ts new file mode 100644 index 0000000..4c9d98d --- /dev/null +++ b/src/utils/object/objectToArray.ts @@ -0,0 +1,9 @@ +export interface KeyAndValue { + key: string; + value: any; +} + +export function objectToArray(obj: Record): KeyAndValue[] { + if(!obj){ return [] } + return Object.keys(obj).map((key: string) => ({ key, value: obj[key] })); +} \ 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" + ] +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..4e690eb --- /dev/null +++ b/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig(() => { + return { + build: { + outDir: 'build', + }, + plugins: [react()], + }; +}); \ No newline at end of file