first push

This commit is contained in:
Moaz Dawalibi 2024-07-31 11:33:27 +03:00
commit fe9107dfb0
215 changed files with 45127 additions and 0 deletions

24
.gitignore vendored Normal file
View 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

BIN
README.md Normal file

Binary file not shown.

76
db.json Normal file
View 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"
}
]
}

33874
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

100
package.json Normal file
View File

@ -0,0 +1,100 @@
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.19",
"@react-google-maps/api": "^2.19.2",
"@szhsin/react-menu": "github:szhsin/react-menu",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@tinymce/tinymce-react": "^4.3.2",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.60",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"@types/socket.io-client": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.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",
"eslint": "^8.56.0",
"formik": "^2.4.5",
"history": "^5.3.0",
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"json-server": "^0.17.4",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-bootstrap": "^2.9.1",
"react-bootstrap-sweetalert": "^5.2.0",
"react-chartjs-2": "^5.2.0",
"react-confirm-alert": "^3.0.6",
"react-data-table-component": "^7.5.4",
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"react-i18next": "^13.3.1",
"react-icons": "^4.11.0",
"react-query": "^3.39.3",
"react-redux": "^8.1.3",
"react-router-dom": "^6.18.0",
"react-scripts": "^5.0.1",
"react-select": "^5.7.7",
"react-simple-star-rating": "^5.1.7",
"react-switch": "^7.0.0",
"react-tabs": "^6.0.2",
"react-toastify": "^9.0.0",
"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"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-toggle": "^4.0.5"
}
}

1
public/Layout/Ar.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

1
public/Layout/En.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="42.67" height="32" viewBox="0 0 640 480"><defs><clipPath id="flagUm4x30"><path fill-opacity=".7" d="M0 0h682.7v512H0z"/></clipPath></defs><g fill-rule="evenodd" clip-path="url(#flagUm4x30)" transform="scale(.9375)"><g stroke-width="1pt"><path fill="#bd3d44" d="M0 0h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8V197H0zm0 78.8h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8v39.4H0zm0 78.8h972.8V512H0z"/><path fill="#fff" d="M0 39.4h972.8v39.4H0zm0 78.8h972.8v39.3H0zm0 78.7h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.8h972.8v39.4H0zm0 78.7h972.8v39.4H0z"/></g><path fill="#192f5d" d="M0 0h389.1v275.7H0z"/><path fill="#fff" d="M32.4 11.8L36 22.7h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9H177l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 39.4l3.5 10.9h11.5L70.6 57L74 67.9l-9-6.7l-9.3 6.7L59 57l-9-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7L124 57l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5L330 57l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 66.9L36 78h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7H29zm64.9 0l3.5 11h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zm64.8 0l3.6 11H177l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7h11.5zm64.9 0l3.5 11H242l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 11h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.2-6.7h11.4zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.5zM64.9 94.5l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 122.1L36 133h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.7l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9H177l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 149.7l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 10.9l-9.3-6.8l-9.3 6.8l3.6-11l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.8l-9.2 6.8l3.5-11l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 10.9l-9.2-6.8l-9.3 6.8l3.5-11l-9.2-6.7h11.4zM32.4 177.2l3.6 11h11.4l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7H29zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 11H177l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.3-6.7h11.5zm64.9 0l3.5 11H242l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.4zm64.8 0l3.6 11h11.4l-9.2 6.7l3.5 10.8l-9.3-6.7l-9.2 6.7l3.5-10.9l-9.2-6.7h11.4zm64.9 0l3.5 11h11.5l-9.3 6.7l3.6 10.8l-9.3-6.7l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zM64.9 204.8l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.3 6.7l3.6 11l-9.3-6.8l-9.3 6.7l3.6-10.9l-9.3-6.7h11.5zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7H191zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 11l-9.3-6.8l-9.2 6.7l3.5-10.9l-9.3-6.7H256zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.5 11l-9.2-6.8l-9.3 6.7l3.5-10.9l-9.2-6.7h11.4zM32.4 232.4l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.3-6.7H29zm64.9 0l3.5 10.9h11.5L103 250l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 10.9H177l-9 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.3-6.7h11.5zm64.9 0l3.5 10.9H242l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.4zm64.8 0l3.6 10.9h11.4l-9.2 6.7l3.5 10.9l-9.3-6.7l-9.2 6.7l3.5-11l-9.2-6.7h11.4zm64.9 0l3.5 10.9h11.5l-9.3 6.7l3.6 10.9l-9.3-6.7l-9.3 6.7l3.6-11l-9.3-6.7h11.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
public/Layout/LoginBg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/Layout/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

1
public/Layout/dark.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 64 64"><path fill="currentColor" d="M43.139 2a29.885 29.885 0 0 1 5.121 16.756c0 16.701-13.686 30.24-30.57 30.24a30.656 30.656 0 0 1-15.689-4.285C7.209 54.963 17.93 62 30.318 62C47.816 62 62 47.969 62 30.66C62 17.867 54.246 6.871 43.139 2z"/></svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@ -0,0 +1,2 @@
[LocalizedFileNames]
Screenshot 2023-11-29 134655.png=@Screenshot 2023-11-29 134655.png,0

1
public/Layout/light.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512"><path fill="currentColor" d="M256 160c-52.9 0-96 43.1-96 96s43.1 96 96 96s96-43.1 96-96s-43.1-96-96-96zm246.4 80.5l-94.7-47.3l33.5-100.4c4.5-13.6-8.4-26.5-21.9-21.9l-100.4 33.5l-47.4-94.8c-6.4-12.8-24.6-12.8-31 0l-47.3 94.7L92.7 70.8c-13.6-4.5-26.5 8.4-21.9 21.9l33.5 100.4l-94.7 47.4c-12.8 6.4-12.8 24.6 0 31l94.7 47.3l-33.5 100.5c-4.5 13.6 8.4 26.5 21.9 21.9l100.4-33.5l47.3 94.7c6.4 12.8 24.6 12.8 31 0l47.3-94.7l100.4 33.5c13.6 4.5 26.5-8.4 21.9-21.9l-33.5-100.4l94.7-47.3c13-6.5 13-24.7.2-31.1zm-155.9 106c-49.9 49.9-131.1 49.9-181 0c-49.9-49.9-49.9-131.1 0-181c49.9-49.9 131.1-49.9 181 0c49.9 49.9 49.9 131.1 0 181z"/></svg>

After

Width:  |  Height:  |  Size: 715 B

BIN
public/Layout/mac.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

17
public/index.html Normal file
View 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>Dashboard - Structure</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

25
public/manifest.json Normal file
View 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
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

BIN
src.zip Normal file

Binary file not shown.

67
src/App.tsx Normal file
View File

@ -0,0 +1,67 @@
import { Fragment, lazy, Suspense } from 'react';
import { Route, Routes } from 'react-router-dom'
import Loading from './Components/Utils/Loading/Loading';
import { RoutesLinks } from './Routes';
import Layout from './Layout/app/Layout';
import Auth from './Pages/Auth/Page';
import useHitRequests from './Hooks/useHitRequests';
const Page404 = lazy(() => import("./Layout/app/NotFoundPage"))
const App = () => {
// hit requests after user status is online
// useHitRequests();
useHitRequests();
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}
key={item}
element={
<Suspense fallback={<Loading />} >
<Layout>
{item?.element ?? "Please Add Element Props in Routes"}
</Layout>
</Suspense>
}
/>
)
})
}
</Fragment>
))
}
</Routes>
)
}
export default App

View File

@ -0,0 +1,63 @@
import {
DownloadOutlined,
RotateLeftOutlined,
RotateRightOutlined,
SwapOutlined,
ZoomInOutlined,
ZoomOutOutlined,
} from '@ant-design/icons';
import React from 'react';
import { Image, Space } from 'antd';
import { BaseURL, BaseURL_IMAGE } from '../../api/config';
const ColumnsImage= ({src}:any) => {
const imageUrl = src;
// or you can download flipped and rotated image
// https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
const onDownload = () => {
fetch(src)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.download = 'image.png';
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
link.remove();
});
};
return (
<Image
width={60}
height={50}
src={imageUrl}
className='p-1 mb-1'
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>
),
}}
/>
);
};
export default ColumnsImage;

View 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
};

View 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>
);
}

View 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;

View 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

View 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;

View 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;

View File

@ -0,0 +1,14 @@
import React from "react";
import { Button, Spinner } from "reactstrap";
const LoadingButton = ({ handleClickLoadingButton,isLoading = false, ...props }:any) => {
return (
<Button onClick={handleClickLoadingButton} type="button" className="btn-primary" disabled={isLoading} {...props}>
{isLoading ? <Spinner style={{ marginRight: "10px" }} size="sm" /> : null}
<span >{props.children}</span>
</Button>
);
};
export { LoadingButton };

View File

@ -0,0 +1,10 @@
import { Spin } from 'antd'
import React from 'react'
function LoadingSpinner() {
return (
<Spin className='primary' />
)
}
export default LoadingSpinner

View 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 };

View File

@ -0,0 +1,10 @@
.back-icon {
font-size: 18px;
margin: 0;
margin-right: 5px;
}
.back-btn {
padding-left: 0.8rem;
padding-right: 1rem;
}

View 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;

View 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;

View File

@ -0,0 +1,59 @@
import React from "react";
import { Card, CardBody } from "reactstrap";
import Chart from "react-apexcharts";
import { ChartTypeEnum } from "../../../enums/ChartTypeEnum";
import { history } from "../../../ProviderContainer";
import { useNavigate } from "react-router-dom";
interface StatisticsCardProps {
className?: string;
iconLeft?: boolean;
icon: React.ReactNode;
count?: string;
CardTitle?: string;
CardContent?: string;
height?: number;
pathWhenClick :string ;
}
const StatisticsCard = (props :StatisticsCardProps) => {
const navigate = useNavigate()
const {
className,
iconLeft = false ,
icon,
count,
CardTitle,
CardContent,
pathWhenClick,
height,
...rest
} = props;
return (
<Card {...rest} onClick={()=>navigate(pathWhenClick , {replace:true})}>
<CardBody
className={`${
className ? className : "stats-card-body"
} d-flex justify-content-center flex-column text-center pb-2 pt-2 primary `}
>
<div className="icon-section">
<div
className={`avatar avatar-stats p-50 m-0 ${ "bg-rgba-primary"}`}
>
<p className="mb-0 text-bold-700">{CardTitle}</p>
<div className="avatar-content ">{icon}</div>
</div>
</div>
<div className={"title-section " + (iconLeft ? "ml-2" : "")}>
<h2 className="text-bold-600 mb-25 ">{count}</h2>
<p className="mb-2 Content">{CardContent}</p>
</div>
</CardBody>
</Card>
);
};
export default StatisticsCard;

View 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;

View 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;

View File

@ -0,0 +1,30 @@
import React from 'react';
import type { RadioChangeEvent } from 'antd';
import { Radio } from 'antd';
const onChange = (e: RadioChangeEvent) => {
};
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;

View File

@ -0,0 +1,39 @@
import React from "react";
import Toggle from "react-toggle";
import "react-toggle/style.css";
import StatusBadge from "./StatusBadge";
import { useTranslation } from "react-i18next";
interface ToggleStatusProps {
}
export const ToggleStatus = ({ object, toggleMutation, ...props }:any) => {
const [t] = useTranslation();
const handleSwitch = () => {
toggleMutation.mutate({
id: object.id,
new_status: !object.is_active,
});
};
return (
<>
<div className="p-0">
<p
className="mb-0 p-0"
style={{ whiteSpace: "nowrap", textAlign: "center" }}
>
{object.is_active ? t("active") : t("inactive")}
</p>
<Toggle
{...props}
className="custom-switch"
disabled={toggleMutation.isLoading}
onChange={handleSwitch}
checked={object.is_active}
/>
</div>
</>
);
};

