Commit 24010b00 authored by Muhammad Ameen's avatar Muhammad Ameen 💻

Biler Plate With React router dom, antd dashboard, eslint and prettier config

parent 2766edfd
module.exports = {
env: {
es6: true,
browser: true,
es2021: true,
},
extends: ["plugin:jest/recommended", "airbnb"],
extends: ["plugin:jest/recommended", "airbnb", "prettier"],
overrides: [],
parserOptions: {
ecmaVersion: "latest",
......@@ -14,6 +15,7 @@ module.exports = {
semi: ["error", "always"],
quotes: ["error", "double"],
"comma-dangle": "off",
"react/prop-types": 0,
"import/prefer-default-export": 0,
"default-param-last": 0,
"react/react-in-jsx-scope": 0,
......@@ -21,6 +23,7 @@ module.exports = {
"react/jsx-uses-react": 0,
"import/no-extraneous-dependencies": ["error", { devDependencies: true }],
"import/no-unresolved": 0,
"react/jsx-props-no-spreading": "off",
"react/function-component-definition": [
2,
{
......
module.exports = {
trailingComma: "es5",
tabWidth: 2,
semi: true,
};
{
"cSpell.words": ["Sider"]
}
This diff is collapsed.
......@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.2.5",
"@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
......@@ -49,16 +50,18 @@
]
},
"devDependencies": {
"eslint": "^8.23.1",
"eslint": "^8.47.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"prettier": "^2.8.8",
"tailwindcss": "^3.1.8"
}
}
import logo from "./logo.svg";
import { Dashboard, Home, Login } from "pages";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import "./App.css";
import { PrivateRoute, PublicRoute } from "routes";
const App = () => (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit
<code>src/App.js</code>
and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
const router = createBrowserRouter([
{
path: "/",
element: (
<PublicRoute>
<Login />
</PublicRoute>
),
},
{
path: "admin",
element: (
<PrivateRoute>
<Dashboard />
</PrivateRoute>
),
children: [
{
path: "home",
element: <Home />,
},
],
},
]);
const App = () => <RouterProvider router={router} />;
export default App;
import {
DeleteOutlined,
EditOutlined,
ShoppingOutlined,
RightCircleFilled,
BarChartOutlined,
UserAddOutlined,
FileDoneOutlined,
PieChartFilled,
CheckOutlined,
WarningOutlined,
CloseCircleFilled,
EyeOutlined,
DownloadOutlined,
CloseOutlined,
MinusCircleOutlined,
PlusOutlined,
UploadOutlined,
UserOutlined,
LockOutlined,
} from "@ant-design/icons";
import { Tooltip } from "antd";
const CustomIcon = ({
name,
classname,
additionalProps,
tooltipText = "noText",
...rest
}) => {
const IconType = {
DeleteOutlined,
EditOutlined,
FileDoneOutlined,
ShoppingOutlined,
RightCircleFilled,
BarChartOutlined,
UserAddOutlined,
PieChartFilled,
CheckOutlined,
WarningOutlined,
CloseCircleFilled,
EyeOutlined,
DownloadOutlined,
CloseOutlined,
MinusCircleOutlined,
PlusOutlined,
UploadOutlined,
UserOutlined,
LockOutlined,
};
const GenericComponent = IconType[name];
return (
<Tooltip title={tooltipText}>
<GenericComponent className={classname} {...rest} />
</Tooltip>
);
};
export default CustomIcon;
export { default as CustomIcon } from "./CustomIcon";
export * from "antd";
export * from "./atoms";
export * from "./molecules";
// import { Dropdown, Menu, Space } from "antd";
import { useEffect } from "react";
// import { useDispatch } from "react-redux";
// import { useNavigate } from "react-router-dom";
// import { userLogout } from "redux/reducers";
import {
// DownOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
// SmileOutlined,
// LogoutOutlined,
} from "@ant-design/icons";
// import { ROUTES, UI_TEXT } from "constants";
// import { useSessionStorage } from "hooks";
import { Button } from "antd";
const HeaderTop = ({ isCollapsed, setCollapsed }) => {
// const { clear } = useSessionStorage();
// const navigate = useNavigate();
// const dispatch = useDispatch();
// const onLogout = () => {
// dispatch(userLogout());
// clear();
// navigate(ROUTES.ADMIN);
// };
// const items = [
// {
// key: "1",
// label: (
// <a target="_blank" rel="noopener noreferrer" href="/">
// {/* {UI_TEXT.CTA.PROFILE} */}
// Profile
// </a>
// ),
// icon: <SmileOutlined />,
// disabled: true,
// },
// {
// key: "2",
// danger: true,
// label: (
// <span tabIndex={-1} role="link" onClick={onLogout}>
// {/* {UI_TEXT.CTA.LOGOUT} */}
// Logout
// </span>
// ),
// icon: <LogoutOutlined />,
// },
// ];
const toggleCollapsed = () => {
setCollapsed(!isCollapsed);
localStorage.setItem("isCollapse", isCollapsed);
};
const checkCollapsed = localStorage.getItem("isCollapse");
useEffect(() => {
if (checkCollapsed === "true") {
setCollapsed(false);
} else if (checkCollapsed === "false") {
setCollapsed(true);
}
}, []);
return (
<Button
type="text"
icon={isCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={toggleCollapsed}
style={{
fontSize: "16px",
width: 64,
height: 64,
}}
/>
);
};
export default HeaderTop;
import { Layout, Menu } from "antd";
import { UserOutlined } from "@ant-design/icons";
import logo from "../../assets/images/logo.png";
const { Sider } = Layout;
const SideNavigation = ({ isCollapsed, navigate }) => (
<Sider
trigger={null}
collapsible
collapsed={isCollapsed}
style={{ backgroundColor: "#fff" }}
>
<div className="logo" style={{ padding: 20 }}>
<img
src={logo}
alt="Logo"
style={{ width: "100%", height: "100%", objectFit: "cover" }}
/>
</div>
<Menu
mode="inline"
defaultSelectedKeys={["1"]}
onClick={({ key }) => {
navigate(key);
}}
items={[
{
key: "/admin",
icon: <UserOutlined />,
label: "Dashboard",
},
{
key: "home",
icon: <UserOutlined />,
label: "Create Papers",
},
{
key: "3",
icon: <UserOutlined />,
label: "Make Exam",
},
]}
/>
</Sider>
);
export default SideNavigation;
export { default as SideNavigation } from "./SideNavigation";
export { default as HeaderTop } from "./HeaderTop";
const COLORS = {
main: "#136263",
primary: "#18a39b",
secondary: "#fff",
};
export default COLORS;
export { default as COLORS } from "./colors";
export { default as useAuth } from "./useAuth";
export { default as useSessionStorage } from "./useSessionStorage";
import useSessionStorage from "./useSessionStorage";
const useAuth = () => {
const { getItem } = useSessionStorage();
const accessToken = getItem("access_token");
const storageRole = getItem("ROLE");
const getToken = () => {
if (accessToken) {
return accessToken;
}
return null;
};
const isAccessible = (roles) => {
const userHasRequiredRole = !!(accessToken && roles.includes(storageRole));
if (accessToken && !userHasRequiredRole) {
return false;
}
return true;
};
return {
getToken,
isAccessible,
};
};
export default useAuth;
const useSessionStorage = () => {
const getItem = (key) => {
const storedValue = sessionStorage.getItem(key);
if (!storedValue) {
return null;
}
return JSON.parse(storedValue);
};
const setItem = (key, value) => {
sessionStorage.setItem(key, JSON.stringify(value));
};
const removeItem = (key) => {
sessionStorage.removeItem(key);
};
const clear = () => {
sessionStorage.clear();
};
return {
getItem,
setItem,
removeItem,
clear,
};
};
export default useSessionStorage;
......@@ -3,17 +3,19 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import { Provider } from "react-redux";
import store from "redux/store";
import { ConfigProvider } from "components";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
<ConfigProvider>
<App />
</ConfigProvider>
</Provider>
</React.StrictMode>,
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
......
import { Layout } from "antd";
import { HeaderTop, SideNavigation } from "components";
import { useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
const { Content, Header } = Layout;
const Dashboard = () => {
const [collapsed, setCollapsed] = useState(false);
const navigate = useNavigate();
return (
<Layout style={{ minHeight: "100vh" }}>
<SideNavigation isCollapsed={collapsed} navigate={navigate} />
<Layout>
<Header style={{ backgroundColor: "#fff", paddingLeft: 10 }}>
<HeaderTop isCollapsed={collapsed} setCollapsed={setCollapsed} />
</Header>
<Content
style={{
margin: "24px 16px",
padding: 24,
minHeight: 280,
background: "#fff",
}}
>
<Outlet />
</Content>
</Layout>
</Layout>
);
};
export default Dashboard;
const Home = () => <div>Home</div>;
export default Home;
export { default as Home } from "./Home";
import { Form, Input, Button, Layout, CustomIcon } from "components";
// import { UserOutlined, LockOutlined } from "@ant-design/icons";
import styles from "./login.module.css";
const { Content } = Layout;
const Login = () => {
const onFinish = (values) => {
console.log("Received values of form:", values);
// Here you can add your login logic using the submitted values
};
return (
<Layout>
<Content className={styles.loginContent}>
<div className={styles.loginForm}>
<h1>Login</h1>
<Form
name="normal_login"
initialValues={{ remember: true }}
onFinish={onFinish}
>
<Form.Item
name="email"
rules={[
{ required: true, message: "Please input your Email!" },
{
type: "email",
message: "Please enter a valid email address!",
},
]}
>
<Input
prefix={
// eslint-disable-next-line react/jsx-wrap-multilines
<CustomIcon
name="UserOutlined"
classname={styles.siteFormItemIcon}
/>
}
placeholder="Email"
/>
</Form.Item>
<Form.Item
name="password"
rules={[
{ required: true, message: "Please input your Password!" },
]}
>
<Input
prefix={
// eslint-disable-next-line react/jsx-wrap-multilines
<CustomIcon
name="LockOutlined"
classname={styles.siteFormItemIcon}
/>
}
type="password"
placeholder="Password"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className={styles.loginFormButton}
>
Log in
</Button>
</Form.Item>
</Form>
</div>
</Content>
</Layout>
);
};
export default Login;
export { default as Login } from "./Login";
/* LoginForm.module.css */
.loginContent {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.loginForm {
width: 300px;
padding: 20px;
border: 1px solid #e8e8e8;
border-radius: 5px;
background-color: #ffffff;
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
}
.loginFormButton {
width: 100%;
}
.siteFormItemIcon {
color: rgba(0, 0, 0, 0.25);
}
export * from "./Login";
export { default as Dashboard } from "./Dashboard";
export * from "./Home";
/* eslint-disable no-unused-vars */
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "hooks";
import { ROUTES } from "constants";
// import Forbidden from "pages/Forbidden";
const PrivateRoute = ({ children, roles }) => {
const location = useLocation();
const { getToken, isAccessible } = useAuth();
const token = getToken();
// const routePermission = isAccessible(roles);
if (!token) {
return <Navigate to="/" state={{ from: location }} />;
}
// if (token && !routePermission) {
// return <Forbidden />;
// }
return children;
};
export default PrivateRoute;
import { Navigate } from "react-router-dom";
import { useAuth } from "hooks";
const PublicRoute = ({ children }) => {
const { getToken } = useAuth();
const token = getToken();
return !token ? children : <Navigate to="/dashboard" />;
};
export default PublicRoute;
export { default as PrivateRoute } from "./Private.route";
export { default as PublicRoute } from "./Public.route";
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment