This commit is contained in:
KarimAldeen 2024-02-20 14:52:34 +03:00
parent 422ba36cff
commit 466d24e2b6
201 changed files with 31264 additions and 265 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

11
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"cSpell.words": [
"aldeen",
"Datepicker",
"formik",
"Karim",
"queryqlent",
"szhsin",
"Viewelement"
]
}

271
README.md
View File

@ -1,270 +1,11 @@
# Structer Dashboard - Open Source # Structer Dashboard
Welcome to Structer Dashboard, an open-source dashboard designed to streamline your project management processes. This guide will walk you through the installation process and highlight the key components and examples for integrating various functionalities into your project. 1. Install the necessary dependencies using PNPM by running
## Installation
To incorporate the Structer Dashboard into your project, follow these steps:
1. Ensure you have Node.js version 18 installed.
2. Install the JSON server by running
```bash
npm i -g json-server
```
3. Run the JSON server with the command .
```bash
json-server --watch db.json
```
4. Install the necessary dependencies using PNPM by running
```bash ```bash
pnpm install npm install
``` ```
5. Launch the dashboard with 5. Launch the dashboard with
```bash pnpm start ``` ```bash
6. Visit http://localhost:3333 to view the dashboard. pnpm start
```
## Components
### 1. Reactstrap
Utilize the following Reactstrap components for a seamless and responsive UI:
- **Navbar**: Easily navigate through the dashboard with dropdown support.
- **Modal**: Display additional information or forms with user-friendly pop-ups.
### 2. Formik
Simplify form management and validation with Formik, offering the following capabilities:
- **Form Creation**: Effortlessly create complex forms with validation rules.
- **Error Handling**: Manage form errors and user input effectively.
- **Validation Input**: Validate input using the Yup schema.
### 3. react-data-table-component
Efficiently manage large datasets with the help of react-data-table-component, which provides:
- **Pagination**: Organize data with pagination and sorting functionality.
- **Customization**: Customize the data table style to align with your project requirements.
### 4. react-i18next
Make your dashboard accessible to a global audience with multilingual support through react-i18next:
- **Multiple Language Support**: Manage content in multiple languages effortlessly.
- **Dynamic Content Translation**: Translate dashboard content dynamically for a personalized user experience.
## Examples
Refer to the following examples to integrate these features seamlessly into your dashboard:
1. Create a folder within the 'pages' directory.
2. Add the page to 'Routes.tsx' with the relevant path and icon, ensuring lazy loading is implemented.
3. Customize your modal forms to suit your specific requirements.
4. Define initial values and validate schemas for your input in the 'formUtils' file.
5. Implement the 'handleSubmit' function to manage form submissions effectively.
## Real Example
First Add Your Route And Page in Routes.tsx
```ts
export const RoutesLinks = [
{
name: "example",
href: "/",
icon: <BsHexagon />,
element: <ExamplePage />
}
// add another route
]
```
Create Folder in Pages with this Structer
```js
// Some Imports
function YourPageName() {
// Your Column For Data Table
const column =useTableColumns()
// Your Data Fetch and status (pendding , loadaing , success)
const {data ,status } = useGetAllExample()
return (
<DashBody status={status as QueryStatusEnum} >
<DashHeader title={'Example'}></DashHeader>
<LyTable
data={data}
isLoading={status === QueryStatusEnum.LOADING}
columns={column}
// another props hear
/>
{/*
// Your Edit and Add Model
// Dynimc Open Using Zustand
*/}
<EditExampleModal />
<AddExampleModal />
</DashBody>
)
}
export default YourPageName
```
Create Your Modal Add and Edit
```js
// Translate
const [t] = useTranslation()
// Your Fetch function
const {mutate , status} = useAddExample()
// Create your Submit Form => using Formik
const handelSubmit = (values:any )=>{
// change values Shap For Send To Api
const dataToSend = getDataToSend(values)
mutate(dataToSend)
// Submit Value
}
return (
// Customized you model
<LayoutModal
isAddModal={true}
// inital value for form
getInitialValues={getInitialValues()}
// validate your schema form input
getValidationSchema={getValidationSchema()}
handleSubmit={handelSubmit}
// Dynmic Close Modal when status Success and make Loading Button if status loading
status={status as QueryStatusEnum}
headerText={`${t('Add')} ${t('example')}`}
>
{/*
add Your Children Component
*/}
<FormExample />
</LayoutModal>
)
```
create your formUtils file with your validate and inital value and getDatasend functions
```js
export const getInitialValues = (objectToEdit: ObjectToEdit | null = null): InitialValues => {
// change your inital value with your state
return {
id:objectToEdit?.id?? 0 ,
name:objectToEdit?.name ?? "",
email:objectToEdit?.email?? ""
}
};
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<ValidateSchema> => {
// validate input
return Yup.object().shape({
name:Yup.string().required('required'),
email:Yup.string().required("required")
});
};
export const getDataToSend = (values: any): FormData => {
const data = { ...values };
// apply your data shap change
const formData = new FormData();
buildFormData(formData, data);
return formData;
};
```
Create your Children Form For Modal
```js
// translated object
const [t] = useTranslation()
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<ValidatedField
name="name"
label={`${t("name")}`}
placeholder={`${t("name")}`}
// custom props
/>
</Col>
<Col>
<ValidatedField
name="email"
label={`${t("email")}`}
placeholder={`${t("email")}`}
/>
</Col>
{/*
or you can use from ze
ro the formik input
<Field
name="name"
...ect
/>
*/}
</Row>
)
```
## Contact
For any queries or support, please reach out to our team at karimaldenlibrary@gmail.com or contact us via phone at +963951968994.
## Conclusion
We hope this documentation assists you in leveraging the full potential of the Structer Dashboard. Customize the dashboard as per your needs and explore its diverse range of features. Should you have any further questions, please do not hesitate to reach out.

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

19601
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

98
package.json Normal file
View File