View 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 };

View 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
}

View File

@ -0,0 +1,57 @@
import React , {ReactNode} from "react";
import { FaEdit, FaEye, FaTrash } from "react-icons/fa";
import CustomConfirmAlert from "../Alert";
import { usePageState } from "../../../lib/state mangment/LayoutPagestate";
type TableActionsProps = {
onDelete?: () => any;
onEdit?: () => any;
onView?:() => any;
showView?: boolean;
showEdit?: boolean;
showDelete?: boolean;
children?: ReactNode;
objectToEdit:any
className?:string
};
const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children,className }:TableActionsProps) => {
// const TableActions = ({ onDelete=()=>{} , objectToEdit,onEdit=()=>{},onView,showEdit=true,showDelete=true,showView=true,children }:TableActionsProps) => {
// console.log(objectToEdit);
const {setObjectToEdit , setIsOpenEditModel} = usePageState()
return (
<div className={`data-list-action TableActions ${className}`}>
{showEdit && <FaEdit onClick={()=>{
setObjectToEdit(objectToEdit)
setIsOpenEditModel()
onEdit()
}} className="cursor-pointer m-2" size={20} />}
{showView && <FaEye onClick={onView} className="cursor-pointer m-2" size={25} />}
{showDelete && (
<FaTrash
onClick={() =>
CustomConfirmAlert({
onConfirm: () => {
onDelete();
},
})
}
className="cursor-pointer"
size={20}
/>
)}
{children}
</div>
);
};
export default TableActions;

View 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>;
}

View 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,
};
};

View 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;
}
}

View 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

View 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;
}
}

View 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

View 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>
);
}

View File

@ -0,0 +1,81 @@
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
import { useTranslation, initReactI18next } from 'react-i18next';
import i18n from 'i18next'; // Make sure this import is correct
import translationEN from '../../translate/en.json';
import translationAR from '../../translate/ar.json';
i18n.use(initReactI18next).init({
resources: {
en: {
translation: translationEN
},
ar: {
translation: translationAR
}
},
lng: 'en',
interpolation: {
escapeValue: false
}
});
let What_the_language = localStorage.getItem('language') ?? "en";
if (What_the_language === "en") {
i18n.changeLanguage('en');
document.body.setAttribute('dir', 'ltr'); document.body.classList.add('en')}
else{
i18n.changeLanguage('ar');
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');
}
export default function Translate() {
const { t, i18n } = useTranslation();
const changeLanguage = (newLanguage : any) => {
i18n.changeLanguage(newLanguage);
if(newLanguage === "Ar"){
i18n.changeLanguage('ar');
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');localStorage.setItem("language", "ar");
What_the_language = "ar"
}
else if(newLanguage === "En"){
i18n.changeLanguage('en');
document.body.setAttribute('dir', 'ltr'); document.body.classList.remove('ar');localStorage.setItem("language", "en");
What_the_language = "en"
}
};
return (
<div className='Translate'>
<Menu menuButton={<MenuButton>
{What_the_language === "ar" ?
<>
{/* <img alt='' src={`/Layout/Ar.svg`} width={20} height={20} /> */}
{t("Arabic")}
</>
:
<>
{/* <img alt='' src={`/Layout/En.svg`} width={20} height={20} /> */}
{t("English")}
</>
}
</MenuButton>} transition>
<MenuItem onClick={() => changeLanguage('Ar')}>
{/* <img alt='' src='/Layout/Ar.svg' width={20} height={20} /> */}
{t("Arabic")}
</MenuItem>
<MenuItem onClick={() => changeLanguage('En')}>
{/* <img alt='' src='/Layout/En.svg' width={20} height={20} /> */}
{t("English")}
</MenuItem>
</Menu>
</div>
);
}

View File

@ -0,0 +1,57 @@
.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;
}
.error{
color: red;
}

View File

@ -0,0 +1,46 @@
import React from "react";
import "./utils/ValidationField.scss";
import {
Date,
Time,
File,
DataRange,
SelectField,
CheckboxField,
MaltyFile,
SearchField,
TextField,
DropFile,
Default,
} from "./View";
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
import LocalSearchField from "./View/LocalSearch";
import NumberFormate from "./View/NumberFormate";
const components: { [key: string]: React.FC<any> } = {
Select: SelectField,
Search: SearchField,
LocalSearch: LocalSearchField,
DataRange: DataRange,
TextArea: TextField,
Date: Date,
Time: Time,
File: File,
DropFile: DropFile,
MaltyFile: MaltyFile,
Checkbox: CheckboxField,
NumberFormate: NumberFormate,
};
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
({ type = "text", ...otherProps }) => {
const Component = components[type ?? ("text" as ValidationFieldType)];
if (!Component) {
return <Default {...otherProps} />;
}
return <Component {...otherProps} />;
},
);
export default ValidationField;

View File

@ -0,0 +1,35 @@
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Checkbox } from "antd";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
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 Checkbox" : ``}>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Checkbox
onChange={onChange || CheckboxhandleChange}
disabled={isDisabled}
checked={formik.values?.[name] ?? false}
className={className}
>
{t(`input.${label ? label : name}`)}
</Checkbox>
</ValidationFieldContainer>
</div>
);
};
export default CheckboxField;

View File

@ -0,0 +1,55 @@
import { Form, DatePicker } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { RangePicker } = DatePicker;
const DataRange = ({
name,
label,
Format,
props,
onChange,
isDisabled,
placeholder,
className,
no_label,
label_icon,
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value);
};
return (
<div className="ValidationField w-100 ">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<RangePicker
placeholder={placeholder}
size="large"
allowClear
className={`${className} w-100`}
format={Format}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
defaultValue={formik.values[name]}
id={name}
/>
</ValidationFieldContainer>
</div>
);
};
export default DataRange;

