React-placeholder
|
@ -25,6 +25,7 @@
|
|||
"react-router-dom": "^6.26.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-toastify": "^10.0.5",
|
||||
"sweetalert2": "^11.12.4",
|
||||
"web-vitals": "^2.1.4"
|
||||
}
|
||||
},
|
||||
|
@ -18652,6 +18653,16 @@
|
|||
"boolbase": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sweetalert2": {
|
||||
"version": "11.12.4",
|
||||
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz",
|
||||
"integrity": "sha512-ZSpyaLbAmn4b7xjnV9x9BFD1UOrCAhIzm1D8dZ443kGxtVKqbTIA5SgXs4xeEtmFfEXUyC3RBgpSlu1AXmCiHA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/limonte"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hbaseapp",
|
||||
"version": "0.1.0",
|
||||
"name": "react-placeholder",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.0",
|
||||
|
@ -20,6 +20,7 @@
|
|||
"react-router-dom": "^6.26.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-toastify": "^10.0.5",
|
||||
"sweetalert2": "^11.12.4",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
Before Width: | Height: | Size: 3.8 KiB |
|
@ -7,37 +7,16 @@
|
|||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
content=""
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Humbingo React Placeholder</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 9.4 KiB |
38
src/App.css
|
@ -1,38 +0,0 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
23
src/App.js
|
@ -1,25 +1,28 @@
|
|||
import React from 'react';
|
||||
import LoginPage from './pages/UI/login/login';
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||
import { AuthProvider } from './auth/AuthContext';
|
||||
import { Auth } from './auth/auth';
|
||||
import Footer from './components/footer'; // Import the Footer component
|
||||
import Home from './pages/UI/home/home';
|
||||
|
||||
import { AuthProvider } from './utils/auth/AuthContext';
|
||||
import { Auth } from './utils/auth/auth';
|
||||
import LoginPage from './components/pages/Login';
|
||||
import Home from './components/pages/Home';
|
||||
import APIManagerStarter from './components/pages/APIManagerStarter';
|
||||
import Footer from './components/common/Footer';
|
||||
|
||||
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<div className='App' style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<Auth component={<Home />} />} exact />
|
||||
|
||||
<Route path="/login" element={<Auth component={<LoginPage />} />} exact />
|
||||
<Route path="/login" element={<Auth component={<LoginPage/>} />} exact />
|
||||
<Route path="/api-manager-starter" element={<Auth component={<APIManagerStarter />} />} exact />
|
||||
</Routes>
|
||||
<Footer />
|
||||
<Footer/>
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
# 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
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
|
@ -1,416 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import Swal from "sweetalert2";
|
||||
import "./style.css";
|
||||
import useAPIManager from "./useAPIManager";
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Fade,
|
||||
Card,
|
||||
CardContent,
|
||||
} from "@mui/material";
|
||||
import FormWithDrawer from "./FormWithDrawer";
|
||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||
import Pagination from "./Pagination";
|
||||
|
||||
function APIManager(props) {
|
||||
const dataSchema = props.data;
|
||||
const { Get, Delete, Patch, getAPI, getHost } = useAPIManager(
|
||||
props.globalConfig,
|
||||
props.token
|
||||
);
|
||||
const dataApi = getAPI(props.data.api);
|
||||
const [apiData, setApiData] = useState([]);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [orderBy, setOrderBy] = useState(""); // Column to sort
|
||||
const [order, setOrder] = useState("asc");
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(50);
|
||||
const [sortedColumn, setSortedColumn] = useState(null);
|
||||
const [filteredData, setFilteredData] = useState([]);
|
||||
const [anchorEls, setAnchorEls] = useState({}); // Updated state for menu anchors
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [nextPage, setNextPage] = useState(null);
|
||||
const [previousPage, setPreviousPage] = useState(null);
|
||||
const [totalCount, setTotalCount] = useState(0);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const updateFormTitle = props.updateFormTitle ? props.updateFormTitle : "";
|
||||
const createFormTitle = props.createFormTitle ? props.createFormTitle : "";
|
||||
const actionBtnName = props.actionBtnName
|
||||
? props.actionBtnName
|
||||
: "Create New Data";
|
||||
const createRequired = props.createRequired === false ? false : true;
|
||||
const editRequired = props.editRequired === false ? false : true;
|
||||
const searchRequired = props.searchRequired === false ? false : true;
|
||||
|
||||
const createField = dataSchema.createField;
|
||||
const editField = dataSchema.editField;
|
||||
const showField = dataSchema.showField;
|
||||
const manageRecord = true;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const getData = async () => {
|
||||
makeApiRequest(dataApi);
|
||||
};
|
||||
|
||||
const makeApiRequest = async (api) => {
|
||||
try {
|
||||
setSearchQuery("");
|
||||
const result = await Get(api ? api : dataApi);
|
||||
setNextPage(result.next);
|
||||
setPreviousPage(result.previous);
|
||||
setTotalCount(result.count);
|
||||
setApiData(result.results);
|
||||
setFilteredData(result.results);
|
||||
} catch (err) {
|
||||
if (err.code == "ERR_BAD_REQUEST") {
|
||||
toast.error("Unauthorised access to page, Contact admin for access.");
|
||||
navigate("/");
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const pageChangeRequest = (url) => {
|
||||
if (getPageUrl(url) != null) {
|
||||
makeApiRequest(getHost() + getPageUrl(url));
|
||||
}
|
||||
};
|
||||
|
||||
async function softDelete(data) {
|
||||
try {
|
||||
await Patch(dataApi, data["id"], { is_deleted: true })
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
|
||||
async function restore(data) {
|
||||
try {
|
||||
await Patch(dataApi, data["id"], { is_deleted: false })
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
|
||||
async function forceDelete(data) {
|
||||
await Swal.fire({
|
||||
title: "Data will be removed permanently! Are you sure?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
await Delete(dataApi, data["id"])
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getValueFromUrl = (url, parameterName) => {
|
||||
try {
|
||||
const regex = new RegExp(`${parameterName}=([^&]+)`);
|
||||
const match = url.match(regex);
|
||||
return match ? match[1] : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
function getPageUrl(url) {
|
||||
let urls = "";
|
||||
try {
|
||||
const urlObject = new URL(url);
|
||||
urls = urlObject.pathname + urlObject.search;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
return () => {
|
||||
setMounted(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading && mounted) {
|
||||
getData();
|
||||
}
|
||||
}, [loading, mounted]);
|
||||
|
||||
const handleSearchInputChange = (event) => {
|
||||
const query = event.target.value;
|
||||
makeApiRequest(dataApi + "?search=" + query);
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
const handleSort = (columnKey) => {
|
||||
const isAsc = orderBy === columnKey && order === "asc";
|
||||
setOrderBy(columnKey);
|
||||
setOrder(isAsc ? "desc" : "asc");
|
||||
setSortedColumn(columnKey);
|
||||
};
|
||||
|
||||
const sortedData = stableSort(filteredData, getComparator(order, orderBy));
|
||||
const visibleRows = sortedData.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
);
|
||||
|
||||
function stableSort(array, comparator) {
|
||||
const stabilizedThis = array.map((el, index) => [el, index]);
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0]);
|
||||
if (order !== 0) return order;
|
||||
return a[1] - b[1];
|
||||
});
|
||||
return stabilizedThis.map((el) => el[0]);
|
||||
}
|
||||
|
||||
function getComparator(order, orderBy) {
|
||||
return order === "desc"
|
||||
? (a, b) => descendingComparator(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||
}
|
||||
|
||||
function descendingComparator(a, b, orderBy) {
|
||||
if (b[orderBy] < a[orderBy]) {
|
||||
return -1;
|
||||
}
|
||||
if (b[orderBy] > a[orderBy]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const handleMenuClick = (event, rowIndex) => {
|
||||
setAnchorEls((prev) => ({
|
||||
...prev,
|
||||
[rowIndex]: event.currentTarget,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleMenuClose = (rowIndex) => {
|
||||
setAnchorEls((prev) => ({
|
||||
...prev,
|
||||
[rowIndex]: null,
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<center>
|
||||
<img
|
||||
src="./assets/img/loader.gif"
|
||||
height="30px"
|
||||
width="30px"
|
||||
alt="Loading"
|
||||
/>
|
||||
</center>
|
||||
) : (
|
||||
<div className="row">
|
||||
<Card
|
||||
className="col-md-12"
|
||||
style={{ marginBottom: "10px", backgroundColor: "#A9A9A9" }}
|
||||
>
|
||||
<CardContent>
|
||||
{createRequired && (
|
||||
<FormWithDrawer
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
actionBtnName={actionBtnName}
|
||||
inputFields={createField}
|
||||
api={dataApi}
|
||||
createFormTitle={createFormTitle}
|
||||
refreshData={() => getData()}
|
||||
buttonStyle={{ margin: "10px", backgroundColor: "#17202A" }}
|
||||
/>
|
||||
)}
|
||||
{searchRequired && (
|
||||
<div className="search-table">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={handleSearchInputChange}
|
||||
placeholder="Search..."
|
||||
className="search-input-table"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<TableContainer component={Paper}>
|
||||
<Table aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow style={{ backgroundColor: "#17202A" }}>
|
||||
{showField.map((th, index) => {
|
||||
const isSorted = orderBy === th.key;
|
||||
const isAsc = orderBy === th.key && order === "asc";
|
||||
return (
|
||||
<TableCell
|
||||
key={index}
|
||||
style={{ color: "#FFF" }}
|
||||
onClick={() => handleSort(th.key)}
|
||||
>
|
||||
{th.label} {isSorted && (isAsc ? "▲" : "▼")}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
{manageRecord && (
|
||||
<TableCell style={{ textAlign: "center", color: "#FFF" }}>
|
||||
Action
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{visibleRows.map((data, rowIndex) => (
|
||||
<TableRow
|
||||
key={rowIndex}
|
||||
style={
|
||||
data.is_disabled
|
||||
? {
|
||||
backgroundColor: "#e0dfff",
|
||||
borderBottom: "2px dashed orange",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{showField.map((field, i) => (
|
||||
<TableCell key={i}>{data[field.key]}</TableCell>
|
||||
))}
|
||||
{manageRecord && (
|
||||
<TableCell>
|
||||
{editRequired && (
|
||||
<FormWithDrawer
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
actionBtnName="Edit"
|
||||
inputFields={editField}
|
||||
data={data}
|
||||
api={dataApi}
|
||||
onClick={() => handleMenuClose(rowIndex)}
|
||||
refreshData={() => getData()}
|
||||
buttonStyle={{ fontSize: "12px", padding: "5px" }}
|
||||
buttonVarient="outlined"
|
||||
updateFormTitle={updateFormTitle}
|
||||
submitBtnTitle="Update Data"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
id={`fade-button-${rowIndex}`}
|
||||
aria-controls={
|
||||
anchorEls[rowIndex]
|
||||
? `fade-menu-${rowIndex}`
|
||||
: undefined
|
||||
}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={
|
||||
anchorEls[rowIndex] ? "true" : undefined
|
||||
}
|
||||
onClick={(event) => handleMenuClick(event, rowIndex)}
|
||||
style={{ padding: "5px" }}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</Button>
|
||||
<Menu
|
||||
id={`fade-menu-${rowIndex}`}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": `fade-button-${rowIndex}`,
|
||||
}}
|
||||
anchorEl={anchorEls[rowIndex]}
|
||||
open={Boolean(anchorEls[rowIndex])}
|
||||
onClose={() => handleMenuClose(rowIndex)}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
{data.is_deleted ? (
|
||||
<MenuItem onClick={() => restore(data)}>
|
||||
Show Record
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={() => softDelete(data)}>
|
||||
Hide Record
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={() => forceDelete(data)}>
|
||||
Remove Record
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Pagination
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
makeApiRequest={pageChangeRequest}
|
||||
getValueFromUrl={getValueFromUrl}
|
||||
dataApi={dataApi}
|
||||
totalCount={totalCount}
|
||||
rowsPerPage={rowsPerPage}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
/>
|
||||
</TableContainer>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default APIManager;
|
|
@ -1,109 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import useApi from "../useApi";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
const AutoCompleteField = (props) => {
|
||||
const [api, setApi] = useState(props.api);
|
||||
const searchOptionHook = props.searchOptionHook;
|
||||
const fieldLabel = props.label;
|
||||
const fieldName = props.name;
|
||||
const optionKey = props.optionKey ? props.optionKey : "id";
|
||||
const [searchData, setSearchData] = useState([]);
|
||||
const [storageData, setStorageData] = useState(
|
||||
JSON.parse(localStorage.getItem("dynamicField"))
|
||||
);
|
||||
const [value, setValue] = useState("");
|
||||
const { Get } = useApi();
|
||||
const [tempValue, setTempValue] = useState(
|
||||
storageData
|
||||
? storageData[storageData.findIndex((item) => item.name === fieldName)]
|
||||
: ""
|
||||
);
|
||||
|
||||
let newApi = async () => {
|
||||
let tempApi = api;
|
||||
if (props.hook != undefined) {
|
||||
tempApi = `${tempApi}${
|
||||
storageData[storageData.findIndex((item) => item.name === props.hook)]
|
||||
.key
|
||||
}/`;
|
||||
}
|
||||
return tempApi;
|
||||
};
|
||||
|
||||
let getdata = async () => {
|
||||
try{
|
||||
const tempApi = await newApi();
|
||||
let response = await Get(tempApi);
|
||||
let _searchData = [];
|
||||
await response.data.map((data) => {
|
||||
_searchData.push({
|
||||
label: data[searchOptionHook],
|
||||
key: data[optionKey],
|
||||
});
|
||||
});
|
||||
setSearchData(_searchData);
|
||||
}catch{
|
||||
toast.error("Something went wrong contact humbingo.")
|
||||
}
|
||||
// await axios
|
||||
// .get(tempApi)
|
||||
// .then(async (response) => {
|
||||
// let _searchData = [];
|
||||
// await response.data.map((data) => {
|
||||
// _searchData.push({
|
||||
// label: data[searchOptionHook],
|
||||
// key: data[optionKey],
|
||||
// });
|
||||
// });
|
||||
|
||||
// setSearchData(_searchData);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// });
|
||||
};
|
||||
useEffect(() => {
|
||||
getdata();
|
||||
}, []);
|
||||
|
||||
let getDataFromJson = (e) => {
|
||||
const acccess = e.target.value;
|
||||
let respData = searchData.find((f) => f.label === acccess);
|
||||
|
||||
respData ? setValue(respData.key) : console.log("this is undefined");
|
||||
if (props.onBlur != undefined) {
|
||||
if (respData != undefined) {
|
||||
props.onBlur(respData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return searchData.length > 0 ? (
|
||||
<>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
id="combo-box-demo"
|
||||
style={props.style}
|
||||
options={searchData}
|
||||
onBlur={(e) => getDataFromJson(e)}
|
||||
value={tempValue}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={fieldLabel} variant="standard" />
|
||||
)}
|
||||
/>
|
||||
{/* onSelect={(e) => getDataFromJson(e)} */}
|
||||
</div>
|
||||
</div>
|
||||
<input value={value} name={fieldName} hidden readOnly />
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoCompleteField;
|
|
@ -1,416 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import Swal from "sweetalert2";
|
||||
import "./style.css";
|
||||
import useAPIManager from "./useAPIManager";
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Fade,
|
||||
Card,
|
||||
CardContent,
|
||||
} from "@mui/material";
|
||||
import FormWithDrawer from "./FormWithDrawer";
|
||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||
import Pagination from "./Pagination";
|
||||
|
||||
function CrudManager(props) {
|
||||
const dataSchema = props.data;
|
||||
const { Get, Delete, Patch, getAPI, getHost } = useAPIManager(
|
||||
props.globalConfig,
|
||||
props.token
|
||||
);
|
||||
const dataApi = getAPI(props.data.api);
|
||||
const [apiData, setApiData] = useState([]);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [orderBy, setOrderBy] = useState(""); // Column to sort
|
||||
const [order, setOrder] = useState("asc");
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(50);
|
||||
const [sortedColumn, setSortedColumn] = useState(null);
|
||||
const [filteredData, setFilteredData] = useState([]);
|
||||
const [anchorEls, setAnchorEls] = useState({}); // Updated state for menu anchors
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [nextPage, setNextPage] = useState(null);
|
||||
const [previousPage, setPreviousPage] = useState(null);
|
||||
const [totalCount, setTotalCount] = useState(0);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const updateFormTitle = props.updateFormTitle ? props.updateFormTitle : "";
|
||||
const createFormTitle = props.createFormTitle ? props.createFormTitle : "";
|
||||
const actionBtnName = props.actionBtnName
|
||||
? props.actionBtnName
|
||||
: "Create New Data";
|
||||
const createRequired = props.createRequired === false ? false : true;
|
||||
const editRequired = props.editRequired === false ? false : true;
|
||||
const searchRequired = props.searchRequired === false ? false : true;
|
||||
|
||||
const createField = dataSchema.createField;
|
||||
const editField = dataSchema.editField;
|
||||
const showField = dataSchema.showField;
|
||||
const manageRecord = true;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const getData = async () => {
|
||||
makeApiRequest(dataApi);
|
||||
};
|
||||
|
||||
const makeApiRequest = async (api) => {
|
||||
try {
|
||||
setSearchQuery("");
|
||||
const result = await Get(api ? api : dataApi);
|
||||
setNextPage(result.next);
|
||||
setPreviousPage(result.previous);
|
||||
setTotalCount(result.count);
|
||||
setApiData(result.results);
|
||||
setFilteredData(result.results);
|
||||
} catch (err) {
|
||||
if (err.code == "ERR_BAD_REQUEST") {
|
||||
toast.error("Unauthorised access to page, Contact admin for access.");
|
||||
navigate("/");
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const pageChangeRequest = (url) => {
|
||||
if (getPageUrl(url) != null) {
|
||||
makeApiRequest(getHost() + getPageUrl(url));
|
||||
}
|
||||
};
|
||||
|
||||
async function softDelete(data) {
|
||||
try {
|
||||
await Patch(dataApi, data["id"], { is_deleted: true })
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
|
||||
async function restore(data) {
|
||||
try {
|
||||
await Patch(dataApi, data["id"], { is_deleted: false })
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
|
||||
async function forceDelete(data) {
|
||||
await Swal.fire({
|
||||
title: "Data will be removed permanently! Are you sure?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
await Delete(dataApi, data["id"])
|
||||
.then(() => {
|
||||
toast.success("Record updated successfully!");
|
||||
getData();
|
||||
})
|
||||
.catch((err) => {
|
||||
getData();
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
} catch {
|
||||
getData();
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getValueFromUrl = (url, parameterName) => {
|
||||
try {
|
||||
const regex = new RegExp(`${parameterName}=([^&]+)`);
|
||||
const match = url.match(regex);
|
||||
return match ? match[1] : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
function getPageUrl(url) {
|
||||
let urls = "";
|
||||
try {
|
||||
const urlObject = new URL(url);
|
||||
urls = urlObject.pathname + urlObject.search;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
return () => {
|
||||
setMounted(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading && mounted) {
|
||||
getData();
|
||||
}
|
||||
}, [loading, mounted]);
|
||||
|
||||
const handleSearchInputChange = (event) => {
|
||||
const query = event.target.value;
|
||||
makeApiRequest(dataApi + "?search=" + query);
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
const handleSort = (columnKey) => {
|
||||
const isAsc = orderBy === columnKey && order === "asc";
|
||||
setOrderBy(columnKey);
|
||||
setOrder(isAsc ? "desc" : "asc");
|
||||
setSortedColumn(columnKey);
|
||||
};
|
||||
|
||||
const sortedData = stableSort(filteredData, getComparator(order, orderBy));
|
||||
const visibleRows = sortedData.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
);
|
||||
|
||||
function stableSort(array, comparator) {
|
||||
const stabilizedThis = array.map((el, index) => [el, index]);
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0]);
|
||||
if (order !== 0) return order;
|
||||
return a[1] - b[1];
|
||||
});
|
||||
return stabilizedThis.map((el) => el[0]);
|
||||
}
|
||||
|
||||
function getComparator(order, orderBy) {
|
||||
return order === "desc"
|
||||
? (a, b) => descendingComparator(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||
}
|
||||
|
||||
function descendingComparator(a, b, orderBy) {
|
||||
if (b[orderBy] < a[orderBy]) {
|
||||
return -1;
|
||||
}
|
||||
if (b[orderBy] > a[orderBy]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const handleMenuClick = (event, rowIndex) => {
|
||||
setAnchorEls((prev) => ({
|
||||
...prev,
|
||||
[rowIndex]: event.currentTarget,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleMenuClose = (rowIndex) => {
|
||||
setAnchorEls((prev) => ({
|
||||
...prev,
|
||||
[rowIndex]: null,
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<center>
|
||||
<img
|
||||
src="./assets/img/loader.gif"
|
||||
height="30px"
|
||||
width="30px"
|
||||
alt="Loading"
|
||||
/>
|
||||
</center>
|
||||
) : (
|
||||
<div className="row">
|
||||
<Card
|
||||
className="col-md-12"
|
||||
style={{ marginBottom: "10px", backgroundColor: "#A9A9A9" }}
|
||||
>
|
||||
<CardContent>
|
||||
{createRequired && (
|
||||
<FormWithDrawer
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
actionBtnName={actionBtnName}
|
||||
inputFields={createField}
|
||||
api={dataApi}
|
||||
createFormTitle={createFormTitle}
|
||||
refreshData={() => getData()}
|
||||
buttonStyle={{ margin: "10px", backgroundColor: "#17202A" }}
|
||||
/>
|
||||
)}
|
||||
{searchRequired && (
|
||||
<div className="search-table">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={handleSearchInputChange}
|
||||
placeholder="Search..."
|
||||
className="search-input-table"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<TableContainer component={Paper}>
|
||||
<Table aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow style={{ backgroundColor: "#17202A" }}>
|
||||
{showField.map((th, index) => {
|
||||
const isSorted = orderBy === th.key;
|
||||
const isAsc = orderBy === th.key && order === "asc";
|
||||
return (
|
||||
<TableCell
|
||||
key={index}
|
||||
style={{ color: "#FFF" }}
|
||||
onClick={() => handleSort(th.key)}
|
||||
>
|
||||
{th.label} {isSorted && (isAsc ? "▲" : "▼")}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
{manageRecord && (
|
||||
<TableCell style={{ textAlign: "center", color: "#FFF" }}>
|
||||
Action
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{visibleRows.map((data, rowIndex) => (
|
||||
<TableRow
|
||||
key={rowIndex}
|
||||
style={
|
||||
data.is_disabled
|
||||
? {
|
||||
backgroundColor: "#e0dfff",
|
||||
borderBottom: "2px dashed orange",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{showField.map((field, i) => (
|
||||
<TableCell key={i}>{data[field.key]}</TableCell>
|
||||
))}
|
||||
{manageRecord && (
|
||||
<TableCell>
|
||||
{editRequired && (
|
||||
<FormWithDrawer
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
actionBtnName="Edit"
|
||||
inputFields={editField}
|
||||
data={data}
|
||||
api={dataApi}
|
||||
onClick={() => handleMenuClose(rowIndex)}
|
||||
refreshData={() => getData()}
|
||||
buttonStyle={{ fontSize: "12px", padding: "5px" }}
|
||||
buttonVarient="outlined"
|
||||
updateFormTitle={updateFormTitle}
|
||||
submitBtnTitle="Update Data"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
id={`fade-button-${rowIndex}`}
|
||||
aria-controls={
|
||||
anchorEls[rowIndex]
|
||||
? `fade-menu-${rowIndex}`
|
||||
: undefined
|
||||
}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={
|
||||
anchorEls[rowIndex] ? "true" : undefined
|
||||
}
|
||||
onClick={(event) => handleMenuClick(event, rowIndex)}
|
||||
style={{ padding: "5px" }}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</Button>
|
||||
<Menu
|
||||
id={`fade-menu-${rowIndex}`}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": `fade-button-${rowIndex}`,
|
||||
}}
|
||||
anchorEl={anchorEls[rowIndex]}
|
||||
open={Boolean(anchorEls[rowIndex])}
|
||||
onClose={() => handleMenuClose(rowIndex)}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
{data.is_deleted ? (
|
||||
<MenuItem onClick={() => restore(data)}>
|
||||
Show Record
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={() => softDelete(data)}>
|
||||
Hide Record
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={() => forceDelete(data)}>
|
||||
Remove Record
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Pagination
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
makeApiRequest={pageChangeRequest}
|
||||
getValueFromUrl={getValueFromUrl}
|
||||
dataApi={dataApi}
|
||||
totalCount={totalCount}
|
||||
rowsPerPage={rowsPerPage}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
/>
|
||||
</TableContainer>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CrudManager;
|
|
@ -1,87 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
const CustomAutocomplete = ({ apiUrl, displayKey, valueKey, onValueChange, inputName, variant , label, sameAsSeen}) => {
|
||||
|
||||
const [options, setOptions] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [selectedOption, setSelectedOption] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
axios.get(apiUrl)
|
||||
.then(response => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [apiUrl]);
|
||||
|
||||
const handleInputChange = (event, newValue) => {
|
||||
setInputValue(newValue);
|
||||
};
|
||||
|
||||
const handleOptionSelect = (event, newValue) => {
|
||||
// console.log("Selected value:");
|
||||
// console.log(newValue);
|
||||
setSelectedOption(newValue);
|
||||
setInputValue(newValue ? newValue[displayKey] : '');
|
||||
onValueChange(newValue, inputName); // Pass the name attribute value along with the value
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
|
||||
inputValue={inputValue}
|
||||
onInputChange={handleInputChange}
|
||||
options={options}
|
||||
loading={loading}
|
||||
onChange={handleOptionSelect}
|
||||
getOptionLabel={(option) => option ? option[displayKey] : ''}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={label}
|
||||
variant={variant}
|
||||
InputProps={{
|
||||
...params.InputProps,
|
||||
endAdornment: (
|
||||
<>
|
||||
{loading ? <CircularProgress color="secondary" size={20} /> : null}
|
||||
{params.InputProps.endAdornment}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
/>
|
||||
<input hidden name={inputName} value={selectedOption ? selectedOption[valueKey] : ''} style={{display: 'none'}}/>
|
||||
{(sameAsSeen)?<input hidden name={"_"+inputName} value={selectedOption ? selectedOption[displayKey] : ''} style={{display: 'none'}}/>:""}
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
CustomAutocomplete.propTypes = {
|
||||
apiUrl: PropTypes.string.isRequired,
|
||||
displayKey: PropTypes.string.isRequired,
|
||||
valueKey: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
inputName: PropTypes.string.isRequired, // Accept inputName prop
|
||||
};
|
||||
|
||||
export default CustomAutocomplete;
|
|
@ -1,112 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import CustomAutocomplete from './Autocomplete';
|
||||
import _Autocomplete from './_Autocomplete';
|
||||
const DynamicAutocomplete = (props) => {
|
||||
// [
|
||||
// {
|
||||
// "apiUrl": 'https://api.udaymotors.in/api/v1/user/',
|
||||
// "displayKey": 'phone_no',
|
||||
// "valueKey": 'id',
|
||||
// "name": "user",
|
||||
// "label":"User"
|
||||
// },
|
||||
// {
|
||||
// "apiUrl": 'https://api.udaymotors.in/api/v1/getInvestmentsByUserId/',
|
||||
// "displayKey": 'investment_id',
|
||||
// "valueKey": 'id',
|
||||
// "name": "investment",
|
||||
// "parent": "user",
|
||||
// "parent_value_key": "id",
|
||||
// "label":"Investments"
|
||||
// },
|
||||
// {
|
||||
// "apiUrl": 'https://api.udaymotors.in/api/v1/transaction/',
|
||||
// "displayKey": 'description',
|
||||
// "name": "transaction",
|
||||
// "valueKey": 'id',
|
||||
// "parent": "investment",
|
||||
// "parent_value_key": "user_id",
|
||||
// "label":"Transactions"
|
||||
// },
|
||||
// ]
|
||||
const [selectedValues, setSelectedValues] = useState([]);
|
||||
const [currentFieldIndex, setCurrentFieldIndex] = useState(0);
|
||||
const [fields, setFields] = useState(props.fields);
|
||||
|
||||
const handleValueChange = (value, name) => {
|
||||
const updatedValues = [...selectedValues];
|
||||
const existingIndex = updatedValues.findIndex(item => item.name === name);
|
||||
if (existingIndex !== -1) {
|
||||
updatedValues[existingIndex] = { value, name };
|
||||
} else {
|
||||
updatedValues.push({ value, name });
|
||||
}
|
||||
|
||||
setSelectedValues(updatedValues);
|
||||
|
||||
// Update the API URL of the next field
|
||||
const nextFieldIndex = currentFieldIndex + 1;
|
||||
if (nextFieldIndex < fields.length) {
|
||||
const nextField = fields[nextFieldIndex];
|
||||
const parentValue = updatedValues.find(item => item.name === nextField.parent)?.value[nextField.parent_value_key];
|
||||
if (parentValue) {
|
||||
const updatedFields = [...fields];
|
||||
updatedFields[nextFieldIndex] = {
|
||||
...nextField,
|
||||
apiUrl: `${nextField.apiUrl}${parentValue}/`
|
||||
};
|
||||
setFields(updatedFields);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentFieldIndex(nextFieldIndex);
|
||||
};
|
||||
|
||||
const handleParentValueChange = (parentName, parentValue) => {
|
||||
// Find the index of the parent field
|
||||
const parentFieldIndex = fields.findIndex(field => field.name === parentName);
|
||||
|
||||
// Reset the selected values and fields configurations for the subsequent fields
|
||||
setSelectedValues(selectedValues.slice(0, parentFieldIndex + 1));
|
||||
setFields(fields.slice(0, parentFieldIndex + 1));
|
||||
setCurrentFieldIndex(parentFieldIndex);
|
||||
};
|
||||
|
||||
return (
|
||||
fields.slice(0, currentFieldIndex + 1).map((field, index) => (
|
||||
<div key={index} style={{marginTop: "10px", marginBottom: "10px"}}>
|
||||
{
|
||||
(field.advance)?
|
||||
<_Autocomplete
|
||||
apiUrl={field.apiUrl}
|
||||
displayKey={field.displayKey}
|
||||
valueKey={field.valueKey}
|
||||
inputName={field.name}
|
||||
onValueChange={handleValueChange}
|
||||
onParentValueChange={handleParentValueChange}
|
||||
variant={field.variant}
|
||||
label={field.label}
|
||||
sameAsSeen={field.sameAsSeen}
|
||||
|
||||
defaultObj={(field.defaultObj)?field.defaultObj:""}
|
||||
/>
|
||||
|
||||
:<CustomAutocomplete
|
||||
apiUrl={field.apiUrl}
|
||||
displayKey={field.displayKey}
|
||||
valueKey={field.valueKey}
|
||||
inputName={field.name}
|
||||
onValueChange={handleValueChange}
|
||||
onParentValueChange={handleParentValueChange}
|
||||
variant={field.variant}
|
||||
label={field.label}
|
||||
sameAsSeen={field.sameAsSeen}
|
||||
/>
|
||||
}
|
||||
|
||||
</div>
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
export default DynamicAutocomplete;
|
|
@ -1,109 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import useAPIManager from '../useAPIManager';
|
||||
|
||||
|
||||
const _Autocomplete = (props) => {
|
||||
const {
|
||||
apiUrl,
|
||||
displayKey,
|
||||
valueKey,
|
||||
onValueChange,
|
||||
inputName,
|
||||
variant,
|
||||
label,
|
||||
sameAsSeen,
|
||||
defaultObj,
|
||||
onBlur,
|
||||
onFocus,
|
||||
options: propOptions
|
||||
} = props;
|
||||
|
||||
|
||||
const [options, setOptions] = useState(propOptions || []);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [selectedOption, setSelectedOption] = useState(defaultObj);
|
||||
const {Get, Post} =useAPIManager(props.globalConfig, props.token);
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getSearchData(apiUrl)
|
||||
}, [apiUrl]);
|
||||
|
||||
const handleInputChange = (event, newValue) => {
|
||||
getSearchData(apiUrl+"?search="+newValue)
|
||||
setInputValue(newValue);
|
||||
};
|
||||
|
||||
const getSearchData = async (api) =>{
|
||||
await Get(api)
|
||||
.then(response => {
|
||||
(response.results)?setOptions(response.results):setOptions(response)
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
const handleOptionSelect = (event, newValue) => {
|
||||
setSelectedOption(newValue);
|
||||
setInputValue(newValue ? newValue[displayKey] : '');
|
||||
onValueChange(newValue, inputName); // Pass the name attribute value along with the value
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Autocomplete
|
||||
inputValue={inputValue}
|
||||
onInputChange={handleInputChange}
|
||||
options={options}
|
||||
loading={loading}
|
||||
onChange={handleOptionSelect}
|
||||
getOptionLabel={(option) => option ? option[displayKey] : ''}
|
||||
defaultValue= { defaultObj}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={label}
|
||||
variant={variant}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
InputProps={{
|
||||
...params.InputProps,
|
||||
endAdornment: (
|
||||
<>
|
||||
{loading ? <CircularProgress color="secondary" size={20} /> : null}
|
||||
{params.InputProps.endAdornment}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
/>
|
||||
{/* {console.log(selectedOption)} */}
|
||||
<input hidden name={inputName} value={selectedOption ? selectedOption[valueKey] : ''} style={{display: 'none'}}/>
|
||||
{(sameAsSeen)?<input hidden name={"_"+inputName} value={selectedOption ? selectedOption[displayKey] : ''} style={{display: 'none'}}/>:""}
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
_Autocomplete.propTypes = {
|
||||
apiUrl: PropTypes.string.isRequired,
|
||||
displayKey: PropTypes.string.isRequired,
|
||||
valueKey: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
inputName: PropTypes.string.isRequired, // Accept inputName prop
|
||||
};
|
||||
|
||||
export default _Autocomplete;
|
|
@ -1,84 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import Drawer from "@mui/material/Drawer";
|
||||
import { Button, TextField } from "@mui/material";
|
||||
import { firstUpperCase } from "./helper.js";
|
||||
function EditRow(props) {
|
||||
const editBlock = props.editBlock ? props.editBlock : [];
|
||||
const [state, setState] = React.useState({
|
||||
top: false,
|
||||
left: false,
|
||||
bottom: false,
|
||||
right: false,
|
||||
});
|
||||
const toggleDrawer = (anchor, open) => (event) => {
|
||||
if (
|
||||
event.type === "keydown" &&
|
||||
(event.key === "Tab" || event.key === "Shift")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setState({ ...state, [anchor]: open });
|
||||
};
|
||||
const [formFields, setFormFields] = useState(props.data);
|
||||
var fields = Object.keys(formFields);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
function doChange(fieldName, value) {
|
||||
setFormFields((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: value,
|
||||
}));
|
||||
localStorage.setItem(props.data.id + "_" + fieldName, value);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* {formFields} */}
|
||||
{["right"].map((anchor) => (
|
||||
<React.Fragment key={anchor}>
|
||||
<Button onClick={toggleDrawer(anchor, true)}>Edit</Button>
|
||||
<Drawer
|
||||
anchor={anchor}
|
||||
open={state[anchor]}
|
||||
onClose={toggleDrawer(anchor, false)}
|
||||
>
|
||||
<div className="row" style={{ padding: "10px", width: "500px" }}>
|
||||
<div className="col-md-12">
|
||||
<h4>Edit Details</h4>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<div key={index} className="dynamic-form-element">
|
||||
{!Object.values(editBlock).includes(field) ? (
|
||||
<>
|
||||
{" "}
|
||||
<TextField
|
||||
variant="standard"
|
||||
name={field}
|
||||
value=""
|
||||
onChange={(e) => doChange(field, e.target.value)}
|
||||
label={firstUpperCase(field)}
|
||||
/>
|
||||
<br />{" "}
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<Button variant="outlined" type="submit">
|
||||
Update
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default EditRow;
|
|
@ -1,17 +0,0 @@
|
|||
import React from 'react'
|
||||
import CrudManager from './CrudManager'
|
||||
import schema from './example.json'
|
||||
|
||||
const CrudManagerExample = () => {
|
||||
|
||||
return (
|
||||
|
||||
<>
|
||||
<CrudManager data={schema} />
|
||||
{/* // <CRUDManager schema={schema}/> */}
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CrudManagerExample
|
|
@ -1,148 +0,0 @@
|
|||
import React,{useState} from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import FormField, { validateField } from "./FormField";
|
||||
import "./style.css";
|
||||
import { toast } from "react-toastify";
|
||||
import useAPIManager from "./useAPIManager";
|
||||
|
||||
const Form = (props) => {
|
||||
const inputFields = props.inputFields;
|
||||
const fieldDefault = props.data ? props.data : [];
|
||||
const submitBtnTitle = props.submitBtnTitle ? props.submitBtnTitle : "Submit";
|
||||
const [externalErrors, setExternalErrors] = useState({});
|
||||
|
||||
const { Get, Delete, Patch, Post, getHost } = useAPIManager(
|
||||
props.globalConfig,
|
||||
props.token
|
||||
);
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
let isFormValid = true;
|
||||
const newExternalErrors = {};
|
||||
inputFields.forEach((field) => {
|
||||
const fieldName = field.name;
|
||||
const value = e.target.elements[fieldName].value;
|
||||
const validationError = validateField(value, field.type, field.label);
|
||||
|
||||
|
||||
if (field.required) { // Check if the field is required
|
||||
const validationError = validateField(value, field.type, field.label);
|
||||
if (validationError) {
|
||||
newExternalErrors[fieldName] = validationError;
|
||||
toast.error(validationError);
|
||||
isFormValid = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setExternalErrors(newExternalErrors);
|
||||
|
||||
if (!isFormValid) {
|
||||
return; // Don't submit if form is invalid
|
||||
}
|
||||
|
||||
var data = new FormData(e.target);
|
||||
let formData = Object.fromEntries(data.entries());
|
||||
// console.log(formData);
|
||||
|
||||
if (fieldDefault["id"]) {
|
||||
|
||||
try{
|
||||
await Patch(props.api,fieldDefault["id"], formData).then(()=>{
|
||||
props.closeDrawer();
|
||||
props.refreshData();
|
||||
toast.success("Record updated successfully!");
|
||||
})
|
||||
.catch((err) => {
|
||||
// console.error(err);
|
||||
toast.error(err || "Error");
|
||||
});
|
||||
}catch{
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
|
||||
// axios
|
||||
// .patch(props.api + `${fieldDefault["id"]}` + "/", formData)
|
||||
// .then((response) => {
|
||||
// props.closeDrawer();
|
||||
// props.refreshData();
|
||||
// toast.success("Record updated successfully!");
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// // console.error(err);
|
||||
// toast.error(err.response?.data?.error || "An error occurred");
|
||||
// });
|
||||
} else {
|
||||
|
||||
|
||||
try{
|
||||
await Post(props.api, formData).then(()=>{
|
||||
props.closeDrawer();
|
||||
props.refreshData();
|
||||
|
||||
toast.success("Record created successfully!");
|
||||
})
|
||||
.catch((err) => {
|
||||
// console.error(err);
|
||||
toast.error(err.response?.data?.error || "Error");
|
||||
});
|
||||
}catch{
|
||||
toast.error("Something Went wrong contact Humbingo");
|
||||
}
|
||||
// axios
|
||||
// .post(props.api, formData)
|
||||
// .then((response) => {
|
||||
// props.closeDrawer();
|
||||
// props.refreshData();
|
||||
|
||||
// toast.success("Record created successfully!");
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// // console.error(err);
|
||||
// toast.error(err.response?.data?.error || "An error occurred");
|
||||
// });
|
||||
}
|
||||
}
|
||||
return (
|
||||
<form onSubmit={handleSubmit} style={{ padding: "20px" }}>
|
||||
{inputFields.map((field, index) => {
|
||||
const fieldName = field["name"];
|
||||
if (props.grid) {
|
||||
return (
|
||||
<div className={props.grid}>
|
||||
<div className="dynamic-form-element" key={index}>
|
||||
<FormField
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
field={field}
|
||||
name={index}
|
||||
defaultValue={fieldDefault[fieldName]}
|
||||
handleChange={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="dynamic-form-element" key={index}>
|
||||
<FormField
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
field={field}
|
||||
name={index}
|
||||
defaultValue={fieldDefault[fieldName]}
|
||||
handleChange={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<br />
|
||||
<Button variant="contained" type="submit" size="small">
|
||||
{submitBtnTitle}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
export default Form;
|
|
@ -1,314 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { TextField, FormHelperText } from "@mui/material";
|
||||
import { firstUpperCase ,validateEmail,validatePassword} from "./helper.js";
|
||||
import "./style.css";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import CustomAutocomplete from "./DynamicForm/Autocomplete.js";
|
||||
import _Autocomplete from "./DynamicForm/_Autocomplete.js";
|
||||
|
||||
import useAPIManager from './useAPIManager.js';
|
||||
export const validateField = (value, type, label) => {
|
||||
let errorMessage = "";
|
||||
if (label) {
|
||||
switch (type) {
|
||||
case "text":
|
||||
if (!value) {
|
||||
errorMessage = ` ${label} is required`;
|
||||
}
|
||||
break;
|
||||
case "select":
|
||||
if (!value) {
|
||||
errorMessage = ` ${label} Please Select Value`;
|
||||
}
|
||||
break;
|
||||
case "simple-select":
|
||||
if (!value) {
|
||||
errorMessage = ` ${label} Please Select`;
|
||||
}
|
||||
break;
|
||||
case "advance-select":
|
||||
if (!value) {
|
||||
errorMessage = ` ${label} Please Select`;
|
||||
}
|
||||
break;
|
||||
case "datetime-local":
|
||||
if (!value) {
|
||||
errorMessage = `${label} Please Select `;
|
||||
}
|
||||
break;
|
||||
case "checkbox":
|
||||
if (!value) {
|
||||
errorMessage = `${label} Must be Checked`;
|
||||
}
|
||||
break;
|
||||
case "email":
|
||||
if (!value || !validateEmail(value)) {
|
||||
errorMessage = `${label} must be a valid email address`;
|
||||
}
|
||||
break;
|
||||
case "password":
|
||||
if (!value || !validatePassword(value)) {
|
||||
errorMessage = `${label} must be at least 8 characters long`;
|
||||
}
|
||||
break;
|
||||
case "phone":
|
||||
if (!value || value.length !== 10) {
|
||||
errorMessage = `${label} must be exactly 10 characters`;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!value || !value.trim()) {
|
||||
errorMessage = `${label} Is Required`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
};
|
||||
|
||||
const FormField = (props) => {
|
||||
const { Get, Delete, Patch, Post, getAPI } = useAPIManager(
|
||||
props.globalConfig,
|
||||
props.token
|
||||
);
|
||||
|
||||
|
||||
const required = props.field.required;
|
||||
const label = firstUpperCase(props.field.label);
|
||||
let fieldLabel = required ? (
|
||||
<span>
|
||||
<b style={{ color: "red" }}>*</b> {label}
|
||||
</span>
|
||||
) : (
|
||||
label
|
||||
);
|
||||
const type = props.field.type;
|
||||
const varient = props.field.varient;
|
||||
const placeholder = props.field.placeholder;
|
||||
const isDynamic = props.field.dynamic;
|
||||
const hook = props.field.hook;
|
||||
const defaultObj = props.field.defaultObj;
|
||||
const dafaultValue = props.field.defaultValue
|
||||
? props.field.defaultValue
|
||||
: props.defaultValue;
|
||||
const name = props.field.name;
|
||||
|
||||
let optionKey = "";
|
||||
let searchApi = "";
|
||||
let searchOptionHook = "";
|
||||
|
||||
if (props.field.search) {
|
||||
searchApi = props.field.search.api ? props.field.search.api : "";
|
||||
// console.log("My Search APu 1", searchApi);
|
||||
searchApi = searchApi.length > 0 ? getAPI(searchApi) : "";
|
||||
|
||||
searchOptionHook = props.field.search.label ? props.field.search.label : "";
|
||||
optionKey = props.field.search.key ? props.field.search.key : "id";
|
||||
}
|
||||
const [value, setValue] = useState(dafaultValue);
|
||||
const [isChecked, setIsChecked] = useState(dafaultValue);
|
||||
const [storageData, setStorageData] = useState([]);
|
||||
|
||||
const [error, setError] = useState(
|
||||
props.field.error ? props.field.error : ""
|
||||
);
|
||||
const [blurred, setBlurred] = useState(false);
|
||||
const [simpleSelectOptions, setSimpleSelectOptions] = useState(
|
||||
props.field.options
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const storedData = localStorage.getItem("dynamicField");
|
||||
if (storedData) {
|
||||
setStorageData(JSON.parse(storedData));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateKey = (name, newKey, newLabel) => {
|
||||
const newData = [...storageData];
|
||||
|
||||
const dataIndex = newData.findIndex((item) => item.name === name);
|
||||
|
||||
if (dataIndex !== -1) {
|
||||
newData[dataIndex].key = newKey;
|
||||
newData[dataIndex].label = newLabel;
|
||||
} else {
|
||||
newData.push({ name, key: newKey, label: newLabel });
|
||||
}
|
||||
|
||||
setStorageData(newData);
|
||||
// Update local storage with the new data
|
||||
localStorage.setItem("dynamicField", JSON.stringify(newData));
|
||||
};
|
||||
|
||||
const changeField = async (e) => {
|
||||
if (e != undefined && e != null) {
|
||||
updateKey(name, e.key, e.label);
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
let handleOnChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
if (blurred) {
|
||||
setError(validateField(e.target.value, type, label));
|
||||
}
|
||||
};
|
||||
const handleChange = () => {
|
||||
const newChecked = !isChecked;
|
||||
setIsChecked(newChecked);
|
||||
if (blurred) {
|
||||
setError(validateField(newChecked, type, label));
|
||||
}
|
||||
};
|
||||
const handleaAutocompleteChange = (newValue, inputName) => {
|
||||
setValue(newValue);
|
||||
if (blurred) {
|
||||
setError(validateField(newValue.target.value, type, label));
|
||||
}
|
||||
};
|
||||
const handleBlur = (e) => {
|
||||
setBlurred(true);
|
||||
setError(validateField(value, type, label));
|
||||
setError(
|
||||
validateField(type === "checkbox" ? isChecked : value, type, label)
|
||||
);
|
||||
|
||||
// Validate the field when blurred
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
setBlurred(false);
|
||||
};
|
||||
|
||||
const renderError = () => {
|
||||
// Check if validation is required for this field
|
||||
const isValidationRequired = required || false;
|
||||
|
||||
if (isValidationRequired && error && blurred) {
|
||||
return <FormHelperText error>{error}</FormHelperText>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function formatDate(inputDate) {
|
||||
const date = new Date(inputDate);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "select":
|
||||
return (
|
||||
<>
|
||||
<CustomAutocomplete
|
||||
apiUrl={searchApi}
|
||||
displayKey={searchOptionHook}
|
||||
valueKey={optionKey}
|
||||
inputName={name}
|
||||
onValueChange={() => {}}
|
||||
variant={varient}
|
||||
label={fieldLabel}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{renderError()}
|
||||
</>
|
||||
);
|
||||
case "simple-select":
|
||||
return (
|
||||
<>
|
||||
<_Autocomplete
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
apiUrl=""
|
||||
displayKey="label"
|
||||
valueKey="type"
|
||||
inputName={name}
|
||||
onValueChange={handleaAutocompleteChange}
|
||||
variant={varient}
|
||||
label={label}
|
||||
defaultObj={defaultObj}
|
||||
options={simpleSelectOptions}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{renderError()}
|
||||
</>
|
||||
);
|
||||
case "advance-select":
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
<_Autocomplete
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
apiUrl={searchApi}
|
||||
displayKey={searchOptionHook}
|
||||
valueKey={optionKey}
|
||||
inputName={name}
|
||||
onValueChange={handleaAutocompleteChange}
|
||||
variant={varient}
|
||||
label={fieldLabel}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
defaultObj={defaultObj}
|
||||
/>
|
||||
{renderError()}
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
return (
|
||||
<div onBlur={handleBlur} onFocus={handleFocus}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox checked={isChecked} onChange={handleChange} />}
|
||||
label={fieldLabel}
|
||||
name={name}
|
||||
/>
|
||||
{renderError()}
|
||||
</div>
|
||||
);
|
||||
case "datetime-local":
|
||||
return (
|
||||
<div style={{ textAlign: "left" }}>
|
||||
<TextField
|
||||
name={name}
|
||||
label={fieldLabel}
|
||||
type={type}
|
||||
style={{ width: "100%" }}
|
||||
variant={varient}
|
||||
placeholder={placeholder}
|
||||
value={formatDate(value)}
|
||||
onChange={handleOnChange}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{renderError()}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
<TextField
|
||||
name={name}
|
||||
label={fieldLabel}
|
||||
type={type}
|
||||
style={{ width: "100%" }}
|
||||
variant={varient}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{renderError()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default FormField;
|
|
@ -1,77 +0,0 @@
|
|||
import React from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import Drawer from "@mui/material/Drawer";
|
||||
import Form from "./Form";
|
||||
|
||||
const FormWithDrawer = (props) => {
|
||||
const [state, setState] = React.useState({
|
||||
top: false,
|
||||
left: false,
|
||||
bottom: false,
|
||||
right: false,
|
||||
});
|
||||
const toggleDrawer = (anchor, open) => (event) => {
|
||||
if (
|
||||
event.type === "keydown" &&
|
||||
(event.key === "Tab" || event.key === "Shift")
|
||||
) {
|
||||
|
||||
return;
|
||||
}
|
||||
setState({ ...state, [anchor]: open });
|
||||
};
|
||||
|
||||
const anchor = props.anchor ? props.anchor : "right";
|
||||
const actionBtnName = props.actionBtnName;
|
||||
const data = props.data;
|
||||
const buttonVarient = props.buttonVarient ? props.buttonVarient : "contained";
|
||||
const updateFormTitle = props.updateFormTitle ? props.updateFormTitle : "";
|
||||
const createFormTitle = props.createFormTitle ? props.createFormTitle : "";
|
||||
const submitBtnTitle = props.submitBtnTitle ? props.submitBtnTitle : "Submit";
|
||||
const handleCloseDrawer = () => {
|
||||
setState({ ...state, [anchor]: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment key={anchor}>
|
||||
<Button
|
||||
onClick={toggleDrawer(anchor, true)}
|
||||
variant={buttonVarient}
|
||||
style={props.buttonStyle}
|
||||
>
|
||||
{actionBtnName}
|
||||
</Button>
|
||||
|
||||
<Drawer
|
||||
anchor={anchor}
|
||||
open={state[anchor]}
|
||||
onClose={toggleDrawer(anchor, false)}
|
||||
>
|
||||
<div className="row" style={{ width: "470px" }}>
|
||||
<div
|
||||
className="col-md-12"
|
||||
style={{ textAlign: "center", color: "#333", padding: "10px" }}
|
||||
>
|
||||
<h4>{createFormTitle}</h4>
|
||||
<h4>{updateFormTitle}</h4>
|
||||
<hr />
|
||||
<div>
|
||||
<Form
|
||||
globalConfig={props.globalConfig}
|
||||
token={props.token}
|
||||
closeDrawer={handleCloseDrawer}
|
||||
refreshData={() => props.refreshData()}
|
||||
submitBtnTitle={submitBtnTitle}
|
||||
inputFields={props.inputFields}
|
||||
api={props.api}
|
||||
data={data}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormWithDrawer;
|
|
@ -1,57 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import AutoCompleteField from "./AutoCompleteField";
|
||||
import FormField from "./FormField";
|
||||
|
||||
const MultiSelect = (props) => {
|
||||
const [hookObj, setHookObj] = useState([]);
|
||||
|
||||
let handleOnChange = (key, value) => {
|
||||
let obj = [];
|
||||
console.log("Field Changed sfsdfds", key, value);
|
||||
localStorage.setItem(key, value);
|
||||
// setHookObj(hookObj => [...hookObj,{"name":key, "value":value}]);
|
||||
console.log("Multi Dtaa", hookObj);
|
||||
|
||||
};
|
||||
|
||||
let loadWithUpdatedApi = (field)=>{
|
||||
// field.search.api=localStorage.getItem(field.search.hook) ;
|
||||
console.log("Sub Search Hook",field.search.hook);
|
||||
field.search.api = `http://127.0.0.1:8000/api/v1/getAllGroupsJoinedByUser/${localStorage.getItem(field.search.hook)}/`;
|
||||
return(<FormField
|
||||
FormField
|
||||
handleChange={(name, value) => handleOnChange(name, value)}
|
||||
field={field}
|
||||
/>)
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
|
||||
},[])
|
||||
let updateRefresh = (refresh)=>{
|
||||
console.log(refresh);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<code>{JSON.stringify(props.field)}</code>
|
||||
|
||||
{props.field.map((f) => {
|
||||
return f.search.hook ? (
|
||||
loadWithUpdatedApi(f)
|
||||
|
||||
) : (
|
||||
<FormField
|
||||
FormField
|
||||
handleChange={(name, value) => handleOnChange(name, value)}
|
||||
refresh={(refresh) => updateRefresh(refresh)}
|
||||
field={f}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default MultiSelect;
|
|
@ -1,98 +0,0 @@
|
|||
import React from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import "./style.css";
|
||||
|
||||
function Pagination({
|
||||
nextPage,
|
||||
previousPage,
|
||||
makeApiRequest,
|
||||
getValueFromUrl,
|
||||
totalCount,
|
||||
rowsPerPage,
|
||||
currentPage,
|
||||
setCurrentPage,
|
||||
dataApi,
|
||||
}) {
|
||||
const totalPages = Math.ceil(totalCount / rowsPerPage);
|
||||
|
||||
const generatePageNumbers = () => {
|
||||
const pages = [];
|
||||
const maxPages = 4;
|
||||
const currentPageIndex = currentPage;
|
||||
|
||||
let startPage = Math.max(1, currentPageIndex - Math.floor(maxPages / 2));
|
||||
let endPage = Math.min(totalPages, startPage + maxPages - 1);
|
||||
|
||||
if (endPage === totalPages) {
|
||||
startPage = Math.max(1, endPage - maxPages + 1);
|
||||
} else if (startPage === 1) {
|
||||
endPage = Math.min(totalPages, startPage + maxPages - 1);
|
||||
}
|
||||
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const handlePreviousClick = () => {
|
||||
if (previousPage) {
|
||||
const previousPageNumber = currentPage - 1;
|
||||
setCurrentPage(previousPageNumber);
|
||||
makeApiRequest(previousPage);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNextClick = () => {
|
||||
if (nextPage) {
|
||||
const nextPageNumber = currentPage + 1;
|
||||
setCurrentPage(nextPageNumber);
|
||||
makeApiRequest(nextPage);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="pagination-container">
|
||||
<Button
|
||||
disabled={!previousPage}
|
||||
onClick={handlePreviousClick}
|
||||
variant="outlined"
|
||||
className="pagination-button"
|
||||
style={{ margin: "5px" }}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
{generatePageNumbers().map((page) => (
|
||||
<Button
|
||||
key={page}
|
||||
onClick={() => {
|
||||
setCurrentPage(page);
|
||||
const apiPage = nextPage
|
||||
? `${nextPage.split("page=")[0]}page=${page}`
|
||||
: `${dataApi}?page=${page}`;
|
||||
makeApiRequest(apiPage);
|
||||
}}
|
||||
variant="outlined"
|
||||
className={`pagination-button ${
|
||||
currentPage === page ? "active-page" : ""
|
||||
}`}
|
||||
style={{ margin: "5px" }}
|
||||
>
|
||||
{page}
|
||||
</Button>
|
||||
))}
|
||||
<Button
|
||||
disabled={!nextPage}
|
||||
onClick={handleNextClick}
|
||||
variant="outlined"
|
||||
className="pagination-button"
|
||||
style={{ margin: "5px" }}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pagination;
|
|
@ -1,33 +0,0 @@
|
|||
import * as React from "react";
|
||||
import Radio from "@mui/material/Radio";
|
||||
import RadioGroup from "@mui/material/RadioGroup";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import FormLabel from "@mui/material/FormLabel";
|
||||
|
||||
export default function RadioButtonsGroup() {
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel id="demo-radio-buttons-group-label">Payment In For </FormLabel>
|
||||
<RadioGroup
|
||||
aria-labelledby="demo-radio-buttons-group-label"
|
||||
defaultValue="subscription_amt"
|
||||
name="transaction_type"
|
||||
row
|
||||
>
|
||||
<FormControlLabel
|
||||
value="subscription_amt"
|
||||
control={<Radio />}
|
||||
label="Subscription"
|
||||
/>
|
||||
{/* <FormControlLabel value="penalty" control={<Radio />} label="Penalty" /> */}
|
||||
<FormControlLabel
|
||||
value="draw_winner"
|
||||
control={<Radio />}
|
||||
label="Draw Winner"
|
||||
/>
|
||||
{/* <FormControlLabel value="custom_amt" control={<Radio />} label="Custom Amount" /> */}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import { Button } from '@mui/material';
|
||||
import React from 'react'
|
||||
import EditRow from './EditRow';
|
||||
import FormWithDrawer from './FormWithDrawer';
|
||||
function TableRow(props) {
|
||||
var row = [];
|
||||
const readBlock = (props.readBlock) ? props.readBlock : [];
|
||||
|
||||
|
||||
|
||||
function getCellArray() {
|
||||
Object.keys(props.data).forEach((key) => {
|
||||
console.log(readBlock);
|
||||
if (!Object.values(readBlock).includes(key)) {
|
||||
Object.entries(props.data).forEach((entry) => {
|
||||
if (key === entry[0]) {
|
||||
row.push(entry[1]);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function removeRow() { }
|
||||
getCellArray();
|
||||
|
||||
|
||||
return (
|
||||
<tr>
|
||||
{
|
||||
row.map((data, index) => {
|
||||
return <td key={index}>{data}</td>
|
||||
})
|
||||
}
|
||||
{(props.manage) ? <>
|
||||
<td>
|
||||
<Button onClick={() => removeRow()}> Delete</Button>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<FormWithDrawer actionBtnName="Edit Data" inputFields={{}} api="https://api.udaymotors.in/api/v1/role/" />
|
||||
{/* <EditRow data={props.data} /> */}
|
||||
</td>
|
||||
</> : ""
|
||||
}
|
||||
|
||||
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
export default TableRow
|
|
@ -1,105 +0,0 @@
|
|||
{
|
||||
"api":"https://api.udaymotors.in/api/v1/group/"
|
||||
,
|
||||
"showField":[
|
||||
{ "label": "Group Name", "key": "title" },
|
||||
{ "label": "Description", "key": "description" },
|
||||
{ "label": "Subscription Amount", "key": "subscription_amt" },
|
||||
{ "label": "Max Allowed Users", "key": "max_users" },
|
||||
{ "label": "Penalty Amount", "key": "penalty_amt" },
|
||||
{ "label": "Start Date", "key": "start_date" },
|
||||
{ "label": "End Date", "key": "end_date" }
|
||||
],
|
||||
"createField":[
|
||||
|
||||
{
|
||||
"label": "Group name",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "title"
|
||||
},
|
||||
{
|
||||
"label": "Group Description",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "description"
|
||||
},
|
||||
{
|
||||
"label": "Start Date",
|
||||
"type": "datetime-local",
|
||||
"variant": "standard",
|
||||
"name": "start_date"
|
||||
},
|
||||
{
|
||||
"label": "End Date",
|
||||
"type": "datetime-local",
|
||||
"variant": "standard",
|
||||
"name": "end_date"
|
||||
},
|
||||
{
|
||||
"label": "Max Users",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "max_users"
|
||||
},
|
||||
{
|
||||
"label": "Subscription Amount",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "subscription_amt"
|
||||
},
|
||||
{
|
||||
"label": "Panelty Amount",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "penalty_amt"
|
||||
}
|
||||
|
||||
],
|
||||
"editField":[
|
||||
|
||||
{
|
||||
"label": "Group name",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "title"
|
||||
},
|
||||
{
|
||||
"label": "Group Description",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "description"
|
||||
},
|
||||
{
|
||||
"label": "Start Date",
|
||||
"type": "datetime-local",
|
||||
"variant": "standard",
|
||||
"name": "start_date"
|
||||
},
|
||||
{
|
||||
"label": "End Date",
|
||||
"type": "datetime-local",
|
||||
"variant": "standard",
|
||||
"name": "end_date"
|
||||
},
|
||||
{
|
||||
"label": "Max Users",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "max_users"
|
||||
},
|
||||
{
|
||||
"label": "Subscription Amount",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "subscription_amt"
|
||||
},
|
||||
{
|
||||
"label": "Panelty Amount",
|
||||
"type": "text",
|
||||
"variant": "standard",
|
||||
"name": "penalty_amt"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
export function camelCase(str) {
|
||||
// Using replace method with regEx
|
||||
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
|
||||
return index === 0 ? word.toLowerCase() : word.toUpperCase();
|
||||
}).replace(/\s+/g, '');
|
||||
}
|
||||
export function firstUpperCase(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
export const validateEmail = (email) => {
|
||||
const emailRegex = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/;
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
export const validatePassword = (password) => {
|
||||
return password.length >= 8;
|
||||
};
|
|
@ -1,46 +0,0 @@
|
|||
.dynamic-form-element {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-table {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-input-table {
|
||||
padding: 8px;
|
||||
border: none; /* Remove border */
|
||||
border-radius: 15px;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.search-input-table:focus {
|
||||
background-color: #f0f0f0; /* Optional: Change background color on focus */
|
||||
}
|
||||
/* pagination */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.pagination-button {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.pagination-button.active-page {
|
||||
background-color: #6c757d;
|
||||
color: rgb(73, 198, 207);
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
import axios from "axios";
|
||||
|
||||
const useAPIManager = (globalConfig, _token) => {
|
||||
const authToken = _token;
|
||||
const global = globalConfig;
|
||||
|
||||
let token = authToken.access;
|
||||
let authorization = _token
|
||||
? {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
||||
const getAPI = (API_NAME) => global.api.host + global.api[API_NAME];
|
||||
|
||||
const getHost = () => global.api.host;
|
||||
|
||||
const Post = async (api, payload) => {
|
||||
let _api = getAPI(api).includes("undefined") ? api : getAPI(api);
|
||||
try {
|
||||
const response = await axios.post(_api, payload, authorization);
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Put = async (api, id, payload) => {
|
||||
let _api = getAPI(api).includes("undefined") ? api : getAPI(api);
|
||||
try {
|
||||
const response = await axios.put(`${_api}${id}/`, payload, authorization);
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Patch = async (api, id, payload) => {
|
||||
let _api = getAPI(api).includes("undefined") ? api : getAPI(api);
|
||||
try {
|
||||
const response = await axios.patch(
|
||||
`${_api}${id}/`,
|
||||
payload,
|
||||
authorization
|
||||
);
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Get = async (api) => {
|
||||
let _api = getAPI(api).includes("undefined") ? api : getAPI(api);
|
||||
try {
|
||||
const response = await axios.get(_api, authorization);
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Delete = async (api, id) => {
|
||||
let _api = getAPI(api).includes("undefined") ? api : getAPI(api);
|
||||
try {
|
||||
const response = await axios.delete(`${_api}${id}/`, authorization);
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return { Post, Put, Patch, Get, Delete, getAPI, getHost };
|
||||
};
|
||||
|
||||
export default useAPIManager;
|
|
@ -1,89 +0,0 @@
|
|||
// useApi.js
|
||||
import { useContext } from 'react';
|
||||
import axios from 'axios';
|
||||
import AuthContext from '../auth/AuthContext';
|
||||
import global from '../global/GlobalJSON.json';
|
||||
|
||||
|
||||
const useApi = () => {
|
||||
const { authToken } = useContext(AuthContext);
|
||||
|
||||
const getAPI = (API_NAME) => global.api.host + global.api[API_NAME];
|
||||
|
||||
const getHost = () => global.api.host;
|
||||
let token = authToken.access;
|
||||
|
||||
const Post = async (api, payload) => {
|
||||
let _api =(getAPI(api).includes('undefined'))?api:getAPI(api);
|
||||
try {
|
||||
const response = await axios.post(_api, payload, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Put = async (api, id, payload) => {
|
||||
let _api =(getAPI(api).includes('undefined'))?api:getAPI(api);
|
||||
try {
|
||||
const response = await axios.put(`${_api}${id}/`, payload, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Patch = async (api, id, payload) => {
|
||||
let _api =(getAPI(api).includes('undefined'))?api:getAPI(api);
|
||||
try {
|
||||
const response = await axios.patch(`${_api}${id}/`, payload, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Get = async (api) => {
|
||||
let _api =(getAPI(api).includes('undefined'))?api:getAPI(api);
|
||||
try {
|
||||
const response = await axios.get(_api, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const Delete = async (api, id) => {
|
||||
let _api =(getAPI(api).includes('undefined'))?api:getAPI(api);
|
||||
try {
|
||||
const response = await axios.delete(`${_api}${id}/`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return response;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return { Post, Put, Patch, Get, Delete, getAPI, getHost };
|
||||
};
|
||||
|
||||
export default useApi;
|
Before Width: | Height: | Size: 186 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 169 KiB |
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import config from "../global/GlobalJSON.json";
|
||||
import config from "../../config/Global.json";
|
||||
const Footer = () => {
|
||||
const {
|
||||
api: { version },
|
|
@ -1,4 +1,7 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
|
@ -14,8 +17,8 @@ import {
|
|||
Collapse,
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import AuthContext from '../auth/AuthContext';
|
||||
|
||||
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
ChevronLeft as ChevronLeftIcon,
|
||||
|
@ -24,11 +27,7 @@ import {
|
|||
ExpandMore,
|
||||
Home as HomeIcon,
|
||||
People as PeopleIcon,
|
||||
CalendarToday as CalendarTodayIcon,
|
||||
AccountBalance as AccountBalanceIcon,
|
||||
Receipt as ReceiptIcon,
|
||||
Paid as PaidIcon,
|
||||
TimeToLeave as LeaveIcon,
|
||||
Settings as SettingsIcon,
|
||||
Help as HelpIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
@ -36,6 +35,10 @@ import { styled, useTheme } from '@mui/material/styles';
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
|
||||
import AuthContext from '../../utils/auth/AuthContext';
|
||||
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(
|
||||
|
@ -141,7 +144,7 @@ const Header = () => {
|
|||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
HbaseApp
|
||||
React-Placeholder
|
||||
</Typography>
|
||||
<IconButton edge="end" color="inherit" onClick={handleMenuOpen}>
|
||||
<FontAwesomeIcon icon={faUserCircle} />
|
||||
|
@ -195,13 +198,13 @@ const Header = () => {
|
|||
<ListItemStyled
|
||||
button
|
||||
component={Link}
|
||||
to="/company"
|
||||
to="/api-manager-starter"
|
||||
onClick={handleNavClick}
|
||||
sx={{ pl: 4 }}
|
||||
className={location.pathname === '/company' ? 'active' : ''}
|
||||
className={location.pathname === '/api-manager-starter' ? 'active' : ''}
|
||||
>
|
||||
<AccountBalanceIcon style={{ marginRight: '10px' }} />
|
||||
<ListItemText primary="Company" />
|
||||
<ListItemText primary="Api Manager Starter" />
|
||||
</ListItemStyled>
|
||||
<ListItemStyled
|
||||
button
|
||||
|
@ -209,10 +212,10 @@ const Header = () => {
|
|||
to="/department"
|
||||
onClick={handleNavClick}
|
||||
sx={{ pl: 4 }}
|
||||
className={location.pathname === '/department' ? 'active' : ''}
|
||||
className={location.pathname === '/page2' ? 'active' : ''}
|
||||
>
|
||||
<PeopleIcon style={{ marginRight: '10px' }} />
|
||||
<ListItemText primary="Department" />
|
||||
<ListItemText primary="Page2" />
|
||||
</ListItemStyled>
|
||||
<ListItemStyled
|
||||
button
|
||||
|
@ -220,10 +223,10 @@ const Header = () => {
|
|||
to="/employee"
|
||||
onClick={handleNavClick}
|
||||
sx={{ pl: 4 }}
|
||||
className={location.pathname === '/employee' ? 'active' : ''}
|
||||
className={location.pathname === '/page3' ? 'active' : ''}
|
||||
>
|
||||
<PeopleIcon style={{ marginRight: '10px' }} />
|
||||
<ListItemText primary="Employee" />
|
||||
<ListItemText primary="Page3" />
|
||||
</ListItemStyled>
|
||||
|
||||
</List>
|
|
@ -1,216 +0,0 @@
|
|||
// import React, { useState, useContext, useEffect } from "react";
|
||||
// import {
|
||||
// AppBar,
|
||||
// Toolbar,
|
||||
// Typography,
|
||||
// Box,
|
||||
// IconButton,
|
||||
// Menu,
|
||||
// MenuItem,
|
||||
// useMediaQuery,
|
||||
// Drawer,
|
||||
// List,
|
||||
// ListItem,
|
||||
// ListItemText,
|
||||
// } from "@mui/material";
|
||||
// import { Link } from "react-router-dom";
|
||||
// import AccountCircle from "@mui/icons-material/AccountCircle";
|
||||
// import MenuIcon from "@mui/icons-material/Menu";
|
||||
// import AuthContext from "../auth/AuthContext";
|
||||
// import useApi from "../api-manager/useApi";
|
||||
// import UserTypeUpdatePassword from "../utils/userTypePassword";
|
||||
|
||||
|
||||
// const Header = () => {
|
||||
// const { user, logOutUser } = useContext(AuthContext);
|
||||
// const [profile, setProfile] = useState(null);
|
||||
// const [userType, setUserType] = useState(null);
|
||||
// const [companyName, setCompanyName] = useState("");
|
||||
// const [companyId, setCompanyId] = useState(null);
|
||||
// const [dialogOpen, setDialogOpen] = useState(false);
|
||||
// const { Get, Post, getHost } = useApi();
|
||||
// const isMobile = useMediaQuery("(max-width:600px)");
|
||||
// const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
|
||||
// const handleClose = () => {
|
||||
// setProfile(null);
|
||||
// };
|
||||
|
||||
// const handleMenu = (event) => {
|
||||
// setProfile(event.currentTarget);
|
||||
// };
|
||||
|
||||
// const toggleDrawer = (open) => (event) => {
|
||||
// if (
|
||||
// event.type === "keydown" &&
|
||||
// (event.key === "Tab" || event.key === "Shift")
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
// setDrawerOpen(open);
|
||||
// };
|
||||
|
||||
// const getUserType = async () => {
|
||||
// try {
|
||||
// const userTypeData = await Get("checkUserType");
|
||||
// setUserType(userTypeData.user_type);
|
||||
|
||||
// const compId = await Get(
|
||||
// `${getHost()}/api/v1/getCompanyByUserOrDeptId?user_id=${user.user_id}`
|
||||
// );
|
||||
// setCompanyName(compId.company_name);
|
||||
// setCompanyId(compId.company_id);
|
||||
// } catch (error) {
|
||||
// setCompanyName("Hey Admin");
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// getUserType();
|
||||
// }, []);
|
||||
|
||||
// const renderLinks = () => {
|
||||
// switch (userType) {
|
||||
// case "department user":
|
||||
// return [
|
||||
// { path: "/", label: "Home" },
|
||||
// { path: "/cheque-form", label: "Print Cheque" },
|
||||
// { path: "/history", label: "Cheque History" },
|
||||
// ];
|
||||
// case "company user":
|
||||
// return [
|
||||
// { path: "/", label: "Home" },
|
||||
// { path: "/cheque-form", label: "Print Cheque" },
|
||||
// { path: "/history", label: "Cheque History" },
|
||||
// { path: "/department", label: "Departments" },
|
||||
// ];
|
||||
// case "superuser":
|
||||
// return [
|
||||
// { path: "/", label: "Humbingo Home" },
|
||||
// { path: "/company", label: "Company" },
|
||||
// { path: "/AdminDepartment", label: "Departments" },
|
||||
// { path: "/licence", label: "Licence" },
|
||||
// { path: "/registree", label: "Registree" },
|
||||
// { path: "/user", label: "User" },
|
||||
// { path: "/role", label: "Role" },
|
||||
// { path: "/history", label: "Cheque History" },
|
||||
// ];
|
||||
// default:
|
||||
// return [];
|
||||
// }
|
||||
// };
|
||||
|
||||
// const linkStyle = {
|
||||
// color: "inherit",
|
||||
// textDecoration: "none",
|
||||
// margin: "0 10px",
|
||||
// };
|
||||
|
||||
// const drawerLinks = (
|
||||
// <Box
|
||||
// sx={{ width: 250 }}
|
||||
// role="presentation"
|
||||
// onClick={toggleDrawer(false)}
|
||||
// onKeyDown={toggleDrawer(false)}
|
||||
// >
|
||||
// <List>
|
||||
// {renderLinks().map((link, index) => (
|
||||
// <ListItem button component={Link} to={link.path} key={index}>
|
||||
// <ListItemText primary={link.label} />
|
||||
// </ListItem>
|
||||
// ))}
|
||||
// </List>
|
||||
// </Box>
|
||||
// );
|
||||
|
||||
// const handlePasswordSubmit = async (newPassword) => {
|
||||
// try {
|
||||
// await Post(
|
||||
// `${getHost()}/api/v1/updatePassword/company/${companyId}/`,
|
||||
// { password: newPassword }
|
||||
// );
|
||||
// alert("Password updated successfully"); // Replace with a better toast notification if needed
|
||||
// } catch (error) {
|
||||
// console.error("Error changing password:", error);
|
||||
// alert("Failed to update password"); // Replace with a better toast notification if needed
|
||||
// }
|
||||
// setDialogOpen(false);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <AppBar position="sticky" sx={{ backgroundColor: "#021526" }}>
|
||||
// <Toolbar>
|
||||
// <IconButton
|
||||
// edge="start"
|
||||
// color="inherit"
|
||||
// aria-label="menu"
|
||||
// onClick={toggleDrawer(true)}
|
||||
// sx={{ display: isMobile ? "block" : "none" }}
|
||||
// >
|
||||
// <MenuIcon />
|
||||
// </IconButton>
|
||||
// <Typography
|
||||
// variant="h6"
|
||||
// sx={{ flexGrow: 1, display: "flex", alignItems: "center" }}
|
||||
// >
|
||||
// <img
|
||||
// src="https://humbingo.com/wp-content/uploads/2024/06/Logo-32-.webp"
|
||||
// height="50px"
|
||||
// alt="Logo"
|
||||
// />
|
||||
// {!isMobile && (
|
||||
// <span style={{ marginLeft: "10px" }}>{companyName}</span>
|
||||
// )}
|
||||
// </Typography>
|
||||
// {!isMobile && (
|
||||
// <Box sx={{ display: "flex" }}>
|
||||
// {renderLinks().map((link, index) => (
|
||||
// <Link to={link.path} style={linkStyle} key={index}>
|
||||
// {link.label}
|
||||
// </Link>
|
||||
// ))}
|
||||
// </Box>
|
||||
// )}
|
||||
// <IconButton
|
||||
// edge="end"
|
||||
// aria-label="account of current user"
|
||||
// aria-haspopup="true"
|
||||
// onClick={handleMenu}
|
||||
// color="inherit"
|
||||
// >
|
||||
// <AccountCircle />
|
||||
// </IconButton>
|
||||
// <Menu
|
||||
// anchorEl={profile}
|
||||
// anchorOrigin={{
|
||||
// vertical: "bottom",
|
||||
// horizontal: "left",
|
||||
// }}
|
||||
// keepMounted
|
||||
// transformOrigin={{
|
||||
// vertical: "top",
|
||||
// horizontal: "left",
|
||||
// }}
|
||||
// open={Boolean(profile)}
|
||||
// onClose={handleClose}
|
||||
// >
|
||||
// {/* <MenuItem onClick={handleClose}>{`Hello, ${user.user_id}`}</MenuItem> */}
|
||||
// <MenuItem onClick={() => setDialogOpen(true)}>Change Password</MenuItem>
|
||||
// <MenuItem onClick={logOutUser} ><span style={{backgroundColor: "#f60002", color: "White", textAlign:"center",padding: "7px", width: "100%", borderRadius: "5px"}}>Logout</span></MenuItem>
|
||||
// </Menu>
|
||||
// </Toolbar>
|
||||
// <Drawer anchor="left" open={drawerOpen} onClose={toggleDrawer(false)}>
|
||||
// {drawerLinks}
|
||||
// </Drawer>
|
||||
// <UserTypeUpdatePassword
|
||||
// open={dialogOpen}
|
||||
// onClose={() => setDialogOpen(false)}
|
||||
// onSubmit={handlePasswordSubmit}
|
||||
// userType={userType}
|
||||
// companyId={companyId}
|
||||
// />
|
||||
// </AppBar>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default Header;
|
|
@ -0,0 +1,21 @@
|
|||
import React, { useContext } from "react";
|
||||
import APIManager from "../external/api-manager/APIManager";
|
||||
import global from "../../config/Global.json";
|
||||
import AuthContext from "../../utils/auth/AuthContext";
|
||||
import schema from "./Schema/APIManagerStarter.json";
|
||||
const APIManagerStarter = () => {
|
||||
let { authToken } = useContext(AuthContext);
|
||||
return (
|
||||
<div className="container">
|
||||
<APIManager
|
||||
globalConfig={global}
|
||||
token={authToken}
|
||||
data={schema}
|
||||
createRequired={false}
|
||||
editRequired={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default APIManagerStarter
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import AuthContext from "../../../auth/AuthContext";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import AuthContext from "../../utils/auth/AuthContext";
|
||||
|
||||
import {
|
||||
Container,
|
||||
TextField,
|
||||
|
@ -12,7 +13,7 @@ import {
|
|||
InputAdornment,
|
||||
} from "@mui/material";
|
||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||
import logo from "../../../logo.png"
|
||||
import logo from "../../assets/img/logo.png"
|
||||
|
||||
const LoginPage = () => {
|
||||
let { loginUser } = useContext(AuthContext);
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"api": "company",
|
||||
"showField": [
|
||||
{ "label": "Name", "key": "name" },
|
||||
{ "label": "Address", "key": "address" },
|
||||
{ "label": "Contact Number", "key": "contact_no" },
|
||||
{ "label": "Email", "key": "email" },
|
||||
{ "label": "Website", "key": "website" }
|
||||
],
|
||||
"createField": [
|
||||
{
|
||||
"label": "Name",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "name",
|
||||
"required":true
|
||||
},
|
||||
{
|
||||
"label": "Address",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "address",
|
||||
"required":true
|
||||
},
|
||||
{
|
||||
"label": "Contact Number",
|
||||
"type": "number",
|
||||
"varient": "standard",
|
||||
"name": "contact_no",
|
||||
"required":true
|
||||
},
|
||||
{
|
||||
"label": "Email",
|
||||
"type": "email",
|
||||
"varient": "standard",
|
||||
"name": "email",
|
||||
"required":true
|
||||
},
|
||||
{
|
||||
"label": "Website",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "website"
|
||||
}
|
||||
],
|
||||
"editField": [
|
||||
{
|
||||
"label": "Name",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"label": "Address",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "address"
|
||||
},
|
||||
{
|
||||
"label": "Contact Number",
|
||||
"type": "number",
|
||||
"varient": "standard",
|
||||
"name": "contact_no"
|
||||
},
|
||||
{
|
||||
"label": "Email",
|
||||
"type": "email",
|
||||
"varient": "standard",
|
||||
"name": "email"
|
||||
},
|
||||
{
|
||||
"label": "Website",
|
||||
"type": "text",
|
||||
"varient": "standard",
|
||||
"name": "website"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import global from "../global/GlobalJSON.json"
|
||||
import global from "./Global.json"
|
||||
|
||||
export function getAPI(API_NAME) {
|
||||
return global.api.host + global.api[API_NAME];
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
"api": {
|
||||
"host": "https://cheque-api.humbingo.in",
|
||||
"host": "https://dev_api_cheque.humbingo.in",
|
||||
"token": "/auth/token/",
|
||||
"refreshToken": "/auth/token/refresh/",
|
||||
"user": "/api/v1/user/",
|
||||
"role": "/api/v1/role/",
|
||||
"permission": "/api/v1/permission/",
|
||||
"checkUserType": "/api/v1/checkUserType",
|
||||
"company":"/api/v1/company",
|
||||
"test":"api/v1/test/",
|
||||
|
||||
"version": "1.0"
|
||||
},
|
||||
"debug": true
|
|
@ -1,13 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
|
@ -1,17 +1,9 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,13 +0,0 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
|
@ -1,5 +0,0 @@
|
|||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
|
@ -1,9 +1,12 @@
|
|||
import { createContext, useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {jwtDecode} from "jwt-decode"; // Make sure jwt-decode is imported correctly
|
||||
import { getAPI } from "../global/Global"; // Make sure this function is properly defined
|
||||
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import {jwtDecode} from "jwt-decode"; // Make sure jwt-decode is imported correctly
|
||||
|
||||
import { getAPI } from "../../config/Global"; // Make sure this function is properly defined
|
||||
|
||||
|
||||
const AuthContext = createContext();
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import {useContext } from "react"
|
||||
import React from 'react'
|
||||
|
||||
import AuthContext from "../auth/AuthContext";
|
||||
import LoginPage from "../pages/UI/login/login";
|
||||
import Header from "../components/header";
|
||||
import AuthContext from "./AuthContext";
|
||||
import LoginPage from "../../components/pages/Login";
|
||||
import Header from "../../components/common/Header";
|
||||
|
||||
|
||||
|