first_push
24
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
pnpm-lock.ymal
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-lock.yaml
|
||||||
11
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"aldeen",
|
||||||
|
"Datepicker",
|
||||||
|
"formik",
|
||||||
|
"Karim",
|
||||||
|
"queryqlent",
|
||||||
|
"szhsin",
|
||||||
|
"Viewelement"
|
||||||
|
]
|
||||||
|
}
|
||||||
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
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/Layout/LoginBg.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/Logo.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
11
public/Logo.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<svg width="72" height="34" viewBox="0 0 72 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_2001_4350)">
|
||||||
|
<path d="M34.9514 12.9924C34.4047 16.0388 33.9491 18.929 33.116 21.6629C31.3456 27.5058 27.9089 31.2239 22.7931 32.6143C21.1138 33.0674 19.4215 33.2236 17.7032 33.2236C12.0406 33.2236 6.37794 33.2236 0.71531 33.2392C0.181591 33.2392 -0.0136719 33.1455 -0.0136719 32.4112C0.0123632 26.2091 0.0123632 20.0226 -0.0136719 13.8204C-0.0136719 13.2268 0.103486 13.0393 0.624188 13.0393C2.99338 13.0706 5.34955 13.0706 7.71875 13.0393C8.23945 13.0393 8.40868 13.1643 8.40868 13.8361C8.36962 17.273 8.40868 20.71 8.38264 24.1469C8.38264 24.7562 8.4998 24.9437 9.03352 24.928C12.0276 24.8968 15.0216 24.9437 18.0156 24.8968C20.3848 24.8656 22.5978 24.2407 24.3812 22.1785C25.3835 21.038 25.7871 19.5539 26.1646 18.0229C27.2451 13.5705 28.3255 9.10244 29.432 4.65003C30.3042 1.18183 32.9598 -0.521025 35.7715 0.556928C37.0863 1.05685 37.9715 2.18167 38.6094 3.57207C41.4862 9.75858 44.5454 15.8201 47.1879 22.1629C47.227 22.2566 47.266 22.3347 47.3571 22.5066C47.6045 21.8973 47.8128 21.3505 48.0471 20.8037C50.6246 14.9453 53.3973 9.2118 56.131 3.46271C57.667 0.244478 60.6871 -0.661628 63.1865 1.19745C64.2539 1.9942 64.8788 3.18151 65.2172 4.58753C66.9616 11.8051 68.6799 19.0071 70.4112 26.2247C70.9189 28.3337 71.4136 30.4428 71.9343 32.5362C72.0514 33.0205 72.0775 33.2392 71.5307 33.2392C69.1225 33.1923 66.7142 33.208 64.306 33.2392C63.8894 33.2392 63.7462 33.0517 63.6551 32.6299C62.3794 26.5997 60.8694 20.6475 59.6848 14.6016C59.5936 14.1329 59.5155 13.6642 59.3984 13.0237C59.1771 13.5236 59.0209 13.8673 58.8777 14.2266C56.6907 19.7101 54.2434 25.0218 51.8742 30.3803C50.4293 33.6454 48.3204 34.2703 45.1962 33.3954C44.0897 33.083 43.3347 32.0675 42.814 30.9115C41.33 27.6776 39.8721 24.4125 38.4401 21.1318C37.3206 18.5384 36.11 16.0076 35.1337 13.3205C35.0816 13.258 35.0556 13.1955 34.9514 12.9924Z" fill="red"/>
|
||||||
|
<path d="M24.1476 11.8056C22.0778 9.71216 19.6825 9.16537 17.1051 9.181C11.4685 9.22786 5.83188 9.19662 0.078125 9.19662C0.559774 8.43112 0.989353 7.74373 1.41893 7.05634C2.62956 5.13477 3.85321 3.2132 5.05082 1.27601C5.24609 0.963559 5.44135 0.791711 5.79282 0.807334C10.5182 0.869824 15.2436 0.682354 19.9689 0.932314C22.0387 1.04167 24.0434 1.63533 25.983 2.57268C26.3605 2.76015 26.3345 2.94762 26.2434 3.32256C25.5404 6.11899 24.8635 8.89979 24.1476 11.8056Z" fill="#AFB0B6"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_2001_4350">
|
||||||
|
<rect width="72" height="33.5103" fill="white" transform="translate(0 0.244873)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/Logo_2.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
17
public/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>Hijab - App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
public/language/ar.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
2
public/language/china.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"><path fill="#DE2910" d="M36 27a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V9a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4v18z"></path><path fill="#FFDE02" d="M11.136 8.977l.736.356l.589-.566l-.111.81l.72.386l-.804.144l-.144.804l-.386-.72l-.81.111l.566-.589zm4.665 2.941l-.356.735l.566.59l-.809-.112l-.386.721l-.144-.805l-.805-.144l.721-.386l-.112-.809l.59.566zm-.957 3.779l.268.772l.817.017l-.651.493l.237.783l-.671-.467l-.671.467l.236-.783l-.651-.493l.817-.017zm-3.708 3.28l.736.356l.589-.566l-.111.81l.72.386l-.804.144l-.144.804l-.386-.72l-.81.111l.566-.589zM7 10.951l.929 2.671l2.826.058l-2.253 1.708l.819 2.706L7 16.479l-2.321 1.615l.819-2.706l-2.253-1.708l2.826-.058z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1009 B |
1
public/language/de.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.2 38.4"><g fill-rule="evenodd" clip-rule="evenodd"><path d="M3.03 0h49.13c1.67 0 3.03 1.36 3.03 3.03v32.33c0 1.66-1.36 3.02-3.02 3.03H3.02C1.36 38.4 0 37.03 0 35.37V3.03C0 1.36 1.36 0 3.03 0z"/><path d="M0 12.8h55.2v22.57c0 1.67-1.36 3.03-3.03 3.03H3.03C1.36 38.4 0 37.04 0 35.37V12.8z" fill="#d00"/><path d="M0 25.6h55.2v9.77c0 1.66-1.36 3.02-3.02 3.03H3.03A3.04 3.04 0 010 35.37V25.6z" fill="#ffce00"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 470 B |
1
public/language/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 |
25
public/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
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
|
||||||
86
src/Components/Columns/ColumnsImage.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import {
|
||||||
|
DownloadOutlined,
|
||||||
|
RotateLeftOutlined,
|
||||||
|
RotateRightOutlined,
|
||||||
|
SwapOutlined,
|
||||||
|
ZoomInOutlined,
|
||||||
|
ZoomOutOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import React from 'react';
|
||||||
|
import { Image, Space } from 'antd';
|
||||||
|
import { ImageBaseURL } from '../../api/config';
|
||||||
|
import useImageError from '../../Hooks/useImageError';
|
||||||
|
|
||||||
|
|
||||||
|
const ColumnsImage= ({src}:any) => {
|
||||||
|
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
|
||||||
|
|
||||||
|
const imageUrl = ImageBaseURL + src || ErrorImage;
|
||||||
|
console.log(imageUrl);
|
||||||
|
|
||||||
|
const handleError = useImageError;
|
||||||
|
// or you can download flipped and rotated image
|
||||||
|
// https://codesandbox.i`o/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
|
||||||
|
const onDownload = () => {
|
||||||
|
fetch(src)
|
||||||
|
.then((response) => response.blob())
|
||||||
|
.then((blob) => {
|
||||||
|
const url = URL.createObjectURL(new Blob([blob]));
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = 'image.png';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
link.remove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
width={45}
|
||||||
|
height={45}
|
||||||
|
src={imageUrl }
|
||||||
|
className='p-1 mb-1 columnImage '
|
||||||
|
preview={{
|
||||||
|
toolbarRender: (
|
||||||
|
_,
|
||||||
|
{
|
||||||
|
transform: { scale },
|
||||||
|
actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },
|
||||||
|
},
|
||||||
|
) => (
|
||||||
|
<Space size={12} className="toolbar-wrapper">
|
||||||
|
<DownloadOutlined onClick={onDownload} />
|
||||||
|
<SwapOutlined rotate={90} onClick={onFlipY} />
|
||||||
|
<SwapOutlined onClick={onFlipX} />
|
||||||
|
<RotateLeftOutlined onClick={onRotateLeft} />
|
||||||
|
<RotateRightOutlined onClick={onRotateRight} />
|
||||||
|
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
|
||||||
|
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
onError={handleError}
|
||||||
|
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColumnsImage;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// name: t("image"),
|
||||||
|
// center: "true",
|
||||||
|
// cell: (row: any) => {
|
||||||
|
// return (
|
||||||
|
// <ColumnsImage src={row?.image} />
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// },
|
||||||
42
src/Components/Columns/ColumnsSwitch.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Switch } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
export interface ColumnsSwitchProps {
|
||||||
|
name: string;
|
||||||
|
Front?: string;
|
||||||
|
Back?: string;
|
||||||
|
onChange?: (checked:any,event:any) => any;
|
||||||
|
icon?: boolean
|
||||||
|
Checked?:boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
const ColumnsSwitch = (props: ColumnsSwitchProps) => {
|
||||||
|
const { name, Front, Back, icon, onChange } = props;
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const onSwitchChange = (checked: boolean, event: Event) => {
|
||||||
|
// formik.setFieldValue("status", checked)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
checkedChildren={icon ? <CheckOutlined /> : Front}
|
||||||
|
unCheckedChildren={icon ? <CloseOutlined /> : Back}
|
||||||
|
onChange={ (checked:any,event:any)=> onChange ? onChange(checked,event) : onSwitchChange(checked,event)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ColumnsSwitch
|
||||||
|
|
||||||
|
|
||||||
|
ColumnsSwitch.defaultProps = {
|
||||||
|
Front: "Front",
|
||||||
|
Back: "Back",
|
||||||
|
onChange: undefined,
|
||||||
|
icon: false,
|
||||||
|
Checked:false
|
||||||
|
|
||||||
|
};
|
||||||
50
src/Components/Ui/Alert.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import React from "react";
|
||||||
|
import { confirmAlert } from "react-confirm-alert";
|
||||||
|
import SweetAlert from "react-bootstrap-sweetalert";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export default function CustomConfirmAlert(options : any) {
|
||||||
|
confirmAlert({
|
||||||
|
customUI: ({ onClose }) => <CustomUI onClose={onClose} options={options} />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomUIProps ={
|
||||||
|
onClose :()=> void
|
||||||
|
options:{
|
||||||
|
title?:string
|
||||||
|
confirmBtnText:string
|
||||||
|
cancelBtnText:string
|
||||||
|
body:string
|
||||||
|
onConfirm:()=>void
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function CustomUI({ onClose, options }:CustomUIProps) {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<SweetAlert
|
||||||
|
title={options.title || t(`delete_are_you_sure`)}
|
||||||
|
customClass="SweetAlert"
|
||||||
|
warning
|
||||||
|
show={true}
|
||||||
|
showCancel
|
||||||
|
reverseButtons
|
||||||
|
cancelBtnBsStyle="danger"
|
||||||
|
confirmBtnText={options.confirmBtnText || t("yes_delete_it")}
|
||||||
|
cancelBtnText={options.cancelBtnText || t("cancel")}
|
||||||
|
onConfirm={() => {
|
||||||
|
options.onConfirm();
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
onCancel={onClose}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
49
src/Components/Ui/CheckboxesVuexy.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface CheckBoxesVuexyProps {
|
||||||
|
className?: string;
|
||||||
|
color: string;
|
||||||
|
defaultChecked?: boolean;
|
||||||
|
checked?: boolean;
|
||||||
|
value?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
onChange?: () => void;
|
||||||
|
size?: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CheckBoxesVuexy: React.FC<CheckBoxesVuexyProps> = ({
|
||||||
|
className = '',
|
||||||
|
color,
|
||||||
|
defaultChecked,
|
||||||
|
checked,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
onClick,
|
||||||
|
onChange,
|
||||||
|
size = 'md',
|
||||||
|
icon,
|
||||||
|
label,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={`vx-checkbox-con ${className} vx-checkbox-${color}`}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={defaultChecked}
|
||||||
|
checked={checked}
|
||||||
|
value={value}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onClick}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<span className={`vx-checkbox vx-checkbox-${size}`}>
|
||||||
|
<span className="vx-checkbox--check">{icon}</span>
|
||||||
|
</span>
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CheckBoxesVuexy;
|
||||||
27
src/Components/Ui/FileInput.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react'
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import { Input } from 'reactstrap';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
|
||||||
|
type FileInputProps = {
|
||||||
|
name:string,
|
||||||
|
label:string,
|
||||||
|
accept:string,
|
||||||
|
onChange:any
|
||||||
|
}
|
||||||
|
function FileInput({name , accept="image/*" ,label , onChange} :FileInputProps) {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="custom-file-input">
|
||||||
|
<label className="custom-file-label" htmlFor="inputGroupFile01">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<Input accept={accept} id="inputGroupFile01" name={name} onChange={onChange}
|
||||||
|
type="file" className="custom-file-input" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileInput
|
||||||
37
src/Components/Ui/HovarableImage.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Tooltip } from "reactstrap";
|
||||||
|
|
||||||
|
const tooltipStyle = {
|
||||||
|
backgroundColor: "white",
|
||||||
|
border: "2px solid lightgrey",
|
||||||
|
maxWidth: "400px",
|
||||||
|
};
|
||||||
|
|
||||||
|
const HovarableImage = ({ id, src, imgPreviewProps = {}, ...props }:any) => {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
const ID = `image_hover_tooltip_${id}`;
|
||||||
|
const toggleTooltip = React.useCallback(() => setIsOpen((prev) => !prev), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{marginInline:"20px"}}>
|
||||||
|
<img id={ID} src={src} alt={props.alt} {...props} style={{ maxWidth:"300px", maxHeight:'50px'}}/>
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
isOpen={isOpen}
|
||||||
|
target={ID}
|
||||||
|
toggle={toggleTooltip}
|
||||||
|
style={tooltipStyle}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{ maxWidth:"300px", maxHeight:'150px'}}
|
||||||
|
src={src}
|
||||||
|
alt={props.alt}
|
||||||
|
{...imgPreviewProps}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HovarableImage;
|
||||||
|
|
||||||
34
src/Components/Ui/ImagePreview.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const ImagePreview = ({ preview, height = 200 }:any) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid lightgray",
|
||||||
|
height: `${height}px`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{preview ? (
|
||||||
|
<img
|
||||||
|
className="p-1"
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
}}
|
||||||
|
height={height}
|
||||||
|
src={preview}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div>{t("image_preview")}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImagePreview;
|
||||||
13
src/Components/Ui/LoadingButton.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Button, Spinner } from "reactstrap";
|
||||||
|
|
||||||
|
const LoadingButton = ({ isLoading = false, ...props }) => {
|
||||||
|
return (
|
||||||
|
<Button className="btn-primary" disabled={isLoading} {...props}>
|
||||||
|
{isLoading ? <Spinner style={{ marginRight: "10px" }} size="sm" /> : null}
|
||||||
|
<span >{props.children}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { LoadingButton };
|
||||||
10
src/Components/Ui/LoadingSpinner.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Spin } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function LoadingSpinner() {
|
||||||
|
return (
|
||||||
|
<Spin className='primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingSpinner
|
||||||
57
src/Components/Ui/PasswordField/PasswordField.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React, { FC, useState } from "react";
|
||||||
|
import { ErrorMessage, useField, Field } from "formik";
|
||||||
|
import { FormGroup } from "reactstrap";
|
||||||
|
// import PropTypes from "prop-types";
|
||||||
|
import { Eye, EyeOff } from "react-feather";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
interface PasswordFieldProps {
|
||||||
|
name: string;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PasswordField: FC<PasswordFieldProps> = ({ name, label, ...props }) => {
|
||||||
|
const [field, meta] = useField({ name, ...props });
|
||||||
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||||
|
const EyeIcon = showPassword ? Eye : EyeOff;
|
||||||
|
|
||||||
|
const toggleShow = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{label && <label className="change_password_body" htmlFor={name}>{label}</label>}
|
||||||
|
<FormGroup className={"position-relative has-icon-left"}>
|
||||||
|
<Field
|
||||||
|
className={
|
||||||
|
|
||||||
|
"form-control " + (meta.touched && meta.error ? "is-invalid" : "")
|
||||||
|
}
|
||||||
|
name={name}
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
autoComplete="new-password"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<div className="form-control-position">
|
||||||
|
<EyeIcon
|
||||||
|
fontSize={12}
|
||||||
|
onClick={toggleShow}
|
||||||
|
className="cursor-pointer change_password_body"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ErrorMessage
|
||||||
|
className="field-error text-danger"
|
||||||
|
component="div"
|
||||||
|
name={field.name}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// PasswordField.propTypes = {
|
||||||
|
// name: PropTypes.string.isRequired,
|
||||||
|
// };
|
||||||
|
|
||||||
|
export { PasswordField };
|
||||||
10
src/Components/Ui/PasswordField/index.css
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
.back-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
padding-left: 0.8rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
24
src/Components/Ui/ProgressBar.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Progress } from "reactstrap";
|
||||||
|
|
||||||
|
const ProgressBar = ({ value,isLoading, isSuccess, isError }:any) => {
|
||||||
|
|
||||||
|
|
||||||
|
let color = "";
|
||||||
|
if (!isLoading && isSuccess) {
|
||||||
|
color = "success";
|
||||||
|
}
|
||||||
|
if (!isLoading && isError) {
|
||||||
|
color = "danger";
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="my-1">
|
||||||
|
<Progress color={color} style={{ height: "1rem" }} className=" mt-4" value={value}>
|
||||||
|
{value}%
|
||||||
|
</Progress>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default ProgressBar;
|
||||||
29
src/Components/Ui/SelectField.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from "react";
|
||||||
|
import Select from "react-select";
|
||||||
|
import { ValidatedField } from "./ValidatedField";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
|
||||||
|
const SelectField = ({ label, name, options, ...props }:any) => {
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ValidatedField
|
||||||
|
CustomField={Select}
|
||||||
|
label={label}
|
||||||
|
name={name}
|
||||||
|
className="React"
|
||||||
|
classNamePrefix="select"
|
||||||
|
options={options || []}
|
||||||
|
value={options?.find((opt:any) => opt.value === formik?.values[name]) || ""}
|
||||||
|
onChange={(opt:any) => formik.setFieldValue(name, opt.value)}
|
||||||
|
onBlur={() => formik.setFieldTouched(name)}
|
||||||
|
key={name}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default SelectField;
|
||||||
|
|
||||||
|
|
||||||
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
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Badge } from "reactstrap";
|
||||||
|
|
||||||
|
const StatusBadge = ({ status }:any) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge color={status ? "success" : "danger"}>
|
||||||
|
{status ? t("active") : t("inacticve")}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default StatusBadge;
|
||||||
38
src/Components/Ui/TableActions.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React , {ReactNode} from "react";
|
||||||
|
import confirmAlert from "./Alert";
|
||||||
|
import { FaEdit, FaTrash } from "react-icons/fa";
|
||||||
|
|
||||||
|
type TableActionsProps = {
|
||||||
|
onDelete: () => any;
|
||||||
|
onEdit: () => void;
|
||||||
|
showEdit?: boolean;
|
||||||
|
showDelete?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const TableActions = ({ onDelete , onEdit,showEdit=true,showDelete=true, children }:TableActionsProps) => {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="data-list-action TableActions">
|
||||||
|
{showEdit && <FaEdit onClick={onEdit} className="cursor-pointer m-1" size={20} />}
|
||||||
|
{showDelete && (
|
||||||
|
<FaTrash
|
||||||
|
onClick={() =>
|
||||||
|
confirmAlert({
|
||||||
|
onConfirm: () => {
|
||||||
|
onDelete();
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="cursor-pointer"
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TableActions;
|
||||||
31
src/Components/Ui/ThreeSwitchState/TripleSwitch.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import type { RadioChangeEvent } from 'antd';
|
||||||
|
import { Radio } from 'antd';
|
||||||
|
|
||||||
|
const onChange = (e: RadioChangeEvent) => {
|
||||||
|
console.log(`radio checked:${e.target.value}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const App: React.FC = () => (
|
||||||
|
<>
|
||||||
|
|
||||||
|
|
||||||
|
<Radio.Group onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
|
||||||
|
<Radio.Button value="a">Hangzhou</Radio.Button>
|
||||||
|
<Radio.Button value="b" disabled>
|
||||||
|
Shanghai
|
||||||
|
</Radio.Button>
|
||||||
|
<Radio.Button value="c">Beijing</Radio.Button>
|
||||||
|
<Radio.Button value="d">Chengdu</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
|
||||||
|
<Radio.Group disabled onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
|
||||||
|
<Radio.Button value="a">Hangzhou</Radio.Button>
|
||||||
|
<Radio.Button value="b">Shanghai</Radio.Button>
|
||||||
|
<Radio.Button value="c">Beijing</Radio.Button>
|
||||||
|
<Radio.Button value="d">Chengdu</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
||||||
39
src/Components/Ui/ToggleStatus.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
import Toggle from "react-toggle";
|
||||||
|
import "react-toggle/style.css";
|
||||||
|
import StatusBadge from "./StatusBadge";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface ToggleStatusProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
export const ToggleStatus = ({ object, 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
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ErrorMessage, useField, Field } from "formik";
|
||||||
|
import { FormGroup } from "reactstrap";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const ValidatedField = ({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
CustomField,
|
||||||
|
icon: Icon,
|
||||||
|
optional,
|
||||||
|
labelIcon = null,
|
||||||
|
formProps,
|
||||||
|
isRequired,
|
||||||
|
|
||||||
|
...props
|
||||||
|
}:any) => {
|
||||||
|
|
||||||
|
const [field, meta] = useField({ name, ...props });
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
let Wrapper = Field;
|
||||||
|
|
||||||
|
if (CustomField) {
|
||||||
|
Wrapper = CustomField;
|
||||||
|
}
|
||||||
|
const fieldProps = props.type === "file" ? {} : { ...field };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{label && (
|
||||||
|
<label htmlFor={name}>
|
||||||
|
{label} {props.required || isRequired ? "*" : ""} {labelIcon}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
<FormGroup
|
||||||
|
className={Icon ? "position-relative has-icon-left" : ""}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
|
<Wrapper
|
||||||
|
className={
|
||||||
|
"form-control " + (meta.touched && meta.error ? "is-invalid" : "")
|
||||||
|
}
|
||||||
|
{...fieldProps}
|
||||||
|
required={props.isRequired}
|
||||||
|
{...props}
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
{Icon && (
|
||||||
|
<div className="form-control-position">
|
||||||
|
<Icon size={15} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ErrorMessage name={field.name}>
|
||||||
|
{(msg) => <span className="field-error text-danger">{t(msg)}</span>}
|
||||||
|
</ErrorMessage>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export { ValidatedField };
|
||||||
17
src/Components/Ui/index.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import Checkbox from './CheckboxesVuexy'
|
||||||
|
import ImagePreview from './ImagePreview'
|
||||||
|
import SelectField from './SelectField'
|
||||||
|
import { useImagePreview } from './useImagePreview'
|
||||||
|
import {ValidatedField} from './ValidatedField'
|
||||||
|
import StatusBadge from './StatusBadge'
|
||||||
|
import HovarableImage from './HovarableImage'
|
||||||
|
|
||||||
|
export {
|
||||||
|
Checkbox,
|
||||||
|
ImagePreview,
|
||||||
|
SelectField,
|
||||||
|
useImagePreview,
|
||||||
|
ValidatedField,
|
||||||
|
StatusBadge,
|
||||||
|
HovarableImage
|
||||||
|
}
|
||||||
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
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from "react";
|
||||||
|
import { confirmAlert } from "react-confirm-alert";
|
||||||
|
import SweetAlert from "react-bootstrap-sweetalert";
|
||||||
|
|
||||||
|
interface CustomUIProps {
|
||||||
|
onClose: () => void;
|
||||||
|
options: {
|
||||||
|
title?: string;
|
||||||
|
confirmBtnText?: string;
|
||||||
|
cancelBtnText?: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
body?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CustomConfirmAlert(options: any) {
|
||||||
|
confirmAlert({
|
||||||
|
customUI: ({ onClose }) => <CustomUI onClose={onClose} options={options} />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function CustomUI({ onClose, options }: CustomUIProps) {
|
||||||
|
const sweetAlertProps: any = {
|
||||||
|
title: options.title || `DELETE, Are you sure?`,
|
||||||
|
warning: true,
|
||||||
|
show: true,
|
||||||
|
showCancel: true,
|
||||||
|
reverseButtons: true,
|
||||||
|
cancelBtnBsStyle: "danger",
|
||||||
|
confirmBtnText: options.confirmBtnText || "Yes, delete it!",
|
||||||
|
cancelBtnText: options.cancelBtnText || "Cancel",
|
||||||
|
onConfirm: () => {
|
||||||
|
options.onConfirm();
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
onCancel: onClose,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SweetAlert {...sweetAlertProps}>{options.body || "You won't be able to revert this!"}</SweetAlert>;
|
||||||
|
}
|
||||||
24
src/Components/Ui/useImagePreview.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export const useImagePreview = (defaultValue:any = null) => {
|
||||||
|
const [preview, setPreview] = useState(defaultValue || null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
URL.revokeObjectURL(preview);
|
||||||
|
|
||||||
|
};
|
||||||
|
}, [preview]);
|
||||||
|
|
||||||
|
const handleImageChange = (event:any) => {
|
||||||
|
|
||||||
|
setPreview(URL.createObjectURL(event.target.files[0]));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
preview,
|
||||||
|
handleImageChange,
|
||||||
|
setPreview,
|
||||||
|
};
|
||||||
|
};
|
||||||
62
src/Components/Utils/BlockModal.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
|
||||||
|
interface BlockModelProps {
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlockModel: React.FC<BlockModelProps> = ({Mutation ,type}) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
|
||||||
|
|
||||||
|
const {isOpenBlock:isOpen , objectID , setIsopenBlock:setIsOpen} = useCommonModelState()
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const blockInput = document.getElementById('block_input') as HTMLInputElement;
|
||||||
|
if (blockInput) {
|
||||||
|
Mutation.mutate({ [key_to_api]: objectID, block_timer: blockInput.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Mutation.isSuccess) {
|
||||||
|
setIsOpen();
|
||||||
|
}
|
||||||
|
}, [Mutation.isSuccess, setIsOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsOpen()}>
|
||||||
|
{t("al")}{type}{t('_block_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('blocking_')}{type}</h1>
|
||||||
|
<BsExclamationCircle className='modal_info' style={{ fontSize: '100px', color: '#f8be86', margin: '20px 0' }} />
|
||||||
|
<div className=''>
|
||||||
|
<Label for='block_input'>{t('date_blocking')}</Label>
|
||||||
|
<Input id='block_input' placeholder={t('date_blocking')} type='date' />
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsOpen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton color='primary' onClick={handleSubmit} isLoading={Mutation.isLoading} type='submit'>
|
||||||
|
{t('add_block_for_')}{type}
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlockModel;
|
||||||
63
src/Components/Utils/GiftModal.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Col, Input, Label, Modal, ModalHeader, Row } from 'reactstrap';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
|
||||||
|
|
||||||
|
interface GiftModalProps {
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const GiftModal: React.FC<GiftModalProps> = ({Mutation ,type }) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const {isOpenGift:isOpen , objectID , setIsopenGift:setIsOpen} = useCommonModelState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Mutation.isSuccess) {
|
||||||
|
setIsOpen();
|
||||||
|
}
|
||||||
|
}, [Mutation.isSuccess, setIsOpen]);
|
||||||
|
|
||||||
|
const handleGift = () => {
|
||||||
|
const enterCodesInput = document.getElementById('enter_codes') as HTMLInputElement;
|
||||||
|
if (enterCodesInput && enterCodesInput.value) {
|
||||||
|
Mutation.mutate({ type, id: objectID, value: enterCodesInput.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsOpen()}>
|
||||||
|
{t("al")}{type} {t('_gift_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<Row>
|
||||||
|
<Col className='' style={{ width: 300 }}>
|
||||||
|
<Label for='enter_codes' className='modal_info'>{t('value')}</Label>
|
||||||
|
<Input id='enter_codes' placeholder={t('value')} type='number' />
|
||||||
|
<Col style={{ marginTop: 20, display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Button color='danger' onClick={() => setIsOpen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton
|
||||||
|
color='primary'
|
||||||
|
onClick={handleGift}
|
||||||
|
type='submit'
|
||||||
|
isLoading={Mutation.isLoading}>
|
||||||
|
{t('give')}
|
||||||
|
</LoadingButton>
|
||||||
|
</Col>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GiftModal;
|
||||||
93
src/Components/Utils/Loading/Loading.scss
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
.Loading{
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 200px;
|
||||||
|
height: 60px;
|
||||||
|
position: relative;
|
||||||
|
left: 40%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--primary);
|
||||||
|
left: 15%;
|
||||||
|
transform-origin: 50%;
|
||||||
|
animation: circle7124 .5s alternate infinite ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes circle7124 {
|
||||||
|
0% {
|
||||||
|
top: 60px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50px 50px 25px 25px;
|
||||||
|
transform: scaleX(1.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle:nth-child(2) {
|
||||||
|
left: 45%;
|
||||||
|
animation-delay: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle:nth-child(3) {
|
||||||
|
left: auto;
|
||||||
|
right: 15%;
|
||||||
|
animation-delay: .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow {
|
||||||
|
width: 20px;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(0,0,0,0.9);
|
||||||
|
position: absolute;
|
||||||
|
top: 62px;
|
||||||
|
transform-origin: 50%;
|
||||||
|
z-index: -1;
|
||||||
|
left: 15%;
|
||||||
|
filter: blur(1px);
|
||||||
|
animation: shadow046 .5s alternate infinite ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadow046 {
|
||||||
|
0% {
|
||||||
|
transform: scaleX(1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: scaleX(1);
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scaleX(.2);
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow:nth-child(4) {
|
||||||
|
left: 45%;
|
||||||
|
animation-delay: .2s
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow:nth-child(5) {
|
||||||
|
left: auto;
|
||||||
|
right: 15%;
|
||||||
|
animation-delay: .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
src/Components/Utils/Loading/Loading.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './Loading.scss'
|
||||||
|
const Loading = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Loading">
|
||||||
|
<div className="wrapper">
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="circle"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Loading
|
||||||
43
src/Components/Utils/SearchBar/SearchBar.scss
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
.SearchBar{
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
max-width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 1rem;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
font-weight: 500;
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--bg);
|
||||||
|
border: none;
|
||||||
|
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.input::placeholder {
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 1rem;
|
||||||
|
fill: var(--bg);
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
14
src/Components/Utils/SearchBar/SearchBar.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './SearchBar.scss'
|
||||||
|
const SearchBar = () => {
|
||||||
|
return (
|
||||||
|
<div className='SearchBar'>
|
||||||
|
<div className="group">
|
||||||
|
<svg className="icon" viewBox="0 0 24 24"><g><path d="M21.53 20.47l-3.66-3.66C19.195 15.24 20 13.214 20 11c0-4.97-4.03-9-9-9s-9 4.03-9 9 4.03 9 9 9c2.215 0 4.24-.804 5.808-2.13l3.66 3.66c.147.146.34.22.53.22s.385-.073.53-.22c.295-.293.295-.767.002-1.06zM3.5 11c0-4.135 3.365-7.5 7.5-7.5s7.5 3.365 7.5 7.5-3.365 7.5-7.5 7.5-7.5-3.365-7.5-7.5z"></path></g></svg>
|
||||||
|
<input placeholder="Search" type="search" className="input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBar
|
||||||
84
src/Components/Utils/Theme.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { usePageState } from '../../lib/state mangment/LayoutPagestate';
|
||||||
|
import { BsFillMoonStarsFill, BsFillSunFill, BsSunglasses } from 'react-icons/bs';
|
||||||
|
|
||||||
|
|
||||||
|
let What_the_Theme = localStorage.getItem('theme') ?? "light";
|
||||||
|
|
||||||
|
if (What_the_Theme === "dark") {
|
||||||
|
|
||||||
|
document.body.classList.add('dark')}
|
||||||
|
else if (What_the_Theme === "glass") {
|
||||||
|
|
||||||
|
document.body.classList.add('glass')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function Theme() {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const {setThemChange} = usePageState()
|
||||||
|
|
||||||
|
const changeTheme = (newTheme : any) => {
|
||||||
|
|
||||||
|
|
||||||
|
if(newTheme === "dark"){
|
||||||
|
document.body.classList.remove('glass');
|
||||||
|
document.body.classList.add('dark');localStorage.setItem("theme", "dark");
|
||||||
|
What_the_Theme = "dark"
|
||||||
|
}
|
||||||
|
else if(newTheme === "light"){
|
||||||
|
document.body.classList.remove('glass');
|
||||||
|
document.body.classList.remove('dark');localStorage.setItem("theme", "light");
|
||||||
|
What_the_Theme = "light"
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(newTheme === "glass"){
|
||||||
|
document.body.classList.remove('dark'); document.body.classList.add('glass'); localStorage.setItem("theme", "glass");
|
||||||
|
What_the_Theme = "glass"
|
||||||
|
|
||||||
|
}
|
||||||
|
setThemChange()
|
||||||
|
};
|
||||||
|
/// BsSunglasses BsFillSunFill BsFillMoonStarsFill
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Theme'>
|
||||||
|
<Menu menuButton={<MenuButton>
|
||||||
|
{(What_the_Theme === "light") ?
|
||||||
|
<>
|
||||||
|
<BsFillSunFill/>
|
||||||
|
{t("light")}
|
||||||
|
</>
|
||||||
|
|
||||||
|
: (What_the_Theme === "dark") ?
|
||||||
|
<>
|
||||||
|
<BsFillMoonStarsFill/>
|
||||||
|
{t("dark")}
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<BsSunglasses/>
|
||||||
|
{t("glass")}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</MenuButton>} transition>
|
||||||
|
<MenuItem onClick={() => changeTheme('light')}>
|
||||||
|
<BsFillSunFill/>
|
||||||
|
{t("light")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => changeTheme('dark')}>
|
||||||
|
<BsFillMoonStarsFill/>
|
||||||
|
{t("dark")} </MenuItem>
|
||||||
|
<MenuItem onClick={() => changeTheme('glass')}>
|
||||||
|
<BsSunglasses/>
|
||||||
|
{t("glass")} </MenuItem>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
src/Components/Utils/Translate.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Menu, Dropdown } from 'antd';
|
||||||
|
import { useLanguage, useLanguageMenu } from '../../Hooks/useChangeLanguage';
|
||||||
|
import i18next from 'i18next';
|
||||||
|
import type { MenuProps } from 'antd';
|
||||||
|
|
||||||
|
export default function Translate() {
|
||||||
|
const { changeLanguage } = useLanguage();
|
||||||
|
const { languageOptions } = useLanguageMenu();
|
||||||
|
|
||||||
|
const handleLanguageChange = (newLanguage:string) => {
|
||||||
|
changeLanguage(newLanguage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const items : MenuProps['items'] = languageOptions.map((option:any,index:number) => ({
|
||||||
|
key: index,
|
||||||
|
label: (
|
||||||
|
<div className='MenuPropsItem' onClick={() => handleLanguageChange(option.code)}>
|
||||||
|
<img alt='' src={option.icon} width={20} height={20} />
|
||||||
|
{option.label}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='Translate'>
|
||||||
|
<Dropdown menu={{items}} placement="bottomLeft">
|
||||||
|
<button className="ant-dropdown-link" onClick={e => e.preventDefault()}>
|
||||||
|
{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
|
||||||
|
))}
|
||||||
|
</button>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
src/Components/Utils/UnBlockModal.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { BsExclamationCircle } from 'react-icons/bs';
|
||||||
|
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { LoadingButton } from '../Ui/LoadingButton';
|
||||||
|
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
|
||||||
|
import { CiLock } from "react-icons/ci";
|
||||||
|
|
||||||
|
interface UnBlockModalProps {
|
||||||
|
|
||||||
|
Mutation:any,
|
||||||
|
type :'customer' |'driver'
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnBlockModal: React.FC<UnBlockModalProps> = ({Mutation ,type }) => {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
|
||||||
|
|
||||||
|
const {isOpenUnBlock:isOpen , objectID , setIsopenUnBlock:setIsopen} = useCommonModelState()
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
Mutation.mutate({ [key_to_api]: objectID });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Mutation.isSuccess) {
|
||||||
|
setIsopen();
|
||||||
|
}
|
||||||
|
}, [Mutation.isSuccess, setIsopen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} centered size='md'>
|
||||||
|
<ModalHeader toggle={() => setIsopen()}>
|
||||||
|
{t("al")}{type} {t('un_block_page')}
|
||||||
|
</ModalHeader>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('un_blocking_')}{type}</h1>
|
||||||
|
<CiLock className='modal_info' style={{ fontSize: '100px', color: 'black', margin: '20px 0' }} />
|
||||||
|
<div className=''>
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsopen()}>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<LoadingButton color='primary' onClick={handleSubmit} type='submit' isLoading={Mutation.isLoading}>
|
||||||
|
{t('un_block_for_')}{type}
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnBlockModal;
|
||||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 ? t(placeholder) : t(name)}
|
||||||
|
allowClear
|
||||||
|
className={`${className} w-100`}
|
||||||
|
defaultValue={getDefaultDate()}
|
||||||
|
size="large"
|
||||||
|
onChange={onChange || onCalendarChange}
|
||||||
|
disabled={isDisabled}
|
||||||
|
format={Format}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateField;
|
||||||
37
src/Components/ValidationField/View/Default.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Form, Input } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
|
||||||
|
const Default = ({ name, label, placeholder, isDisabled, onChange, props,type }: any) => {
|
||||||
|
const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
|
||||||
|
|
||||||
|
// console.log(isError);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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);
|
||||||
|
;
|
||||||
64
src/Components/ValidationField/View/File.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { Button, Upload, UploadFile } from 'antd'
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
|
import { ImageBaseURL } from '../../../api/config';
|
||||||
|
|
||||||
|
|
||||||
|
const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
|
||||||
|
const { formik, t ,isError,errorMsg} = useFormField(name, props)
|
||||||
|
let FormikName = formik.values[name];
|
||||||
|
const imageUrl = formik.values[name] ? ImageBaseURL + FormikName : '';
|
||||||
|
const fileList: UploadFile[] = [
|
||||||
|
|
||||||
|
{
|
||||||
|
uid: '-1',
|
||||||
|
name: '',
|
||||||
|
status: 'done',
|
||||||
|
url: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage"),
|
||||||
|
thumbUrl: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage")
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const FilehandleChange = (value:any) => {
|
||||||
|
|
||||||
|
formik.setFieldValue(name, value.file.originFileObj)
|
||||||
|
|
||||||
|
};
|
||||||
|
const customRequest = async ({ onSuccess}: any) => {
|
||||||
|
onSuccess();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="ValidationField">
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`${label || name}`)} {isError ? errorMsg : ''}
|
||||||
|
</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
|
||||||
92
src/Components/ValidationField/View/MaltyFile.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { Button, Upload } from "antd";
|
||||||
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
|
import { ImageBaseURL } from "../../../api/config";
|
||||||
|
|
||||||
|
const MaltyFile = ({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
onChange,
|
||||||
|
isDisabled,
|
||||||
|
placeholder,
|
||||||
|
className,
|
||||||
|
props,
|
||||||
|
}: any) => {
|
||||||
|
const { formik, t, isError } = useFormField(name, props);
|
||||||
|
|
||||||
|
let imageUrl = formik?.values?.[name] ?? null;
|
||||||
|
// console.log(imageUrl);
|
||||||
|
|
||||||
|
// Mapping formik values to fileList format
|
||||||
|
const fileList = imageUrl
|
||||||
|
? imageUrl.map((file: any, index: number) => {
|
||||||
|
let fileUrl = "";
|
||||||
|
|
||||||
|
if (typeof file === "string") {
|
||||||
|
fileUrl = file.replace("public", "/storage");
|
||||||
|
}
|
||||||
|
console.log(file instanceof File);
|
||||||
|
|
||||||
|
return file instanceof File
|
||||||
|
? {
|
||||||
|
uid: index,
|
||||||
|
name: file?.name,
|
||||||
|
status: "done",
|
||||||
|
originFileObj: file,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
uid: index,
|
||||||
|
id: file?.id,
|
||||||
|
name: file?.name,
|
||||||
|
status: "done",
|
||||||
|
url: ImageBaseURL + fileUrl || "",
|
||||||
|
thumbUrl: ImageBaseURL + fileUrl || "",
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const FilehandleChange = ({ fileList }: any) => {
|
||||||
|
if (fileList.length === 0) {
|
||||||
|
formik.setFieldValue(name, null);
|
||||||
|
} else {
|
||||||
|
formik.setFieldValue(
|
||||||
|
name,
|
||||||
|
fileList.map((file: any) => file?.originFileObj ?? file),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom request function
|
||||||
|
const customRequest = async ({ onSuccess }: any) => {
|
||||||
|
// Perform any necessary actions before onSuccess is called
|
||||||
|
onSuccess();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ValidationField upload_image_button">
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`input.${label || name}`)}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Upload
|
||||||
|
disabled={isDisabled}
|
||||||
|
listType="picture"
|
||||||
|
fileList={fileList} // Using fileList instead of defaultFileList
|
||||||
|
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 />}
|
||||||
|
>
|
||||||
|
{t(`input.` + placeholder) ?? t("input.upload_image")}
|
||||||
|
</Button>
|
||||||
|
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||||
|
</Upload>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MaltyFile;
|
||||||
69
src/Components/ValidationField/View/SearchField.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { Form, Select } from 'antd';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import useFormField from '../../../Hooks/useFormField';
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const SearchField = ({ name, label, placeholder, isDisabled, searchBy, option, isMulti, onChange, className, loading,props }: any) => {
|
||||||
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
useEffect(() => {
|
||||||
|
const searchParams = new URLSearchParams(location?.search);
|
||||||
|
setSearchQuery(searchParams?.get('search') || '');
|
||||||
|
console.log(searchParams);
|
||||||
|
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
|
||||||
|
formik?.setFieldValue(name, value);
|
||||||
|
|
||||||
|
console.log(value);
|
||||||
|
};
|
||||||
|
const SearchHandleChange = (value:any) => {
|
||||||
|
if (value || value !== "") {
|
||||||
|
navigate(`${window?.location?.pathname}?${searchBy}=${value}`);
|
||||||
|
} else {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
params.delete(searchBy ?? "search");
|
||||||
|
navigate(`${window?.location.pathname}?${params.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
// for write later
|
||||||
|
function OrderStatus() {
|
||||||
|
return (
|
||||||
|
<div>OrderStatus</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrderStatus
|
||||||
42
src/Extensions/Editor/HtmlEditor.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Editor } from '@tinymce/tinymce-react';
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
|
||||||
|
interface HtmlEditorProps {
|
||||||
|
langCode: number;
|
||||||
|
name: string;
|
||||||
|
editorState: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HtmlEditor: FC<HtmlEditorProps> = ({ langCode, name, editorState, ...props }) => {
|
||||||
|
const formik = useFormikContext();
|
||||||
|
|
||||||
|
const ar: boolean = langCode === 2;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Editor
|
||||||
|
apiKey='6xf0byrgd7m2j28p9dfjittsq884x9j0d3e6dsterqrvtvez'
|
||||||
|
value={editorState}
|
||||||
|
init={{
|
||||||
|
height: 500,
|
||||||
|
menubar: false,
|
||||||
|
directionality: ar ? "rtl" : "ltr",
|
||||||
|
plugins: [
|
||||||
|
'advlist autolink lists link image charmap print preview anchor',
|
||||||
|
'searchreplace visualblocks code fullscreen',
|
||||||
|
'insertdatetime media table paste code help wordcount'
|
||||||
|
],
|
||||||
|
toolbar: 'undo redo | formatselect | ' +
|
||||||
|
'bold italic backcolor | alignleft aligncenter ' +
|
||||||
|
'alignright alignjustify | bullist numlist outdent indent | ' +
|
||||||
|
'removeformat | help',
|
||||||
|
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
|
||||||
|
}}
|
||||||
|
onEditorChange={(newValue, editor) => {
|
||||||
|
formik.setFieldValue(name, newValue)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { HtmlEditor };
|
||||||
43
src/Extensions/Editor/SingleLangEditor.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
// import PropTypes from "";
|
||||||
|
import { HtmlEditor } from "./HtmlEditor";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface SingleLangEditorProps {
|
||||||
|
langCode: number;
|
||||||
|
property: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROPERTY_TYPES: string[] = [
|
||||||
|
"privacy_description",
|
||||||
|
"conditions_description",
|
||||||
|
"about_us_description",
|
||||||
|
"product_description",
|
||||||
|
"auction_description"
|
||||||
|
];
|
||||||
|
|
||||||
|
const SingleLangEditor: FC<SingleLangEditorProps> = ({ langCode, property }) => {
|
||||||
|
const formik:any = useFormikContext();
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const label = `${t(property)} (${t(`lang_${langCode}`)})`;
|
||||||
|
const fieldName = `translated_fields[${langCode}][${property}]`;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h5 className="Information_title">{label}</h5>
|
||||||
|
<HtmlEditor
|
||||||
|
langCode={langCode}
|
||||||
|
name={fieldName}
|
||||||
|
editorState={formik.values.translated_fields[langCode][property]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SingleLangEditor.propTypes = {
|
||||||
|
// langCode: PropTypes.oneOf([1, 2]).isRequired,
|
||||||
|
// property: PropTypes.oneOf(PROPERTY_TYPES).isRequired,
|
||||||
|
// };
|
||||||
|
|
||||||
|
export default SingleLangEditor;
|
||||||
23
src/Extensions/Editor/StatusCard.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Card, CardBody, Spinner } from "reactstrap";
|
||||||
|
|
||||||
|
interface StatusCardProps {
|
||||||
|
isLoading: boolean;
|
||||||
|
isError: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatusCard= ({ isLoading, isError }:StatusCardProps) => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardBody
|
||||||
|
className="d-flex align-items-center justify-content-center"
|
||||||
|
style={{ height: "15rem" }}
|
||||||
|
>
|
||||||
|
{isLoading && <Spinner size="lg" color="primary" />}
|
||||||
|
{isError && <h4>Failed !</h4>}
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatusCard;
|
||||||
65
src/Extensions/FileGenerator/generateAddModal.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
|
||||||
|
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
function Add${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
|
||||||
|
const handelSubmit = (values:any )=>{
|
||||||
|
|
||||||
|
const dataToSend = getDataToSend(values)
|
||||||
|
|
||||||
|
mutate(dataToSend)
|
||||||
|
// Submit Value
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
|
||||||
|
isAddModal={true}
|
||||||
|
getInitialValues={getInitialValues()}
|
||||||
|
handleSubmit={handelSubmit}
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
headerText={t('Add') +t('${(fileName)}')}
|
||||||
|
|
||||||
|
getValidationSchema={getValidationSchema()}>
|
||||||
|
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Add${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
40
src/Extensions/FileGenerator/generateApi.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const fileName = process.argv[2]
|
||||||
|
|
||||||
|
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import useAddMutation from "./helper/useAddMutation"
|
||||||
|
import useDeleteMutation from "./helper/useDeleteMutation"
|
||||||
|
import useGetQuery from "./helper/useGetQuery"
|
||||||
|
import useUpdateMutation from "./helper/useUpdateMutation"
|
||||||
|
|
||||||
|
const API = {
|
||||||
|
GET: "/api/admin/${fileName}",
|
||||||
|
ADD: "/api/admin/${fileName}/create",
|
||||||
|
UPDATE: "/api/admin/${fileName}/update",
|
||||||
|
DELETE: "/api/admin/${fileName}/delete",
|
||||||
|
};
|
||||||
|
|
||||||
|
const KEY = "${fileName.toUpperCase()}";
|
||||||
|
export const useGet${capitalizeFirstLetter(fileName)} = (params?:any) => useGetQuery(KEY, API.GET, params);
|
||||||
|
export const useAdd${capitalizeFirstLetter(fileName)} = () => useAddMutation(KEY, API.ADD);
|
||||||
|
export const useUpdate${capitalizeFirstLetter(fileName)} = () => useUpdateMutation(KEY, API.UPDATE);
|
||||||
|
export const useDelete${capitalizeFirstLetter(fileName)} = () =>useDeleteMutation(KEY, API.DELETE);
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/api/'+fileName+".ts",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
63
src/Extensions/FileGenerator/generateColumn.js
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Actions from "../../Components/Ui/tables/Actions";
|
||||||
|
|
||||||
|
function fnDelete(props :any ){}
|
||||||
|
|
||||||
|
const useTableColumns :any = () => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
|
||||||
|
{
|
||||||
|
name: t("email"),
|
||||||
|
sortable: false,
|
||||||
|
center: "true",
|
||||||
|
cell: (row:any) => row?.email
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "#",
|
||||||
|
sortable: false,
|
||||||
|
center: "true",
|
||||||
|
cell: (row) => (
|
||||||
|
<Actions
|
||||||
|
|
||||||
|
// importnat to return the row in on Edit Function to store in objectToEdit That Upper in Edit Modal
|
||||||
|
onEdit={() => row}
|
||||||
|
onView={()=>{}}
|
||||||
|
objectToEdit={row}
|
||||||
|
showEdit={true}
|
||||||
|
// showDelete={false}
|
||||||
|
onDelete={() => fnDelete({ id: row.id })}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTableColumns;
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/useTableColumns"+".tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
30
src/Extensions/FileGenerator/generateDashboard.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
|
||||||
|
const fileName = process.argv[2]
|
||||||
|
|
||||||
|
|
||||||
|
const CreateApi = `node src/Extensions/FileGenerator/generateApi.js ${fileName}`
|
||||||
|
const CreatePage = `node src/Extensions/FileGenerator/generatePage.js ${fileName}`
|
||||||
|
const CreateColumn = `node src/Extensions/FileGenerator/generateColumn.js ${fileName}`
|
||||||
|
const CreateformUtil= `node src/Extensions/FileGenerator/generateformUtils.js ${fileName}`
|
||||||
|
const CreateAddModal= `node src/Extensions/FileGenerator/generateAddModal.js ${fileName}`
|
||||||
|
const CreateEditModal= `node src/Extensions/FileGenerator/generateEditModal.js ${fileName}`
|
||||||
|
const CreateForm= `node src/Extensions/FileGenerator/generateForm.js ${fileName}`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const RunCommand = async()=>{
|
||||||
|
|
||||||
|
exec(CreatePage)
|
||||||
|
exec(CreateApi)
|
||||||
|
setTimeout(()=>{},100)
|
||||||
|
exec(CreateColumn)
|
||||||
|
exec(CreateformUtil)
|
||||||
|
exec(CreateAddModal)
|
||||||
|
exec(CreateEditModal)
|
||||||
|
exec(CreateForm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCommand()
|
||||||
47
src/Extensions/FileGenerator/generateEditModal.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { usePageState } from '../../lib/state mangment/LayoutPagestate'
|
||||||
|
|
||||||
|
function Edit${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
const {objectToEdit} = usePageState()
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
isAddModal={false}
|
||||||
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
|
handleSubmit={() => { }}
|
||||||
|
headerText='Edit Modal'
|
||||||
|
getValidationSchema={getValidationSchema(objectToEdit)}>
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Edit${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Edit"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
66
src/Extensions/FileGenerator/generateForm.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import { Col, Row } from 'reactstrap';
|
||||||
|
import 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
|
|
@ -0,0 +1,64 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
|
||||||
|
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
|
||||||
|
import { useAdd${capitalizeFirstLetter(fileName)} } from '../../api/${(fileName)}'
|
||||||
|
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
function Add${capitalizeFirstLetter(fileName)}Modal() {
|
||||||
|
|
||||||
|
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const {mutate , status} = useAdd${capitalizeFirstLetter(fileName)}()
|
||||||
|
const handelSubmit = (values:any )=>{
|
||||||
|
|
||||||
|
const dataToSend = getDataToSend(values)
|
||||||
|
|
||||||
|
mutate(dataToSend)
|
||||||
|
// Submit Value
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<LayoutModal
|
||||||
|
|
||||||
|
isAddModal={true}
|
||||||
|
getInitialValues={getInitialValues()}
|
||||||
|
handleSubmit={handelSubmit}
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
headerText={t('Add') +t('${(fileName)}')}
|
||||||
|
getValidationSchema={getValidationSchema()}>
|
||||||
|
|
||||||
|
<Form${capitalizeFirstLetter(fileName)} />
|
||||||
|
</LayoutModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Add${capitalizeFirstLetter(fileName)}Modal
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"Add"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
67
src/Extensions/FileGenerator/generatePage.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderPath = 'src/Pages/'+fileName;
|
||||||
|
|
||||||
|
|
||||||
|
if (!fs.existsSync(folderPath)) {
|
||||||
|
fs.mkdirSync(folderPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import React from 'react'
|
||||||
|
import DashBody from '../../Layout/Dashboard/DashBody'
|
||||||
|
import DashHeader from '../../Layout/Dashboard/DashHeader'
|
||||||
|
import LyTable from '../../Layout/Dashboard/LyTable'
|
||||||
|
import useTableColumns from './useTableColumns'
|
||||||
|
import { useGet${capitalizeFirstLetter(fileName)}} from '../../api/${fileName}'
|
||||||
|
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||||
|
import Edit${capitalizeFirstLetter(fileName)}Modal from './Edit${capitalizeFirstLetter(fileName)}Modal'
|
||||||
|
import Add${capitalizeFirstLetter(fileName)}Modal from './Add${capitalizeFirstLetter(fileName)}Modal'
|
||||||
|
|
||||||
|
function ${capitalizeFirstLetter(fileName)}Page() {
|
||||||
|
|
||||||
|
const column =useTableColumns()
|
||||||
|
const {data ,status } = useGet${capitalizeFirstLetter(fileName)}()
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
// Pass Status to Layout
|
||||||
|
<DashBody status={status as QueryStatusEnum} >
|
||||||
|
<DashHeader title={'${capitalizeFirstLetter(fileName)}'}></DashHeader>
|
||||||
|
|
||||||
|
<LyTable
|
||||||
|
data={data}
|
||||||
|
isLoading={false}
|
||||||
|
columns={column}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Edit${capitalizeFirstLetter(fileName)}Modal />
|
||||||
|
<Add${capitalizeFirstLetter(fileName)}Modal />
|
||||||
|
|
||||||
|
</DashBody>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ${capitalizeFirstLetter(fileName)}Page
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+capitalizeFirstLetter(fileName)+"Page.tsx",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
73
src/Extensions/FileGenerator/generateformUtils.js
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get the file name from the command line arguments
|
||||||
|
const fileName = process.argv[2];
|
||||||
|
|
||||||
|
// Check if a file name is provided
|
||||||
|
if (!fileName) {
|
||||||
|
console.error('Please provide a file name.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let FileContiner = `
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { buildFormData } from "../../api/helper/buildFormData";
|
||||||
|
|
||||||
|
interface formUtilCommon {
|
||||||
|
name:string,
|
||||||
|
email:string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObjectToEdit extends formUtilCommon {
|
||||||
|
|
||||||
|
id?:number,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InitialValues extends ObjectToEdit {
|
||||||
|
|
||||||
|
}
|
||||||
|
interface ValidateSchema extends formUtilCommon{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInitialValues = (objectToEdit: ObjectToEdit | null = null): InitialValues => {
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
id:objectToEdit?.id?? 0 ,
|
||||||
|
name:objectToEdit?.name ?? "",
|
||||||
|
email:objectToEdit?.email?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<ValidateSchema> => {
|
||||||
|
// validate input
|
||||||
|
return Yup.object().shape({
|
||||||
|
name:Yup.string().required('required'),
|
||||||
|
email:Yup.string().required("required")
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDataToSend = (values: any): FormData => {
|
||||||
|
const data = { ...values };
|
||||||
|
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
buildFormData(formData, data);
|
||||||
|
return formData;
|
||||||
|
};
|
||||||
|
|
||||||
|
`
|
||||||
|
fs.writeFileSync('src/Pages/'+fileName+"/"+"formUtil.ts",
|
||||||
|
FileContiner
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`File "${fileName}" generated successfully.`);
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirstLetter(word) {
|
||||||
|
return (word).charAt(0).toUpperCase() + (word).slice(1);
|
||||||
|
}
|
||||||
45
src/Hooks/WithDrawer.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React, { useState, ReactNode } from 'react';
|
||||||
|
import type { DrawerProps } from 'antd';
|
||||||
|
import { Drawer, Space } from 'antd';
|
||||||
|
|
||||||
|
interface WithDrawerProps {
|
||||||
|
button: React.ReactNode;
|
||||||
|
children: ReactNode;
|
||||||
|
title:string;
|
||||||
|
className?:string,
|
||||||
|
iopen?:boolean,
|
||||||
|
setOpen:any
|
||||||
|
}
|
||||||
|
|
||||||
|
const WithDrawer: React.FC<WithDrawerProps> = ({ children,title ="Basic Drawer",className ,setOpen, iopen }) => {
|
||||||
|
const [placement, setPlacement] = useState<DrawerProps['placement']>('right');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Drawer
|
||||||
|
// title={title}
|
||||||
|
placement={placement}
|
||||||
|
closable={false}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
open={iopen}
|
||||||
|
key={placement}
|
||||||
|
>
|
||||||
|
<div className={className}>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithDrawer;
|
||||||
|
|
||||||
|
|
||||||
|
// <WithDrawer
|
||||||
|
// button={<Button type="primary">Open</Button>}
|
||||||
|
// >
|
||||||
|
// {/* Your content goes here */}
|
||||||
|
// </WithDrawer>
|
||||||
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
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const isEmpty = (Type:any) => {
|
||||||
|
return !Type || (Array.isArray(Type) && Type.length === 0);
|
||||||
|
};
|
||||||
75
src/Hooks/useChangeLanguage.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { initReactI18next, useTranslation } from 'react-i18next';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
import translationEN from '../translate/en.json';
|
||||||
|
import translationAR from '../translate/ar.json';
|
||||||
|
import translationCN from '../translate/cn.json';
|
||||||
|
|
||||||
|
const language = localStorage.getItem('language') ?? 'en';
|
||||||
|
|
||||||
|
i18n.use(initReactI18next).init({
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
translation: translationEN
|
||||||
|
},
|
||||||
|
ar: {
|
||||||
|
translation: translationAR
|
||||||
|
},
|
||||||
|
cn: {
|
||||||
|
translation: translationCN
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lng: language,
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export function useLanguage() {
|
||||||
|
useEffect(() => {
|
||||||
|
changeLanguage(language);
|
||||||
|
}, [language]);
|
||||||
|
|
||||||
|
const changeLanguage = (newLanguage:any) => {
|
||||||
|
i18n.changeLanguage(newLanguage);
|
||||||
|
localStorage.setItem('language', newLanguage);
|
||||||
|
applyLanguageStyles(newLanguage);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { changeLanguage };
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLanguageStyles(language:any) {
|
||||||
|
if (language === 'ar') {
|
||||||
|
document.body.setAttribute('dir', 'rtl');
|
||||||
|
document.body.classList.remove('cn');
|
||||||
|
document.body.classList.add('ar');
|
||||||
|
} else if (language === 'en') {
|
||||||
|
document.body.setAttribute('dir', 'ltr');
|
||||||
|
document.body.classList.remove('ar', 'cn');
|
||||||
|
document.body.classList.add('en');
|
||||||
|
} else if (language === 'cn') {
|
||||||
|
document.body.setAttribute('dir', 'ltr');
|
||||||
|
document.body.classList.remove('ar');
|
||||||
|
document.body.classList.add('cn');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLanguageMenu() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { changeLanguage } = useLanguage();
|
||||||
|
|
||||||
|
const languageOptions = [
|
||||||
|
{ code: 'ar', icon: '/language/ar.svg', label: t('Arabic') },
|
||||||
|
{ code: 'en', icon: '/language/en.svg', label: t('English') },
|
||||||
|
{ code: 'cn', icon: '/language/china.svg', label: t('Chinese') }
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleLanguageChange = (code:any) => {
|
||||||
|
changeLanguage(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { languageOptions, handleLanguageChange };
|
||||||
|
}
|
||||||
16
src/Hooks/useEventListener.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export const useEventListener = (eventType:any, callback:any, element = window) => {
|
||||||
|
const callbackRef = useRef(callback);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
callbackRef.current = callback;
|
||||||
|
}, [callback]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e:any) => callbackRef.current(e);
|
||||||
|
element.addEventListener(eventType, handler);
|
||||||
|
|
||||||
|
return () => element.removeEventListener(eventType, handler);
|
||||||
|
}, [eventType, element]);
|
||||||
|
};
|
||||||
16
src/Hooks/useFormField.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useField, useFormikContext } from 'formik';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
|
||||||
|
const useFormField = (name: string, props?: any) => {
|
||||||
|
const [field, meta] = useField({ name, ...props });
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const isError = meta.touched && meta.error;
|
||||||
|
|
||||||
|
const errorMsg = meta.error ? t(meta.error.toString()) : '';
|
||||||
|
|
||||||
|
return { Field, field, meta, formik, isError, errorMsg, t };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFormField;
|
||||||
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
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const useImageError = ({currentTarget}:any) => {
|
||||||
|
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
|
||||||
|
currentTarget.onerror = null;
|
||||||
|
currentTarget.src=`${ErrorImage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useImageError
|
||||||
21
src/Hooks/useLoadingState.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
function useLoadingState(initialValue: boolean, duration: number): [boolean, () => void] {
|
||||||
|
const [loading, setLoading] = useState<boolean>(initialValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, duration);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
|
}, [duration]);
|
||||||
|
|
||||||
|
const resetLoading = () => {
|
||||||
|
setLoading(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return [loading, resetLoading];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useLoadingState;
|
||||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from "react";
|
||||||
|
import AddButtonLayout from "./AddButton/AddButtonLayout";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface DashHeaderProp {
|
||||||
|
title: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
showAddButton?: boolean;
|
||||||
|
haveAddModal?:boolean
|
||||||
|
}
|
||||||
|
const DashHeader = ({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
haveAddModal= true ,
|
||||||
|
showAddButton = true,
|
||||||
|
}: DashHeaderProp) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="Page_Header">
|
||||||
|
<h3>{t(`${title}`)}</h3>
|
||||||
|
{children}
|
||||||
|
{showAddButton && <AddButtonLayout haveAddModal={haveAddModal}/>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashHeader;
|
||||||
42
src/Layout/Dashboard/FormPage.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Formik, Form } from 'formik';
|
||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { Button } from "reactstrap";
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormPageProps {
|
||||||
|
handleSubmit: (values: any) => void
|
||||||
|
initialValues: FormValues;
|
||||||
|
validationSchema: any;
|
||||||
|
title?: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormPage: React.FC<FormPageProps> = ({ children, handleSubmit, initialValues, validationSchema, title = "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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||