View File

@ -0,0 +1,57 @@
import { Form, DatePicker } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import dayjs from "dayjs";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const Date = ({
name,
label,
picker = "date",
isDisabled,
props,
onChange,
placeholder,
className,
no_label,
label_icon,
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const FormikValue = formik.values[name];
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value);
};
return (
<div className="ValidationField w-100 ">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<DatePicker
picker={picker}
placeholder={t(`input.${placeholder}`)}
allowClear
className={`${className} w-100`}
value={FormikValue ? FormikValue : null}
size="large"
onChange={onChange || onCalendarChange}
disabled={isDisabled}
id={name}
/>
{/* <DatePicker onChange={onChange} /> */}
</ValidationFieldContainer>
</div>
);
};
export default Date;

View File

@ -0,0 +1,50 @@
import { Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Field } from "formik";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { FieldProps } from "../utils/types";
const Default = ({
name,
label,
placeholder,
isDisabled,
onChange,
type,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t } = useFormField(name, props);
return (
<div className="ValidationField w-100">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={Input}
type={type ?? "text"}
placeholder={t(`input.${placeholder || label || name}`)}
name={name}
id={name}
disabled={isDisabled}
size="large"
{...(type === "number" && { min: 0 })}
{...props}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(Default);

View File

@ -0,0 +1,93 @@
import React, { useEffect, useState } from "react";
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
import { message, Upload } from "antd";
import type { GetProp, UploadProps } from "antd";
import useFormField from "../../../Hooks/useFormField";
type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
const DropFile = ({
name,
label,
onChange,
isDisabled,
placeholder,
className,
props,
no_label,
label_icon,
}: any) => {
const { formik, t, isError } = useFormField(name, props);
let FormikName = formik?.values[name];
const FormikValue =
typeof FormikName === "string"
? FormikName
: FormikName instanceof File
? URL.createObjectURL(FormikName)
: "";
const [imageUrl, setImageUrl] = useState<any>(FormikValue ?? "");
useEffect(() => {
setImageUrl(FormikName);
}, [FormikName]);
const getBase64 = (img: FileType, callback: (url: string) => void) => {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result as string));
reader.readAsDataURL(img);
};
const handleChange: UploadProps["onChange"] = (info) => {
if (info.file.status === "done") {
getBase64(info.file.originFileObj as FileType, (url) => {
setImageUrl(url);
});
}
formik.setFieldValue(name, info.file.originFileObj);
};
const customRequest = async ({ onSuccess }: any) => {
onSuccess();
};
const uploadButton = (
<button style={{ border: 0, background: "none" }} type="button">
<div className={`CustomFile ${isError ? "uploader_error" : ""} `}>
{t("input.drag_and_drop_or_click_here_to_select_the_file")}
</div>
</button>
);
return (
<div className="ValidationField w-100">
<label htmlFor={name} className="text">
{t(`input.${label || name}`)}
</label>
<Upload
name="avatar"
listType="picture-card"
className={`avatar-uploader ${isError ? "uploader_error" : ""} ${className}`}
showUploadList={false}
customRequest={customRequest}
onChange={onChange || handleChange}
id={name}
>
{imageUrl ? (
<img
src={
imageUrl instanceof File
? URL.createObjectURL(imageUrl)
: imageUrl
}
alt=""
style={{ width: "100%" }}
/>
) : (
uploadButton
)}
</Upload>
</div>
);
};
export default DropFile;

View File

@ -0,0 +1,82 @@
import { Button, Upload, UploadFile } from "antd";
import useFormField from "../../../Hooks/useFormField";
import { UploadOutlined } from "@ant-design/icons";
import React, { useMemo } from "react";
const File = ({
name,
label,
onChange,
isDisabled,
placeholder,
className,
props,
}: any) => {
const { formik, t, isError, errorMsg } = useFormField(name, props);
let imageUrl = formik?.values?.[name] ?? null;
const fileList: UploadFile[] = useMemo(() => {
if (!imageUrl) {
return [];
}
return [
typeof imageUrl === "string"
? {
uid: "-1",
name: "",
status: "done",
url: imageUrl,
thumbUrl: imageUrl,
}
: {
uid: imageUrl.uid || "-1",
name: imageUrl.name || "",
status: "done",
originFileObj: imageUrl,
},
];
}, [imageUrl]);
const FilehandleChange = (value: any) => {
if (value.fileList.length === 0) {
formik.setFieldValue(name, null);
} else {
formik.setFieldValue(name, value?.file?.originFileObj);
}
};
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
onSuccess();
};
return (
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
<label htmlFor={name} className="text">
{t(`input.${label || name}`)}
</label>
<Upload
disabled={isDisabled}
listType="picture"
maxCount={1}
fileList={[...fileList]}
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={` w-100`}
id={name}
>
<Button
className={isError ? "isError w-100 " : " w-100"}
icon={<UploadOutlined />}
>
{placeholder
? t(`input.${placeholder}`)
: t("input.Click_to_upload_the_image")}
</Button>
<div className="Error_color"> {isError ? "required" : ""}</div>
{errorMsg}
</Upload>
</div>
);
};
export default React.memo(File);

View File