@ -0,0 +1,98 @@
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.19",
"@react-google-maps/api": "^2.19.2",
"@szhsin/react-menu": "github:szhsin/react-menu",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@tinymce/tinymce-react": "^4.3.2",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.60",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"@types/socket.io-client": "^3.0.0",
"@wojtekmaj/react-daterange-picker": "^5.4.4",
"antd": "^5.12.1",
"apexcharts": "^3.44.2",
"axios": "^1.6.0",
"bootstrap": "^5.3.2",
"chart.js": "^4.4.0",
"dayjs": "^1.11.10",
"formik": "^2.4.5",
"history": "^5.3.0",
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"json-server": "^0.17.4",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-bootstrap": "^2.9.1",
"react-bootstrap-sweetalert": "^5.2.0",
"react-chartjs-2": "^5.2.0",
"react-confirm-alert": "^3.0.6",
"react-data-table-component": "^7.5.4",
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"react-i18next": "^13.3.1",
"react-icons": "^4.11.0",
"react-query": "^3.39.3",
"react-redux": "^8.1.3",
"react-router-dom": "^6.18.0",
"react-scripts": "5.0.1",
"react-select": "^5.7.7",
"react-simple-star-rating": "^5.1.7",
"react-switch": "^7.0.0",
"react-tabs": "^6.0.2",
"react-toastify": "^9.1.3",
"react-toggle": "^4.1.3",
"reactstrap": "^9.2.0",
"redux": "^4.2.1",
"sass": "^1.69.5",
"socket.io-client": "^4.7.2",
"styled-components": "5.3.3",
"ts-xlsx": "^0.0.11",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4",
"xlsx": "^0.18.5",
"yup": "^1.3.2",
"zustand": "^4.4.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"g:api": "node src/Extensions/FileGenerator/generateApi.js",
"g:column": "node src/Extensions/FileGenerator/generateColumn.js",
"g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js",
"g:page": "node src/Extensions/FileGenerator/generatePage.js",
"g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js ",
"g:modal:add": "node src/Extensions/FileGenerator/generateEditModal.js ",
"g:model:edit": "node src/Extensions/FileGenerator/generateEditModal.js "
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-toggle": "^4.0.5"
}
}

1
public/Layout/Ar.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/Layout/CarImage.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

