test
This commit is contained in:
parent
aafbb62447
commit
edf92d1a22
24
.gitignore
vendored
Normal file
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
11
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"aldeen",
|
||||
"Datepicker",
|
||||
"formik",
|
||||
"Karim",
|
||||
"queryqlent",
|
||||
"szhsin",
|
||||
"Viewelement"
|
||||
]
|
||||
}
|
||||
76
db.json
Normal file
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
104
package.json
Normal file
104
package.json
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.14.19",
|
||||
"@mui/x-charts": "^6.19.4",
|
||||
"@react-google-maps/api": "^2.19.2",
|
||||
"@szhsin/react-menu": "github:szhsin/react-menu",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@tinymce/tinymce-react": "^4.3.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.60",
|
||||
"@types/react": "^18.2.33",
|
||||
"@types/react-dom": "^18.2.14",
|
||||
"@types/socket.io-client": "^3.0.0",
|
||||
"@wojtekmaj/react-daterange-picker": "^5.4.4",
|
||||
"antd": "^5.12.1",
|
||||
"apexcharts": "^3.44.2",
|
||||
"axios": "^1.6.0",
|
||||
"bootstrap": "^5.3.2",
|
||||
"chart.js": "^4.4.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"formik": "^2.4.5",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^23.6.0",
|
||||
"i18next-browser-languagedetector": "^7.1.0",
|
||||
"json-server": "^0.17.4",
|
||||
"moment": "^2.30.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-apexcharts": "^1.4.1",
|
||||
"react-bootstrap": "^2.9.1",
|
||||
"react-bootstrap-sweetalert": "^5.2.0",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-confirm-alert": "^3.0.6",
|
||||
"react-data-table-component": "^7.5.4",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-i18next": "^13.3.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-input-range": "^1.3.0",
|
||||
"react-number-format": "^5.3.4",
|
||||
"react-query": "^3.39.3",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.18.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-select": "^5.7.7",
|
||||
"react-simple-star-rating": "^5.1.7",
|
||||
"react-switch": "^7.0.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactstrap": "^9.2.0",
|
||||
"redux": "^4.2.1",
|
||||
"sass": "^1.69.5",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"styled-components": "5.3.3",
|
||||
"ts-xlsx": "^0.0.11",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4",
|
||||
"xlsx": "^0.18.5",
|
||||
"yup": "^1.3.2",
|
||||
"zustand": "^4.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"g:api": "node src/Extensions/FileGenerator/generateApi.js",
|
||||
"g:column": "node src/Extensions/FileGenerator/generateColumn.js",
|
||||
"g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js",
|
||||
"g:page": "node src/Extensions/FileGenerator/generatePage.js",
|
||||
"g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js ",
|
||||
"g:modal:add": "node src/Extensions/FileGenerator/generateEditModal.js ",
|
||||
"g:model:edit": "node src/Extensions/FileGenerator/generateEditModal.js "
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-toggle": "^4.0.5"
|
||||
}
|
||||
}
|
||||
1
public/Layout/Ar.svg
Normal file
1
public/Layout/Ar.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 10 KiB |
1
public/Layout/En.svg
Normal file
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 |
BIN
public/Layout/LoginBg.jpg
Normal file
BIN
public/Layout/LoginBg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
82
public/Logo.svg
Normal file
82
public/Logo.svg
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="98" viewBox="0 0 200 98">
|
||||
<defs>
|
||||
<filter id="Rectangle_65" x="0" y="0" width="200" height="98" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="6" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-opacity="0.161"/>
|
||||
<feComposite operator="in" in2="blur"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4896" x="25" y="21" width="41.686" height="51" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-2"/>
|
||||
<feFlood flood-opacity="0.251"/>
|
||||
<feComposite operator="in" in2="blur-2"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4897">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-3"/>
|
||||
<feFlood flood-opacity="0.161" result="color"/>
|
||||
<feComposite operator="out" in="SourceGraphic" in2="blur-3"/>
|
||||
<feComposite operator="in" in="color"/>
|
||||
<feComposite operator="in" in2="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4898" x="52.326" y="21.048" width="50.953" height="50.952" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-4"/>
|
||||
<feFlood flood-opacity="0.251"/>
|
||||
<feComposite operator="in" in2="blur-4"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4899" x="90.006" y="21" width="23.295" height="51" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-5"/>
|
||||
<feFlood flood-color="#252525" flood-opacity="0.251"/>
|
||||
<feComposite operator="in" in2="blur-5"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4900" x="100.927" y="21" width="41.828" height="51" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-6"/>
|
||||
<feFlood flood-opacity="0.251"/>
|
||||
<feComposite operator="in" in2="blur-6"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
<filter id="Path_4901" x="128.536" y="21" width="45.894" height="51" filterUnits="userSpaceOnUse">
|
||||
<feOffset dy="3" input="SourceAlpha"/>
|
||||
<feGaussianBlur stdDeviation="3" result="blur-7"/>
|
||||
<feFlood flood-opacity="0.251"/>
|
||||
<feComposite operator="in" in2="blur-7"/>
|
||||
<feComposite in="SourceGraphic"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="Group_583" data-name="Group 583" transform="translate(-127 6)">
|
||||
<g transform="matrix(1, 0, 0, 1, 127, -6)" filter="url(#Rectangle_65)">
|
||||
<path id="Rectangle_65-2" data-name="Rectangle 65" d="M0,0H176a0,0,0,0,1,0,0V49a25,25,0,0,1-25,25H25A25,25,0,0,1,0,49V0A0,0,0,0,1,0,0Z" transform="translate(12 6)" fill="#55338a"/>
|
||||
</g>
|
||||
<g id="Big_Logo" data-name="Big Logo" transform="translate(582.611 1585.844)">
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4896)">
|
||||
<path id="Path_4896-2" data-name="Path 4896" d="M-416.316-1538.463v6.619h-5.295v-25.766h5.295v13.852h6.477a5.859,5.859,0,0,0,3.711-1.158,7.069,7.069,0,0,0,2.222-2.931,9.58,9.58,0,0,0,.709-3.806,10.192,10.192,0,0,0-.756-3.806,7.073,7.073,0,0,0-2.222-2.931,5.79,5.79,0,0,0-3.664-1.158h-11.772v-5.3h11.772a11.3,11.3,0,0,1,5.2,1.135,11.328,11.328,0,0,1,3.711,2.978,12.835,12.835,0,0,1,2.245,4.231,15.973,15.973,0,0,1,.756,4.846,16.438,16.438,0,0,1-.733,4.846,12.494,12.494,0,0,1-2.222,4.231,11.253,11.253,0,0,1-3.735,2.979,11.443,11.443,0,0,1-5.224,1.134Zm3.31-17.114h2.931a2.435,2.435,0,0,1,1.726.638,3.842,3.842,0,0,1,1.017,1.631,6.623,6.623,0,0,1,.331,2.1,6.618,6.618,0,0,1-.331,2.1,3.846,3.846,0,0,1-1.017,1.632,2.435,2.435,0,0,1-1.726.638h-2.931Z" transform="translate(455.61 1591.84)" fill="#fff"/>
|
||||
</g>
|
||||
<g data-type="innerShadowGroup">
|
||||
<path id="Path_4897-2" data-name="Path 4897" d="M-269.586-1524.651a6.02,6.02,0,0,0-2.411.488,6.236,6.236,0,0,0-1.953,1.312,5.911,5.911,0,0,0-1.282,1.922,6.135,6.135,0,0,0-.458,2.38,6.359,6.359,0,0,0,.458,2.411,5.822,5.822,0,0,0,1.282,1.953,6.219,6.219,0,0,0,1.953,1.312,6,6,0,0,0,2.411.488,5.782,5.782,0,0,0,2.38-.488,6.317,6.317,0,0,0,1.922-1.312,6.227,6.227,0,0,0,1.312-1.953,6.014,6.014,0,0,0,.488-2.411,5.9,5.9,0,0,0-1.77-4.333A5.9,5.9,0,0,0-269.586-1524.651Z" transform="translate(-108.198 -29.802)" fill="#ffc90a"/>
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4897)">
|
||||
<path id="Path_4897-3" data-name="Path 4897" d="M-269.586-1524.651a6.02,6.02,0,0,0-2.411.488,6.236,6.236,0,0,0-1.953,1.312,5.911,5.911,0,0,0-1.282,1.922,6.135,6.135,0,0,0-.458,2.38,6.359,6.359,0,0,0,.458,2.411,5.822,5.822,0,0,0,1.282,1.953,6.219,6.219,0,0,0,1.953,1.312,6,6,0,0,0,2.411.488,5.782,5.782,0,0,0,2.38-.488,6.317,6.317,0,0,0,1.922-1.312,6.227,6.227,0,0,0,1.312-1.953,6.014,6.014,0,0,0,.488-2.411,5.9,5.9,0,0,0-1.77-4.333A5.9,5.9,0,0,0-269.586-1524.651Z" transform="translate(347.41 1562.04)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4898)">
|
||||
<path id="Path_4898-2" data-name="Path 4898" d="M-284.253-1554.614a16.493,16.493,0,0,0-3.541-5.224,16.95,16.95,0,0,0-5.24-3.522,15.909,15.909,0,0,0-6.4-1.3,16.078,16.078,0,0,0-6.421,1.3,16.581,16.581,0,0,0-5.24,3.522,16.7,16.7,0,0,0-3.517,5.224,15.964,15.964,0,0,0-1.3,6.406,16.128,16.128,0,0,0,1.3,6.43,16.62,16.62,0,0,0,3.517,5.248,16.567,16.567,0,0,0,5.24,3.522,16.059,16.059,0,0,0,6.421,1.3,15.891,15.891,0,0,0,6.4-1.3,16.935,16.935,0,0,0,5.24-3.522,16.413,16.413,0,0,0,3.541-5.248,16.131,16.131,0,0,0,1.3-6.43A15.967,15.967,0,0,0-284.253-1554.614Zm-4.871,10.756a11.543,11.543,0,0,1-2.387,3.569,11.06,11.06,0,0,1-3.546,2.411,10.882,10.882,0,0,1-4.349.874,10.877,10.877,0,0,1-4.35-.874,11.309,11.309,0,0,1-3.569-2.411,11.319,11.319,0,0,1-2.411-3.569,10.874,10.874,0,0,1-.875-4.349,10.88,10.88,0,0,1,.875-4.35,11.062,11.062,0,0,1,2.411-3.546,11.543,11.543,0,0,1,3.569-2.388,10.9,10.9,0,0,1,4.35-.875,10.9,10.9,0,0,1,4.349.875,11.284,11.284,0,0,1,3.546,2.388,11.276,11.276,0,0,1,2.387,3.546,10.9,10.9,0,0,1,.875,4.35A10.892,10.892,0,0,1-289.124-1543.858Z" transform="translate(377.23 1591.71)" fill="#fff"/>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4899)">
|
||||
<path id="Path_4899-2" data-name="Path 4899" d="M-164.859-1531.844h-5.3v-33h5.3Z" transform="translate(269.16 1591.84)" fill="#fff"/>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4900)">
|
||||
<path id="Path_4900-2" data-name="Path 4900" d="M-109.378-1542.718v-22.126h5.3v33h-4.775l-13.758-22.032v22.032h-5.295v-33h4.775Z" transform="translate(237.84 1591.84)" fill="#fff"/>
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, -455.61, -1591.84)" filter="url(#Path_4901)">
|
||||
<path id="Path_4901-2" data-name="Path 4901" d="M6.782-1564.844v5.3h-11.3v27.7h-5.3v-27.7h-11.3v-5.3Z" transform="translate(158.65 1591.84)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
17
public/index.html
Normal file
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>Point - App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
25
public/manifest.json
Normal file
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
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
69
src/App.tsx
Normal file
69
src/App.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Fragment, lazy, Suspense } from 'react';
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import Loading from './Components/Utils/Loading/Loading';
|
||||
import { RoutesLinks } from './Routes';
|
||||
import Layout from './Layout/app/Layout';
|
||||
import Auth from './Pages/Auth/Page';
|
||||
const Page404 = lazy(() => import("./Layout/app/NotFoundPage"))
|
||||
|
||||
const App = () => {
|
||||
|
||||
|
||||
return (
|
||||
<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
|
||||
97
src/Components/Columns/ColumnsImage.tsx
Normal file
97
src/Components/Columns/ColumnsImage.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import {
|
||||
DownloadOutlined,
|
||||
RotateLeftOutlined,
|
||||
RotateRightOutlined,
|
||||
SwapOutlined,
|
||||
ZoomInOutlined,
|
||||
ZoomOutOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { Image, Space } from 'antd';
|
||||
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 = 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 = (url:any) => {
|
||||
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
|
||||
// Extract file name from URL
|
||||
const fileName = url.split('/').pop();
|
||||
link.download = fileName || 'download'; // If file name cannot be extracted, set a default name
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
link.remove();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching resource:', error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
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
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
|
||||
|
||||
};
|
||||
50
src/Components/Ui/Alert.tsx
Normal file
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
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
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
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
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
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
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
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
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
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
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;
|
||||
|
||||
|
||||
58
src/Components/Ui/StaticsCard/StaticCard.tsx
Normal file
58
src/Components/Ui/StaticsCard/StaticCard.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React from "react";
|
||||
import { Card, CardBody } from "reactstrap";
|
||||
import { ChartTypeEnum } from "../../../enums/ChartTypeEnum";
|
||||
import { history } from "../../../ProviderContainer";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface StatisticsCardProps {
|
||||
className?: string;
|
||||
iconLeft?: boolean;
|
||||
icon: React.ReactNode;
|
||||
count?: string;
|
||||
CardTitle?: string;
|
||||
CardContent?: string;
|
||||
height?: number;
|
||||
pathWhenClick :string ;
|
||||
|
||||
}
|
||||
|
||||
const StatisticsCard = (props :StatisticsCardProps) => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const {
|
||||
className,
|
||||
iconLeft = false ,
|
||||
icon,
|
||||
count,
|
||||
CardTitle,
|
||||
CardContent,
|
||||
pathWhenClick,
|
||||
height,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Card className="p-4 gomecards" {...rest} onClick={()=>navigate(pathWhenClick )}>
|
||||
<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
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
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
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
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, handleSwitch, 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
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
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
|
||||
}
|
||||
58
src/Components/Ui/tables/Actions.tsx
Normal file
58
src/Components/Ui/tables/Actions.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React , {ReactNode} from "react";
|
||||
import { FaEdit, FaEye, FaTrash } from "react-icons/fa";
|
||||
import CustomConfirmAlert from "../Alert";
|
||||
import { usePageState } from "../../../lib/state mangment/LayoutPagestate";
|
||||
|
||||
type TableActionsProps = {
|
||||
onDelete?: () => any;
|
||||
onEdit?: () => any;
|
||||
onView?:() => any;
|
||||
showView?: boolean;
|
||||
showEdit?: boolean;
|
||||
showDelete?: boolean;
|
||||
children?: ReactNode;
|
||||
objectToEdit:any
|
||||
className?:string
|
||||
|
||||
};
|
||||
|
||||
|
||||
const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children,className }:TableActionsProps) => {
|
||||
// const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children }:TableActionsProps) => {
|
||||
|
||||
// console.log(objectToEdit);
|
||||
|
||||
const {setObjectToEdit , setIsOpenEditModel} = usePageState()
|
||||
return (
|
||||
<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
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
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
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
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
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
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
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
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
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>
|
||||
);
|
||||
}
|
||||
33
src/Components/Utils/Translate.tsx
Normal file
33
src/Components/Utils/Translate.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
|
||||
import { useLanguage, useLanguageMenu } from '../../Hooks/useChangeLanguage';
|
||||
import i18next from 'i18next';
|
||||
|
||||
export default function Translate() {
|
||||
const { changeLanguage } = useLanguage();
|
||||
const { languageOptions } = useLanguageMenu();
|
||||
|
||||
const handleLanguageChange = (newLanguage:string) => {
|
||||
changeLanguage(newLanguage);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='Translate'>
|
||||
<Menu menuButton={<MenuButton>
|
||||
{languageOptions.map((option:any,index:number) => (
|
||||
option.code === i18next.language ?
|
||||
<React.Fragment key={index}>
|
||||
<img alt='' src={option.icon} width={20} height={20} /> {option.label}
|
||||
</React.Fragment>
|
||||
: null
|
||||
))}
|
||||
</MenuButton>} transition>
|
||||
{languageOptions.map((option:any) => (
|
||||
<MenuItem key={option.code} onClick={() => handleLanguageChange(option.code)}>
|
||||
<img alt='' src={option.icon} width={20} height={20} /> {option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
60
src/Components/Utils/UnBlockModal.tsx
Normal file
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;
|
||||
18
src/Components/ValidationField/Ui/KarimSpinner.tsx
Normal file
18
src/Components/ValidationField/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;
|
||||
48
src/Components/ValidationField/Ui/SearchBar.scss
Normal file
48
src/Components/ValidationField/Ui/SearchBar.scss
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
.SearchBar{
|
||||
// margin-top: 20px;
|
||||
.group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
max-width: 350px;
|
||||
width: 350px;
|
||||
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 1rem;
|
||||
padding-left: 2.5rem;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
font-weight: 500;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
border: none;
|
||||
box-shadow: 2px 2px 7px 0 var(--bg);
|
||||
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--subtext);
|
||||
opacity: .4;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
fill: var(--subtext);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
35
src/Components/ValidationField/Ui/SearchBar.tsx
Normal file
35
src/Components/ValidationField/Ui/SearchBar.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React, { useState } from 'react'
|
||||
import './SearchBar.scss'
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
const SearchBar = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchParams] = useSearchParams()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (event:any) => {
|
||||
const { value } = event.target;
|
||||
setSearchQuery(value);
|
||||
updateUrlParams(value);
|
||||
};
|
||||
|
||||
const updateUrlParams = (value:any) => {
|
||||
navigate(`?search=${value}`);
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='SearchBar'>
|
||||
<div className="group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" className="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1b5stb0 icon" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="SearchIcon"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></svg>
|
||||
<input placeholder="Search Product...." type="search"
|
||||
className="input"
|
||||
value={searchQuery}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchBar
|
||||
78
src/Components/ValidationField/ValidationField.scss
Normal file
78
src/Components/ValidationField/ValidationField.scss
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
.ValidationField{
|
||||
>*{
|
||||
width: 100%;
|
||||
}
|
||||
.text,.ant-form-item{
|
||||
margin-bottom:7px !important;
|
||||
|
||||
}
|
||||
|
||||
>span{
|
||||
margin-bottom: 0px !important;
|
||||
&:focus-within{
|
||||
border-color: var(--primary) ;
|
||||
box-shadow: 0 0 0 1px var(--primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:has(.is-invalid){
|
||||
border-color: red !important;
|
||||
|
||||
}
|
||||
input{
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active{
|
||||
-webkit-box-shadow: 0 0 0 30px white inset !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-upload-select{
|
||||
width: 100%;
|
||||
}
|
||||
.Checkboxs{
|
||||
padding: 4%;
|
||||
}
|
||||
.SearchField{
|
||||
button{
|
||||
background: var(--primary);
|
||||
}
|
||||
}
|
||||
.text{
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
input:disabled{
|
||||
color: var(--text) !important;
|
||||
}
|
||||
|
||||
.isError{
|
||||
outline: red 1px solid;
|
||||
color: red;
|
||||
}
|
||||
.Error_color{
|
||||
color: red;
|
||||
|
||||
}
|
||||
input:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||
}
|
||||
input:-webkit-autofill:focus {
|
||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||
}
|
||||
|
||||
/* Remove autofill background color on hover */
|
||||
input:-webkit-autofill:hover {
|
||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
||||
}
|
||||
|
||||
|
||||
.ant-upload-list .ant-upload-list-item{
|
||||
height:78px !important;
|
||||
}
|
||||
|
||||
34
src/Components/ValidationField/ValidationField.tsx
Normal file
34
src/Components/ValidationField/ValidationField.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import React from "react";
|
||||
import "./ValidationField.scss";
|
||||
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField ,TextAreaField} from './View';
|
||||
import { ValidationFieldProps } from "./types";
|
||||
import MaltyFile from "./View/MaltyFile";
|
||||
import SearchField from "./View/SearchField";
|
||||
|
||||
const ValidationField: React.FC<ValidationFieldProps> = ({type , ...otherProps}) => {
|
||||
|
||||
switch (type) {
|
||||
case 'Select':
|
||||
return <SelectField {...otherProps} />;
|
||||
case 'Search':
|
||||
return <SearchField {...otherProps} />;
|
||||
case "DataRange":
|
||||
return <DataRange {...otherProps} />;
|
||||
case "Date":
|
||||
return <Date {...otherProps} />;
|
||||
case "Time":
|
||||
return <Time {...otherProps} />;
|
||||
case "File":
|
||||
return <File {...otherProps} />;
|
||||
case "MaltyFile":
|
||||
return <MaltyFile {...otherProps} />;
|
||||
case "Checkbox":
|
||||
return <CheckboxField {...otherProps} />;
|
||||
case "TextArea":
|
||||
return <TextAreaField {...otherProps} />;
|
||||
default:
|
||||
return <Default {...otherProps} type={type}/>;
|
||||
}
|
||||
};
|
||||
|
||||
export default React.memo(ValidationField);
|
||||
36
src/Components/ValidationField/View/CheckboxField.tsx
Normal file
36
src/Components/ValidationField/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 ? label : name}`)}
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckboxField
|
||||
44
src/Components/ValidationField/View/DataRange.tsx
Normal file
44
src/Components/ValidationField/View/DataRange.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { Form, DatePicker } from 'antd'
|
||||
|
||||
import React from 'react'
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const DataRange = ({ name, label, Format, props, onChange, isDisabled, placeholder, className }: any) => {
|
||||
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props)
|
||||
const onCalendarChange = (value: any) => {
|
||||
|
||||
formik.setFieldValue(name, value)
|
||||
|
||||
};
|
||||
return (
|
||||
|
||||
<div className='ValidationField'>
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`${label ? label : name}`)}
|
||||
</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}
|
||||
defaultValue={formik.values[name]}
|
||||
/>
|
||||
|
||||
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DataRange
|
||||
51
src/Components/ValidationField/View/Date.tsx
Normal file
51
src/Components/ValidationField/View/Date.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { Form, DatePicker } from 'antd';
|
||||
import React from 'react';
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const DateField = ({ name, label, picker = "date", isDisabled, props, onChange, placeholder, className, Format }: any) => {
|
||||
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
|
||||
const onCalendarChange = (value: any) => {
|
||||
formik.setFieldValue(name, value);
|
||||
};
|
||||
|
||||
// Function to check if a date is valid
|
||||
const isValidDate = (date: any) => {
|
||||
return date && !isNaN(date.valueOf()) && dayjs(date).isValid();
|
||||
};
|
||||
|
||||
// Set a default invalid date if the provided defaultValue is invalid
|
||||
const getDefaultDate = () => {
|
||||
const defaultValue = formik.values[name];
|
||||
return isValidDate(defaultValue) ? defaultValue : null; // Set to null or any other default invalid date
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='ValidationField'>
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<DatePicker
|
||||
picker={picker}
|
||||
placeholder={placeholder}
|
||||
allowClear
|
||||
className={`${className} w-100`}
|
||||
defaultValue={getDefaultDate()}
|
||||
size="large"
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
format={Format}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DateField;
|
||||
36
src/Components/ValidationField/View/Default.tsx
Normal file
36
src/Components/ValidationField/View/Default.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
|
||||
const Default = ({ name, label, placeholder, isDisabled, onChange, props,type }: any) => {
|
||||
const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="ValidationField 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={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/ValidationField/View/File.tsx
Normal file
60
src/Components/ValidationField/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';
|
||||
|
||||
|
||||
const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
|
||||
const { formik, t ,isError} = useFormField(name, props)
|
||||
let FormikName = formik.values[name];
|
||||
|
||||
const fileList: UploadFile[] = [
|
||||
|
||||
{
|
||||
uid: '-1',
|
||||
name: '',
|
||||
status: 'done',
|
||||
url: FormikName,
|
||||
thumbUrl: FormikName
|
||||
}
|
||||
];
|
||||
const FilehandleChange = (value:any) => {
|
||||
|
||||
formik.setFieldValue(name, value.file.originFileObj)
|
||||
|
||||
};
|
||||
const customRequest = async ({ onSuccess}: any) => {
|
||||
onSuccess();
|
||||
};
|
||||
return (
|
||||
<div className="ValidationField">
|
||||
<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
|
||||
49
src/Components/ValidationField/View/MaltyFile.tsx
Normal file
49
src/Components/ValidationField/View/MaltyFile.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { Button, Upload } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
|
||||
const MaltyFile = ({ name, label, onChange, isDisabled, placholder, className, props }: any) => {
|
||||
const { formik, t, isError } = useFormField(name, props);
|
||||
const fileList = formik?.values[name] ? formik?.values[name]?.map((file: any, index: number) => ({
|
||||
uid: index,
|
||||
name: file.name,
|
||||
status: 'done',
|
||||
url: file.url || '',
|
||||
thumbUrl: file.url || '',
|
||||
})) : [];
|
||||
|
||||
const FilehandleChange = ({ file, fileList }: any) => {
|
||||
formik.setFieldValue(name, fileList.map((file: any) => file.originFileObj));
|
||||
};
|
||||
|
||||
const customRequest = async ({ onSuccess }: any) => {
|
||||
// Perform any necessary actions before onSuccess is called
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ValidationField">
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`${label || name}`)}
|
||||
</label>
|
||||
|
||||
<Upload
|
||||
disabled={isDisabled}
|
||||
listType="picture"
|
||||
defaultFileList={[...fileList]}
|
||||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={`${className} w-100`}
|
||||
multiple // Allow multiple files to be selected
|
||||
|
||||
>
|
||||
<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 MaltyFile;
|
||||
67
src/Components/ValidationField/View/SearchField.tsx
Normal file
67
src/Components/ValidationField/View/SearchField.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { Form, Select } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
const SearchField = ({ name, label, placeholder, isDisabled, searchBy, option, isMulti, onChange, className, loading,props }: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const location = useLocation()
|
||||
|
||||
const navigate = useNavigate()
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(window?.location?.search);
|
||||
setSearchQuery(searchParams?.get('search') || '');
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
|
||||
formik?.setFieldValue(name, value);
|
||||
|
||||
console.log(value);
|
||||
};
|
||||
const SearchHandleChange = (value:any) => {
|
||||
if (value || value !== "") {
|
||||
navigate(`${window?.location?.pathname}?${searchBy}=${value}`);
|
||||
} else {
|
||||
const params = new URLSearchParams(location.search);
|
||||
params.delete(searchBy ?? "search");
|
||||
navigate(`${window?.location.pathname}?${params.toString()}`);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='ValidationField'>
|
||||
<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`}
|
||||
value={formik.values[name]}
|
||||
// defaultValue={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelecthandleChange}
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
onSearch={SearchHandleChange}
|
||||
loading={loading}
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SearchField);
|
||||
41
src/Components/ValidationField/View/SelectField.tsx
Normal file
41
src/Components/ValidationField/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='ValidationField'>
|
||||
<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`}
|
||||
value={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelecthandleChange}
|
||||
|
||||
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(SelectField);
|
||||
45
src/Components/ValidationField/View/TextArea.tsx
Normal file
45
src/Components/ValidationField/View/TextArea.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import useFormField from '../../../Hooks/useFormField';
|
||||
const { TextArea } = Input;
|
||||
|
||||
const TextAreaField = ({ name, label, placeholder, isDisabled, onChange, props,type }: any) => {
|
||||
const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
|
||||
|
||||
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
console.log('Change:', e.target.value);
|
||||
formik.setFieldValue(name, e.target.value)
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ValidationField w-100" >
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? 'error' : ''}
|
||||
help={isError ? errorMsg : ''}
|
||||
>
|
||||
<Field
|
||||
as={TextArea}
|
||||
placeholder={t(`${placeholder ?placeholder : name}`)}
|
||||
name={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
onChange={onChange || handleChange}
|
||||
|
||||
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TextAreaField);
|
||||
;
|
||||
41
src/Components/ValidationField/View/Time.tsx
Normal file
41
src/Components/ValidationField/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='ValidationField'>
|
||||
<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/ValidationField/View/index.tsx
Normal file
22
src/Components/ValidationField/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";
|
||||
import TextAreaField from './TextArea'
|
||||
|
||||
|
||||
|
||||
export {
|
||||
Time,
|
||||
SelectField,
|
||||
Date,
|
||||
DataRange,
|
||||
CheckboxField,
|
||||
Default,
|
||||
File,
|
||||
TextAreaField
|
||||
|
||||
}
|
||||
21
src/Components/ValidationField/index.tsx
Normal file
21
src/Components/ValidationField/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,
|
||||
|
||||
|
||||
}
|
||||
132
src/Components/ValidationField/types.ts
Normal file
132
src/Components/ValidationField/types.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
// export interface ValidationFieldProps {
|
||||
// name: string;
|
||||
// type?: "text" | "Select" | "DataRange" | "Date" | "Time" | "File" | "number" | "Checkbox" | "password";
|
||||
// placeholder?: string;
|
||||
// label?: string;
|
||||
// className?: string;
|
||||
// option?: any[];
|
||||
// isMulti?: boolean;
|
||||
// isDisabled?: boolean;
|
||||
// picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||
// Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
|
||||
// onChange?: (value: any) => void;
|
||||
// Group?: boolean
|
||||
// dir?:'ltr' | "rtl"
|
||||
// }
|
||||
|
||||
export interface ValidationFieldPropsText {
|
||||
name: string;
|
||||
type: "text";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
}
|
||||
|
||||
export interface ValidationFieldPropsSelect {
|
||||
name: string;
|
||||
type: "Select";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?:any;
|
||||
dir?:'ltr' | "rtl";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
|
||||
}
|
||||
|
||||
export interface ValidationFieldPropsSearch{
|
||||
name: string;
|
||||
type: "Search";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
searchBy:string;
|
||||
loading?:boolean;
|
||||
|
||||
}
|
||||
export interface ValidationFieldPropsDataRange {
|
||||
name: string;
|
||||
type: "DataRange";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
|
||||
}
|
||||
export interface ValidationFieldPropsDate {
|
||||
name: string;
|
||||
type: "Date";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS" | "YYYY-MM-DD HH:MM:SS";
|
||||
|
||||
|
||||
}
|
||||
|
||||
export interface ValidationFieldPropsTime {
|
||||
name: string;
|
||||
type: "Time";
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
|
||||
}
|
||||
|
||||
export interface ValidationFieldPropsFile {
|
||||
name: string;
|
||||
type: "File" | "MaltyFile";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
|
||||
}
|
||||
export interface ValidationFieldPropsCheckbox {
|
||||
name: string;
|
||||
type: "Checkbox";
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
Group?: boolean
|
||||
|
||||
}
|
||||
export interface ValidationFieldPropstext {
|
||||
name: string;
|
||||
type?: "text" | "number" | "password" | "TextArea";
|
||||
label?: string;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?:'ltr' | "rtl"
|
||||
Group?: boolean
|
||||
|
||||
}
|
||||
|
||||
|
||||
export type ValidationFieldProps = ValidationFieldPropsText| ValidationFieldPropsSelect| ValidationFieldPropsDataRange| ValidationFieldPropsDate| ValidationFieldPropsTime| ValidationFieldPropsFile| ValidationFieldPropsCheckbox| ValidationFieldPropstext | ValidationFieldPropsSearch;
|
||||
10
src/Components/order/OrderStatus.tsx
Normal file
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
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
43
src/Extensions/Editor/SingleLangEditor.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React, { FC } from "react";
|
||||
// import PropTypes from "";
|
||||
import { HtmlEditor } from "./HtmlEditor";
|
||||
import { useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface SingleLangEditorProps {
|
||||
langCode: number;
|
||||
property: string;
|
||||
}
|
||||
|
||||
const PROPERTY_TYPES: string[] = [
|
||||
"privacy_description",
|
||||
"conditions_description",
|
||||
"about_us_description",
|
||||
"product_description",
|
||||
"auction_description"
|
||||
];
|
||||
|
||||
const SingleLangEditor: FC<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
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
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
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
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
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
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
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 ValidationField from '../../Components/ValidationField/ValidationField';
|
||||
import { FakeSelectData } from '../../Layout/app/Const';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
function Form${capitalizeFirstLetter(fileName)}() {
|
||||
const formik = useFormikContext<any>();
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
|
||||
<Col>
|
||||
// name from form utils
|
||||
<ValidationField name="name" type="text"label='name' placeholder='placeholder' />
|
||||
<ValidationField name="number" type="number" label='number' placeholder='placeholder' />
|
||||
<ValidationField name="select" type="select"label='select' option={FakeSelectData} isMulti={true} placeholder='placeholder' />
|
||||
<ValidationField name="Multiselect" type="select"label='Multiselect' option={FakeSelectData} Disabled={true} placeholder='placeholder'/>
|
||||
|
||||
|
||||
</Col>
|
||||
<Col>
|
||||
<ValidationField name="date" type="date" label='date' placeholder='placeholder' />
|
||||
<ValidationField name="time" type="text"label='time' placeholder='placeholder' />
|
||||
<ValidationField name="CheckBox" name2='CheckBox2' type="checkbox" label='CheckBox' placeholder='placeholder' group={true} />
|
||||
<ValidationField 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
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
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
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
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>
|
||||
13
src/Hooks/imageUrlToFile.tsx
Normal file
13
src/Hooks/imageUrlToFile.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export async function fetchImage(imageUrl:any) {
|
||||
try {
|
||||
const response = await fetch(imageUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
return new File([blob], 'image.png', { type: 'image/png' });
|
||||
} catch (error) {
|
||||
console.error('Error fetching image:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
3
src/Hooks/isEmpty.tsx
Normal file
3
src/Hooks/isEmpty.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const isEmpty = (Type:any) => {
|
||||
return !Type || (Array.isArray(Type) && Type.length === 0);
|
||||
};
|
||||
62
src/Hooks/useChangeLanguage.tsx
Normal file
62
src/Hooks/useChangeLanguage.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { useEffect } from 'react';
|
||||
import { initReactI18next, useTranslation } from 'react-i18next';
|
||||
import i18n from 'i18next';
|
||||
import translationEN from '../translate/en.json';
|
||||
import translationAR from '../translate/ar.json';
|
||||
|
||||
|
||||
let language = localStorage.getItem('language') ?? 'en';
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
en: {
|
||||
translation: translationEN
|
||||
},
|
||||
ar: {
|
||||
translation: translationAR
|
||||
}
|
||||
},
|
||||
lng: language ?? "en",
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
}
|
||||
});
|
||||
|
||||
// console.log(navigator.language,"navigator.language");
|
||||
|
||||
export function useLanguage() {
|
||||
useEffect(() => {
|
||||
changeLanguage(language);
|
||||
}, []);
|
||||
|
||||
const changeLanguage = (newLanguage:any) => {
|
||||
i18n.changeLanguage(newLanguage);
|
||||
localStorage.setItem('language', newLanguage);
|
||||
applyLanguageStyles(newLanguage);
|
||||
};
|
||||
|
||||
return { changeLanguage };
|
||||
}
|
||||
|
||||
function applyLanguageStyles(language:any) {
|
||||
if (language === 'ar') {
|
||||
document.body.setAttribute('dir', 'rtl');
|
||||
document.body.classList.remove('de');
|
||||
document.body.classList.add('ar');
|
||||
} else if (language === 'en') {
|
||||
document.body.setAttribute('dir', 'ltr');
|
||||
document.body.classList.remove('ar', 'de');
|
||||
document.body.classList.add('en');
|
||||
}
|
||||
}
|
||||
|
||||
export function useLanguageMenu() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const languageOptions = [
|
||||
{ code: 'ar', icon: '/Layout/Ar.svg', label: t('Arabic') },
|
||||
{ code: 'en', icon: '/Layout/En.svg', label: t('English') },
|
||||
];
|
||||
|
||||
return { languageOptions };
|
||||
}
|
||||
16
src/Hooks/useEventListener.tsx
Normal file
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
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;
|
||||
16
src/Hooks/useFormatToSelect.tsx
Normal file
16
src/Hooks/useFormatToSelect.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const useFormatToSelect = (Data : any) => {
|
||||
const format = (data :any) => {
|
||||
if (!data) return [];
|
||||
const language = localStorage.getItem("language") ?? "en";
|
||||
|
||||
return data.map((item :any) => ({
|
||||
value: item?.id,
|
||||
label: item?.name[language],
|
||||
}));
|
||||
};
|
||||
|
||||
return format(Data);
|
||||
};
|
||||
|
||||
export default useFormatToSelect;
|
||||
|
||||
9
src/Hooks/useImageError.tsx
Normal file
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
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;
|
||||
20
src/Hooks/useNavigateOnSuccess.ts
Normal file
20
src/Hooks/useNavigateOnSuccess.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
|
||||
function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess?:any) {
|
||||
|
||||
const navigate = useNavigate()
|
||||
useEffect(()=>{
|
||||
|
||||
if(isSuccess){
|
||||
if (typeof callbackAfterSuccess === 'function') {
|
||||
callbackAfterSuccess()
|
||||
}
|
||||
navigate(to_path )
|
||||
}
|
||||
},[isSuccess])
|
||||
|
||||
}
|
||||
|
||||
export default useNavigateOnSuccess
|
||||
38
src/Hooks/usePagination.tsx
Normal file
38
src/Hooks/usePagination.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { Pagination } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
export const PaginationBody = ({ data }: any) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const pagination = location?.search || '';
|
||||
const currentPage = parseInt(new URLSearchParams(location.search).get("page") || "1", 10);
|
||||
const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "15", 10);
|
||||
|
||||
const [searchParams] = useSearchParams()
|
||||
const onChange = (page: number, pageSize?: number) => {
|
||||
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}`);
|
||||
};
|
||||
|
||||
const onShowSizeChange = (current: number, pageSize: number) => {
|
||||
navigate(`?page=${current}&per_page=${pageSize}`);
|
||||
};
|
||||
const [t] = useTranslation()
|
||||
|
||||
return (
|
||||
<Pagination
|
||||
className='text-center mt-3 paginateStyle'
|
||||
total={data}
|
||||
showTotal={(total: any) => `${t(`Total`)} ${total} ${t(`items`)}`}
|
||||
pageSize={pageSize}
|
||||
pageSizeOptions={[6, 15, 22, 30]}
|
||||
defaultCurrent={currentPage}
|
||||
current={currentPage}
|
||||
onChange={onChange}
|
||||
onShowSizeChange={onShowSizeChange}
|
||||
// showQuickJumper
|
||||
showSizeChanger
|
||||
|
||||
/>
|
||||
);
|
||||
};
|
||||
18
src/Hooks/useWindowSize.tsx
Normal file
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;
|
||||
};
|
||||
33
src/Layout/Dashboard/AddButton/AddButton.tsx
Normal file
33
src/Layout/Dashboard/AddButton/AddButton.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react'
|
||||
import './Add_Button.scss'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
|
||||
|
||||
|
||||
|
||||
const AddButton = (props :any) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
|
||||
return (
|
||||
<div className='Add_Button' {...props} >
|
||||
<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 AddButton
|
||||
41
src/Layout/Dashboard/AddButton/AddButtonLayout.tsx
Normal file
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
|
||||
77
src/Layout/Dashboard/AddButton/Add_Button.scss
Normal file
77
src/Layout/Dashboard/AddButton/Add_Button.scss
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
.Add_Button{
|
||||
button {
|
||||
border: 2px solid var(--primary);
|
||||
background-color: var(--primary);
|
||||
border-radius: 0.5vw;
|
||||
height: 40px;
|
||||
width: 70px;
|
||||
font-size: 1vw ;
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||
}
|
||||
button span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--bg);
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color:var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.Add_Button{
|
||||
button{
|
||||
font-size: 2vw !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 470px) {
|
||||
.Add_Button{
|
||||
button{
|
||||
font-size: 3vw !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Add_Button_notification{
|
||||
button {
|
||||
border: 2px solid var(--primary);
|
||||
background-color: var(--primary);
|
||||
border-radius: 0.5vw;
|
||||
height: 40px;
|
||||
width: 180px;
|
||||
font-size: 1vw ;
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||
}
|
||||
button span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--bg);
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color:var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.Add_Button_notification{
|
||||
button{
|
||||
font-size: 2vw !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 470px) {
|
||||
.Add_Button_notification{
|
||||
button{
|
||||
font-size: 3vw !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/Layout/Dashboard/DashBody.tsx
Normal file
32
src/Layout/Dashboard/DashBody.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { Spinner } from "reactstrap"
|
||||
import { QueryStatusEnum } from "../../config/QueryStatus"
|
||||
import LoadingPage from "../app/LoadingPage"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { BsEmojiFrown } from "react-icons/bs";
|
||||
import ErrorPage from "../app/ErrorPage";
|
||||
|
||||
|
||||
const DashBody = ({ children , status }: { children: React.ReactNode ,status?:QueryStatusEnum }) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
// Add You Custom Loadaing Page
|
||||
if(status === QueryStatusEnum.LOADING){
|
||||
|
||||
return <LoadingPage />
|
||||
}
|
||||
|
||||
// Add Your Custom Error Page
|
||||
if(status === QueryStatusEnum.ERROR){
|
||||
return (
|
||||
<ErrorPage/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='Page' >
|
||||
{ children }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DashBody
|
||||
27
src/Layout/Dashboard/DashHeader.tsx
Normal file
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
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 = "Edit 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;
|
||||
67
src/Layout/Dashboard/FormikForm.tsx
Normal file
67
src/Layout/Dashboard/FormikForm.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { Formik, Form } from 'formik';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from "reactstrap";
|
||||
import * as Yup from 'yup';
|
||||
|
||||
interface FormValues {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface FormikFormProps {
|
||||
handleSubmit: (values: any) => void
|
||||
initialValues: FormValues;
|
||||
validationSchema: any;
|
||||
title?: string;
|
||||
children: ReactNode;
|
||||
ButtonName?:string
|
||||
}
|
||||
const FormikForm: React.FC<FormikFormProps> = ({ children, handleSubmit, initialValues, validationSchema, title = "Add New Item" ,ButtonName="إضافة"}) => {
|
||||
const [t]= useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formik => (
|
||||
<Form >
|
||||
<div className="Card">
|
||||
{children}
|
||||
|
||||
</div>
|
||||
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormikForm;
|
||||
|
||||
|
||||
|
||||
{/* <FormikForm
|
||||
handleSubmit={() => {
|
||||
|
||||
}}
|
||||
initialValues={() => {
|
||||
return {
|
||||
id: null,
|
||||
name: "",
|
||||
|
||||
}
|
||||
}}
|
||||
validationSchema={() => {
|
||||
return Yup.object().shape({
|
||||
name: Yup.string().required('required'),
|
||||
|
||||
});
|
||||
}}
|
||||
|
||||
>
|
||||
|
||||
</FormikForm> */}
|
||||
77
src/Layout/Dashboard/LayoutModal.tsx
Normal file
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
|
||||
39
src/Layout/Dashboard/LyTable.tsx
Normal file
39
src/Layout/Dashboard/LyTable.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
import DataTable from 'react-data-table-component';
|
||||
import { Card, CardBody, Spinner } from 'reactstrap';
|
||||
import { PaginationBody } from '../../Hooks/usePagination';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const LyTable = (props?: any) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
return (
|
||||
<div className='LayoutBody'>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<DataTable
|
||||
columns={props?.column}
|
||||
data={props?.data}
|
||||
progressPending={props?.isLoading}
|
||||
noDataComponent={<h6 className="my-4">{t("no_records")}</h6>}
|
||||
noHeader
|
||||
pagination
|
||||
progressComponent={<Spinner />}
|
||||
|
||||
{...(props.is_pagination && {
|
||||
paginationServer: true,
|
||||
paginationComponent: () => <PaginationBody data={props?.total} />
|
||||
})}
|
||||
|
||||
{...props}
|
||||
/>
|
||||
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default LyTable
|
||||
36
src/Layout/Dashboard/PageStructure.tsx
Normal file
36
src/Layout/Dashboard/PageStructure.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import React, { FC } from "react";
|
||||
import StatusCard from "../../Extensions/Editor/StatusCard";
|
||||
import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface PageStructureProps {
|
||||
title?: string;
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
data?: any;
|
||||
children:any
|
||||
}
|
||||
|
||||
const PageStructure: FC<PageStructureProps> = ({
|
||||
title,
|
||||
isLoading,
|
||||
isError,
|
||||
data,
|
||||
children,
|
||||
}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
if (!data) return <StatusCard isLoading={isLoading} isError={isError} />;
|
||||
return (
|
||||
<Card className="Information_title">
|
||||
{title && (
|
||||
<CardHeader>
|
||||
<CardTitle>{t(title)}</CardTitle>
|
||||
</CardHeader>
|
||||
)}
|
||||
<CardBody>{children}</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageStructure;
|
||||
56
src/Layout/Dashboard/SearchField.tsx
Normal file
56
src/Layout/Dashboard/SearchField.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { Input } from 'antd';
|
||||
import { SearchProps } from 'antd/es/input'
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
const { Search } = Input;
|
||||
|
||||
const SearchField = ({ searchBy }: any) => {
|
||||
const navigate = useNavigate()
|
||||
const [searchParams,] = useSearchParams();
|
||||
const location = useLocation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const [searchValue, setSearchValue] = useState(searchParams.get(searchBy ?? "search") || "");
|
||||
|
||||
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
|
||||
if (value || value !== "") {
|
||||
navigate(`${location?.pathname}?${searchBy ?? "search"}=${value}`);
|
||||
} else {
|
||||
const params = new URLSearchParams(location.search);
|
||||
params.delete(searchBy ?? "search");
|
||||
navigate(`${location.pathname}?${params.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
const onChange = (e: any) => {
|
||||
const value = e.target.value
|
||||
setSearchValue(e.target.value);
|
||||
if (value === "") {
|
||||
const params = new URLSearchParams(location.search);
|
||||
params.delete(searchBy ?? "search");
|
||||
navigate(`${location.pathname}?${params.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className='SearchField'
|
||||
>
|
||||
<Search
|
||||
allowClear
|
||||
enterButton={t("search")}
|
||||
size="middle"
|
||||
placeholder={t(searchBy ?? "search")}
|
||||
onSearch={onSearch}
|
||||
style={{ width: 250 }}
|
||||
value={searchValue}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchField
|
||||
46
src/Layout/Dashboard/SelectField.tsx
Normal file
46
src/Layout/Dashboard/SelectField.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { Select } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
const SelectField = ({ selectBy, lebel, option }: any) => {
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [t] = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
setSearchQuery(searchParams.get(selectBy) || '');
|
||||
}, [location.search, selectBy,setSearchQuery]);
|
||||
|
||||
|
||||
const handleSelectChange = (value: any) => {
|
||||
if (value) {
|
||||
console.log(`${location.pathname}?${selectBy}=${value}`);
|
||||
navigate(`${location.pathname}?${selectBy}=${value}`);
|
||||
}
|
||||
}
|
||||
const handleonClear = () => {
|
||||
navigate(`${location.pathname}`);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='SelectField'>
|
||||
<Select
|
||||
placeholder={t(`${lebel}`)}
|
||||
options={option}
|
||||
size="large"
|
||||
className={`w-100 custoumSelectField `}
|
||||
allowClear
|
||||
onClear={handleonClear}
|
||||
onChange={handleSelectChange}
|
||||
value={searchQuery}
|
||||
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectField);
|
||||
52
src/Layout/Dashboard/SelectWSearchField.tsx
Normal file
52
src/Layout/Dashboard/SelectWSearchField.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { Select } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [t] = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
setSearchQuery(searchParams.get('search') || '');
|
||||
}, []);
|
||||
|
||||
const handleSearchChange = (value: any) => {
|
||||
if (value || value !== "") {
|
||||
navigate(`${location.pathname}?${selectBy}=${value}`);
|
||||
} else {
|
||||
const params = new URLSearchParams(location.search);
|
||||
params.delete(selectBy ?? "search");
|
||||
navigate(`${location.pathname}?${params.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectChange = (value: any) => {
|
||||
if (value) {
|
||||
console.log(`${location.pathname}?${submiteBy}=${value}`);
|
||||
|
||||
navigate(`${location.pathname}?${submiteBy}=${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='SelectWSearchField'>
|
||||
<Select
|
||||
placeholder={t(`${lebel}`)}
|
||||
options={option}
|
||||
size="large"
|
||||
className={`w-100`}
|
||||
allowClear
|
||||
onChange={handleSelectChange}
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
onSearch={handleSearchChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectWSearchField);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user