@ -0,0 +1,76 @@
import { Select } from "antd";
import React, { useState } from "react";
import useFormField from "../../../Hooks/useFormField";
import { translateOptions } from "../utils/translatedOptions";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const LocalSelectField = ({
name,
label,
placeholder,
isDisabled,
option,
isMulti,
onChange,
className,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
// State to manage the search input value
const [searchValue, setSearchValue] = useState("");
const handleSearch = (
input: string,
option: { value: string; label: React.ReactNode | undefined },
) =>
option?.label?.toString().toLowerCase().includes(input.toLowerCase()) ||
option?.value?.toString().toLowerCase().includes(input.toLowerCase());
const handleSelectChange = (value: any) => {
formik.setFieldValue(name, value);
if (onChange) onChange(value);
};
const handleSearchChange = (input: string) => {
setSearchValue(input); // Update the search input value
};
return (
<div className="ValidationField w-100">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={translateOptions(option, t)}
size="large"
className={`${className} ${isError ? "SelectError" : ""} w-100`}
value={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={handleSelectChange}
showSearch
filterOption={handleSearch} // Custom filter function
searchValue={searchValue} // Control the search input value
onSearch={handleSearchChange} // Update search input value on change
id={name}
fieldNames={{ label: "name", value: "id" }}
{...props}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(LocalSelectField);

View File

@ -0,0 +1,86 @@
import React, { useMemo } from "react";
import { Button, Upload } from "antd";
import { UploadOutlined } from "@ant-design/icons";
import useFormField from "../../../Hooks/useFormField";
const MaltyFile = ({
name,
label,
onChange,
isDisabled,
placeholder,
className,
props,
}: any) => {
const { formik, t, isError } = useFormField(name, props);
let imageUrl = formik?.values?.[name] ?? null;
// Memoizing the fileList to prevent unnecessary recalculations
const fileList = useMemo(() => {
return imageUrl
? imageUrl.map((file: any, index: number) => {
return file instanceof File
? {
uid: index,
name: file?.name,
status: "done",
originFileObj: file,
}
: {
uid: index,
id: file?.id,
name: file?.name,
status: "done",
url: file?.url || "",
thumbUrl: file?.url || "",
};
})
: [];
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
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 memoized fileList
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={`${className} w-100`}
multiple // Allow multiple files to be selected
id={name}
>
<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;

View File

@ -0,0 +1,62 @@
import { InputNumber } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Field } from "formik";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const NumberFormate = ({
name,
label,
placeholder,
isDisabled,
type,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const SelectableChange = (value: {
value: string;
label: React.ReactNode;
}) => {
formik.setFieldValue(name, value);
};
return (
<div className="ValidationField w-100">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={InputNumber}
formatter={(value: any) =>
`${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value: any) =>
value?.replace(/\$\s?|(,*)/g, "") as unknown as number
}
min="0"
type={type ?? "text"}
value={formik.values[name]}
onChange={SelectableChange}
placeholder={t(`input.${placeholder || label || name}`)}
name={name}
disabled={isDisabled}
size="large"
id={name}
{...props}
// onChange={onChange ? onChange : handleChange}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(NumberFormate);

View File

@ -0,0 +1,140 @@
import { Select, Spin } from "antd";
import React, { useEffect, useState } from "react";
import useFormField from "../../../Hooks/useFormField";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { SearchFieldProps } from "../utils/types";
import { useValidationValidationParamState } from "../state/ValidationValidationParamState";
const SearchField = ({
name,
label,
placeholder,
isDisabled,
searchBy = "search",
option = [],
isMulti,
onChange,
className,
no_label,
label_icon,
isLoading,
canChangePage,
PageName,
page,
...props
}: SearchFieldProps) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const { pushValidationParamState, setValidationParamState } =
useValidationValidationParamState();
const [AllPagesOption, setAllPagesOption] = useState<any>([]);
useEffect(() => {
if (option?.length > 0) {
const NewOption = [...option, ...AllPagesOption];
const FilteredOption = NewOption.filter(
(value, index, self) =>
index === self.findIndex((t) => t.id === value.id),
);
const sortedNewOption = FilteredOption.sort((a, b) => a.id - b.id);
setAllPagesOption(sortedNewOption);
}
}, [option]);
useEffect(() => {
if (page === 1) {
setAllPagesOption(option);
}
}, [page]);
const SelectableChange = (value: any) => {
formik?.setFieldValue(name, value);
const isCleared = value?.length === 0 || !value;
if (isCleared) {
if (PageName) {
setValidationParamState({
[PageName]: 1,
});
}
}
console.log(value, "value");
};
const handleChange = ((value: string) => {
if (PageName) {
pushValidationParamState({
[PageName]: 1,
});
}
pushValidationParamState({
[searchBy]: value,
});
});
const handleBlur = () => {
if (PageName && page === 1) {
setValidationParamState({
[PageName]: null,
});
}
if (PageName && page !== 1) {
setValidationParamState({
[PageName]: 1,
});
setAllPagesOption([]);
}
};
const handleScroll = (event: any) => {
const target = event.target;
const isAtBottom =
target.scrollHeight === target.scrollTop + target.clientHeight;
if (isAtBottom && canChangePage && PageName && page) {
console.log("Scrolled to the last option!");
let newPage = page + 1;
pushValidationParamState({
[PageName]: newPage,
});
}
};
return (
<div className="ValidationField w-100">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={AllPagesOption}
size="large"
className={`${className} w-100`}
value={formik.values[name]}
// loading={isLoading}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelectableChange}
showSearch
optionFilterProp="name"
notFoundContent={isLoading ? <Spin /> : t("validation.undefined")}
onSearch={handleChange}
onBlur={handleBlur}
id={name}
onPopupScroll={handleScroll}
fieldNames={{ label: "name", value: "id" }}
{...props}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(SearchField);

View File

@ -0,0 +1,63 @@
import { Select } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { translateOptions } from "../utils/translatedOptions";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { SelectFieldProps } from "../utils/types";
const SelectField = ({
name,
label,
placeholder,
isDisabled,
option,
isMulti,
onChange,
className,
no_label,
label_icon,
isLoading,
...props
}: SelectFieldProps) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const SelectableChange = (value: {
value: string;
label: React.ReactNode;
}) => {
formik.setFieldValue(name, value);
};
const options = translateOptions(option, t);
return (
<div className="ValidationField w-100">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={options}
{...(isLoading && { loading: isLoading })}
size="large"
className={`${className} ${isError ? "SelectError" : ""} w-100`}
value={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelectableChange}
showSearch={false}
id={name}
{...props}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(SelectField);

View File

@ -0,0 +1,45 @@
import { Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Field } from "formik";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { TextArea } = Input;
const TextAreaField = ({
name,
label,
placeholder,
isDisabled,
onChange,
props,
}: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
formik.setFieldValue(name, e.target.value);
};
return (
<div className="ValidationField w-100">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={TextArea}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
name={name}
disabled={isDisabled}
size="large"
onChange={onChange || handleChange}
id={name}
// onChange={onChange ? onChange : handleChange}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(TextAreaField);

View File

@ -0,0 +1,57 @@
import { Form, Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { TextArea } = Input;
const TextField = ({
name,
label,
label2,
placeholder,
isDisabled,
onChange,
props,
no_label,
label_icon,
className,
}: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
formik.setFieldValue(name, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={TextArea}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
name={name}
disabled={isDisabled}
size="large"
showCount
maxLength={1000}
onChange={onChange || TextFilehandleChange}
style={{ height: 120 }}
id={name}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(TextField);

View File

@ -0,0 +1,60 @@
import { Form, TimePicker } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import dayjs from "dayjs";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
const Time = ({
name,
label,
className,
isDisabled,
onChange,
props,
placeholder,
no_label,
label_icon,
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value);
};
const Formatter = "H:mm";
const FormikValue = formik.values[name];
return (
<div className="ValidationField w-100 ">
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<TimePicker
allowClear
className={`${className} w-100`}
size="large"
value={FormikValue ? dayjs(FormikValue, Formatter) : null}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
placeholder={t(`input.${placeholder || label || name}`)}
format={Formatter}
id={name}
/>
</Form.Item>
</div>
);
};
export default Time;

View File

@ -0,0 +1,25 @@
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 MaltyFile from "./MaltyFile";
import SearchField from "./SearchField";
import TextField from "./TextField";
import DropFile from "./DropFile.tsx";
export {
Time,
SelectField,
Date,
DataRange,
CheckboxField,
Default,
File,
MaltyFile,
SearchField,
TextField,
DropFile,
};

View File

@ -0,0 +1,24 @@
import React, { FC } from "react";
import { Form } from "antd";
interface ValidationFieldContainerProps {
children: React.ReactNode;
isError: boolean;
errorMsg: string;
}
export const ValidationFieldContainer: FC<ValidationFieldContainerProps> = ({
children,
isError,
errorMsg,
}) => (
<div className="ValidationFieldContainer">
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
{children}
</Form.Item>
</div>
);

View File

@ -0,0 +1,39 @@
import React from "react";
import { MdOutlineEdit } from "react-icons/md";
interface ValidationFieldLabelProps {
name: string;
label?: string;
no_label?: boolean;
label_icon?: boolean;
placeholder?: string;
t: (key: string) => string;
}
export const ValidationFieldLabel: React.FC<ValidationFieldLabelProps> = ({
name,
label,
placeholder,
no_label,
label_icon,
t,
}) => (
<>
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
</>
);

View File

@ -0,0 +1,14 @@
import { useState } from "react";
import { ErrorMessage, useField, Field, useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
import { FaExclamationCircle } from "react-icons/fa";
export {
useState,
ErrorMessage,
useField,
Field,
useFormikContext,
useTranslation,
FaExclamationCircle,
};

View File

@ -0,0 +1,36 @@
import { create } from "zustand";
interface ValidationParamState {
[key: string]: any;
}
interface ModalState {
ValidationParamState: ValidationParamState;
setValidationParamState: (validationParamState: ValidationParamState) => void;
pushValidationParamState: (
validationParamState: ValidationParamState,
) => void;
clearValidationParamState: () => void;
}
export const useValidationValidationParamState = create<ModalState>((set) => ({
ValidationParamState: {},
setValidationParamState: (validationParamState) =>
set(() => ({
ValidationParamState: validationParamState,
})),
pushValidationParamState: (validationParamState) =>
set((state) => ({
ValidationParamState: {
...state.ValidationParamState,
...validationParamState,
},
})),
clearValidationParamState: () =>
set({
ValidationParamState: {},
}),
}));

View File

@ -0,0 +1,200 @@
.LabelWithIcon {
display: flex;
width: 100%;
justify-content: space-between;
}
.ValidationField {
margin-bottom: 1.3vw;
position: relative;
> * {
width: 100%;
}
.text,
.ant-form-item {
margin-bottom: 7px !important;
> span {
color: transparent;
}
}
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
border: 1px solid var(--border-color);
}
.SelectError {
.ant-select-selector {
border: 1px solid red !important;
}
}
// .ValidationField{
// .ant-select-selector{
// border: 1px solid var(--border-color) ;
// }
// }
> 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%;
}
.Checkbox {
padding: 4%;
}
.SearchField {
button {
background: var(--primary);
}
}
.text {
color: var(--text);
margin-bottom: 15px;
font-weight: bold;
}
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 */
}
.upload_image_button {
.ant-btn {
min-height: 3vw !important;
border: 0.1vw solid var(--border-color);
display: flex;
align-items: center;
justify-content: center;
}
}
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
min-height: 3vw !important;
}
.ant-select-multiple.ant-select-lg .ant-select-selection-overflow {
min-height: 3vw !important;
}
.ant-upload-list .ant-upload-list-item {
// height:3vw !important;
border: 1px solid var(--border-color) !important;
}
.TowValidationItems {
display: flex;
gap: 3%;
> label {
display: none;
}
}
.ant-select .ant-select-arrow {
inset-inline-end: 1vw;
}
.ant-input-affix-wrapper-lg {
padding: 0.5vw 1vw;
font-size: 1vw;
min-height: 3vw;
border-radius: 0.6vw;
}
.ant-picker-outlined {
padding: 0.5vw 1vw;
font-size: 1vw;
min-height: 3vw;
border-radius: 0.6vw;
border: 1px solid var(--border-color);
}
.ant-select-single.ant-select-lg .ant-select-selector {
min-height: 3vw;
border-radius: 0.6vw;
}
.ant-select-single .ant-select-selector .ant-select-selection-search-input {
min-height: 3vw;
}
.ant-select-outlined .ant-select-selector {
min-height: 3vw !important;
border-radius: 0.6vw !important;
}
.ant-select-single.ant-select-lg {
min-height: 3vw;
}
.ant-upload-wrapper .ant-upload-list .ant-upload-list-item {
width: 21.5vw;
}
.ant-input-number-outlined {
width: 100%;
height: 3vw;
}
.ant-input-number-lg input.ant-input-number-input {
width: 100%;
height: 3vw;
}
.bigRow {
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
> *.w-100 {
width: 48% !important;
}
}
.ant-input-number-affix-wrapper-lg {
width: 100%;
height: 3vw;
border-radius: 0.6vw;
border: 1px solid var(--border-color);
}
.TwoSelectGroup {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.TwoSelectGroupbutton {
margin-bottom: 20px;
}

View File

@ -0,0 +1,11 @@
import { create } from "zustand";
interface ValidationState {
Validation: any[];
setValidation: (value: any[]) => void;
}
export const useValidationState = create<ValidationState>((set) => ({
Validation: [],
setValidation: (value) => set((state) => ({ Validation: value })),
}));

View File

@ -0,0 +1,7 @@
export const translateOptions = (options: any, t: any) => {
return options?.map((opt: any) => ({
...opt,
label: t(`${opt?.label}`),
name: t(`${opt?.name}`),
}));
};

View File

@ -0,0 +1,80 @@
import { InputProps, SelectProps } from "antd";
// Common properties for all field types
interface BaseFieldProps {
name: string;
label?: string;
placeholder?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
no_label?: boolean;
label_icon?: boolean;
}
// Specific field type properties
export type SelectFieldProps = BaseFieldProps &
SelectProps & {
type: "Select" | "LocalSearch";
option: any[];
isMulti?: boolean;
isLoading?: boolean;
searchBy?: string;
canChangePage?: boolean;
PageName?: string;
page?: number;
};
export type SearchFieldProps = BaseFieldProps &
SelectProps & {
type: "Search";
option: any[];
isMulti?: boolean;
isLoading: boolean;
searchBy: string;
canChangePage: boolean;
PageName: string;
page: number;
};
type DateFieldProps = BaseFieldProps & {
type: "DataRange" | "Date" | "Time";
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
picker?: "data" | "week" | "month" | "quarter" | "year";
};
type FileFieldProps = BaseFieldProps & {
type: "File" | "MaltyFile" | "DropFile";
};
type CheckboxFieldProps = BaseFieldProps & {
type: "Checkbox";
Group?: boolean;
};
export type FieldProps = BaseFieldProps &
InputProps & {
type?:
| "text"
| "number"
| "password"
| "email"
| "TextArea"
| "NumberFormate";
label2?: string;
Group?: boolean;
[key: string]: any;
};
// Union type for all field types
export type ValidationFieldProps =
| SelectFieldProps
| DateFieldProps
| FileFieldProps
| CheckboxFieldProps
| SearchFieldProps
| FieldProps;
// Validation field type
export type ValidationFieldType = ValidationFieldProps["type"];

View 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 };

View File

@ -0,0 +1,43 @@
import React, { FC } from "react";
import PropTypes from "prop-types";
import { HtmlEditor } from "./HtmlEditor";
import { useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
interface SingleLangEditorProps {
langCode: number;
property: string;
}
const PROPERTY_TYPES: string[] = [
"privacy_description",
"conditions_description",
"about_us_description",
"product_description",
"auction_description"
];
const SingleLangEditor: FC<SingleLangEditorProps> = ({ langCode, property }) => {
const formik:any = useFormikContext();
const {t} = useTranslation();
const label = `${t(property)} (${t(`lang_${langCode}`)})`;
const fieldName = `translated_fields[${langCode}][${property}]`;
return (
<>
<h5 className="Information_title">{label}</h5>
<HtmlEditor
langCode={langCode}
name={fieldName}
editorState={formik.values.translated_fields[langCode][property]}
/>
</>
);
};
SingleLangEditor.propTypes = {
langCode: PropTypes.oneOf([1, 2]).isRequired,
property: PropTypes.oneOf(PROPERTY_TYPES).isRequired,
};
export default SingleLangEditor;

View 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;

View 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);
}

View 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/${fileName}",
ADD: "/api/${fileName}/create",
UPDATE: "/api/${fileName}/update",
DELETE: "/api/${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);
}

View 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.`);

View 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()

View File

@ -0,0 +1,58 @@
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 { InitialValues, getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
import { usePageState } from '../../lib/state mangment/LayoutPagestate'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useUpdate${capitalizeFirstLetter(fileName)} } from '../../api/${capitalizeFirstLetter(fileName)}'
function Edit${capitalizeFirstLetter(fileName)}Modal() {
const {objectToEdit} = usePageState()
const {status, mutate} = useUpdate${capitalizeFirstLetter(fileName)};
const handleSubmit = (value:InitialValues)=>{
const dataToSend = getDataToSend({...value})
mutate(dataToSend)
}
return (
<LayoutModal
isAddModal={false}
getInitialValues={getInitialValues(objectToEdit)}
handleSubmit={() => { }}
status={status as QueryStatusEnum}
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);
}

View File

@ -0,0 +1,58 @@
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/Karimalden/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' />
</Col>
<Col>
</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);
}

View 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);
}

View 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);
}

View 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
View 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>

View 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]);
};