1582
public/Layout/CarImage2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

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

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg class="Logo" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1367 766.99">
<defs>
<style>
.cls-2 {
fill:white;
}
.cls-3 {
fill: transparent;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1-2">
<g>
<g>
<g>
<path class="cls-2" d="M540.42,301c0-7.67,0-15.33,.96-22.04,0-1.92,.96-1.92,1.92-2.87,33.54-15.33,54.62-42.17,69-75.71,4.79-11.5,7.67-22.04,8.62-34.5,.96-7.67,0-15.33-2.87-23-4.79-10.54-16.29-15.33-28.75-12.46-16.29,3.83-26.83,15.33-36.42,28.75-11.5,17.25-16.29,37.37-20.12,56.54-6.71,31.62-8.62,63.25-10.54,94.87-1.92,26.83-3.83,53.67-7.67,80.5-.96,4.79-1.92,9.58-2.87,14.37-.96,4.79,0,4.79,3.83,3.83,16.29-3.83,31.62-8.62,46-16.29,31.62-15.33,59.42-35.46,84.33-58.46,45.04-43.12,81.46-92,105.41-149.5,13.42-31.62,21.08-65.17,18.21-99.66,0-3.83,0-8.62-.96-12.46-.96-4.79,.96-6.71,4.79-8.62,8.62-3.83,18.21-4.79,27.79-4.79,6.71,0,13.42,.96,21.08,2.87,14.37,2.87,24.92,12.46,32.58,23.96,.96,.96,1.92,2.87,.96,3.83s-2.87,0-3.83,0c-5.75-1.92-11.5-3.83-18.21-2.87-13.42,.96-22.04,8.62-24.92,23-2.87,10.54-2.87,22.04-2.87,33.54,.96,20.12,3.83,40.25,3.83,60.37,0,5.75-1.92,11.5-4.79,17.25-43.12,71.87-98.71,130.33-172.5,171.54-39.29,22.04-79.54,37.37-123.62,47.92-3.83,.96-5.75,1.92-6.71,5.75-14.37,56.54-39.29,107.33-77.62,151.41-25.87,30.67-56.54,54.62-93.92,69.96-18.21,7.67-37.37,11.5-57.5,6.71-26.83-6.71-42.17-24.92-49.83-50.79-14.37-48.87,8.62-104.46,53.67-132.25,17.25-10.54,35.46-18.21,56.54-17.25,5.75,0,10.54,.96,16.29,1.92,4.79,.96,6.71,3.83,6.71,7.67,.96,13.42-4.79,24.92-14.37,33.54-3.83,3.83-7.67,3.83-12.46,0-4.79-3.83-9.58-7.67-14.37-9.58-18.21-9.58-35.46-5.75-48.87,9.58-13.42,15.33-20.12,33.54-24.92,52.71-3.83,17.25-4.79,34.5-3.83,51.75,.96,10.54,2.87,21.08,8.62,30.67,6.71,10.54,16.29,13.42,27.79,10.54,10.54-2.87,20.12-9.58,27.79-18.21,31.62-31.62,56.54-68.04,73.79-110.21,22.04-55.58,39.29-113.08,47.92-172.5,4.79-33.54,10.54-67.08,20.12-99.66,12.46-42.17,28.75-82.42,56.54-116.91,17.25-21.08,36.42-39.29,63.25-46.96,23-6.71,44.08-3.83,61.33,14.37,10.54,10.54,14.37,23.96,15.33,38.33,1.92,25.87-6.71,48.87-18.21,71.87-18.21,35.46-46,61.33-82.42,77.62q1.92,0,.96,0h0Z"/>
<path class="cls-2" d="M1056.95,437.08c-2.87-2.87-6.71-5.75-9.58-8.62s-3.83-2.87-5.75,.96c-10.54,24.92-22.04,49.83-34.5,74.75-11.5,21.08-23,42.17-39.29,59.42-5.75,6.71-12.46,12.46-20.12,16.29-15.33,7.67-29.71,1.92-36.42-13.42-3.83-7.67-3.83-15.33-3.83-23.96,0-21.08,5.75-41.21,12.46-60.37,7.67-21.08,15.33-41.21,29.71-58.46,9.58-11.5,21.08-22.04,36.42-25.87,6.71-1.92,13.42-.96,19.17,0,1.92,0,2.87,.96,3.83-1.92,9.58-23.96,19.17-48.87,26.83-73.79-3.83,3.83-7.67,8.62-11.5,12.46-9.58,10.54-21.08,21.08-33.54,27.79-27.79,17.25-56.54,13.42-80.5-9.58-14.37-13.42-23-30.67-29.71-48.87-1.92-4.79-3.83-9.58-6.71-15.33-.96,8.62-.96,16.29-2.87,24.92-6.71,46-15.33,92-40.25,132.25-15.33,25.87-37.37,45.04-67.08,53.67-.96,0-1.92,0-2.87,.96-.96,0-1.92,.96-1.92-.96,32.58-10.54,52.71-34.5,66.12-65.17,8.62-20.12,13.42-41.21,17.25-63.25,5.75-38.33,4.79-76.67,4.79-115,0-12.46-1.92-24.92,0-36.42,.96-8.62,4.79-15.33,13.42-19.17,5.75-2.87,10.54-1.92,16.29,.96,7.67,5.75,12.46,13.42,16.29,21.08,9.58,17.25,15.33,36.42,23,54.62,4.79,12.46,10.54,24.92,18.21,36.42,12.46,18.21,24.92,20.12,42.17,6.71,17.25-13.42,30.67-30.67,42.17-48.87,27.79-41.21,50.79-86.25,70.92-132.25,5.75-13.42,11.5-26.83,18.21-39.29,4.79-8.62,12.46-12.46,22.04-11.5,8.62,.96,14.37,6.71,16.29,15.33,1.92,6.71,.96,12.46-.96,19.17-12.46,43.12-26.83,86.25-40.25,128.41-18.21,55.58-37.37,111.16-59.42,164.83-.96,2.87-.96,3.83,.96,5.75,3.83,3.83,6.71,7.67,10.54,12.46q.96,2.87,0,2.87h0Z"/>
<path class="cls-2" d="M561.5,456.25c1.92,3.83,.96,8.62,.96,12.46-.96,44.08,4.79,87.21,22.04,127.46,4.79,12.46,11.5,23,21.08,32.58,17.25,18.21,40.25,23,63.25,12.46,20.12-9.58,35.46-23.96,50.79-40.25,2.87-2.87,5.75-5.75,7.67-9.58,1.92-2.87,4.79-2.87,7.67-1.92,16.29,5.75,28.75,15.33,37.37,29.71,2.87,4.79,2.87,9.58-.96,14.37-15.33,20.12-34.5,35.46-59.42,41.21-22.04,4.79-43.12,.96-63.25-9.58-27.79-14.37-48.87-36.42-66.12-62.29-19.17-29.71-31.62-62.29-38.33-96.79-1.92-8.62-1.92-17.25-2.87-25.87,0-1.92,0-2.87,.96-3.83,5.75-6.71,12.46-13.42,19.17-20.12h0Z"/>
<path class="cls-2" d="M844.21,140.96c0-6.71,4.79-11.5,10.54-11.5s11.5,5.75,11.5,11.5-4.79,11.5-11.5,11.5c-5.75,0-10.54-4.79-10.54-11.5h0Z"/>
</g>
<path class="cls-3" d="M935.25,547.29c-.96-4.79-1.92-8.62-1.92-12.46,.96-32.58,6.71-64.21,21.08-93.91,6.71-12.46,14.37-23.96,27.79-31.62,5.75-2.87,12.46-4.79,19.17-3.83,2.87,0,2.87,.96,1.92,2.87-17.25,41.21-33.54,83.37-55.58,121.71-2.88,6.71-7.67,11.5-12.46,17.25h0Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,54 @@
import React from 'react'
const KarimLogo = () => {
return (
<>
<svg
className="KarimLogo"
id="Layer_1"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1367 766.99"
>
<defs>
<style
dangerouslySetInnerHTML={{
__html:
"\n .cls-2 {\n fill:white;\n }\n\n .cls-3 {\n fill: transparent;\n }\n "
}}
/>
</defs>
<g id="Layer_1-2" data-name="Layer 1-2">
<g>
<g>
<g>
<path
className="cls-2"
d="M540.42,301c0-7.67,0-15.33,.96-22.04,0-1.92,.96-1.92,1.92-2.87,33.54-15.33,54.62-42.17,69-75.71,4.79-11.5,7.67-22.04,8.62-34.5,.96-7.67,0-15.33-2.87-23-4.79-10.54-16.29-15.33-28.75-12.46-16.29,3.83-26.83,15.33-36.42,28.75-11.5,17.25-16.29,37.37-20.12,56.54-6.71,31.62-8.62,63.25-10.54,94.87-1.92,26.83-3.83,53.67-7.67,80.5-.96,4.79-1.92,9.58-2.87,14.37-.96,4.79,0,4.79,3.83,3.83,16.29-3.83,31.62-8.62,46-16.29,31.62-15.33,59.42-35.46,84.33-58.46,45.04-43.12,81.46-92,105.41-149.5,13.42-31.62,21.08-65.17,18.21-99.66,0-3.83,0-8.62-.96-12.46-.96-4.79,.96-6.71,4.79-8.62,8.62-3.83,18.21-4.79,27.79-4.79,6.71,0,13.42,.96,21.08,2.87,14.37,2.87,24.92,12.46,32.58,23.96,.96,.96,1.92,2.87,.96,3.83s-2.87,0-3.83,0c-5.75-1.92-11.5-3.83-18.21-2.87-13.42,.96-22.04,8.62-24.92,23-2.87,10.54-2.87,22.04-2.87,33.54,.96,20.12,3.83,40.25,3.83,60.37,0,5.75-1.92,11.5-4.79,17.25-43.12,71.87-98.71,130.33-172.5,171.54-39.29,22.04-79.54,37.37-123.62,47.92-3.83,.96-5.75,1.92-6.71,5.75-14.37,56.54-39.29,107.33-77.62,151.41-25.87,30.67-56.54,54.62-93.92,69.96-18.21,7.67-37.37,11.5-57.5,6.71-26.83-6.71-42.17-24.92-49.83-50.79-14.37-48.87,8.62-104.46,53.67-132.25,17.25-10.54,35.46-18.21,56.54-17.25,5.75,0,10.54,.96,16.29,1.92,4.79,.96,6.71,3.83,6.71,7.67,.96,13.42-4.79,24.92-14.37,33.54-3.83,3.83-7.67,3.83-12.46,0-4.79-3.83-9.58-7.67-14.37-9.58-18.21-9.58-35.46-5.75-48.87,9.58-13.42,15.33-20.12,33.54-24.92,52.71-3.83,17.25-4.79,34.5-3.83,51.75,.96,10.54,2.87,21.08,8.62,30.67,6.71,10.54,16.29,13.42,27.79,10.54,10.54-2.87,20.12-9.58,27.79-18.21,31.62-31.62,56.54-68.04,73.79-110.21,22.04-55.58,39.29-113.08,47.92-172.5,4.79-33.54,10.54-67.08,20.12-99.66,12.46-42.17,28.75-82.42,56.54-116.91,17.25-21.08,36.42-39.29,63.25-46.96,23-6.71,44.08-3.83,61.33,14.37,10.54,10.54,14.37,23.96,15.33,38.33,1.92,25.87-6.71,48.87-18.21,71.87-18.21,35.46-46,61.33-82.42,77.62q1.92,0,.96,0h0Z"
/>
<path
className="cls-2"
d="M1056.95,437.08c-2.87-2.87-6.71-5.75-9.58-8.62s-3.83-2.87-5.75,.96c-10.54,24.92-22.04,49.83-34.5,74.75-11.5,21.08-23,42.17-39.29,59.42-5.75,6.71-12.46,12.46-20.12,16.29-15.33,7.67-29.71,1.92-36.42-13.42-3.83-7.67-3.83-15.33-3.83-23.96,0-21.08,5.75-41.21,12.46-60.37,7.67-21.08,15.33-41.21,29.71-58.46,9.58-11.5,21.08-22.04,36.42-25.87,6.71-1.92,13.42-.96,19.17,0,1.92,0,2.87,.96,3.83-1.92,9.58-23.96,19.17-48.87,26.83-73.79-3.83,3.83-7.67,8.62-11.5,12.46-9.58,10.54-21.08,21.08-33.54,27.79-27.79,17.25-56.54,13.42-80.5-9.58-14.37-13.42-23-30.67-29.71-48.87-1.92-4.79-3.83-9.58-6.71-15.33-.96,8.62-.96,16.29-2.87,24.92-6.71,46-15.33,92-40.25,132.25-15.33,25.87-37.37,45.04-67.08,53.67-.96,0-1.92,0-2.87,.96-.96,0-1.92,.96-1.92-.96,32.58-10.54,52.71-34.5,66.12-65.17,8.62-20.12,13.42-41.21,17.25-63.25,5.75-38.33,4.79-76.67,4.79-115,0-12.46-1.92-24.92,0-36.42,.96-8.62,4.79-15.33,13.42-19.17,5.75-2.87,10.54-1.92,16.29,.96,7.67,5.75,12.46,13.42,16.29,21.08,9.58,17.25,15.33,36.42,23,54.62,4.79,12.46,10.54,24.92,18.21,36.42,12.46,18.21,24.92,20.12,42.17,6.71,17.25-13.42,30.67-30.67,42.17-48.87,27.79-41.21,50.79-86.25,70.92-132.25,5.75-13.42,11.5-26.83,18.21-39.29,4.79-8.62,12.46-12.46,22.04-11.5,8.62,.96,14.37,6.71,16.29,15.33,1.92,6.71,.96,12.46-.96,19.17-12.46,43.12-26.83,86.25-40.25,128.41-18.21,55.58-37.37,111.16-59.42,164.83-.96,2.87-.96,3.83,.96,5.75,3.83,3.83,6.71,7.67,10.54,12.46q.96,2.87,0,2.87h0Z"
/>
<path
className="cls-2"
d="M561.5,456.25c1.92,3.83,.96,8.62,.96,12.46-.96,44.08,4.79,87.21,22.04,127.46,4.79,12.46,11.5,23,21.08,32.58,17.25,18.21,40.25,23,63.25,12.46,20.12-9.58,35.46-23.96,50.79-40.25,2.87-2.87,5.75-5.75,7.67-9.58,1.92-2.87,4.79-2.87,7.67-1.92,16.29,5.75,28.75,15.33,37.37,29.71,2.87,4.79,2.87,9.58-.96,14.37-15.33,20.12-34.5,35.46-59.42,41.21-22.04,4.79-43.12,.96-63.25-9.58-27.79-14.37-48.87-36.42-66.12-62.29-19.17-29.71-31.62-62.29-38.33-96.79-1.92-8.62-1.92-17.25-2.87-25.87,0-1.92,0-2.87,.96-3.83,5.75-6.71,12.46-13.42,19.17-20.12h0Z"
/>
<path
className="cls-2"
d="M844.21,140.96c0-6.71,4.79-11.5,10.54-11.5s11.5,5.75,11.5,11.5-4.79,11.5-11.5,11.5c-5.75,0-10.54-4.79-10.54-11.5h0Z"
/>
</g>
<path
className="cls-3"
d="M935.25,547.29c-.96-4.79-1.92-8.62-1.92-12.46,.96-32.58,6.71-64.21,21.08-93.91,6.71-12.46,14.37-23.96,27.79-31.62,5.75-2.87,12.46-4.79,19.17-3.83,2.87,0,2.87,.96,1.92,2.87-17.25,41.21-33.54,83.37-55.58,121.71-2.88,6.71-7.67,11.5-12.46,17.25h0Z"
/>
</g>
</g>
</g>
</svg>
</>
)
}
export default KarimLogo

BIN
public/Layout/LoginBg.jpg Normal file

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: 16 KiB

BIN
public/Layout/MainLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 144 KiB

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/logorayan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
public/Layout/mac.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

BIN
public/Layout/order.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 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>Etaxi - App</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:

71
src/App.tsx Normal file
View File

@ -0,0 +1,71 @@
import { Fragment, lazy, Suspense } from 'react';
import { Route, Routes } from 'react-router-dom'
import Loading from './Components/Utils/Loading/Loading';
import { RoutesLinks } from './Routes';
import Layout from './Layout/app/Layout';
import Auth from './Pages/Auth/Page';
import EditAccount from './Pages/Account/EditAccount/EditAccount';
import { toast } from 'react-toastify';
const Page404 = lazy(() => import("./Layout/app/NotFoundPage"))
const App = () => {
return (
<Routes >
{/* 404 Page */}
<Route path="*" element={<Suspense fallback={<Loading />}> <Page404 /></Suspense>} />
{/* Login Page */}
{/* <Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} /> */}
{/* route not in navigation */}
{/* All Routes */}
{RoutesLinks?.map((item: any, index: number) => (
<Fragment key={index}>
if(item?.element){
<Route
key={index}
path={item.href}
element={
<Suspense fallback={<Loading />} >
<Layout>
{item?.element ?? "Please Add Element Props in Routes"}
</Layout>
</Suspense>
}
/>
}else{
item?.children?.map((item:any)=>{
return(
<Route
path={item.href}
element={
<Suspense fallback={<Loading />} >
<Layout>
{item?.element ?? "Please Add Element Props in Routes"}
</Layout>
</Suspense>
}
/>
)
})
}
</Fragment>
))
}
</Routes>
)
}
export default App

View File

@ -0,0 +1,84 @@
import {
DownloadOutlined,
RotateLeftOutlined,
RotateRightOutlined,
SwapOutlined,
ZoomInOutlined,
ZoomOutOutlined,
} from '@ant-design/icons';
import React from 'react';
import { Image, Space } from 'antd';
import { ImageBaseURL } from '../../api/config';
import useImageError from '../../Hooks/useImageError';
const ColumnsImage= ({src}:any) => {
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
const imageUrl = ImageBaseURL + src || ErrorImage;
const handleError = useImageError;
// or you can download flipped and rotated image
// https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
const onDownload = () => {
fetch(src)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.download = 'image.png';
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
link.remove();
});
};
return (
<Image
width={45}
height={45}
src={imageUrl }
className='p-1 mb-1 columnImage '
preview={{
toolbarRender: (
_,
{
transform: { scale },
actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },
},
) => (
<Space size={12} className="toolbar-wrapper">
<DownloadOutlined onClick={onDownload} />
<SwapOutlined rotate={90} onClick={onFlipY} />
<SwapOutlined onClick={onFlipX} />
<RotateLeftOutlined onClick={onRotateLeft} />
<RotateRightOutlined onClick={onRotateRight} />
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
</Space>
),
}}
onError={handleError}
/>
);
};
export default ColumnsImage;
// {
// name: t("image"),
// center: "true",
// cell: (row: any) => {
// return (
// <ColumnsImage src={row?.image} />
// )
// }
// },

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,72 @@
.KarimField{
>*{
width: 100%;
}
.text,.ant-form-item{
margin-bottom:7px !important;
}
>span{
margin-bottom: 0px !important;
&:focus-within{
border-color: var(--primary) ;
box-shadow: 0 0 0 1px var(--primary);
cursor: pointer;
}
&:has(.is-invalid){
border-color: red !important;
}
input{
color: var(--text);
background: var(--bg);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active{
-webkit-box-shadow: 0 0 0 30px white inset !important;
}
}
}
.ant-upload-select{
width: 100%;
}
.Checkboxs{
padding: 4%;
}
.SearchField{
button{
background: var(--primary);
}
}
.text{
color: var(--text);
}
input:disabled{
color: var(--text) !important;
}
.isError{
outline: red 1px solid;
color: red;
}
.Error_color{
color: red;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
input:-webkit-autofill:focus {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
/* Remove autofill background color on hover */
input:-webkit-autofill:hover {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}

View File

@ -0,0 +1,25 @@
import React from "react";
import "./KarimField.scss";
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View';
import { KarimFieldProps } from "./types";
const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
switch (type) {
case 'Select':
return <SelectField {...otherProps} />;
case "DataRange":
return <DataRange {...otherProps} />;
case "Date":
return <Date {...otherProps} />;
case "Time":
return <Time {...otherProps} />;
case "File":
return <File {...otherProps} />;
case "Checkbox":
return <CheckboxField {...otherProps} />;
default:
return <Default {...otherProps} />;
}
};
export default React.memo(KarimField);

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Spin } from 'antd';
interface Props {
loading: boolean;
children: React.ReactNode;
className?: string;
}
const KarimSpinner: React.FC<Props> = ({ loading, className, children }) => {
return (
<div className={className ?? ""}>
{loading ? <div className='text-center'><Spin /></div> : children}
</div>
);
};
export default KarimSpinner;

View File

@ -0,0 +1,36 @@
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
import { Checkbox, Form } from 'antd';
const CheckboxField = ({ name, label, isDisabled, onChange,Group,className, props }: any) => {
const { t, formik,isError,errorMsg} = useFormField(name, props)
const CheckboxhandleChange = (value: any) => {
formik.setFieldValue(name, value?.target?.checked)
};
return (
<div className={Group ? "d-inline mt-3 Checkboxs" :``}>
<Form.Item
hasFeedback
validateStatus={isError ? 'error' : ''}
help={isError ? errorMsg : ''}
>
<Checkbox
onChange={onChange || CheckboxhandleChange}
disabled={isDisabled}
checked={formik.values[name] ?? false}
className={className}
>
{t(label)}
</Checkbox>
</Form.Item>
</div>
)
}
export default CheckboxField

View File

@ -0,0 +1,43 @@
import { Form, DatePicker } from 'antd'
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
const { RangePicker } = DatePicker;
const DataRange = ({ name, label,Format ,props ,onChange,isDisabled,placeholder,className}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props)
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value)
};
return (
<div className='KarimField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<RangePicker
placeholder={placeholder}
size="large"
allowClear
className={`${className} w-100`}
format={Format}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
/>
</Form.Item>
</div>
)
}
export default DataRange

