Done
24
.gitignore
vendored
Normal file
|
|
@ -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
|
||||||
11
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"aldeen",
|
||||||
|
"Datepicker",
|
||||||
|
"formik",
|
||||||
|
"Karim",
|
||||||
|
"queryqlent",
|
||||||
|
"szhsin",
|
||||||
|
"Viewelement"
|
||||||
|
]
|
||||||
|
}
|
||||||
271
README.md
|
|
@ -1,270 +1,11 @@
|
||||||
# Structer Dashboard - Open Source
|
# Structer Dashboard
|
||||||
|
|
||||||
Welcome to Structer Dashboard, an open-source dashboard designed to streamline your project management processes. This guide will walk you through the installation process and highlight the key components and examples for integrating various functionalities into your project.
|
1. Install the necessary dependencies using PNPM by running
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To incorporate the Structer Dashboard into your project, follow these steps:
|
|
||||||
|
|
||||||
1. Ensure you have Node.js version 18 installed.
|
|
||||||
2. Install the JSON server by running
|
|
||||||
```bash
|
|
||||||
npm i -g json-server
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Run the JSON server with the command .
|
|
||||||
```bash
|
|
||||||
json-server --watch db.json
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Install the necessary dependencies using PNPM by running
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Launch the dashboard with
|
5. Launch the dashboard with
|
||||||
```bash pnpm start ```
|
```bash
|
||||||
6. Visit http://localhost:3333 to view the dashboard.
|
pnpm start
|
||||||
|
```
|
||||||
## Components
|
|
||||||
|
|
||||||
### 1. Reactstrap
|
|
||||||
|
|
||||||
Utilize the following Reactstrap components for a seamless and responsive UI:
|
|
||||||
|
|
||||||
- **Navbar**: Easily navigate through the dashboard with dropdown support.
|
|
||||||
- **Modal**: Display additional information or forms with user-friendly pop-ups.
|
|
||||||
|
|
||||||
### 2. Formik
|
|
||||||
|
|
||||||
Simplify form management and validation with Formik, offering the following capabilities:
|
|
||||||
|
|
||||||
- **Form Creation**: Effortlessly create complex forms with validation rules.
|
|
||||||
- **Error Handling**: Manage form errors and user input effectively.
|
|
||||||
- **Validation Input**: Validate input using the Yup schema.
|
|
||||||
|
|
||||||
### 3. react-data-table-component
|
|
||||||
|
|
||||||
Efficiently manage large datasets with the help of react-data-table-component, which provides:
|
|
||||||
|
|
||||||
- **Pagination**: Organize data with pagination and sorting functionality.
|
|
||||||
- **Customization**: Customize the data table style to align with your project requirements.
|
|
||||||
|
|
||||||
### 4. react-i18next
|
|
||||||
|
|
||||||
Make your dashboard accessible to a global audience with multilingual support through react-i18next:
|
|
||||||
|
|
||||||
- **Multiple Language Support**: Manage content in multiple languages effortlessly.
|
|
||||||
- **Dynamic Content Translation**: Translate dashboard content dynamically for a personalized user experience.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Refer to the following examples to integrate these features seamlessly into your dashboard:
|
|
||||||
|
|
||||||
1. Create a folder within the 'pages' directory.
|
|
||||||
2. Add the page to 'Routes.tsx' with the relevant path and icon, ensuring lazy loading is implemented.
|
|
||||||
3. Customize your modal forms to suit your specific requirements.
|
|
||||||
4. Define initial values and validate schemas for your input in the 'formUtils' file.
|
|
||||||
5. Implement the 'handleSubmit' function to manage form submissions effectively.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Real Example
|
|
||||||
|
|
||||||
First Add Your Route And Page in Routes.tsx
|
|
||||||
|
|
||||||
```ts
|
|
||||||
|
|
||||||
|
|
||||||
export const RoutesLinks = [
|
|
||||||
{
|
|
||||||
name: "example",
|
|
||||||
href: "/",
|
|
||||||
icon: <BsHexagon />,
|
|
||||||
element: <ExamplePage />
|
|
||||||
}
|
|
||||||
// add another route
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Create Folder in Pages with this Structer
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Some Imports
|
|
||||||
function YourPageName() {
|
|
||||||
|
|
||||||
// Your Column For Data Table
|
|
||||||
const column =useTableColumns()
|
|
||||||
|
|
||||||
// Your Data Fetch and status (pendding , loadaing , success)
|
|
||||||
const {data ,status } = useGetAllExample()
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<DashBody status={status as QueryStatusEnum} >
|
|
||||||
<DashHeader title={'Example'}></DashHeader>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<LyTable
|
|
||||||
data={data}
|
|
||||||
isLoading={status === QueryStatusEnum.LOADING}
|
|
||||||
columns={column}
|
|
||||||
// another props hear
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/*
|
|
||||||
// Your Edit and Add Model
|
|
||||||
// Dynimc Open Using Zustand
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<EditExampleModal />
|
|
||||||
<AddExampleModal />
|
|
||||||
</DashBody>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default YourPageName
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Create Your Modal Add and Edit
|
|
||||||
|
|
||||||
```js
|
|
||||||
|
|
||||||
// Translate
|
|
||||||
const [t] = useTranslation()
|
|
||||||
|
|
||||||
// Your Fetch function
|
|
||||||
const {mutate , status} = useAddExample()
|
|
||||||
|
|
||||||
|
|
||||||
// Create your Submit Form => using Formik
|
|
||||||
const handelSubmit = (values:any )=>{
|
|
||||||
|
|
||||||
|
|
||||||
// change values Shap For Send To Api
|
|
||||||
const dataToSend = getDataToSend(values)
|
|
||||||
|
|
||||||
mutate(dataToSend)
|
|
||||||
// Submit Value
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
// Customized you model
|
|
||||||
<LayoutModal
|
|
||||||
|
|
||||||
isAddModal={true}
|
|
||||||
// inital value for form
|
|
||||||
getInitialValues={getInitialValues()}
|
|
||||||
// validate your schema form input
|
|
||||||
getValidationSchema={getValidationSchema()}
|
|
||||||
handleSubmit={handelSubmit}
|
|
||||||
// Dynmic Close Modal when status Success and make Loading Button if status loading
|
|
||||||
status={status as QueryStatusEnum}
|
|
||||||
headerText={`${t('Add')} ${t('example')}`}
|
|
||||||
>
|
|
||||||
{/*
|
|
||||||
add Your Children Component
|
|
||||||
*/}
|
|
||||||
<FormExample />
|
|
||||||
</LayoutModal>
|
|
||||||
)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
create your formUtils file with your validate and inital value and getDatasend functions
|
|
||||||
|
|
||||||
```js
|
|
||||||
|
|
||||||
export const getInitialValues = (objectToEdit: ObjectToEdit | null = null): InitialValues => {
|
|
||||||
|
|
||||||
|
|
||||||
// change your inital value with your state
|
|
||||||
return {
|
|
||||||
id:objectToEdit?.id?? 0 ,
|
|
||||||
name:objectToEdit?.name ?? "",
|
|
||||||
email:objectToEdit?.email?? ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<ValidateSchema> => {
|
|
||||||
// validate input
|
|
||||||
return Yup.object().shape({
|
|
||||||
name:Yup.string().required('required'),
|
|
||||||
email:Yup.string().required("required")
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDataToSend = (values: any): FormData => {
|
|
||||||
const data = { ...values };
|
|
||||||
|
|
||||||
// apply your data shap change
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
buildFormData(formData, data);
|
|
||||||
return formData;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Create your Children Form For Modal
|
|
||||||
```js
|
|
||||||
|
|
||||||
|
|
||||||
// translated object
|
|
||||||
const [t] = useTranslation()
|
|
||||||
return (
|
|
||||||
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
|
|
||||||
<Col>
|
|
||||||
|
|
||||||
<ValidatedField
|
|
||||||
name="name"
|
|
||||||
label={`${t("name")}`}
|
|
||||||
placeholder={`${t("name")}`}
|
|
||||||
// custom props
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<ValidatedField
|
|
||||||
name="email"
|
|
||||||
label={`${t("email")}`}
|
|
||||||
placeholder={`${t("email")}`}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
{/*
|
|
||||||
or you can use from ze
|
|
||||||
ro the formik input
|
|
||||||
<Field
|
|
||||||
name="name"
|
|
||||||
...ect
|
|
||||||
/>
|
|
||||||
*/}
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
|
|
||||||
For any queries or support, please reach out to our team at karimaldenlibrary@gmail.com or contact us via phone at +963951968994.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
We hope this documentation assists you in leveraging the full potential of the Structer Dashboard. Customize the dashboard as per your needs and explore its diverse range of features. Should you have any further questions, please do not hesitate to reach out.
|
|
||||||
|
|
|
||||||
76
db.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
19601
package-lock.json
generated
Normal file
98
package.json
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@mui/icons-material": "^5.14.19",
|
||||||
|
"@react-google-maps/api": "^2.19.2",
|
||||||
|
"@szhsin/react-menu": "github:szhsin/react-menu",
|
||||||
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
|
"@testing-library/react": "^13.4.0",
|
||||||
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"@tinymce/tinymce-react": "^4.3.2",
|
||||||
|
"@types/jest": "^27.5.2",
|
||||||
|
"@types/node": "^16.18.60",
|
||||||
|
"@types/react": "^18.2.33",
|
||||||
|
"@types/react-dom": "^18.2.14",
|
||||||
|
"@types/socket.io-client": "^3.0.0",
|
||||||
|
"@wojtekmaj/react-daterange-picker": "^5.4.4",
|
||||||
|
"antd": "^5.12.1",
|
||||||
|
"apexcharts": "^3.44.2",
|
||||||
|
"axios": "^1.6.0",
|
||||||
|
"bootstrap": "^5.3.2",
|
||||||
|
"chart.js": "^4.4.0",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
|
"formik": "^2.4.5",
|
||||||
|
"history": "^5.3.0",
|
||||||
|
"i18next": "^23.6.0",
|
||||||
|
"i18next-browser-languagedetector": "^7.1.0",
|
||||||
|
"json-server": "^0.17.4",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-apexcharts": "^1.4.1",
|
||||||
|
"react-bootstrap": "^2.9.1",
|
||||||
|
"react-bootstrap-sweetalert": "^5.2.0",
|
||||||
|
"react-chartjs-2": "^5.2.0",
|
||||||
|
"react-confirm-alert": "^3.0.6",
|
||||||
|
"react-data-table-component": "^7.5.4",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-feather": "^2.0.10",
|
||||||
|
"react-i18next": "^13.3.1",
|
||||||
|
"react-icons": "^4.11.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-redux": "^8.1.3",
|
||||||
|
"react-router-dom": "^6.18.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"react-select": "^5.7.7",
|
||||||
|
"react-simple-star-rating": "^5.1.7",
|
||||||
|
"react-switch": "^7.0.0",
|
||||||
|
"react-tabs": "^6.0.2",
|
||||||
|
"react-toastify": "^9.1.3",
|
||||||
|
"react-toggle": "^4.1.3",
|
||||||
|
"reactstrap": "^9.2.0",
|
||||||
|
"redux": "^4.2.1",
|
||||||
|
"sass": "^1.69.5",
|
||||||
|
"socket.io-client": "^4.7.2",
|
||||||
|
"styled-components": "5.3.3",
|
||||||
|
"ts-xlsx": "^0.0.11",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"web-vitals": "^2.1.4",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"yup": "^1.3.2",
|
||||||
|
"zustand": "^4.4.5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject",
|
||||||
|
"g:api": "node src/Extensions/FileGenerator/generateApi.js",
|
||||||
|
"g:column": "node src/Extensions/FileGenerator/generateColumn.js",
|
||||||
|
"g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js",
|
||||||
|
"g:page": "node src/Extensions/FileGenerator/generatePage.js",
|
||||||
|
"g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js ",
|
||||||
|
"g:modal:add": "node src/Extensions/FileGenerator/generateEditModal.js ",
|
||||||
|
"g:model:edit": "node src/Extensions/FileGenerator/generateEditModal.js "
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react-toggle": "^4.0.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/Layout/Ar.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/Layout/CarImage.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
1582
public/Layout/CarImage2.svg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
1
public/Layout/En.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="42.67" height="32" viewBox="0 0 640 480"><defs><clipPath id="flagUm4x30"><path fill-opacity=".7" d="M0 0h682.7v512H0z"/></clipPath></defs><g fill-rule="evenodd" clip-path="url(#flagUm4x30)" transform="scale(.9375)"><g stroke-width="1pt"><path fill="#bd3d44" d="M0 0h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8V197H0zm0 78.8h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8v39.4H0zm0 78.8h972.8V512H0z"/><path fill="#fff" d="M0 39.4h972.8v39.4H0zm0 78.8h972.8v39.3H0zm0 78.7h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8v39.4H0z"/></g><path fill="#192f5d" d="M0 0h389.1v275.7H0z"/><path fill="#fff" d="M32.4 11.8L36 22.7h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9H177l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 39.4l3.5 10.9h11.5L70.6 57L74 67.9l-9-6.7l-9.3 6.7L59 57l-9-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7L124 57l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5L330 57l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 66.9L36 78h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7H29zm64.9 0l3.5 11h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zm64.8 0l3.6 11H177l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7h11.5zm64.9 0l3.5 11H242l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 11h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.2-6.7h11.4zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.5zM64.9 94.5l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 122.1L36 133h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9H177l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 149.7l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zM32.4 177.2l3.6 11h11.4l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 11H177l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 11H242l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 11h11.4l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 204.8l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 232.4l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5L103 250l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 10.9H177l-9 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.5z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
27
public/Layout/KarimLogo.svg
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg class="Logo" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1367 766.99">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-2 {
|
||||||
|
fill:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-3 {
|
||||||
|
fill: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_1-2" data-name="Layer 1-2">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="cls-2" d="M540.42,301c0-7.67,0-15.33,.96-22.04,0-1.92,.96-1.92,1.92-2.87,33.54-15.33,54.62-42.17,69-75.71,4.79-11.5,7.67-22.04,8.62-34.5,.96-7.67,0-15.33-2.87-23-4.79-10.54-16.29-15.33-28.75-12.46-16.29,3.83-26.83,15.33-36.42,28.75-11.5,17.25-16.29,37.37-20.12,56.54-6.71,31.62-8.62,63.25-10.54,94.87-1.92,26.83-3.83,53.67-7.67,80.5-.96,4.79-1.92,9.58-2.87,14.37-.96,4.79,0,4.79,3.83,3.83,16.29-3.83,31.62-8.62,46-16.29,31.62-15.33,59.42-35.46,84.33-58.46,45.04-43.12,81.46-92,105.41-149.5,13.42-31.62,21.08-65.17,18.21-99.66,0-3.83,0-8.62-.96-12.46-.96-4.79,.96-6.71,4.79-8.62,8.62-3.83,18.21-4.79,27.79-4.79,6.71,0,13.42,.96,21.08,2.87,14.37,2.87,24.92,12.46,32.58,23.96,.96,.96,1.92,2.87,.96,3.83s-2.87,0-3.83,0c-5.75-1.92-11.5-3.83-18.21-2.87-13.42,.96-22.04,8.62-24.92,23-2.87,10.54-2.87,22.04-2.87,33.54,.96,20.12,3.83,40.25,3.83,60.37,0,5.75-1.92,11.5-4.79,17.25-43.12,71.87-98.71,130.33-172.5,171.54-39.29,22.04-79.54,37.37-123.62,47.92-3.83,.96-5.75,1.92-6.71,5.75-14.37,56.54-39.29,107.33-77.62,151.41-25.87,30.67-56.54,54.62-93.92,69.96-18.21,7.67-37.37,11.5-57.5,6.71-26.83-6.71-42.17-24.92-49.83-50.79-14.37-48.87,8.62-104.46,53.67-132.25,17.25-10.54,35.46-18.21,56.54-17.25,5.75,0,10.54,.96,16.29,1.92,4.79,.96,6.71,3.83,6.71,7.67,.96,13.42-4.79,24.92-14.37,33.54-3.83,3.83-7.67,3.83-12.46,0-4.79-3.83-9.58-7.67-14.37-9.58-18.21-9.58-35.46-5.75-48.87,9.58-13.42,15.33-20.12,33.54-24.92,52.71-3.83,17.25-4.79,34.5-3.83,51.75,.96,10.54,2.87,21.08,8.62,30.67,6.71,10.54,16.29,13.42,27.79,10.54,10.54-2.87,20.12-9.58,27.79-18.21,31.62-31.62,56.54-68.04,73.79-110.21,22.04-55.58,39.29-113.08,47.92-172.5,4.79-33.54,10.54-67.08,20.12-99.66,12.46-42.17,28.75-82.42,56.54-116.91,17.25-21.08,36.42-39.29,63.25-46.96,23-6.71,44.08-3.83,61.33,14.37,10.54,10.54,14.37,23.96,15.33,38.33,1.92,25.87-6.71,48.87-18.21,71.87-18.21,35.46-46,61.33-82.42,77.62q1.92,0,.96,0h0Z"/>
|
||||||
|
<path class="cls-2" d="M1056.95,437.08c-2.87-2.87-6.71-5.75-9.58-8.62s-3.83-2.87-5.75,.96c-10.54,24.92-22.04,49.83-34.5,74.75-11.5,21.08-23,42.17-39.29,59.42-5.75,6.71-12.46,12.46-20.12,16.29-15.33,7.67-29.71,1.92-36.42-13.42-3.83-7.67-3.83-15.33-3.83-23.96,0-21.08,5.75-41.21,12.46-60.37,7.67-21.08,15.33-41.21,29.71-58.46,9.58-11.5,21.08-22.04,36.42-25.87,6.71-1.92,13.42-.96,19.17,0,1.92,0,2.87,.96,3.83-1.92,9.58-23.96,19.17-48.87,26.83-73.79-3.83,3.83-7.67,8.62-11.5,12.46-9.58,10.54-21.08,21.08-33.54,27.79-27.79,17.25-56.54,13.42-80.5-9.58-14.37-13.42-23-30.67-29.71-48.87-1.92-4.79-3.83-9.58-6.71-15.33-.96,8.62-.96,16.29-2.87,24.92-6.71,46-15.33,92-40.25,132.25-15.33,25.87-37.37,45.04-67.08,53.67-.96,0-1.92,0-2.87,.96-.96,0-1.92,.96-1.92-.96,32.58-10.54,52.71-34.5,66.12-65.17,8.62-20.12,13.42-41.21,17.25-63.25,5.75-38.33,4.79-76.67,4.79-115,0-12.46-1.92-24.92,0-36.42,.96-8.62,4.79-15.33,13.42-19.17,5.75-2.87,10.54-1.92,16.29,.96,7.67,5.75,12.46,13.42,16.29,21.08,9.58,17.25,15.33,36.42,23,54.62,4.79,12.46,10.54,24.92,18.21,36.42,12.46,18.21,24.92,20.12,42.17,6.71,17.25-13.42,30.67-30.67,42.17-48.87,27.79-41.21,50.79-86.25,70.92-132.25,5.75-13.42,11.5-26.83,18.21-39.29,4.79-8.62,12.46-12.46,22.04-11.5,8.62,.96,14.37,6.71,16.29,15.33,1.92,6.71,.96,12.46-.96,19.17-12.46,43.12-26.83,86.25-40.25,128.41-18.21,55.58-37.37,111.16-59.42,164.83-.96,2.87-.96,3.83,.96,5.75,3.83,3.83,6.71,7.67,10.54,12.46q.96,2.87,0,2.87h0Z"/>
|
||||||
|
<path class="cls-2" d="M561.5,456.25c1.92,3.83,.96,8.62,.96,12.46-.96,44.08,4.79,87.21,22.04,127.46,4.79,12.46,11.5,23,21.08,32.58,17.25,18.21,40.25,23,63.25,12.46,20.12-9.58,35.46-23.96,50.79-40.25,2.87-2.87,5.75-5.75,7.67-9.58,1.92-2.87,4.79-2.87,7.67-1.92,16.29,5.75,28.75,15.33,37.37,29.71,2.87,4.79,2.87,9.58-.96,14.37-15.33,20.12-34.5,35.46-59.42,41.21-22.04,4.79-43.12,.96-63.25-9.58-27.79-14.37-48.87-36.42-66.12-62.29-19.17-29.71-31.62-62.29-38.33-96.79-1.92-8.62-1.92-17.25-2.87-25.87,0-1.92,0-2.87,.96-3.83,5.75-6.71,12.46-13.42,19.17-20.12h0Z"/>
|
||||||
|
<path class="cls-2" d="M844.21,140.96c0-6.71,4.79-11.5,10.54-11.5s11.5,5.75,11.5,11.5-4.79,11.5-11.5,11.5c-5.75,0-10.54-4.79-10.54-11.5h0Z"/>
|
||||||
|
</g>
|
||||||
|
<path class="cls-3" d="M935.25,547.29c-.96-4.79-1.92-8.62-1.92-12.46,.96-32.58,6.71-64.21,21.08-93.91,6.71-12.46,14.37-23.96,27.79-31.62,5.75-2.87,12.46-4.79,19.17-3.83,2.87,0,2.87,.96,1.92,2.87-17.25,41.21-33.54,83.37-55.58,121.71-2.88,6.71-7.67,11.5-12.46,17.25h0Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
54
public/Layout/KarimLogo.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const KarimLogo = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<svg
|
||||||
|
className="KarimLogo"
|
||||||
|
id="Layer_1"
|
||||||
|
data-name="Layer 1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1367 766.99"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html:
|
||||||
|
"\n .cls-2 {\n fill:white;\n }\n\n .cls-3 {\n fill: transparent;\n }\n "
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_1-2" data-name="Layer 1-2">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
className="cls-2"
|
||||||
|
d="M540.42,301c0-7.67,0-15.33,.96-22.04,0-1.92,.96-1.92,1.92-2.87,33.54-15.33,54.62-42.17,69-75.71,4.79-11.5,7.67-22.04,8.62-34.5,.96-7.67,0-15.33-2.87-23-4.79-10.54-16.29-15.33-28.75-12.46-16.29,3.83-26.83,15.33-36.42,28.75-11.5,17.25-16.29,37.37-20.12,56.54-6.71,31.62-8.62,63.25-10.54,94.87-1.92,26.83-3.83,53.67-7.67,80.5-.96,4.79-1.92,9.58-2.87,14.37-.96,4.79,0,4.79,3.83,3.83,16.29-3.83,31.62-8.62,46-16.29,31.62-15.33,59.42-35.46,84.33-58.46,45.04-43.12,81.46-92,105.41-149.5,13.42-31.62,21.08-65.17,18.21-99.66,0-3.83,0-8.62-.96-12.46-.96-4.79,.96-6.71,4.79-8.62,8.62-3.83,18.21-4.79,27.79-4.79,6.71,0,13.42,.96,21.08,2.87,14.37,2.87,24.92,12.46,32.58,23.96,.96,.96,1.92,2.87,.96,3.83s-2.87,0-3.83,0c-5.75-1.92-11.5-3.83-18.21-2.87-13.42,.96-22.04,8.62-24.92,23-2.87,10.54-2.87,22.04-2.87,33.54,.96,20.12,3.83,40.25,3.83,60.37,0,5.75-1.92,11.5-4.79,17.25-43.12,71.87-98.71,130.33-172.5,171.54-39.29,22.04-79.54,37.37-123.62,47.92-3.83,.96-5.75,1.92-6.71,5.75-14.37,56.54-39.29,107.33-77.62,151.41-25.87,30.67-56.54,54.62-93.92,69.96-18.21,7.67-37.37,11.5-57.5,6.71-26.83-6.71-42.17-24.92-49.83-50.79-14.37-48.87,8.62-104.46,53.67-132.25,17.25-10.54,35.46-18.21,56.54-17.25,5.75,0,10.54,.96,16.29,1.92,4.79,.96,6.71,3.83,6.71,7.67,.96,13.42-4.79,24.92-14.37,33.54-3.83,3.83-7.67,3.83-12.46,0-4.79-3.83-9.58-7.67-14.37-9.58-18.21-9.58-35.46-5.75-48.87,9.58-13.42,15.33-20.12,33.54-24.92,52.71-3.83,17.25-4.79,34.5-3.83,51.75,.96,10.54,2.87,21.08,8.62,30.67,6.71,10.54,16.29,13.42,27.79,10.54,10.54-2.87,20.12-9.58,27.79-18.21,31.62-31.62,56.54-68.04,73.79-110.21,22.04-55.58,39.29-113.08,47.92-172.5,4.79-33.54,10.54-67.08,20.12-99.66,12.46-42.17,28.75-82.42,56.54-116.91,17.25-21.08,36.42-39.29,63.25-46.96,23-6.71,44.08-3.83,61.33,14.37,10.54,10.54,14.37,23.96,15.33,38.33,1.92,25.87-6.71,48.87-18.21,71.87-18.21,35.46-46,61.33-82.42,77.62q1.92,0,.96,0h0Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="cls-2"
|
||||||
|
d="M1056.95,437.08c-2.87-2.87-6.71-5.75-9.58-8.62s-3.83-2.87-5.75,.96c-10.54,24.92-22.04,49.83-34.5,74.75-11.5,21.08-23,42.17-39.29,59.42-5.75,6.71-12.46,12.46-20.12,16.29-15.33,7.67-29.71,1.92-36.42-13.42-3.83-7.67-3.83-15.33-3.83-23.96,0-21.08,5.75-41.21,12.46-60.37,7.67-21.08,15.33-41.21,29.71-58.46,9.58-11.5,21.08-22.04,36.42-25.87,6.71-1.92,13.42-.96,19.17,0,1.92,0,2.87,.96,3.83-1.92,9.58-23.96,19.17-48.87,26.83-73.79-3.83,3.83-7.67,8.62-11.5,12.46-9.58,10.54-21.08,21.08-33.54,27.79-27.79,17.25-56.54,13.42-80.5-9.58-14.37-13.42-23-30.67-29.71-48.87-1.92-4.79-3.83-9.58-6.71-15.33-.96,8.62-.96,16.29-2.87,24.92-6.71,46-15.33,92-40.25,132.25-15.33,25.87-37.37,45.04-67.08,53.67-.96,0-1.92,0-2.87,.96-.96,0-1.92,.96-1.92-.96,32.58-10.54,52.71-34.5,66.12-65.17,8.62-20.12,13.42-41.21,17.25-63.25,5.75-38.33,4.79-76.67,4.79-115,0-12.46-1.92-24.92,0-36.42,.96-8.62,4.79-15.33,13.42-19.17,5.75-2.87,10.54-1.92,16.29,.96,7.67,5.75,12.46,13.42,16.29,21.08,9.58,17.25,15.33,36.42,23,54.62,4.79,12.46,10.54,24.92,18.21,36.42,12.46,18.21,24.92,20.12,42.17,6.71,17.25-13.42,30.67-30.67,42.17-48.87,27.79-41.21,50.79-86.25,70.92-132.25,5.75-13.42,11.5-26.83,18.21-39.29,4.79-8.62,12.46-12.46,22.04-11.5,8.62,.96,14.37,6.71,16.29,15.33,1.92,6.71,.96,12.46-.96,19.17-12.46,43.12-26.83,86.25-40.25,128.41-18.21,55.58-37.37,111.16-59.42,164.83-.96,2.87-.96,3.83,.96,5.75,3.83,3.83,6.71,7.67,10.54,12.46q.96,2.87,0,2.87h0Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="cls-2"
|
||||||
|
d="M561.5,456.25c1.92,3.83,.96,8.62,.96,12.46-.96,44.08,4.79,87.21,22.04,127.46,4.79,12.46,11.5,23,21.08,32.58,17.25,18.21,40.25,23,63.25,12.46,20.12-9.58,35.46-23.96,50.79-40.25,2.87-2.87,5.75-5.75,7.67-9.58,1.92-2.87,4.79-2.87,7.67-1.92,16.29,5.75,28.75,15.33,37.37,29.71,2.87,4.79,2.87,9.58-.96,14.37-15.33,20.12-34.5,35.46-59.42,41.21-22.04,4.79-43.12,.96-63.25-9.58-27.79-14.37-48.87-36.42-66.12-62.29-19.17-29.71-31.62-62.29-38.33-96.79-1.92-8.62-1.92-17.25-2.87-25.87,0-1.92,0-2.87,.96-3.83,5.75-6.71,12.46-13.42,19.17-20.12h0Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="cls-2"
|
||||||
|
d="M844.21,140.96c0-6.71,4.79-11.5,10.54-11.5s11.5,5.75,11.5,11.5-4.79,11.5-11.5,11.5c-5.75,0-10.54-4.79-10.54-11.5h0Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
className="cls-3"
|
||||||
|
d="M935.25,547.29c-.96-4.79-1.92-8.62-1.92-12.46,.96-32.58,6.71-64.21,21.08-93.91,6.71-12.46,14.37-23.96,27.79-31.62,5.75-2.87,12.46-4.79,19.17-3.83,2.87,0,2.87,.96,1.92,2.87-17.25,41.21-33.54,83.37-55.58,121.71-2.88,6.71-7.67,11.5-12.46,17.25h0Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default KarimLogo
|
||||||
BIN
public/Layout/LoginBg.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/Layout/Logo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/Layout/MainLogo.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/Layout/body_background.png
Normal file
|
After Width: | Height: | Size: 373 KiB |
1
public/Layout/dark.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 64 64"><path fill="currentColor" d="M43.139 2a29.885 29.885 0 0 1 5.121 16.756c0 16.701-13.686 30.24-30.57 30.24a30.656 30.656 0 0 1-15.689-4.285C7.209 54.963 17.93 62 30.318 62C47.816 62 62 47.969 62 30.66C62 17.867 54.246 6.871 43.139 2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 324 B |
2
public/Layout/desktop.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[LocalizedFileNames]
|
||||||
|
Screenshot 2023-11-29 134655.png=@Screenshot 2023-11-29 134655.png,0
|
||||||
9
public/Layout/etaxlogo.svg
Normal file
|
After Width: | Height: | Size: 144 KiB |
1
public/Layout/light.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512"><path fill="currentColor" d="M256 160c-52.9 0-96 43.1-96 96s43.1 96 96 96s96-43.1 96-96s-43.1-96-96-96zm246.4 80.5l-94.7-47.3l33.5-100.4c4.5-13.6-8.4-26.5-21.9-21.9l-100.4 33.5l-47.4-94.8c-6.4-12.8-24.6-12.8-31 0l-47.3 94.7L92.7 70.8c-13.6-4.5-26.5 8.4-21.9 21.9l33.5 100.4l-94.7 47.4c-12.8 6.4-12.8 24.6 0 31l94.7 47.3l-33.5 100.5c-4.5 13.6 8.4 26.5 21.9 21.9l100.4-33.5l47.3 94.7c6.4 12.8 24.6 12.8 31 0l47.3-94.7l100.4 33.5c13.6 4.5 26.5-8.4 21.9-21.9l-33.5-100.4l94.7-47.3c13-6.5 13-24.7.2-31.1zm-155.9 106c-49.9 49.9-131.1 49.9-181 0c-49.9-49.9-49.9-131.1 0-181c49.9-49.9 131.1-49.9 181 0c49.9 49.9 49.9 131.1 0 181z"/></svg>
|
||||||
|
After Width: | Height: | Size: 715 B |
BIN
public/Layout/logorayan.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/Layout/mac.webp
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
public/Layout/order.gif
Normal file
|
After Width: | Height: | Size: 303 KiB |
17
public/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>Etaxi - App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
25
public/manifest.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
71
src/App.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
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';
|
||||||
|
import EditAccount from './Pages/Account/EditAccount/EditAccount';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
const Page404 = lazy(() => import("./Layout/app/NotFoundPage"))
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Routes >
|
||||||
|
{/* 404 Page */}
|
||||||
|
<Route path="*" element={<Suspense fallback={<Loading />}> <Page404 /></Suspense>} />
|
||||||
|
{/* Login Page */}
|
||||||
|
{/* <Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} /> */}
|
||||||
|
|
||||||
|
{/* route not in navigation */}
|
||||||
|
|
||||||
|
|
||||||
|
{/* All Routes */}
|
||||||
|
{RoutesLinks?.map((item: any, index: number) => (
|
||||||
|
|
||||||
|
<Fragment key={index}>
|
||||||
|
|
||||||
|
|
||||||
|
if(item?.element){
|
||||||
|
<Route
|
||||||
|
key={index}
|
||||||
|
path={item.href}
|
||||||
|
element={
|
||||||
|
<Suspense fallback={<Loading />} >
|
||||||
|
<Layout>
|
||||||
|
{item?.element ?? "Please Add Element Props in Routes"}
|
||||||
|
</Layout>
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}else{
|
||||||
|
item?.children?.map((item:any)=>{
|
||||||
|
return(
|
||||||
|
<Route
|
||||||
|
path={item.href}
|
||||||
|
element={
|
||||||
|
<Suspense fallback={<Loading />} >
|
||||||
|
<Layout>
|
||||||
|
{item?.element ?? "Please Add Element Props in Routes"}
|
||||||
|
</Layout>
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Routes>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
84
src/Components/Columns/ColumnsImage.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
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 (
|
||||||
|
<Image
|
||||||
|
width={45}
|
||||||
|
height={45}
|
||||||
|
src={imageUrl }
|
||||||
|
className='p-1 mb-1 columnImage '
|
||||||
|
preview={{
|
||||||
|
toolbarRender: (
|
||||||
|
_,
|
||||||
|
{
|
||||||
|
transform: { scale },
|
||||||
|
actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },
|
||||||
|
},
|
||||||
|
) => (
|
||||||
|
<Space size={12} className="toolbar-wrapper">
|
||||||
|
<DownloadOutlined onClick={onDownload} />
|
||||||
|
<SwapOutlined rotate={90} onClick={onFlipY} />
|
||||||
|
<SwapOutlined onClick={onFlipX} />
|
||||||
|
<RotateLeftOutlined onClick={onRotateLeft} />
|
||||||
|
<RotateRightOutlined onClick={onRotateRight} />
|
||||||
|
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
|
||||||
|
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
onError={handleError}
|
||||||
|
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColumnsImage;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// name: t("image"),
|
||||||
|
// center: "true",
|
||||||
|
// cell: (row: any) => {
|
||||||
|
// return (
|
||||||
|
// <ColumnsImage src={row?.image} />
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// },
|
||||||
42
src/Components/Columns/ColumnsSwitch.tsx
Normal file
|
|
@ -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<any>();
|
||||||
|
const onSwitchChange = (checked: boolean, event: Event) => {
|
||||||
|
// formik.setFieldValue("status", checked)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
checkedChildren={icon ? <CheckOutlined /> : Front}
|
||||||
|
unCheckedChildren={icon ? <CloseOutlined /> : 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
|
||||||
|
|
||||||
|
};
|
||||||
72
src/Components/Karimalden/KarimField.scss
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
.KarimField{
|
||||||
|
>*{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.text,.ant-form-item{
|
||||||
|
margin-bottom:7px !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
>span{
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
&:focus-within{
|
||||||
|
border-color: var(--primary) ;
|
||||||
|
box-shadow: 0 0 0 1px var(--primary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&:has(.is-invalid){
|
||||||
|
border-color: red !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
input{
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
input:-webkit-autofill:active{
|
||||||
|
-webkit-box-shadow: 0 0 0 30px white inset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-select{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.Checkboxs{
|
||||||
|
padding: 4%;
|
||||||
|
}
|
||||||
|
.SearchField{
|
||||||
|
button{
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.text{
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled{
|
||||||
|
color: var(--text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isError{
|
||||||
|
outline: red 1px solid;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.Error_color{
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
}
|
||||||
|
input:-webkit-autofill {
|
||||||
|
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||||
|
}
|
||||||
|
input:-webkit-autofill:focus {
|
||||||
|
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove autofill background color on hover */
|
||||||
|
input:-webkit-autofill:hover {
|
||||||
|
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||||
|
}
|
||||||
25
src/Components/Karimalden/KarimField.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from "react";
|
||||||
|
import "./KarimField.scss";
|
||||||
|
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View';
|
||||||
|
import { KarimFieldProps } from "./types";
|
||||||
|
|
||||||
|
const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'Select':
|
||||||
|
return <SelectField {...otherProps} />;
|
||||||
|
case "DataRange":
|
||||||
|
return <DataRange {...otherProps} />;
|
||||||
|
case "Date":
|
||||||
|
return <Date {...otherProps} />;
|
||||||
|
case "Time":
|
||||||
|
return <Time {...otherProps} />;
|
||||||
|
case "File":
|
||||||
|
return <File {...otherProps} />;
|
||||||
|
case "Checkbox":
|
||||||
|
return <CheckboxField {...otherProps} />;
|
||||||
|
default:
|
||||||
|
return <Default {...otherProps} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(KarimField);
|
||||||
18
src/Components/Karimalden/Ui/KarimSpinner.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
loading: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KarimSpinner: React.FC<Props> = ({ loading, className, children }) => {
|
||||||
|
return (
|
||||||
|
<div className={className ?? ""}>
|
||||||
|
{loading ? <div className='text-center'><Spin /></div> : children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KarimSpinner;
|
||||||
36
src/Components/Karimalden/View/CheckboxField.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
import { Checkbox, Form } from 'antd';
|
||||||
|
|
||||||
|
const CheckboxField = ({ name, label, isDisabled, onChange,Group,className, props }: any) => {
|
||||||
|
|
||||||
|
const { t, formik,isError,errorMsg} = useFormField(name, props)
|
||||||
|
const CheckboxhandleChange = (value: any) => {
|
||||||
|
formik.setFieldValue(name, value?.target?.checked)
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className={Group ? "d-inline mt-3 Checkboxs" :``}>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? 'error' : ''}
|
||||||
|
help={isError ? errorMsg : ''}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
onChange={onChange || CheckboxhandleChange}
|
||||||
|
disabled={isDisabled}
|
||||||
|
checked={formik.values[name] ?? false}
|
||||||
|
className={className}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
{t(label)}
|
||||||
|
</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckboxField
|
||||||
43
src/Components/Karimalden/View/DataRange.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Form, DatePicker } from 'antd'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const DataRange = ({ name, label,Format ,props ,onChange,isDisabled,placeholder,className}: any) => {
|
||||||
|
|
||||||
|
const { errorMsg, isError, t, formik } = useFormField(name, props)
|
||||||
|
const onCalendarChange = (value: any) => {
|
||||||
|
|
||||||
|
formik.setFieldValue(name, value)
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className='KarimField'>
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label}`)}
|
||||||
|
</label>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
<RangePicker
|
||||||
|
placeholder={placeholder}
|
||||||
|
size="large"
|
||||||
|
allowClear
|
||||||
|
className={`${className} w-100`}
|
||||||
|
format={Format}
|
||||||
|
onChange={onChange || onCalendarChange}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataRange
|
||||||
43
src/Components/Karimalden/View/Date.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Form, DatePicker } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const Date = ({ name, label,picker="date" ,isDisabled,props,onChange,placeholder ,className}: any) => {
|
||||||
|
|
||||||
|
const { errorMsg, isError, t, formik } = useFormField(name, props)
|
||||||
|
const onCalendarChange = (value: any) => {
|
||||||
|
|
||||||
|
formik.setFieldValue(name, value)
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className='KarimField'>
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label}`)}
|
||||||
|
</label>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
<DatePicker
|
||||||
|
picker={picker}
|
||||||
|
placeholder={placeholder}
|
||||||
|
allowClear
|
||||||
|
className={`${className} w-100`}
|
||||||
|
// defaultValue={formik.values[name]}
|
||||||
|
size="large"
|
||||||
|
onChange={onChange || onCalendarChange}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Date
|
||||||
34
src/Components/Karimalden/View/Default.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Form, Input } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
|
||||||
|
const Default = ({ name, label, placeholder, isDisabled, onChange, props }: any) => {
|
||||||
|
const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="KarimField w-100" >
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label ? label : name}`)}
|
||||||
|
</label>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? 'error' : ''}
|
||||||
|
help={isError ? errorMsg : ''}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
as={Input}
|
||||||
|
type="text"
|
||||||
|
placeholder={t(`${placeholder ?placeholder : name}`)}
|
||||||
|
name={name}
|
||||||
|
disabled={isDisabled}
|
||||||
|
size="large"
|
||||||
|
// onChange={onChange ? onChange : handleChange}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(Default);
|
||||||
|
;
|
||||||
60
src/Components/Karimalden/View/File.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { Button, Upload, UploadFile } from 'antd'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
|
import { ImageBaseURL } from '../../../api/config';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
|
||||||
|
const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
|
||||||
|
const { formik, t ,isError} = useFormField(name, props)
|
||||||
|
const imageUrl = formik.values[name] ? ImageBaseURL + formik.values[name] : '';
|
||||||
|
const fileList: UploadFile[] = [
|
||||||
|
|
||||||
|
{
|
||||||
|
uid: '-1',
|
||||||
|
name: '',
|
||||||
|
status: 'done',
|
||||||
|
url: imageUrl,
|
||||||
|
thumbUrl: imageUrl,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const FilehandleChange = (value:any) => {
|
||||||
|
|
||||||
|
formik.setFieldValue(name, value.file.originFileObj)
|
||||||
|
|
||||||
|
};
|
||||||
|
const customRequest = async ({ onSuccess}: any) => {
|
||||||
|
onSuccess();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="KarimField">
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label || name}`)}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Upload
|
||||||
|
disabled={isDisabled}
|
||||||
|
listType="picture"
|
||||||
|
maxCount={1}
|
||||||
|
defaultFileList={[...fileList]}
|
||||||
|
onChange={onChange || FilehandleChange}
|
||||||
|
customRequest={customRequest}
|
||||||
|
className={`${className} w-100`}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
<Button className={isError ? "isError w-100" : " w-100"} icon={<UploadOutlined />}>
|
||||||
|
{placholder ?? t("upload_image") }
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
<div className='Error_color'> {isError ? "required" : ""}</div>
|
||||||
|
</Upload>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default File
|
||||||
46
src/Components/Karimalden/View/SearchField.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Input } from 'antd';
|
||||||
|
import { SearchProps } from 'antd/es/input'
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
const { Search } = Input;
|
||||||
|
|
||||||
|
const SearchField = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [searchParams,] = useSearchParams();
|
||||||
|
const location =useLocation()
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const [searchValue, setSearchValue] = useState(searchParams.get('search')|| "");
|
||||||
|
|
||||||
|
|
||||||
|
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
|
||||||
|
// console.log(value);
|
||||||
|
|
||||||
|
navigate(`${location?.pathname}?search=${value}`, { replace: true });
|
||||||
|
}
|
||||||
|
const onChange = (e :any) => {
|
||||||
|
setSearchValue(e.target.value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='SearchField'>
|
||||||
|
<Search
|
||||||
|
allowClear
|
||||||
|
enterButton={t("search")}
|
||||||
|
// size="middle"
|
||||||
|
size="large"
|
||||||
|
placeholder={t("search")}
|
||||||
|
onSearch={onSearch}
|
||||||
|
style={{ width: 250 }}
|
||||||
|
value={searchValue}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchField
|
||||||
41
src/Components/Karimalden/View/SelectField.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { Form, Select } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
|
||||||
|
const SelectField = ({ name, label, placeholder, isDisabled,option,isMulti,onChange,className, props}: any) => {
|
||||||
|
|
||||||
|
const { errorMsg, isError, t ,formik} = useFormField(name, props)
|
||||||
|
const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
|
||||||
|
formik.setFieldValue(name, value)
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className='KarimField'>
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label ? label : name}`)}
|
||||||
|
</label>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
placeholder={t(`${placeholder ?placeholder : name}`)}
|
||||||
|
disabled={isDisabled}
|
||||||
|
options={option}
|
||||||
|
size="large"
|
||||||
|
className={`${className} w-100`}
|
||||||
|
defaultValue={formik.values[name]}
|
||||||
|
allowClear
|
||||||
|
{...(isMulti && { mode: "multiple" })}
|
||||||
|
onChange={onChange || SelecthandleChange}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(SelectField);
|
||||||
41
src/Components/Karimalden/View/Time.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { Form, TimePicker } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
|
||||||
|
const Time = ({ name, label,className,isDisabled,onChange,props }: any) => {
|
||||||
|
|
||||||
|
const { errorMsg, isError, t, formik } = useFormField(name, props)
|
||||||
|
const onCalendarChange = (value: any) => {
|
||||||
|
|
||||||
|
formik.setFieldValue(name, value)
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className='KarimField'>
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label}`)}
|
||||||
|
</label>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
<TimePicker
|
||||||
|
allowClear
|
||||||
|
className={`${className} w-100`}
|
||||||
|
size="large"
|
||||||
|
defaultValue={formik.values[name]}
|
||||||
|
onChange={onChange || onCalendarChange}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Time
|
||||||
22
src/Components/Karimalden/View/index.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import Time from "./Time";
|
||||||
|
import SelectField from "./SelectField";
|
||||||
|
import Date from "./Date";
|
||||||
|
import DataRange from "./DataRange";
|
||||||
|
import CheckboxField from "./CheckboxField";
|
||||||
|
import Default from "./Default";
|
||||||
|
import File from "./File";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
Time,
|
||||||
|
SelectField,
|
||||||
|
Date,
|
||||||
|
DataRange,
|
||||||
|
CheckboxField,
|
||||||
|
Default,
|
||||||
|
File
|
||||||
|
|
||||||
|
}
|
||||||
21
src/Components/Karimalden/index.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { ErrorMessage, useField, Field, useFormikContext } from 'formik';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FaExclamationCircle } from 'react-icons/fa';
|
||||||
|
import Select from 'react-select';
|
||||||
|
import { convert_data_to_select } from '../../Layout/app/Const';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
useState,
|
||||||
|
ErrorMessage, useField, Field, useFormikContext,
|
||||||
|
useTranslation,
|
||||||
|
FaExclamationCircle,
|
||||||
|
Select,
|
||||||
|
convert_data_to_select,
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
114
src/Components/Karimalden/types.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
|
||||||
|
// export interface KarimFieldProps {
|
||||||
|
// name: string;
|
||||||
|
// type?: "text" | "Select" | "DataRange" | "Date" | "Time" | "File" | "number" | "Checkbox" | "password";
|
||||||
|
// placeholder?: string;
|
||||||
|
// label?: string;
|
||||||
|
// className?: string;
|
||||||
|
// option?: any[];
|
||||||
|
// isMulti?: boolean;
|
||||||
|
// isDisabled?: boolean;
|
||||||
|
// picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||||
|
// Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
|
||||||
|
// onChange?: (value: any) => void;
|
||||||
|
// Group?: boolean
|
||||||
|
// dir?:'ltr' | "rtl"
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface KarimFieldPropsText {
|
||||||
|
name: string;
|
||||||
|
type: "text";
|
||||||
|
placeholder?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KarimFieldPropsSelect {
|
||||||
|
name: string;
|
||||||
|
type: "Select";
|
||||||
|
placeholder?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl";
|
||||||
|
option: any[];
|
||||||
|
isMulti?: boolean;
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface KarimFieldPropsDataRange {
|
||||||
|
name: string;
|
||||||
|
type: "DataRange";
|
||||||
|
placeholder?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
|
||||||
|
}
|
||||||
|
export interface KarimFieldPropsDate {
|
||||||
|
name: string;
|
||||||
|
type: "Date";
|
||||||
|
placeholder?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KarimFieldPropsTime {
|
||||||
|
name: string;
|
||||||
|
type: "Time";
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KarimFieldPropsFile {
|
||||||
|
name: string;
|
||||||
|
type: "File";
|
||||||
|
placeholder?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface KarimFieldPropsCheckbox {
|
||||||
|
name: string;
|
||||||
|
type: "Checkbox";
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
Group?: boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface KarimFieldPropstext {
|
||||||
|
name: string;
|
||||||
|
type?: "text" | "number" | "password";
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
dir?:'ltr' | "rtl"
|
||||||
|
Group?: boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type KarimFieldProps = KarimFieldPropsText| KarimFieldPropsSelect| KarimFieldPropsDataRange| KarimFieldPropsDate| KarimFieldPropsTime| KarimFieldPropsFile| KarimFieldPropsCheckbox| KarimFieldPropstext;
|
||||||
50
src/Components/Ui/Alert.tsx
Normal file
|
|
@ -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 }) => <CustomUI onClose={onClose} options={options} />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomUIProps ={
|
||||||
|
onClose :()=> void
|
||||||
|
options:{
|
||||||
|
title?:string
|
||||||
|
confirmBtnText:string
|
||||||
|
cancelBtnText:string
|
||||||
|
body:string
|
||||||
|
onConfirm:()=>void
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function CustomUI({ onClose, options }:CustomUIProps) {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<SweetAlert
|
||||||
|
title={options.title || t(`delete_are_you_sure`)}
|
||||||
|
customClass="SweetAlert"
|
||||||
|
warning
|
||||||
|
show={true}
|
||||||
|
showCancel
|
||||||
|
reverseButtons
|
||||||
|
cancelBtnBsStyle="danger"
|
||||||
|
confirmBtnText={options.confirmBtnText || t("yes_delete_it")}
|
||||||
|
cancelBtnText={options.cancelBtnText || t("cancel")}
|
||||||
|
onConfirm={() => {
|
||||||
|
options.onConfirm();
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
onCancel={onClose}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
49
src/Components/Ui/CheckboxesVuexy.tsx
Normal file
|
|
@ -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<CheckBoxesVuexyProps> = ({
|
||||||
|
className = '',
|
||||||
|
color,
|
||||||
|
defaultChecked,
|
||||||
|
checked,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
onClick,
|
||||||
|
onChange,
|
||||||
|
size = 'md',
|
||||||
|
icon,
|
||||||
|
label,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={`vx-checkbox-con ${className} vx-checkbox-${color}`}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={defaultChecked}
|
||||||
|
checked={checked}
|
||||||
|
value={value}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onClick}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<span className={`vx-checkbox vx-checkbox-${size}`}>
|
||||||
|
<span className="vx-checkbox--check">{icon}</span>
|
||||||
|
</span>
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CheckBoxesVuexy;
|
||||||
27
src/Components/Ui/FileInput.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<div className="custom-file-input">
|
||||||
|
<label className="custom-file-label" htmlFor="inputGroupFile01">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<Input accept={accept} id="inputGroupFile01" name={name} onChange={onChange}
|
||||||
|
type="file" className="custom-file-input" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileInput
|
||||||
37
src/Components/Ui/HovarableImage.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<div style={{marginInline:"20px"}}>
|
||||||
|
<img id={ID} src={src} alt={props.alt} {...props} style={{ maxWidth:"300px", maxHeight:'50px'}}/>
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
isOpen={isOpen}
|
||||||
|
target={ID}
|
||||||
|
toggle={toggleTooltip}
|
||||||
|
style={tooltipStyle}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{ maxWidth:"300px", maxHeight:'150px'}}
|
||||||
|
src={src}
|
||||||
|
alt={props.alt}
|
||||||
|
{...imgPreviewProps}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HovarableImage;
|
||||||
|
|
||||||
34
src/Components/Ui/ImagePreview.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const ImagePreview = ({ preview, height = 200 }:any) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid lightgray",
|
||||||
|
height: `${height}px`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{preview ? (
|
||||||
|
<img
|
||||||
|
className="p-1"
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
}}
|
||||||
|
height={height}
|
||||||
|
src={preview}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div>{t("image_preview")}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImagePreview;
|
||||||
13
src/Components/Ui/LoadingButton.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Button, Spinner } from "reactstrap";
|
||||||
|
|
||||||
|
const LoadingButton = ({ isLoading = false, ...props }) => {
|
||||||
|
return (
|
||||||
|
<Button className="btn-primary" disabled={isLoading} {...props}>
|
||||||
|
{isLoading ? <Spinner style={{ marginRight: "10px" }} size="sm" /> : null}
|
||||||
|
<span >{props.children}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { LoadingButton };
|
||||||
10
src/Components/Ui/LoadingSpinner.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Spin } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function LoadingSpinner() {
|
||||||
|
return (
|
||||||
|
<Spin className='primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingSpinner
|
||||||
57
src/Components/Ui/PasswordField/PasswordField.tsx
Normal file
|
|
@ -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<PasswordFieldProps> = ({ name, label, ...props }) => {
|
||||||
|
const [field, meta] = useField({ name, ...props });
|
||||||
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||||
|
const EyeIcon = showPassword ? Eye : EyeOff;
|
||||||
|
|
||||||
|
const toggleShow = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{label && <label className="change_password_body" htmlFor={name}>{label}</label>}
|
||||||
|
<FormGroup className={"position-relative has-icon-left"}>
|
||||||
|
<Field
|
||||||
|
className={
|
||||||
|
|
||||||
|
"form-control " + (meta.touched && meta.error ? "is-invalid" : "")
|
||||||
|
}
|
||||||
|
name={name}
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
autoComplete="new-password"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<div className="form-control-position">
|
||||||
|
<EyeIcon
|
||||||
|
fontSize={12}
|
||||||
|
onClick={toggleShow}
|
||||||
|
className="cursor-pointer change_password_body"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ErrorMessage
|
||||||
|
className="field-error text-danger"
|
||||||
|
component="div"
|
||||||
|
name={field.name}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PasswordField.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { PasswordField };
|
||||||
10
src/Components/Ui/PasswordField/index.css
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
.back-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
padding-left: 0.8rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
24
src/Components/Ui/ProgressBar.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<div className="my-1">
|
||||||
|
<Progress color={color} style={{ height: "1rem" }} className=" mt-4" value={value}>
|
||||||
|
{value}%
|
||||||
|
</Progress>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default ProgressBar;
|
||||||
29
src/Components/Ui/SelectField.tsx
Normal file
|
|
@ -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<any>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ValidatedField
|
||||||
|
CustomField={Select}
|
||||||
|
label={label}
|
||||||
|
name={name}
|
||||||
|
className="React"
|
||||||
|
classNamePrefix="select"
|
||||||
|
options={options || []}
|
||||||
|
value={options?.find((opt:any) => opt.value === formik?.values[name]) || ""}
|
||||||
|
onChange={(opt:any) => formik.setFieldValue(name, opt.value)}
|
||||||
|
onBlur={() => formik.setFieldTouched(name)}
|
||||||
|
key={name}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default SelectField;
|
||||||
|
|
||||||
|
|
||||||
59
src/Components/Ui/StaticsCard/StaticCard.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Card, CardBody } from "reactstrap";
|
||||||
|
import Chart from "react-apexcharts";
|
||||||
|
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 (
|
||||||
|
<Card {...rest} onClick={()=>navigate(pathWhenClick , {replace:true})}>
|
||||||
|
<CardBody
|
||||||
|
className={`${
|
||||||
|
className ? className : "stats-card-body"
|
||||||
|
} d-flex justify-content-center flex-column text-center pb-2 pt-2 primary `}
|
||||||
|
>
|
||||||
|
<div className="icon-section">
|
||||||
|
<div
|
||||||
|
className={`avatar avatar-stats p-50 m-0 ${ "bg-rgba-primary"}`}
|
||||||
|
>
|
||||||
|
<p className="mb-0 text-bold-700">{CardTitle}</p>
|
||||||
|
<div className="avatar-content ">{icon}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={"title-section " + (iconLeft ? "ml-2" : "")}>
|
||||||
|
<h2 className="text-bold-600 mb-25 ">{count}</h2>
|
||||||
|
<p className="mb-2 Content">{CardContent}</p>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatisticsCard;
|
||||||
17
src/Components/Ui/StatusBadge.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Badge color={status ? "success" : "danger"}>
|
||||||
|
{status ? t("active") : t("inacticve")}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default StatusBadge;
|
||||||
38
src/Components/Ui/TableActions.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<div className="data-list-action TableActions">
|
||||||
|
{showEdit && <FaEdit onClick={onEdit} className="cursor-pointer m-1" size={20} />}
|
||||||
|
{showDelete && (
|
||||||
|
<FaTrash
|
||||||
|
onClick={() =>
|
||||||
|
confirmAlert({
|
||||||
|
onConfirm: () => {
|
||||||
|
onDelete();
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="cursor-pointer"
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TableActions;
|
||||||
31
src/Components/Ui/ThreeSwitchState/TripleSwitch.tsx
Normal file
|
|
@ -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 = () => (
|
||||||
|
<>
|
||||||
|
|
||||||
|
|
||||||
|
<Radio.Group onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
|
||||||
|
<Radio.Button value="a">Hangzhou</Radio.Button>
|
||||||
|
<Radio.Button value="b" disabled>
|
||||||
|
Shanghai
|
||||||
|
</Radio.Button>
|
||||||
|
<Radio.Button value="c">Beijing</Radio.Button>
|
||||||
|
<Radio.Button value="d">Chengdu</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
|
||||||
|
<Radio.Group disabled onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
|
||||||
|
<Radio.Button value="a">Hangzhou</Radio.Button>
|
||||||
|
<Radio.Button value="b">Shanghai</Radio.Button>
|
||||||
|
<Radio.Button value="c">Beijing</Radio.Button>
|
||||||
|
<Radio.Button value="d">Chengdu</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
||||||
39
src/Components/Ui/ToggleStatus.tsx
Normal file
|
|
@ -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, toggleMutation, ...props }:any) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const handleSwitch = () => {
|
||||||
|
toggleMutation.mutate({
|
||||||
|
id: object.id,
|
||||||
|
new_status: !object.is_active,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="p-0">
|
||||||
|
<p
|
||||||
|
className="mb-0 p-0"
|
||||||
|
style={{ whiteSpace: "nowrap", textAlign: "center" }}
|
||||||
|
>
|
||||||
|
{object.is_active ? t("active") : t("inactive")}
|
||||||
|
</p>
|
||||||
|
<Toggle
|
||||||
|
{...props}
|
||||||
|
className="custom-switch"
|
||||||
|
disabled={toggleMutation.isLoading}
|
||||||
|
onChange={handleSwitch}
|
||||||
|
checked={object.is_active}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
64
src/Components/Ui/ValidatedField.tsx
Normal file
|
|
@ -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 && (
|
||||||
|
<label htmlFor={name}>
|
||||||
|
{label} {props.required || isRequired ? "*" : ""} {labelIcon}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
<FormGroup
|
||||||
|
className={Icon ? "position-relative has-icon-left" : ""}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
|
<Wrapper
|
||||||
|
className={
|
||||||
|
"form-control " + (meta.touched && meta.error ? "is-invalid" : "")
|
||||||
|
}
|
||||||
|
{...fieldProps}
|
||||||
|
required={props.isRequired}
|
||||||
|
{...props}
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
{Icon && (
|
||||||
|
<div className="form-control-position">
|
||||||
|
<Icon size={15} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ErrorMessage name={field.name}>
|
||||||
|
{(msg) => <span className="field-error text-danger">{t(msg)}</span>}
|
||||||
|
</ErrorMessage>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export { ValidatedField };
|
||||||
17
src/Components/Ui/index.tsx
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
57
src/Components/Ui/tables/Actions.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React , {ReactNode} from "react";
|
||||||
|
import { FaEdit, FaEye, FaTrash } from "react-icons/fa";
|
||||||
|
import CustomConfirmAlert from "../Alert";
|
||||||
|
import { usePageState } from "../../../lib/state mangment/LayoutPagestate";
|
||||||
|
|
||||||
|
type TableActionsProps = {
|
||||||
|
onDelete?: () => any;
|
||||||
|
onEdit?: () => any;
|
||||||
|
onView?:() => any;
|
||||||
|
showView?: boolean;
|
||||||
|
showEdit?: boolean;
|
||||||
|
showDelete?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
objectToEdit:any
|
||||||
|
className?:string
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children,className }:TableActionsProps) => {
|
||||||
|
// const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children }:TableActionsProps) => {
|
||||||
|
|
||||||
|
// console.log(objectToEdit);
|
||||||
|
|
||||||
|
const {setObjectToEdit , setIsOpenEditModel} = usePageState()
|
||||||
|
return (
|
||||||
|
<div className={`data-list-action TableActions ${className}`}>
|
||||||
|
{showEdit && <FaEdit onClick={()=>{
|
||||||
|
setObjectToEdit(objectToEdit)
|
||||||
|
setIsOpenEditModel()
|
||||||
|
onEdit()
|
||||||
|
|
||||||
|
}} className="cursor-pointer m-2" size={20} />}
|
||||||
|
{showView && <FaEye onClick={onView} className="cursor-pointer m-2" size={25} />}
|
||||||
|
|
||||||
|
{showDelete && (
|
||||||
|
<FaTrash
|
||||||
|
onClick={() =>
|
||||||
|
CustomConfirmAlert({
|
||||||
|
onConfirm: () => {
|
||||||
|
|
||||||
|
|
||||||
|
onDelete();
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="cursor-pointer"
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TableActions;
|
||||||
40
src/Components/Ui/tables/ConfirmAlert.tsx
Normal file
|
|
@ -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 }) => <CustomUI onClose={onClose} options={options} />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <SweetAlert {...sweetAlertProps}>{options.body || "You won't be able to revert this!"}</SweetAlert>;
|
||||||
|
}
|
||||||
24
src/Components/Ui/useImagePreview.tsx
Normal file
|
|
@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
62
src/Components/Utils/BlockModal.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
|
||||||
|
interface BlockModelProps {
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlockModel: React.FC<BlockModelProps> = ({Mutation ,type}) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
|
||||||
|
|
||||||
|
const {isOpenBlock:isOpen , objectID , setIsopenBlock:setIsOpen} = useCommonModelState()
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const blockInput = document.getElementById('block_input') as HTMLInputElement;
|
||||||
|
if (blockInput) {
|
||||||
|
Mutation.mutate({ [key_to_api]: objectID, block_timer: blockInput.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Mutation.isSuccess) {
|
||||||
|
setIsOpen();
|
||||||
|
}
|
||||||
|
}, [Mutation.isSuccess, setIsOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsOpen()}>
|
||||||
|
{t("al")}{type}{t('_block_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('blocking_')}{type}</h1>
|
||||||
|
<BsExclamationCircle className='modal_info' style={{ fontSize: '100px', color: '#f8be86', margin: '20px 0' }} />
|
||||||
|
<div className=''>
|
||||||
|
<Label for='block_input'>{t('date_blocking')}</Label>
|
||||||
|
<Input id='block_input' placeholder={t('date_blocking')} type='date' />
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsOpen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton color='primary' onClick={handleSubmit} isLoading={Mutation.isLoading} type='submit'>
|
||||||
|
{t('add_block_for_')}{type}
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlockModel;
|
||||||
63
src/Components/Utils/GiftModal.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Col, Input, Label, Modal, ModalHeader, Row } from 'reactstrap';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
|
||||||
|
|
||||||
|
interface GiftModalProps {
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftModal: React.FC<GiftModalProps> = ({Mutation ,type }) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const {isOpenGift:isOpen , objectID , setIsopenGift:setIsOpen} = useCommonModelState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Mutation.isSuccess) {
|
||||||
|
setIsOpen();
|
||||||
|
}
|
||||||
|
}, [Mutation.isSuccess, setIsOpen]);
|
||||||
|
|
||||||
|
const handleGift = () => {
|
||||||
|
const enterCodesInput = document.getElementById('enter_codes') as HTMLInputElement;
|
||||||
|
if (enterCodesInput && enterCodesInput.value) {
|
||||||
|
Mutation.mutate({ type, id: objectID, value: enterCodesInput.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsOpen()}>
|
||||||
|
{t("al")}{type} {t('_gift_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<Row>
|
||||||
|
<Col className='' style={{ width: 300 }}>
|
||||||
|
<Label for='enter_codes' className='modal_info'>{t('value')}</Label>
|
||||||
|
<Input id='enter_codes' placeholder={t('value')} type='number' />
|
||||||
|
<Col style={{ marginTop: 20, display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Button color='danger' onClick={() => setIsOpen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton
|
||||||
|
color='primary'
|
||||||
|
onClick={handleGift}
|
||||||
|
type='submit'
|
||||||
|
isLoading={Mutation.isLoading}>
|
||||||
|
{t('give')}
|
||||||
|
</LoadingButton>
|
||||||
|
</Col>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GiftModal;
|
||||||
93
src/Components/Utils/Loading/Loading.scss
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
src/Components/Utils/Loading/Loading.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './Loading.scss'
|
||||||
|
const Loading = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Loading">
|
||||||
|
<div className="wrapper">
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Loading
|
||||||
43
src/Components/Utils/SearchBar/SearchBar.scss
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
14
src/Components/Utils/SearchBar/SearchBar.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './SearchBar.scss'
|
||||||
|
const SearchBar = () => {
|
||||||
|
return (
|
||||||
|
<div className='SearchBar'>
|
||||||
|
<div className="group">
|
||||||
|
<svg className="icon" viewBox="0 0 24 24"><g><path d="M21.53 20.47l-3.66-3.66C19.195 15.24 20 13.214 20 11c0-4.97-4.03-9-9-9s-9 4.03-9 9 4.03 9 9 9c2.215 0 4.24-.804 5.808-2.13l3.66 3.66c.147.146.34.22.53.22s.385-.073.53-.22c.295-.293.295-.767.002-1.06zM3.5 11c0-4.135 3.365-7.5 7.5-7.5s7.5 3.365 7.5 7.5-3.365 7.5-7.5 7.5-7.5-3.365-7.5-7.5z"></path></g></svg>
|
||||||
|
<input placeholder="Search" type="search" className="input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBar
|
||||||
84
src/Components/Utils/Theme.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { usePageState } from '../../lib/state mangment/LayoutPagestate';
|
||||||
|
import { BsFillMoonStarsFill, BsFillSunFill, BsSunglasses } from 'react-icons/bs';
|
||||||
|
|
||||||
|
|
||||||
|
let What_the_Theme = localStorage.getItem('theme') ?? "light";
|
||||||
|
|
||||||
|
if (What_the_Theme === "dark") {
|
||||||
|
|
||||||
|
document.body.classList.add('dark')}
|
||||||
|
else if (What_the_Theme === "glass") {
|
||||||
|
|
||||||
|
document.body.classList.add('glass')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function Theme() {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const {setThemChange} = usePageState()
|
||||||
|
|
||||||
|
const changeTheme = (newTheme : any) => {
|
||||||
|
|
||||||
|
|
||||||
|
if(newTheme === "dark"){
|
||||||
|
document.body.classList.remove('glass');
|
||||||
|
document.body.classList.add('dark');localStorage.setItem("theme", "dark");
|
||||||
|
What_the_Theme = "dark"
|
||||||
|
}
|
||||||
|
else if(newTheme === "light"){
|
||||||
|
document.body.classList.remove('glass');
|
||||||
|
document.body.classList.remove('dark');localStorage.setItem("theme", "light");
|
||||||
|
What_the_Theme = "light"
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(newTheme === "glass"){
|
||||||
|
document.body.classList.remove('dark'); document.body.classList.add('glass'); localStorage.setItem("theme", "glass");
|
||||||
|
What_the_Theme = "glass"
|
||||||
|
|
||||||
|
}
|
||||||
|
setThemChange()
|
||||||
|
};
|
||||||
|
/// BsSunglasses BsFillSunFill BsFillMoonStarsFill
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Theme'>
|
||||||
|
<Menu menuButton={<MenuButton>
|
||||||
|
{(What_the_Theme === "light") ?
|
||||||
|
<>
|
||||||
|
<BsFillSunFill/>
|
||||||
|
{t("light")}
|
||||||
|
</>
|
||||||
|
|
||||||
|
: (What_the_Theme === "dark") ?
|
||||||
|
<>
|
||||||
|
<BsFillMoonStarsFill/>
|
||||||
|
{t("dark")}
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<BsSunglasses/>
|
||||||
|
{t("glass")}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</MenuButton>} transition>
|
||||||
|
<MenuItem onClick={() => changeTheme('light')}>
|
||||||
|
<BsFillSunFill/>
|
||||||
|
{t("light")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => changeTheme('dark')}>
|
||||||
|
<BsFillMoonStarsFill/>
|
||||||
|
{t("dark")} </MenuItem>
|
||||||
|
<MenuItem onClick={() => changeTheme('glass')}>
|
||||||
|
<BsSunglasses/>
|
||||||
|
{t("glass")} </MenuItem>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
77
src/Components/Utils/Translate.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
|
||||||
|
import { useTranslation, initReactI18next } from 'react-i18next';
|
||||||
|
import i18n from 'i18next'; // Make sure this import is correct
|
||||||
|
import translationEN from '../../translate/en.json';
|
||||||
|
import translationAR from '../../translate/ar.json';
|
||||||
|
|
||||||
|
i18n.use(initReactI18next).init({
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
translation: translationEN
|
||||||
|
},
|
||||||
|
ar: {
|
||||||
|
translation: translationAR
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lng: 'en',
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let What_the_language = localStorage.getItem('language') ?? "en";
|
||||||
|
|
||||||
|
if (What_the_language === "en") {
|
||||||
|
|
||||||
|
i18n.changeLanguage('en');
|
||||||
|
document.body.setAttribute('dir', 'ltr'); document.body.classList.add('en')}
|
||||||
|
else{
|
||||||
|
i18n.changeLanguage('ar');
|
||||||
|
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function Translate() {
|
||||||
|
const { t, i18n } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
|
const changeLanguage = (newLanguage : any) => {
|
||||||
|
i18n.changeLanguage(newLanguage);
|
||||||
|
if(newLanguage === "Ar"){
|
||||||
|
i18n.changeLanguage('ar');
|
||||||
|
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');localStorage.setItem("language", "ar");
|
||||||
|
What_the_language = "ar"
|
||||||
|
}
|
||||||
|
else if(newLanguage === "En"){
|
||||||
|
i18n.changeLanguage('en');
|
||||||
|
document.body.setAttribute('dir', 'ltr'); document.body.classList.remove('ar');localStorage.setItem("language", "en");
|
||||||
|
What_the_language = "en"
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Translate'>
|
||||||
|
<Menu menuButton={<MenuButton>
|
||||||
|
{What_the_language === "ar" ?
|
||||||
|
<>
|
||||||
|
<img alt='' src={`/Layout/Ar.svg`} width={20} height={20} /> {t("Arabic")}
|
||||||
|
</>
|
||||||
|
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<img alt='' src={`/Layout/En.svg`} width={20} height={20} /> {t("English")}
|
||||||
|
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</MenuButton>} transition>
|
||||||
|
<MenuItem onClick={() => changeLanguage('Ar')}>
|
||||||
|
<img alt='' src='/Layout/Ar.svg' width={20} height={20} /> {t("Arabic")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => changeLanguage('En')}>
|
||||||
|
<img alt='' src='/Layout/En.svg' width={20} height={20} /> {t("English")}
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
src/Components/Utils/UnBlockModal.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { CiLock } from "react-icons/ci";
|
||||||
|
|
||||||
|
interface UnBlockModalProps {
|
||||||
|
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnBlockModal: React.FC<UnBlockModalProps> = ({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 (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsopen()}>
|
||||||
|
{t("al")}{type} {t('un_block_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('un_blocking_')}{type}</h1>
|
||||||
|
<CiLock className='modal_info' style={{ fontSize: '100px', color: 'black', margin: '20px 0' }} />
|
||||||
|
<div className=''>
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsopen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton color='primary' onClick={handleSubmit} type='submit' isLoading={Mutation.isLoading}>
|
||||||
|
{t('un_block_for_')}{type}
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnBlockModal;
|
||||||
10
src/Components/order/OrderStatus.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
// for write later
|
||||||
|
function OrderStatus() {
|
||||||
|
return (
|
||||||
|
<div>OrderStatus</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrderStatus
|
||||||
42
src/Extensions/Editor/HtmlEditor.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Editor } from '@tinymce/tinymce-react';
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
|
||||||
|
interface HtmlEditorProps {
|
||||||
|
langCode: number;
|
||||||
|
name: string;
|
||||||
|
editorState: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HtmlEditor: FC<HtmlEditorProps> = ({ langCode, name, editorState, ...props }) => {
|
||||||
|
const formik = useFormikContext();
|
||||||
|
|
||||||
|
const ar: boolean = langCode === 2;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Editor
|
||||||
|
apiKey='6xf0byrgd7m2j28p9dfjittsq884x9j0d3e6dsterqrvtvez'
|
||||||
|
value={editorState}
|
||||||
|
init={{
|
||||||
|
height: 500,
|
||||||
|
menubar: false,
|
||||||
|
directionality: ar ? "rtl" : "ltr",
|
||||||
|
plugins: [
|
||||||
|
'advlist autolink lists link image charmap print preview anchor',
|
||||||
|
'searchreplace visualblocks code fullscreen',
|
||||||
|
'insertdatetime media table paste code help wordcount'
|
||||||
|
],
|
||||||
|
toolbar: 'undo redo | formatselect | ' +
|
||||||
|
'bold italic backcolor | alignleft aligncenter ' +
|
||||||
|
'alignright alignjustify | bullist numlist outdent indent | ' +
|
||||||
|
'removeformat | help',
|
||||||
|
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
|
||||||
|
}}
|
||||||
|
onEditorChange={(newValue, editor) => {
|
||||||
|
formik.setFieldValue(name, newValue)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { HtmlEditor };
|
||||||
43
src/Extensions/Editor/SingleLangEditor.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { HtmlEditor } from "./HtmlEditor";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface SingleLangEditorProps {
|
||||||
|
langCode: number;
|
||||||
|
property: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROPERTY_TYPES: string[] = [
|
||||||
|
"privacy_description",
|
||||||
|
"conditions_description",
|
||||||
|
"about_us_description",
|
||||||
|
"product_description",
|
||||||
|
"auction_description"
|
||||||
|
];
|
||||||
|
|
||||||
|
const SingleLangEditor: FC<SingleLangEditorProps> = ({ langCode, property }) => {
|
||||||
|
const formik:any = useFormikContext();
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const label = `${t(property)} (${t(`lang_${langCode}`)})`;
|
||||||
|
const fieldName = `translated_fields[${langCode}][${property}]`;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h5 className="Information_title">{label}</h5>
|
||||||
|
<HtmlEditor
|
||||||
|
langCode={langCode}
|
||||||
|
name={fieldName}
|
||||||
|
editorState={formik.values.translated_fields[langCode][property]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SingleLangEditor.propTypes = {
|
||||||
|
langCode: PropTypes.oneOf([1, 2]).isRequired,
|
||||||
|
property: PropTypes.oneOf(PROPERTY_TYPES).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SingleLangEditor;
|
||||||
23
src/Extensions/Editor/StatusCard.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Card, CardBody, Spinner } from "reactstrap";
|
||||||
|
|
||||||
|
interface StatusCardProps {
|
||||||
|
isLoading: boolean;
|
||||||
|
isError: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatusCard= ({ isLoading, isError }:StatusCardProps) => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardBody
|
||||||
|
className="d-flex align-items-center justify-content-center"
|
||||||
|
style={{ height: "15rem" }}
|
||||||
|
>
|
||||||
|
{isLoading && <Spinner size="lg" color="primary" />}
|
||||||
|
{isError && <h4>Failed !</h4>}
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatusCard;
|
||||||
65
src/Extensions/FileGenerator/generateAddModal.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
|
||||||
|
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
function Add${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
|
||||||
|
const handelSubmit = (values:any )=>{
|
||||||
|
|
||||||
|
const dataToSend = getDataToSend(values)
|
||||||
|
|
||||||
|
mutate(dataToSend)
|
||||||
|
// Submit Value
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
|
||||||
|
isAddModal={true}
|
||||||
|
getInitialValues={getInitialValues()}
|
||||||
|
handleSubmit={handelSubmit}
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
headerText={t('Add') +t('${(fileName)}')}
|
||||||
|
|
||||||
|
getValidationSchema={getValidationSchema()}>
|
||||||
|
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Add${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
40
src/Extensions/FileGenerator/generateApi.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const fileName = process.argv[2]
|
||||||
|
|
||||||
|
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import useAddMutation from "./helper/useAddMutation"
|
||||||
|
import useDeleteMutation from "./helper/useDeleteMutation"
|
||||||
|
import useGetQuery from "./helper/useGetQuery"
|
||||||
|
import useUpdateMutation from "./helper/useUpdateMutation"
|
||||||
|
|
||||||
|
const API = {
|
||||||
|
GET: "/api/admin/${fileName}",
|
||||||
|
ADD: "/api/admin/${fileName}/create",
|
||||||
|
UPDATE: "/api/admin/${fileName}/update",
|
||||||
|
DELETE: "/api/admin/${fileName}/delete",
|
||||||
|
};
|
||||||
|
|
||||||
|
const KEY = "${fileName.toUpperCase()}";
|
||||||
|
export const useGet${capitalizeFirstLetter(fileName)} = (params?:any) => useGetQuery(KEY, API.GET, params);
|
||||||
|
export const useAdd${capitalizeFirstLetter(fileName)} = () => useAddMutation(KEY, API.ADD);
|
||||||
|
export const useUpdate${capitalizeFirstLetter(fileName)} = () => useUpdateMutation(KEY, API.UPDATE);
|
||||||
|
export const useDelete${capitalizeFirstLetter(fileName)} = () =>useDeleteMutation(KEY, API.DELETE);
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/api/'+fileName+".ts",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
63
src/Extensions/FileGenerator/generateColumn.js
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Actions from "../../Components/Ui/tables/Actions";
|
||||||
|
|
||||||
|
function fnDelete(props :any ){}
|
||||||
|
|
||||||
|
const useTableColumns :any = () => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
|
||||||
|
{
|
||||||
|
name: t("email"),
|
||||||
|
sortable: false,
|
||||||
|
center: "true",
|
||||||
|
cell: (row:any) => row?.email
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "#",
|
||||||
|
sortable: false,
|
||||||
|
center: "true",
|
||||||
|
cell: (row) => (
|
||||||
|
<Actions
|
||||||
|
|
||||||
|
// importnat to return the row in on Edit Function to store in objectToEdit That Upper in Edit Modal
|
||||||
|
onEdit={() => row}
|
||||||
|
onView={()=>{}}
|
||||||
|
objectToEdit={row}
|
||||||
|
showEdit={true}
|
||||||
|
// showDelete={false}
|
||||||
|
onDelete={() => fnDelete({ id: row.id })}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTableColumns;
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/useTableColumns"+".tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
30
src/Extensions/FileGenerator/generateDashboard.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
|
||||||
|
const fileName = process.argv[2]
|
||||||
|
|
||||||
|
|
||||||
|
const CreateApi = `node src/Extensions/FileGenerator/generateApi.js ${fileName}`
|
||||||
|
const CreatePage = `node src/Extensions/FileGenerator/generatePage.js ${fileName}`
|
||||||
|
const CreateColumn = `node src/Extensions/FileGenerator/generateColumn.js ${fileName}`
|
||||||
|
const CreateformUtil= `node src/Extensions/FileGenerator/generateformUtils.js ${fileName}`
|
||||||
|
const CreateAddModal= `node src/Extensions/FileGenerator/generateAddModal.js ${fileName}`
|
||||||
|
const CreateEditModal= `node src/Extensions/FileGenerator/generateEditModal.js ${fileName}`
|
||||||
|
const CreateForm= `node src/Extensions/FileGenerator/generateForm.js ${fileName}`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const RunCommand = async()=>{
|
||||||
|
|
||||||
|
exec(CreatePage)
|
||||||
|
exec(CreateApi)
|
||||||
|
setTimeout(()=>{},100)
|
||||||
|
exec(CreateColumn)
|
||||||
|
exec(CreateformUtil)
|
||||||
|
exec(CreateAddModal)
|
||||||
|
exec(CreateEditModal)
|
||||||
|
exec(CreateForm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCommand()
|
||||||
47
src/Extensions/FileGenerator/generateEditModal.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { usePageState } from '../../lib/state mangment/LayoutPagestate'
|
||||||
|
|
||||||
|
function Edit${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
const {objectToEdit} = usePageState()
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
isAddModal={false}
|
||||||
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
|
handleSubmit={() => { }}
|
||||||
|
headerText='Edit Modal'
|
||||||
|
getValidationSchema={getValidationSchema(objectToEdit)}>
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Edit${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Edit"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
66
src/Extensions/FileGenerator/generateForm.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import { Col, Row } from 'reactstrap';
|
||||||
|
import KarimField from '../../Components/Karimalden/KarimField';
|
||||||
|
import { FakeSelectData } from '../../Layout/app/Const';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
|
||||||
|
import { DatePicker } from 'antd';
|
||||||
|
|
||||||
|
function Form${capitalizeFirstLetter(fileName)}() {
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
|
||||||
|
<Col>
|
||||||
|
// name from form utils
|
||||||
|
<KarimField name="name" type="text"label='name' placeholder='placeholder' />
|
||||||
|
<KarimField name="number" type="number" label='number' placeholder='placeholder' />
|
||||||
|
<KarimField name="select" type="select"label='select' option={FakeSelectData} isMulti={true} placeholder='placeholder' />
|
||||||
|
<KarimField name="Multiselect" type="select"label='Multiselect' option={FakeSelectData} Disabled={true} placeholder='placeholder'/>
|
||||||
|
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<KarimField name="date" type="date" label='date' placeholder='placeholder' />
|
||||||
|
<KarimField name="time" type="text"label='time' placeholder='placeholder' />
|
||||||
|
<KarimField name="CheckBox" name2='CheckBox2' type="checkbox" label='CheckBox' placeholder='placeholder' group={true} />
|
||||||
|
<KarimField name="DateFrom" name2="DateTo" type="DataRange" />
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form${capitalizeFirstLetter(fileName)}
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+'/Form'+ capitalizeFirstLetter(fileName)+".tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
64
src/Extensions/FileGenerator/generateModel.js
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
|
||||||
|
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
function Add${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
|
||||||
|
const handelSubmit = (values:any )=>{
|
||||||
|
|
||||||
|
const dataToSend = getDataToSend(values)
|
||||||
|
|
||||||
|
mutate(dataToSend)
|
||||||
|
// Submit Value
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
|
||||||
|
isAddModal={true}
|
||||||
|
getInitialValues={getInitialValues()}
|
||||||
|
handleSubmit={handelSubmit}
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
headerText={t('Add') +t('${(fileName)}')}
|
||||||
|
getValidationSchema={getValidationSchema()}>
|
||||||
|
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Add${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
67
src/Extensions/FileGenerator/generatePage.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderPath = 'src/Pages/'+fileName;
|
||||||
|
|
||||||
|
|
||||||
|
if (!fs.existsSync(folderPath)) {
|
||||||
|
fs.mkdirSync(folderPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import DashBody from '../../Layout/Dashboard/DashBody'
|
||||||
|
import DashHeader from '../../Layout/Dashboard/DashHeader'
|
||||||
|
import LyTable from '../../Layout/Dashboard/LyTable'
|
||||||
|
import useTableColumns from './useTableColumns'
|
||||||
|
import { useGet${capitalizeFirstLetter(fileName)}} from '../../api/${fileName}'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import Edit${capitalizeFirstLetter(fileName)}Modal from './Edit${capitalizeFirstLetter(fileName)}Modal'
|
||||||
|
import Add${capitalizeFirstLetter(fileName)}Modal from './Add${capitalizeFirstLetter(fileName)}Modal'
|
||||||
|
|
||||||
|
function ${capitalizeFirstLetter(fileName)}Page() {
|
||||||
|
|
||||||
|
const column =useTableColumns()
|
||||||
|
const {data ,status } = useGet${capitalizeFirstLetter(fileName)}()
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
// Pass Status to Layout
|
||||||
|
<DashBody status={status as QueryStatusEnum} >
|
||||||
|
<DashHeader title={'${capitalizeFirstLetter(fileName)}'}></DashHeader>
|
||||||
|
|
||||||
|
<LyTable
|
||||||
|
data={data}
|
||||||
|
isLoading={false}
|
||||||
|
columns={column}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Edit${capitalizeFirstLetter(fileName)}Modal />
|
||||||
|
<Add${capitalizeFirstLetter(fileName)}Modal />
|
||||||
|
|
||||||
|
</DashBody>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ${capitalizeFirstLetter(fileName)}Page
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+capitalizeFirstLetter(fileName)+"Page.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
73
src/Extensions/FileGenerator/generateformUtils.js
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { buildFormData } from "../../api/helper/buildFormData";
|
||||||
|
|
||||||
|
interface formUtilCommon {
|
||||||
|
name:string,
|
||||||
|
email:string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObjectToEdit extends formUtilCommon {
|
||||||
|
|
||||||
|
id?:number,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InitialValues extends ObjectToEdit {
|
||||||
|
|
||||||
|
}
|
||||||
|
interface ValidateSchema extends formUtilCommon{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInitialValues = (objectToEdit: ObjectToEdit | null = null): InitialValues => {
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
id:objectToEdit?.id?? 0 ,
|
||||||
|
name:objectToEdit?.name ?? "",
|
||||||
|
email:objectToEdit?.email?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<ValidateSchema> => {
|
||||||
|
// validate input
|
||||||
|
return Yup.object().shape({
|
||||||
|
name:Yup.string().required('required'),
|
||||||
|
email:Yup.string().required("required")
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDataToSend = (values: any): FormData => {
|
||||||
|
const data = { ...values };
|
||||||
|
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
buildFormData(formData, data);
|
||||||
|
return formData;
|
||||||
|
};
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"formUtil.ts",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
45
src/Hooks/WithDrawer.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React, { useState, ReactNode } from 'react';
|
||||||
|
import type { DrawerProps } from 'antd';
|
||||||
|
import { Drawer, Space } from 'antd';
|
||||||
|
|
||||||
|
interface WithDrawerProps {
|
||||||
|
button: React.ReactNode;
|
||||||
|
children: ReactNode;
|
||||||
|
title:string;
|
||||||
|
className?:string,
|
||||||
|
iopen?:boolean,
|
||||||
|
setOpen:any
|
||||||
|
}
|
||||||
|
|
||||||
|
const WithDrawer: React.FC<WithDrawerProps> = ({ children,title ="Basic Drawer",className ,setOpen, iopen }) => {
|
||||||
|
const [placement, setPlacement] = useState<DrawerProps['placement']>('right');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Drawer
|
||||||
|
title={title}
|
||||||
|
placement={placement}
|
||||||
|
closable={false}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
open={iopen}
|
||||||
|
key={placement}
|
||||||
|
>
|
||||||
|
<div className={className}>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithDrawer;
|
||||||
|
|
||||||
|
|
||||||
|
// <WithDrawer
|
||||||
|
// button={<Button type="primary">Open</Button>}
|
||||||
|
// >
|
||||||
|
// {/* Your content goes here */}
|
||||||
|
// </WithDrawer>
|
||||||
16
src/Hooks/useEventListener.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export const useEventListener = (eventType:any, callback:any, element = window) => {
|
||||||
|
const callbackRef = useRef(callback);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
callbackRef.current = callback;
|
||||||
|
}, [callback]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e:any) => callbackRef.current(e);
|
||||||
|
element.addEventListener(eventType, handler);
|
||||||
|
|
||||||
|
return () => element.removeEventListener(eventType, handler);
|
||||||
|
}, [eventType, element]);
|
||||||
|
};
|
||||||
16
src/Hooks/useFormField.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useField, useFormikContext } from 'formik';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
|
||||||
|
const useFormField = (name: string, props?: any) => {
|
||||||
|
const [field, meta] = useField({ name, ...props });
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const isError = meta.touched && meta.error;
|
||||||
|
|
||||||
|
const errorMsg = meta.error ? t(meta.error.toString()) : '';
|
||||||
|
|
||||||
|
return { Field, field, meta, formik, isError, errorMsg, t };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFormField;
|
||||||
9
src/Hooks/useImageError.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const useImageError = ({currentTarget}:any) => {
|
||||||
|
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
|
||||||
|
currentTarget.onerror = null;
|
||||||
|
currentTarget.src=`${ErrorImage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useImageError
|
||||||
21
src/Hooks/useLoadingState.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
function useLoadingState(initialValue: boolean, duration: number): [boolean, () => void] {
|
||||||
|
const [loading, setLoading] = useState<boolean>(initialValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, duration);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
|
}, [duration]);
|
||||||
|
|
||||||
|
const resetLoading = () => {
|
||||||
|
setLoading(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return [loading, resetLoading];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useLoadingState;
|
||||||
18
src/Hooks/useNavigateOnSuccess.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
|
||||||
|
function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess:any) {
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
useEffect(()=>{
|
||||||
|
|
||||||
|
if(isSuccess){
|
||||||
|
callbackAfterSuccess()
|
||||||
|
navigate(to_path , {replace:true})
|
||||||
|
}
|
||||||
|
},[isSuccess])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useNavigateOnSuccess
|
||||||
34
src/Hooks/usePagination.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Pagination } from "antd";
|
||||||
|
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
|
export const PaginationBody = ({ data }: any) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const pagination = location?.search || '';
|
||||||
|
const currentPage = parseInt(new URLSearchParams(location.search).get("page") || "1", 10);
|
||||||
|
const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "8", 10);
|
||||||
|
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
const onChange = (page: number, pageSize?: number) => {
|
||||||
|
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}&search=${searchParams.get('search')}`, { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onShowSizeChange = (current: number, pageSize: number) => {
|
||||||
|
navigate(`?page=${current}&per_page=${pageSize}&search=${searchParams.get('search')}`, { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pagination
|
||||||
|
className='text-center mt-3 paginateStyle'
|
||||||
|
total={data}
|
||||||
|
showTotal={(total: any) => `Total ${total} items`}
|
||||||
|
pageSize={pageSize}
|
||||||
|
pageSizeOptions={[8, 16, 24, 32, 40]}
|
||||||
|
defaultCurrent={currentPage}
|
||||||
|
current={currentPage} // Adding this line will set the current page correctly
|
||||||
|
onChange={onChange}
|
||||||
|
onShowSizeChange={onShowSizeChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
18
src/Hooks/useWindowSize.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useEventListener } from "./useEventListener";
|
||||||
|
|
||||||
|
export const useWindowSize = () => {
|
||||||
|
const [windowSize, setWindowSize] = useState({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEventListener("resize", () => {
|
||||||
|
setWindowSize({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return windowSize;
|
||||||
|
};
|
||||||
41
src/Layout/Dashboard/AddButton/AddButtonLayout.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './Add_Button.scss'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const AddButtonLayout = ({haveAddModal}:any) => {
|
||||||
|
const { setIsOpenAddModel , setObjectToEdit } = usePageState()
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Add_Button' onClick={()=>{
|
||||||
|
if(haveAddModal){
|
||||||
|
|
||||||
|
setIsOpenAddModel()
|
||||||
|
}
|
||||||
|
setObjectToEdit(null)
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<button>
|
||||||
|
<span >
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path fill="currentColor" d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
|
||||||
|
</svg>{" "}
|
||||||
|
{t("Add")}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddButtonLayout
|
||||||
36
src/Layout/Dashboard/AddButton/Add_Button.scss
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
.Add_Button{
|
||||||
|
button {
|
||||||
|
border: 2px solid var(--primary);
|
||||||
|
background-color: var(--primary);
|
||||||
|
border-radius: 0.5vw;
|
||||||
|
padding:.5vw 1vw;
|
||||||
|
font-size: 1vw ;
|
||||||
|
display: flex; justify-content: center; align-items: center;
|
||||||
|
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||||
|
}
|
||||||
|
button span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--bg);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color:var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
.Add_Button{
|
||||||
|
button{
|
||||||
|
font-size: 2vw !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 470px) {
|
||||||
|
.Add_Button{
|
||||||
|
button{
|
||||||
|
font-size: 3vw !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/Layout/Dashboard/DashBody.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Spinner } from "reactstrap"
|
||||||
|
import { QueryStatusEnum } from "../../config/QueryStatus"
|
||||||
|
import LoadingPage from "../app/LoadingPage"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { BsEmojiFrown } from "react-icons/bs";
|
||||||
|
|
||||||
|
|
||||||
|
const DashBody = ({ children , status }: { children: React.ReactNode ,status?:QueryStatusEnum }) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
// Add You Custom Loadaing Page
|
||||||
|
if(status === QueryStatusEnum.LOADING){
|
||||||
|
|
||||||
|
return <LoadingPage />
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Your Custom Error Page
|
||||||
|
if(status === QueryStatusEnum.ERROR){
|
||||||
|
return (
|
||||||
|
<div className="error_show">
|
||||||
|
<span className="error_icon"><BsEmojiFrown/></span>
|
||||||
|
<span className="error_text">
|
||||||
|
{t("Ops")}...<br/>
|
||||||
|
{t("An Error According")} <br/>
|
||||||
|
{t("Please Try Again Later")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Page' >
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DashBody
|
||||||
27
src/Layout/Dashboard/DashHeader.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from "react";
|
||||||
|
import AddButtonLayout from "./AddButton/AddButtonLayout";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface DashHeaderProp {
|
||||||
|
title: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
showAddButton?: boolean;
|
||||||
|
haveAddModal?:boolean
|
||||||
|
}
|
||||||
|
const DashHeader = ({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
haveAddModal= true ,
|
||||||
|
showAddButton = true,
|
||||||
|
}: DashHeaderProp) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="Page_Header">
|
||||||
|
<h3>{t(`${title}`)}</h3>
|
||||||
|
{children}
|
||||||
|
{showAddButton && <AddButtonLayout haveAddModal={haveAddModal}/>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashHeader;
|
||||||
42
src/Layout/Dashboard/FormPage.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Formik, Form } from 'formik';
|
||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { Button } from "reactstrap";
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormPageProps {
|
||||||
|
handleSubmit: (values: any) => void
|
||||||
|
initialValues: FormValues;
|
||||||
|
validationSchema: any;
|
||||||
|
title?: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormPage: React.FC<FormPageProps> = ({ children, handleSubmit, initialValues, validationSchema, title = "Add New Item" }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h4 className='text-bold'>{title}</h4>
|
||||||
|
<div className="Card">
|
||||||
|
<Formik
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{formik => (
|
||||||
|
<Form>
|
||||||
|
{children}
|
||||||
|
<div className='w-100 d-flex justify-content-center'>
|
||||||
|
<Button type='submit' color="primary" className='mt-4 w-25 text-center'>Submit</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormPage;
|
||||||
77
src/Layout/Dashboard/LayoutModal.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Form, Formik } from 'formik'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'
|
||||||
|
import { usePageState } from '../../lib/state mangment/LayoutPagestate'
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { LoadingButton } from '../../Components/Ui/LoadingButton';
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus';
|
||||||
|
|
||||||
|
interface LayoutModalProps {
|
||||||
|
isAddModal: boolean;
|
||||||
|
headerText: string;
|
||||||
|
handleSubmit: (values: any) => void;
|
||||||
|
getInitialValues: any;
|
||||||
|
getValidationSchema: any;
|
||||||
|
children: React.ReactNode;
|
||||||
|
status?:QueryStatusEnum
|
||||||
|
}
|
||||||
|
function LayoutModal({isAddModal , headerText , handleSubmit =()=>{} , getInitialValues , getValidationSchema,status ,children}:LayoutModalProps) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const {isOpenAddModel ,setIsOpenAddModel , setIsOpenEditModel ,isOpenEditModel , objectToEdit , CloseAllModal} = usePageState(state => state)
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if(status === QueryStatusEnum.SUCCESS){
|
||||||
|
CloseAllModal()
|
||||||
|
}
|
||||||
|
},[status , CloseAllModal])
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
return (
|
||||||
|
<Modal centered isOpen={isAddModal ? isOpenAddModel :isOpenEditModel} size="lg" >
|
||||||
|
<ModalHeader className='ModalHeader' toggle={() => isAddModal ?setIsOpenAddModel() : setIsOpenEditModel()} >
|
||||||
|
{t(headerText)}
|
||||||
|
</ModalHeader>
|
||||||
|
{
|
||||||
|
|
||||||
|
(( objectToEdit != null && isOpenEditModel) || isOpenAddModel) &&
|
||||||
|
<Formik
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
initialValues={getInitialValues}
|
||||||
|
validationSchema={getValidationSchema}
|
||||||
|
>
|
||||||
|
|
||||||
|
{(formik) => (
|
||||||
|
<Form>
|
||||||
|
<ModalBody>
|
||||||
|
{children}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
disabled={status === QueryStatusEnum.LOADING}
|
||||||
|
onClick={() => isAddModal ?setIsOpenAddModel() : setIsOpenEditModel()}
|
||||||
|
color="danger"
|
||||||
|
>
|
||||||
|
{t("cancel")}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton
|
||||||
|
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
isLoading={status === QueryStatusEnum.LOADING}
|
||||||
|
>
|
||||||
|
{t(isAddModal ? "Add" :"edit")}
|
||||||
|
</LoadingButton>
|
||||||
|
</ModalFooter>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
}
|
||||||
|
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutModal
|
||||||