View File

@ -0,0 +1,25 @@
import { useField, useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
import { Field } from "formik";
import { useValidationState } from "../Components/ValidationField/utils/ValidationState";
import { useEffect } from "react";
const useFormField = (name: string, props?: any) => {
const [field, meta] = useField({ name, ...props });
const { t } = useTranslation();
const formik = useFormikContext<any>();
const { Validation, setValidation } = useValidationState((state) => state);
const isError = !!((meta.touched && meta.error) || Validation[name as any]);
const errorMsg =
!!isError && meta.error
? t(meta.error.toString())
: t(Validation[name as any]) ?? "";
return { Field, field, meta, formik, isError, errorMsg, t };
};
export default useFormField;

View File

@ -0,0 +1,35 @@
import { useEffect } from 'react'
import useTriggerStatus from './useTriggerStatus'
import useAxios from '../api/helper/useAxios'
import { IRequestSend } from '../interfaces/axiosInterface'
function useHitRequests() {
const axios = useAxios()
const isOnline = useTriggerStatus()
useEffect(() => {
if (isOnline) {
const data = JSON.parse(localStorage.getItem("offlineApi") || "[]");
// check if offlineApi is empty.
if(data.length < 0) {
return ;
}
// mapping on offline requests
data.map((item: IRequestSend) => {
axios[item.method](item.endPoint, item.data,{headers:item?.headers})
})
// set response array to an empty array to clear response inside it
localStorage.setItem("offlineApi", "[]");
}
}, [isOnline])
}
export default useHitRequests

View 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;

View 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 , {replace:true})
}
},[isSuccess])
}
export default useNavigateOnSuccess