View File

@ -0,0 +1,43 @@
import { Form, DatePicker } from 'antd'
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
import dayjs from 'dayjs';
const Date = ({ name, label,picker="date" ,isDisabled,props,onChange,placeholder ,className}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props)
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value)
};
return (
<div className='KarimField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<DatePicker
picker={picker}
placeholder={placeholder}
allowClear
className={`${className} w-100`}
// defaultValue={formik.values[name]}
size="large"
onChange={onChange || onCalendarChange}
disabled={isDisabled}
/>
</Form.Item>
</div>
)
}
export default Date

View File

@ -0,0 +1,34 @@
import { Form, Input } from 'antd'
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
const Default = ({ name, label, placeholder, isDisabled, onChange, props }: any) => {
const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
return (
<div className="KarimField w-100" >
<label htmlFor={name} className="text">
{t(`${label ? label : name}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? 'error' : ''}
help={isError ? errorMsg : ''}
>
<Field
as={Input}
type="text"
placeholder={t(`${placeholder ?placeholder : name}`)}
name={name}
disabled={isDisabled}
size="large"
// onChange={onChange ? onChange : handleChange}
/>
</Form.Item>
</div>
);
};
export default React.memo(Default);
;

View File

@ -0,0 +1,60 @@
import { Button, Upload, UploadFile } from 'antd'
import useFormField from '../../../Hooks/useFormField';
import { UploadOutlined } from '@ant-design/icons';
import { ImageBaseURL } from '../../../api/config';
import { useTranslation } from 'react-i18next';
const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
const { formik, t ,isError} = useFormField(name, props)
const imageUrl = formik.values[name] ? ImageBaseURL + formik.values[name] : '';
const fileList: UploadFile[] = [
{
uid: '-1',
name: '',
status: 'done',
url: imageUrl,
thumbUrl: imageUrl,
}
];
const FilehandleChange = (value:any) => {
formik.setFieldValue(name, value.file.originFileObj)
};
const customRequest = async ({ onSuccess}: any) => {
onSuccess();
};
return (
<div className="KarimField">
<label htmlFor={name} className="text">
{t(`${label || name}`)}
</label>
<Upload
disabled={isDisabled}
listType="picture"
maxCount={1}
defaultFileList={[...fileList]}
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={`${className} w-100`}
>
<Button className={isError ? "isError w-100" : " w-100"} icon={<UploadOutlined />}>
{placholder ?? t("upload_image") }
</Button>
<div className='Error_color'> {isError ? "required" : ""}</div>
</Upload>
</div>
)
}
export default File

View File

@ -0,0 +1,46 @@
import { Input } from 'antd';
import { SearchProps } from 'antd/es/input'
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
const { Search } = Input;
const SearchField = () => {
const navigate = useNavigate()
const [searchParams,] = useSearchParams();
const location =useLocation()
const {t} = useTranslation();
const [searchValue, setSearchValue] = useState(searchParams.get('search')|| "");
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
// console.log(value);
navigate(`${location?.pathname}?search=${value}`, { replace: true });
}
const onChange = (e :any) => {
setSearchValue(e.target.value);
}
return (
<div className='SearchField'>
<Search
allowClear
enterButton={t("search")}
// size="middle"
size="large"
placeholder={t("search")}
onSearch={onSearch}
style={{ width: 250 }}
value={searchValue}
onChange={onChange}
/>
</div>
)
}
export default SearchField

View File

@ -0,0 +1,41 @@
import { Form, Select } from 'antd'
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
const SelectField = ({ name, label, placeholder, isDisabled,option,isMulti,onChange,className, props}: any) => {
const { errorMsg, isError, t ,formik} = useFormField(name, props)
const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
formik.setFieldValue(name, value)
};
return (
<div className='KarimField'>
<label htmlFor={name} className="text">
{t(`${label ? label : name}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<Select
placeholder={t(`${placeholder ?placeholder : name}`)}
disabled={isDisabled}
options={option}
size="large"
className={`${className} w-100`}
defaultValue={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelecthandleChange}
/>
</Form.Item>
</div>
)
}
export default React.memo(SelectField);

View File

@ -0,0 +1,41 @@
import { Form, TimePicker } from 'antd'
import React from 'react'
import useFormField from '../../../Hooks/useFormField';
const Time = ({ name, label,className,isDisabled,onChange,props }: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props)
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value)
};
return (
<div className='KarimField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<TimePicker
allowClear
className={`${className} w-100`}
size="large"
defaultValue={formik.values[name]}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
/>
</Form.Item>
</div>
)
}
export default Time

