Merge pull request 'Code Structure Change' (#3) from trf2 into master

Reviewed-on: https://git.humbingo.in/Humbingo/react-placeholder/pulls/3
master
Jay Patel 2024-08-22 06:59:46 +00:00
commit f655bb3261
13 changed files with 47 additions and 739 deletions

View File

View File

@ -1,28 +1,15 @@
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
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';
import { BrowserRouter} from 'react-router-dom';
import { AuthProvider } from './utils/secure-route/AuthContext';
import RouteManager from './utils/secure-route/routeManager/RouteManager';
import './assets/css/App.css'
const App = () => {
return (
<BrowserRouter>
<AuthProvider>
<Routes>
<Route path="/" element={<Auth component={<Home />} />} exact />
<Route path="/login" element={<Auth component={<LoginPage/>} />} exact />
<Route path="/api-manager-starter" element={<Auth component={<APIManagerStarter />} />} exact />
</Routes>
<Footer/>
</AuthProvider>
</BrowserRouter>
<BrowserRouter>
<AuthProvider>
<RouteManager />
</AuthProvider>
</BrowserRouter>
);
};

4
src/assets/css/App.css Normal file
View File

@ -0,0 +1,4 @@
body{
margin: 0px;
padding: 0px;
}

View File

@ -1,45 +0,0 @@
import React from "react";
import config from "../../config/Global.json";
const Footer = () => {
const {
api: { version },
} = config;
return (
<div
style={{
backgroundColor: "#021526",
color: "white",
padding: "10px 20px",
textAlign: "center",
position: "fixed",
bottom: 0,
width: "100%",
boxShadow: "0 -2px 5px rgba(0, 0, 0, 0.2)",
zIndex: 0,
}}
>
<div className="container">
<p className="mb-0">
<span className="d-inline">
Developed by{" "}
<a
href="https://www.humbingo.com"
target="_blank"
rel="noopener noreferrer"
style={{ color: "inherit", textDecoration: "none" }}
>
Humbingo Consultancy Services
</a>
</span>
<span className="d-inline">
{" "}
| &copy; {new Date().getFullYear()} Cheque Print App. All rights
reserved.
</span>
<span className="d-inline"> | Version: {version}</span>
</p>
</div>
</div>
);
};
export default Footer;

View File

@ -1,283 +0,0 @@
import React, { useContext, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import {
AppBar,
Toolbar,
Typography,
IconButton,
Menu,
MenuItem,
Drawer,
CssBaseline,
List,
ListItem,
ListItemText,
Collapse,
Divider,
} from '@mui/material';
import {
Menu as MenuIcon,
ChevronLeft as ChevronLeftIcon,
ChevronRight as ChevronRightIcon,
ExpandLess,
ExpandMore,
Home as HomeIcon,
People as PeopleIcon,
AccountBalance as AccountBalanceIcon,
Settings as SettingsIcon,
Help as HelpIcon,
} from '@mui/icons-material';
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' })(
({ theme, open }) => ({
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: `-${drawerWidth}px`,
...(open && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
}),
})
);
const AppBarStyled = styled(AppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})(({ theme, open }) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
justifyContent: 'flex-end',
}));
const ListItemStyled = styled(ListItem)(({ theme }) => ({
'&.active': {
backgroundColor: theme.palette.action.hover,
},
}));
const Header = () => {
const { user, logOutUser } = useContext(AuthContext);
const [profileAnchorEl, setProfileAnchorEl] = useState(null);
const [drawerOpen, setDrawerOpen] = useState(false);
const [manageOpen, setManageOpen] = useState(false);
const [manageDataOpen, setManageDataOpen] = useState(false);
const theme = useTheme();
const location = useLocation();
const handleMenuOpen = (event) => {
setProfileAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setProfileAnchorEl(null);
};
const handleLogout = () => {
logOutUser();
handleMenuClose();
};
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen);
};
const toggleManageDropdown = () => {
setManageOpen(!manageOpen);
};
const toggleManageDataDropdown = () => {
setManageDataOpen(!manageDataOpen);
};
const handleNavClick = () => {
setDrawerOpen(false);
};
return (
<>
<CssBaseline />
<AppBarStyled position="fixed" open={drawerOpen} style={{ backgroundColor: '#35424a' }}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={toggleDrawer}
edge="start"
sx={{ mr: 2, ...(drawerOpen && { display: 'none' }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
React-Placeholder
</Typography>
<IconButton edge="end" color="inherit" onClick={handleMenuOpen}>
<FontAwesomeIcon icon={faUserCircle} />
</IconButton>
<Menu
anchorEl={profileAnchorEl}
open={Boolean(profileAnchorEl)}
onClose={handleMenuClose}
>
<MenuItem onClick={handleMenuClose}>{`Hello, ${user?.user_id}`}</MenuItem>
<MenuItem onClick={handleLogout}>Logout</MenuItem>
</Menu>
</Toolbar>
</AppBarStyled>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
}}
variant="persistent"
anchor="left"
open={drawerOpen}
>
<DrawerHeader>
<IconButton onClick={toggleDrawer}>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</DrawerHeader>
<Divider />
<List>
<ListItemStyled
button
component={Link}
to="/"
onClick={handleNavClick}
className={location.pathname === '/' ? 'active' : ''}
>
<HomeIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Home" />
</ListItemStyled>
<ListItemStyled button onClick={toggleManageDropdown}>
<ListItemText primary="Manage" />
{manageOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemStyled>
<Collapse in={manageOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemStyled
button
component={Link}
to="/api-manager-starter"
onClick={handleNavClick}
sx={{ pl: 4 }}
className={location.pathname === '/api-manager-starter' ? 'active' : ''}
>
<AccountBalanceIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Api Manager Starter" />
</ListItemStyled>
<ListItemStyled
button
component={Link}
to="/department"
onClick={handleNavClick}
sx={{ pl: 4 }}
className={location.pathname === '/page2' ? 'active' : ''}
>
<PeopleIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Page2" />
</ListItemStyled>
<ListItemStyled
button
component={Link}
to="/employee"
onClick={handleNavClick}
sx={{ pl: 4 }}
className={location.pathname === '/page3' ? 'active' : ''}
>
<PeopleIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Page3" />
</ListItemStyled>
</List>
</Collapse>
<ListItemStyled button onClick={toggleManageDataDropdown}>
<ListItemText primary="Manage Data" />
{manageDataOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemStyled>
<Collapse in={manageDataOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemStyled
button
component={Link}
to="/rules"
onClick={handleNavClick}
sx={{ pl: 4 }}
className={location.pathname === '/rules' ? 'active' : ''}
>
<SettingsIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Rules" />
</ListItemStyled>
</List>
</Collapse>
<Divider />
<ListItemStyled
button
component={Link}
to="/setting"
onClick={handleNavClick}
className={location.pathname === '/setting' ? 'active' : ''}
>
<SettingsIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Setting" />
</ListItemStyled>
<ListItemStyled
button
component={Link}
to="/su"
onClick={handleNavClick}
className={location.pathname === '/su' ? 'active' : ''}
>
<HelpIcon style={{ marginRight: '10px' }} />
<ListItemText primary="Support" />
</ListItemStyled>
</List>
</Drawer>
<Main open={drawerOpen}>
<DrawerHeader />
</Main>
</>
);
};
export default Header;

View File

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

View File

@ -1,12 +0,0 @@
import React from "react";
function Home() {
return (
<div>
<h1>Home Page</h1>
</div>
);
}
export default Home;

View File

@ -1,122 +0,0 @@
import React, { useContext, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import AuthContext from "../../utils/auth/AuthContext";
import {
Container,
TextField,
Typography,
Paper,
Button,
Grid,
IconButton,
InputAdornment,
} from "@mui/material";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import logo from "../../assets/img/logo.png"
const LoginPage = () => {
let { loginUser } = useContext(AuthContext);
let { authToken } = useContext(AuthContext);
const [showPassword, setShowPassword] = useState(false);
let navigate = useNavigate();
let checkLogedIn = () => {
if (authToken) {
navigate("/");
} else {
console.error("Unable to check authToken");
return false;
}
};
const handleClickShowPassword = () => {
setShowPassword((prev) => !prev);
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
useEffect(() => {
checkLogedIn();
}, [authToken]);
return (
<div>
<form onSubmit={loginUser}>
<Container component="main" maxWidth="xs">
<Paper
elevation={3}
sx={{
marginTop: 8,
padding: 4,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
{/* Logo */}
<img
src={`${logo}`}
alt="Logo"
style={{ height: 100, width: 150, marginBottom: 20 }} // Adjust size and margin as needed
/>
<h1 style={{ fontFamily: "consolas" }}>
</h1>
<Typography component="h1" variant="h5">
Login
</Typography>
<TextField
margin="normal"
required
fullWidth
name="username"
label="User Name"
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type={showPassword ? "text" : "password"}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<br />
<Button
variant="contained"
color="primary"
fullWidth
type="Submit"
name="submit"
>
Login
</Button>
</Paper>
</Container>
</form>
</div>
);
};
export default LoginPage;

View File

@ -1,79 +0,0 @@
{
"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"
}
]
}

View File

@ -0,0 +1,28 @@
import Home from "../utils/secure-route/components/Home";
import Login from "../utils/secure-route/components/Login";
// Example Components
import APIManagerStarter from "../utils/api-manager/Examples/APIManagerStarter";
import YourComponent from "../utils/secure-route/routeManager/YourComponent";
const routes = [
{
"path": "/",
"component": Home
},
{
"path": "/login",
"component": Login
},
{
"path": "/your-component",
"component": YourComponent
},
{
"path": "/api-manager-starter",
"component":APIManagerStarter
}
]
export const getRoutes = ()=> routes;

View File

@ -8,10 +8,13 @@
"permission": "/api/v1/permission/",
"checkUserType": "/api/v1/checkUserType",
"company":"/api/v1/company",
"test":"api/v1/test/",
"test":"/api/v1/test/",
"department":"/api/v1/department/"
"version": "1.0"
},
"debug": true
"version": "1.0",
"debug": true,
"appName":"Humbingo"
}

View File

@ -1,134 +0,0 @@
import { createContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
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();
export default AuthContext;
export const AuthProvider = ({ children }) => {
const [authToken, setAuthToken] = useState(() =>
localStorage.getItem("authToken") ? JSON.parse(localStorage.getItem("authToken")) : null
);
const [user, setUser] = useState(() =>
localStorage.getItem("authToken") ? jwtDecode(localStorage.getItem("authToken")) : {}
);
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const loginUser = (e) => {
e.preventDefault();
axios
.post(getAPI("token"), {
username: e.target.username.value,
password: e.target.password.value,
})
.then((response) => {
checkSuperUser(response.data.access);
setAuthToken(response.data);
setUser(jwtDecode(response.data.access));
localStorage.setItem("authToken", JSON.stringify(response.data));
})
.catch((error) => {
toast.error("Username or password incorrect");
console.error(error);
});
};
const checkSuperUser = async (token) => {
try {
const response = await axios.get(getAPI("isSuperUser"), {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.data.is_superuser) {
toast.success("Welcome, Admin");
} else {
logOutUser("User is not super user");
}
} catch (error) {
toast.error("Error checking user permissions");
console.error(error);
}
};
const refreshToken = () => {
if (authToken?.refresh) {
axios
.post(getAPI("refreshToken"), { refresh: `${authToken.refresh}` })
.then((response) => {
checkSuperUserRefresh(response.data.access);
setAuthToken(response.data);
setUser(jwtDecode(response.data.access));
localStorage.setItem("authToken", JSON.stringify(response.data));
})
.catch(logOutUser); // Log out if there's an error
} else {
logOutUser();
}
if (loading) {
setLoading(false);
}
};
const logOutUser = (message) => {
setAuthToken(null);
setUser({});
localStorage.removeItem("authToken");
if (message) toast.error(message);
navigate("login");
};
const checkSuperUserRefresh = async (token) => {
try {
const response = await axios.get(getAPI("isSuperUser"), {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.data.is_superuser) {
logOutUser("User is not super user");
}
} catch (error) {
toast.error("Error checking user permissions");
console.error(error);
}
};
useEffect(() => {
if (loading) {
refreshToken();
}
const fourMinutes = 1000 * 60 * 4;
const interval = setInterval(() => {
if (authToken) {
refreshToken();
}
}, fourMinutes);
return () => clearInterval(interval);
}, [authToken, loading]);
const contextData = {
user,
loginUser,
logOutUser,
authToken,
};
return (
<AuthContext.Provider value={contextData}>
{loading ? null : children}
</AuthContext.Provider>
);
};

View File

@ -1,18 +0,0 @@
import {useContext } from "react"
import React from 'react'
import AuthContext from "./AuthContext";
import LoginPage from "../../components/pages/Login";
import Header from "../../components/common/Header";
export const Auth = (props) => {
let {user} = useContext(AuthContext);
return (
(user.user_id)? <><Header />{props.component}</> : <LoginPage/>
)
}
export default Auth;