View File

@ -0,0 +1,37 @@
import { Pagination } from "antd";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
export const PaginationBody = ({ data }: any) => {
const navigate = useNavigate();
const location = useLocation();
const pagination = location?.search || '';
const currentPage = parseInt(new URLSearchParams(location.search).get("page") || "1", 10);
const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "10", 10);
const [searchParams] = useSearchParams()
const onChange = (page: number, pageSize?: number) => {
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}&search=${searchParams.get('search')}`, { replace: true });
};
const onShowSizeChange = (current: number, pageSize: number) => {
navigate(`?page=${current}&per_page=${pageSize}&search=${searchParams.get('search')}`, { replace: true });
};
return (
<Pagination
className='text-center mt-3 paginateStyle'
total={data}
showTotal={(total: any) => `Total ${total} items`}
pageSize={pageSize}
pageSizeOptions={[8, 16, 24, 32, 40]}
defaultCurrent={currentPage}
current={currentPage} // Adding this line will set the current page correctly
onChange={onChange}
onShowSizeChange={onShowSizeChange}
/>
);
};

View File

@ -0,0 +1,40 @@
import { Pagination } from "antd";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
export const PaginationBodyCustom = ({ data }: any) => {
console.log(data , "ibrahimmmmmmmmm");
const navigate = useNavigate();
const location = useLocation();
console.log(location);
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") || "3", 10);
console.log(pageSize);
const [searchParams] = useSearchParams()
const onChange = (page: number, pageSize?: number) => {
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}&search=${searchParams.get('search')}`, { replace: true });
};
const onShowSizeChange = (current: number, pageSize: number) => {
navigate(`?page=${current}&per_page=${pageSize}&search=${searchParams.get('search')}`, { replace: true });
};
return (
<Pagination
className='text-center mt-3 paginateStyle'
total={data}
showTotal={(total: any) => `Total ${total} items`}
pageSize={pageSize}
pageSizeOptions={[8, 16, 24, 32, 40]}
defaultCurrent={currentPage}
current={currentPage} // Adding this line will set the current page correctly
onChange={onChange}
onShowSizeChange={onShowSizeChange}
/>
);
};