View File

@ -0,0 +1,22 @@
import Time from "./Time";
import SelectField from "./SelectField";
import Date from "./Date";
import DataRange from "./DataRange";
import CheckboxField from "./CheckboxField";
import Default from "./Default";
import File from "./File";
export {
Time,
SelectField,
Date,
DataRange,
CheckboxField,
Default,
File
}

View File

@ -0,0 +1,21 @@
import { useState } from 'react';
import { ErrorMessage, useField, Field, useFormikContext } from 'formik';
import { useTranslation } from 'react-i18next';
import { FaExclamationCircle } from 'react-icons/fa';
import Select from 'react-select';
import { convert_data_to_select } from '../../Layout/app/Const';
export {
useState,
ErrorMessage, useField, Field, useFormikContext,
useTranslation,
FaExclamationCircle,
Select,
convert_data_to_select,
}

View File

@ -0,0 +1,114 @@
// export interface KarimFieldProps {
// name: string;
// type?: "text" | "Select" | "DataRange" | "Date" | "Time" | "File" | "number" | "Checkbox" | "password";
// placeholder?: string;
// label?: string;
// className?: string;
// option?: any[];
// isMulti?: boolean;
// isDisabled?: boolean;
// picker?: "data" | "week" | "month" | "quarter" | "year";
// Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
// onChange?: (value: any) => void;
// Group?: boolean
// dir?:'ltr' | "rtl"
// }
export interface KarimFieldPropsText {
name: string;
type: "text";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
}
export interface KarimFieldPropsSelect {
name: string;
type: "Select";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl";
option: any[];
isMulti?: boolean;
}
export interface KarimFieldPropsDataRange {
name: string;
type: "DataRange";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
}
export interface KarimFieldPropsDate {
name: string;
type: "Date";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
picker?: "data" | "week" | "month" | "quarter" | "year";
}
export interface KarimFieldPropsTime {
name: string;
type: "Time";
label?: string;
placeholder?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
}
export interface KarimFieldPropsFile {
name: string;
type: "File";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
}
export interface KarimFieldPropsCheckbox {
name: string;
type: "Checkbox";
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
Group?: boolean
}
export interface KarimFieldPropstext {
name: string;
type?: "text" | "number" | "password";
label?: string;
className?: string;
placeholder?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
Group?: boolean
}
export type KarimFieldProps = KarimFieldPropsText| KarimFieldPropsSelect| KarimFieldPropsDataRange| KarimFieldPropsDate| KarimFieldPropsTime| KarimFieldPropsFile| KarimFieldPropsCheckbox| KarimFieldPropstext;

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,13 @@
import React from "react";
import { Button, Spinner } from "reactstrap";
const LoadingButton = ({ isLoading = false, ...props }) => {
return (
<Button className="btn-primary" disabled={isLoading} {...props}>
{isLoading ? <Spinner style={{ marginRight: "10px" }} size="sm" /> : null}
<span >{props.children}</span>
</Button>
);
};
export { LoadingButton };

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,31 @@
import React from 'react';
import type { RadioChangeEvent } from 'antd';
import { Radio } from 'antd';
const onChange = (e: RadioChangeEvent) => {
console.log(`radio checked:${e.target.value}`);
};
const App: React.FC = () => (
<>
<Radio.Group onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
<Radio.Button value="a">Hangzhou</Radio.Button>
<Radio.Button value="b" disabled>
Shanghai
</Radio.Button>
<Radio.Button value="c">Beijing</Radio.Button>
<Radio.Button value="d">Chengdu</Radio.Button>
</Radio.Group>
<Radio.Group disabled onChange={onChange} defaultValue="a" style={{ marginTop: 16 }}>
<Radio.Button value="a">Hangzhou</Radio.Button>
<Radio.Button value="b">Shanghai</Radio.Button>
<Radio.Button value="c">Beijing</Radio.Button>
<Radio.Button value="d">Chengdu</Radio.Button>
</Radio.Group>
</>
);
export default App;

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,62 @@
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { BsExclamationCircle } from 'react-icons/bs';
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
import { LoadingButton } from '../Ui/LoadingButton';
interface BlockModelProps {
Mutation:any,
type :'customer' |'driver'
}
const BlockModel: React.FC<BlockModelProps> = ({Mutation ,type}) => {
const {t} = useTranslation();
const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
const {isOpenBlock:isOpen , objectID , setIsopenBlock:setIsOpen} = useCommonModelState()
const handleSubmit = () => {
const blockInput = document.getElementById('block_input') as HTMLInputElement;
if (blockInput) {
Mutation.mutate({ [key_to_api]: objectID, block_timer: blockInput.value });
}
};
useEffect(() => {
if (Mutation.isSuccess) {
setIsOpen();
}
}, [Mutation.isSuccess, setIsOpen]);
return (
<Modal isOpen={isOpen} centered size='md'>
<ModalHeader toggle={() => setIsOpen()}>
{t("al")}{type}{t('_block_page')}
</ModalHeader>
<Card>
<CardBody>
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('blocking_')}{type}</h1>
<BsExclamationCircle className='modal_info' style={{ fontSize: '100px', color: '#f8be86', margin: '20px 0' }} />
<div className=''>
<Label for='block_input'>{t('date_blocking')}</Label>
<Input id='block_input' placeholder={t('date_blocking')} type='date' />
<div style={{ marginTop: 20 }}>
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsOpen()}>
{t('cancel')}
</Button>
<LoadingButton color='primary' onClick={handleSubmit} isLoading={Mutation.isLoading} type='submit'>
{t('add_block_for_')}{type}
</LoadingButton>
</div>
</div>
</div>
</CardBody>
</Card>
</Modal>
);
};
export default BlockModel;

View File

@ -0,0 +1,63 @@
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { BsExclamationCircle } from 'react-icons/bs';
import { Button, Card, CardBody, Col, Input, Label, Modal, ModalHeader, Row } from 'reactstrap';
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
import { LoadingButton } from '../Ui/LoadingButton';
interface GiftModalProps {
Mutation:any,
type :'customer' |'driver'
}
const GiftModal: React.FC<GiftModalProps> = ({Mutation ,type }) => {
const {t} = useTranslation();
const {isOpenGift:isOpen , objectID , setIsopenGift:setIsOpen} = useCommonModelState()
useEffect(() => {
if (Mutation.isSuccess) {
setIsOpen();
}
}, [Mutation.isSuccess, setIsOpen]);
const handleGift = () => {
const enterCodesInput = document.getElementById('enter_codes') as HTMLInputElement;
if (enterCodesInput && enterCodesInput.value) {
Mutation.mutate({ type, id: objectID, value: enterCodesInput.value });
}
};
return (
<Modal isOpen={isOpen} centered size='md'>
<ModalHeader toggle={() => setIsOpen()}>
{t("al")}{type} {t('_gift_page')}
</ModalHeader>
<Card>
<CardBody>
<Row>
<Col className='' style={{ width: 300 }}>
<Label for='enter_codes' className='modal_info'>{t('value')}</Label>
<Input id='enter_codes' placeholder={t('value')} type='number' />
<Col style={{ marginTop: 20, display: 'flex', justifyContent: 'space-between' }}>
<Button color='danger' onClick={() => setIsOpen()}>
{t('cancel')}
</Button>
<LoadingButton
color='primary'
onClick={handleGift}
type='submit'
isLoading={Mutation.isLoading}>
{t('give')}
</LoadingButton>
</Col>
</Col>
</Row>
</CardBody>
</Card>
</Modal>
);
};
export default GiftModal;

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