View File

@ -0,0 +1,21 @@
import { useState, useEffect } from 'react';
const useTriggerStatus = () => {
// initialized with the current online status of the browser (navigator.onLine).
const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine);
const updateOnlineStatus = () => {
setIsOnline(navigator.onLine);
};
useEffect(() => {
// Adds an event listener that calls updateOnlineStatus when the browser goes online or offline
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
}, []);
return isOnline;
};
export default useTriggerStatus;

View 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;
};

View 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

View File

@ -0,0 +1,40 @@
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

View File

@ -0,0 +1,36 @@
.Add_Button{
button {
border: 2px solid var(--primary);
background-color: var(--primary);
border-radius: 0.5vw;
padding:.5vw 1vw;
font-size: 1vw ;
display: flex; justify-content: center; align-items: center;
box-shadow: 2px 2px 7px 0 var(--primary);
}
button span {
display: flex;
align-items: center;
color: var(--bg);
font-weight: bold;
}
button:hover {
background-color:var(--primary);
}
}
@media screen and (max-width: 700px) {
.Add_Button{
button{
font-size: 2vw !important;
}
}
}
@media screen and (max-width: 470px) {
.Add_Button{
button{
font-size: 3vw !important;
}
}
}

View File

@ -0,0 +1,38 @@
import { Spinner } from "reactstrap"
import { QueryStatusEnum } from "../../config/QueryStatus"
import LoadingPage from "../app/LoadingPage"
import { useTranslation } from "react-i18next"
import { BsEmojiFrown } from "react-icons/bs";
const DashBody = ({ children , status }: { children: React.ReactNode ,status?:QueryStatusEnum }) => {
const {t} = useTranslation();
// Add You Custom Loadaing Page
if(status === QueryStatusEnum.LOADING){
return <LoadingPage />
}
// Add Your Custom Error Page
if(status === QueryStatusEnum.ERROR){
return (
<div className="error_show">
<span className="error_icon"><BsEmojiFrown/></span>
<span className="error_text">
{t("Ops")}...<br/>
{t("An Error According")} <br/>
{t("Please Try Again Later")}
</span>
</div>
)
}
return (
<div className='Page' >
{ children }
</div>
)
}
export default DashBody

View 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;

View 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';
import useTriggerStatus from '../../Hooks/useTriggerStatus';
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)
const isOnline = useTriggerStatus()
const [t] = useTranslation()
useEffect(() => {
if (status === QueryStatusEnum.SUCCESS) {
CloseAllModal()
}
}, [status, CloseAllModal])
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

Some files were not shown because too many files have changed in this diff Show More