View File

@ -0,0 +1,60 @@
import React, { useEffect } from 'react';
import { BsExclamationCircle } from 'react-icons/bs';
import { Button, Card, CardBody, Input, Label, Modal, ModalHeader } from 'reactstrap';
import { useTranslation } from 'react-i18next';
import { LoadingButton } from '../Ui/LoadingButton';
import { useCommonModelState } from '../../lib/state mangment/driver&customer/ModelState';
import { CiLock } from "react-icons/ci";
interface UnBlockModalProps {
Mutation:any,
type :'customer' |'driver'
}
const UnBlockModal: React.FC<UnBlockModalProps> = ({Mutation ,type }) => {
const {t} = useTranslation();
const key_to_api = type == t('customer') ? t('customer_id') : t("driver_id")
const {isOpenUnBlock:isOpen , objectID , setIsopenUnBlock:setIsopen} = useCommonModelState()
const handleSubmit = () => {
Mutation.mutate({ [key_to_api]: objectID });
};
useEffect(() => {
if (Mutation.isSuccess) {
setIsopen();
}
}, [Mutation.isSuccess, setIsopen]);
return (
<Modal isOpen={isOpen} centered size='md'>
<ModalHeader toggle={() => setIsopen()}>
{t("al")}{type} {t('un_block_page')}
</ModalHeader>
<Card>
<CardBody>
<div style={{ width: '100%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<h1 className='modal_info' style={{ fontWeight: 'bold' }}> {t('un_blocking_')}{type}</h1>
<CiLock className='modal_info' style={{ fontSize: '100px', color: 'black', margin: '20px 0' }} />
<div className=''>
<div style={{ marginTop: 20 }}>
<Button color='danger' style={{ marginInline: 10 }} onClick={() => setIsopen()}>
{t('cancel')}
</Button>
<LoadingButton color='primary' onClick={handleSubmit} type='submit' isLoading={Mutation.isLoading}>
{t('un_block_for_')}{type}
</LoadingButton>
</div>
</div>
</div>
</CardBody>
</Card>
</Modal>
);
};
export default UnBlockModal;

View File

@ -0,0 +1,10 @@
import React from 'react'
// for write later
function OrderStatus() {
return (
<div>OrderStatus</div>
)
}
export default OrderStatus

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/admin/${fileName}",
ADD: "/api/admin/${fileName}/create",
UPDATE: "/api/admin/${fileName}/update",
DELETE: "/api/admin/${fileName}/delete",
};
const KEY = "${fileName.toUpperCase()}";
export const useGet${capitalizeFirstLetter(fileName)} = (params?:any) => useGetQuery(KEY, API.GET, params);
export const useAdd${capitalizeFirstLetter(fileName)} = () => useAddMutation(KEY, API.ADD);
export const useUpdate${capitalizeFirstLetter(fileName)} = () => useUpdateMutation(KEY, API.UPDATE);
export const useDelete${capitalizeFirstLetter(fileName)} = () =>useDeleteMutation(KEY, API.DELETE);
`
fs.writeFileSync('src/api/'+fileName+".ts",
FileContiner
);
console.log(`File "${fileName}" generated successfully.`);
function capitalizeFirstLetter(word) {
return (word).charAt(0).toUpperCase() + (word).slice(1);
}

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,47 @@
const fs = require('fs');
// Get the file name from the command line arguments
const fileName = process.argv[2];
// Check if a file name is provided
if (!fileName) {
console.error('Please provide a file name.');
process.exit(1);
}
let FileContiner = `
import React from 'react'
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
import Form${capitalizeFirstLetter(fileName)} from './Form${capitalizeFirstLetter(fileName)}'
import { getInitialValues, getValidationSchema } from './formUtil'
import { usePageState } from '../../lib/state mangment/LayoutPagestate'
function Edit${capitalizeFirstLetter(fileName)}Modal() {
const {objectToEdit} = usePageState()
return (
<LayoutModal
isAddModal={false}
getInitialValues={getInitialValues(objectToEdit)}
handleSubmit={() => { }}
headerText='Edit Modal'
getValidationSchema={getValidationSchema(objectToEdit)}>
<Form${capitalizeFirstLetter(fileName)} />
</LayoutModal>
)
}
export default Edit${capitalizeFirstLetter(fileName)}Modal
`
fs.writeFileSync('src/Pages/'+fileName+"/"+"Edit"+ capitalizeFirstLetter(fileName)+"Modal.tsx",
FileContiner
);
console.log(`File "${fileName}" generated successfully.`);
function capitalizeFirstLetter(word) {
return (word).charAt(0).toUpperCase() + (word).slice(1);
}

View File

@ -0,0 +1,66 @@
const fs = require('fs');
// Get the file name from the command line arguments
const fileName = process.argv[2];
// Check if a file name is provided
if (!fileName) {
console.error('Please provide a file name.');
process.exit(1);
}
let FileContiner = `
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../Components/Karimalden/KarimField';
import { FakeSelectData } from '../../Layout/app/Const';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
function Form${capitalizeFirstLetter(fileName)}() {
const formik = useFormikContext<any>();
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
// name from form utils
<KarimField name="name" type="text"label='name' placeholder='placeholder' />
<KarimField name="number" type="number" label='number' placeholder='placeholder' />
<KarimField name="select" type="select"label='select' option={FakeSelectData} isMulti={true} placeholder='placeholder' />
<KarimField name="Multiselect" type="select"label='Multiselect' option={FakeSelectData} Disabled={true} placeholder='placeholder'/>
</Col>
<Col>
<KarimField name="date" type="date" label='date' placeholder='placeholder' />
<KarimField name="time" type="text"label='time' placeholder='placeholder' />
<KarimField name="CheckBox" name2='CheckBox2' type="checkbox" label='CheckBox' placeholder='placeholder' group={true} />
<KarimField name="DateFrom" name2="DateTo" type="DataRange" />
</Col>
</Row>
)
}
export default Form${capitalizeFirstLetter(fileName)}
`
fs.writeFileSync('src/Pages/'+fileName+'/Form'+ capitalizeFirstLetter(fileName)+".tsx",
FileContiner
);
console.log(`File "${fileName}" generated successfully.`);
function capitalizeFirstLetter(word) {
return (word).charAt(0).toUpperCase() + (word).slice(1);
}

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,16 @@
import { useField, useFormikContext } from 'formik';
import { useTranslation } from 'react-i18next';
import { Field } from 'formik';
const useFormField = (name: string, props?: any) => {
const [field, meta] = useField({ name, ...props });
const { t } = useTranslation();
const formik = useFormikContext<any>();
const isError = meta.touched && meta.error;
const errorMsg = meta.error ? t(meta.error.toString()) : '';
return { Field, field, meta, formik, isError, errorMsg, t };
};
export default useFormField;

View File

@ -0,0 +1,9 @@
import React from 'react'
const useImageError = ({currentTarget}:any) => {
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
currentTarget.onerror = null;
currentTarget.src=`${ErrorImage}`;
}
export default useImageError

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,18 @@
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess:any) {
const navigate = useNavigate()
useEffect(()=>{
if(isSuccess){
callbackAfterSuccess()
navigate(to_path , {replace:true})
}
},[isSuccess])
}
export default useNavigateOnSuccess

View File

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

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,41 @@
import React from 'react'
import './Add_Button.scss'
import { useTranslation } from 'react-i18next'
import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
const AddButtonLayout = ({haveAddModal}:any) => {
const { setIsOpenAddModel , setObjectToEdit } = usePageState()
const [t] = useTranslation();
return (
<div className='Add_Button' onClick={()=>{
if(haveAddModal){
setIsOpenAddModel()
}
setObjectToEdit(null)
}}>
<button>
<span >
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={24}
height={24}
>
<path fill="none" d="M0 0h24v24H0z" />
<path fill="currentColor" d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
</svg>{" "}
{t("Add")}
</span>
</button>
</div>
)
}
export default AddButtonLayout

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,42 @@
import { Formik, Form } from 'formik';
import React, { ReactNode } from 'react';
import { Button } from "reactstrap";
import * as Yup from 'yup';
interface FormValues {
[key: string]: any;
}
interface FormPageProps {
handleSubmit: (values: any) => void
initialValues: FormValues;
validationSchema: any;
title?: string;
children: ReactNode;
}
const FormPage: React.FC<FormPageProps> = ({ children, handleSubmit, initialValues, validationSchema, title = "Add New Item" }) => {
return (
<>
<h4 className='text-bold'>{title}</h4>
<div className="Card">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{formik => (
<Form>
{children}
<div className='w-100 d-flex justify-content-center'>
<Button type='submit' color="primary" className='mt-4 w-25 text-center'>Submit</Button>
</div>
</Form>
)}
</Formik>
</div>
</>
);
};
export default FormPage;

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';
interface LayoutModalProps {
isAddModal: boolean;
headerText: string;
handleSubmit: (values: any) => void;
getInitialValues: any;
getValidationSchema: any;
children: React.ReactNode;
status?:QueryStatusEnum
}
function LayoutModal({isAddModal , headerText , handleSubmit =()=>{} , getInitialValues , getValidationSchema,status ,children}:LayoutModalProps) {
const {isOpenAddModel ,setIsOpenAddModel , setIsOpenEditModel ,isOpenEditModel , objectToEdit , CloseAllModal} = usePageState(state => state)
useEffect(()=>{
if(status === QueryStatusEnum.SUCCESS){
CloseAllModal()
}
},[status , CloseAllModal])
const [t] = useTranslation()
return (
<Modal centered isOpen={isAddModal ? isOpenAddModel :isOpenEditModel} size="lg" >
<ModalHeader className='ModalHeader' toggle={() => isAddModal ?setIsOpenAddModel() : setIsOpenEditModel()} >
{t(headerText)}
</ModalHeader>
{
(( objectToEdit != null && isOpenEditModel) || isOpenAddModel) &&
<Formik
onSubmit={handleSubmit}
initialValues={getInitialValues}
validationSchema={getValidationSchema}
>
{(formik) => (
<Form>
<ModalBody>
{children}
</ModalBody>
<ModalFooter>
<Button
disabled={status === QueryStatusEnum.LOADING}
onClick={() => isAddModal ?setIsOpenAddModel() : setIsOpenEditModel()}
color="danger"
>
{t("cancel")}
</Button>
<LoadingButton
type="submit"
color="primary"
isLoading={status === QueryStatusEnum.LOADING}
>
{t(isAddModal ? "Add" :"edit")}
</LoadingButton>
</ModalFooter>
</Form>
)}
</Formik>
}
</Modal>
)
}
export default LayoutModal

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