├── .gitignore ├── README.md ├── package.json ├── public ├── _redirects ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.tsx ├── Routes.tsx ├── components │ ├── Dashboard.tsx │ ├── DynamicForm.tsx │ ├── InnerContent.tsx │ ├── Login.tsx │ ├── NewUser.tsx │ ├── PermissionDenied.tsx │ ├── ProtectedRoutes.tsx │ ├── PublicRoutes.tsx │ ├── Settings.tsx │ ├── Sidebar.tsx │ ├── SingleUser.tsx │ ├── Tab1.tsx │ ├── Tab2.tsx │ ├── Tab3.tsx │ ├── TabInner.tsx │ ├── TabNav.tsx │ ├── Tabs.tsx │ ├── Users.tsx │ └── WithPermission.tsx ├── config │ ├── index.ts │ └── navigation.ts ├── index.tsx └── styles.css ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Router 2 | 3 | 4 | ## [Video Tutorial](https://youtu.be/3kNpIbTEuos) 5 | ## [Demo link ](https://bw-react-router.netlify.app) 6 | 7 | ### Tasks 8 | 9 | - Create react app 10 | - Setup components 11 | - Setup Router 12 | - Style 13 | 14 | ## Tech stacks 15 | + React 16 | + React Router DOM 17 | + CSS 18 | 19 | ### Run Locally 20 | + Clone this Repository or download the Repository 21 | + Run `yarn install` (Assuming you have node installed, yarn ready to use) 22 | + Run `yarn start` 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-t", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^12.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "@types/jest": "^27.0.1", 10 | "@types/node": "^16.7.13", 11 | "@types/react": "^17.0.20", 12 | "@types/react-dom": "^17.0.9", 13 | "@types/uuid": "^8.3.4", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-router-dom": "^6.2.1", 17 | "react-scripts": "5.0.0", 18 | "typescript": "^4.4.2", 19 | "uuid": "^8.3.2", 20 | "web-vitals": "^2.1.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebikashweb/react-router-6/d705c652405f338ec6b2f9070ad312dd8a98681a/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebikashweb/react-router-6/d705c652405f338ec6b2f9070ad312dd8a98681a/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebikashweb/react-router-6/d705c652405f338ec6b2f9070ad312dd8a98681a/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MainRoutes from './Routes' 4 | import Sidebar from './components/Sidebar' 5 | 6 | 7 | import './styles.css' 8 | 9 | function App() { 10 | return ( 11 |
12 | 13 | {/** Sidebar */} 14 | 15 | 16 | {/** Inner container */} 17 | 18 |
19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /src/Routes.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Routes, Route, Navigate} from "react-router-dom" 3 | 4 | import InnerContent from "./components/InnerContent" 5 | import Dashboard from "./components/Dashboard" 6 | import Tabs from "./components/Tabs" 7 | import Settings from "./components/Settings" 8 | import Login from "./components/Login" 9 | import Users from "./components/Users" 10 | import SingleUser from "./components/SingleUser" 11 | import NewUser from "./components/NewUser" 12 | import DynamicForm from "./components/DynamicForm" 13 | 14 | import Tab1 from "./components/Tab1" 15 | import Tab2 from "./components/Tab2" 16 | import Tab3 from "./components/Tab3" 17 | 18 | import ProtectedRoutes from "./components/ProtectedRoutes" 19 | import PublicRoutes from "./components/PublicRoutes" 20 | import PermissionDenied from "./components/PermissionDenied" 21 | 22 | const MainRoutes = () => ( 23 | 24 | {/** Protected Routes */} 25 | {/** Wrap all Route under ProtectedRoutes element */} 26 | }> 27 | }> 28 | } /> 29 | } /> 30 | }> 31 | } /> 32 | } /> 33 | }> 34 | } /> 35 | 36 | } /> 37 | 38 | } /> 39 | } /> 40 | } 43 | /> 44 | } /> 45 | } /> 46 | 47 | 48 | 49 | {/** Public Routes */} 50 | {/** Wrap all Route under PublicRoutes element */} 51 | }> 52 | } /> 53 | 54 | 55 | {/** Permission denied route */} 56 | } /> 57 | 58 | ) 59 | 60 | export default MainRoutes 61 | -------------------------------------------------------------------------------- /src/components/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Dashboard=() =>{ 4 | return
5 |

Welcome to the main Dashboard

6 |
; 7 | } 8 | 9 | export default Dashboard; 10 | -------------------------------------------------------------------------------- /src/components/DynamicForm.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {v4 as uuidv4} from "uuid" 3 | 4 | const DynamicForm = () => { 5 | const [inviteMembers, setInviteMembers] = React.useState([ 6 | { 7 | name: "", 8 | email: "", 9 | id: uuidv4(), 10 | }, 11 | ]) 12 | 13 | const [teamsMembers, setTeamMembers] = React.useState([ 14 | { 15 | teamName: "", 16 | id: uuidv4(), 17 | members: [ 18 | { 19 | name: "", 20 | email: "", 21 | id: uuidv4(), 22 | }, 23 | ], 24 | }, 25 | ]) 26 | 27 | //add new form field for adding member 28 | const addMemberRow = () => { 29 | //Todo generate random id 30 | 31 | let _inviteMembers = [...inviteMembers] 32 | _inviteMembers.push({ 33 | name: "", 34 | email: "", 35 | id: uuidv4(), 36 | }) 37 | setInviteMembers(_inviteMembers) 38 | } 39 | 40 | //remove form field for adding member 41 | const removeMemberRow = (id: string) => { 42 | //Todo generate random id 43 | 44 | let _inviteMembers = [...inviteMembers] 45 | _inviteMembers = _inviteMembers.filter((member) => member.id !== id) 46 | setInviteMembers(_inviteMembers) 47 | } 48 | 49 | //handle email row change 50 | const handleMemberChange = ( 51 | id: string, 52 | event: React.ChangeEvent, 53 | ) => { 54 | //find the index to be changed 55 | const index = inviteMembers.findIndex((m) => m.id === id) 56 | 57 | let _inviteMembers = [...inviteMembers] as any 58 | _inviteMembers[index][event.target.name] = event.target.value 59 | setInviteMembers(_inviteMembers) 60 | } 61 | 62 | //handle invitation for members 63 | const handleInvitation = () => { 64 | console.table(inviteMembers) 65 | } 66 | 67 | //handle add team 68 | const handleAddTeam = () => { 69 | let _teamsMembers = [...teamsMembers] 70 | _teamsMembers.push({ 71 | teamName: "", 72 | id: uuidv4(), 73 | members: [ 74 | { 75 | name: "", 76 | email: "", 77 | id: uuidv4(), 78 | }, 79 | ], 80 | }) 81 | setTeamMembers(_teamsMembers) 82 | } 83 | 84 | //handle new memeber inside selected team 85 | const addNewMemberInTeam = (id: string) => { 86 | const index = teamsMembers.findIndex((team) => team.id === id) 87 | let _teamsMembers = [...teamsMembers] 88 | _teamsMembers[index].members.push({ 89 | name: "", 90 | email: "", 91 | id: uuidv4(), 92 | }) 93 | setTeamMembers(_teamsMembers) 94 | } 95 | 96 | //handle team data 97 | const handleTeamData = ( 98 | id: string, 99 | event: React.ChangeEvent, 100 | ) => { 101 | const index = teamsMembers.findIndex((team) => team.id === id) 102 | 103 | let _teamsMembers = [...teamsMembers] as any 104 | 105 | _teamsMembers[index][event.target.name] = event.target.value 106 | setTeamMembers(_teamsMembers) 107 | } 108 | //handle inner member data in team 109 | const handleMemberInTeamData = ( 110 | teamId: string, 111 | memberId: string, 112 | event: React.ChangeEvent, 113 | ) => { 114 | const teamIndex = teamsMembers.findIndex((team) => team.id === teamId) 115 | let _teamsMembers = [...teamsMembers] as any 116 | const memberIndex = teamsMembers[teamIndex].members.findIndex( 117 | (m) => m.id === memberId, 118 | ) 119 | _teamsMembers[teamIndex].members[memberIndex][event.target.name] = 120 | event.target.value 121 | setTeamMembers(_teamsMembers) 122 | } 123 | 124 | //save teams data 125 | const saveTeamData = () => { 126 | console.table(teamsMembers) 127 | } 128 | 129 | return ( 130 |
131 |

Invite new members to your team

132 | 133 |
134 | {inviteMembers.map((member) => ( 135 |
136 |
137 | 138 | handleMemberChange(member.id, e)} 142 | /> 143 |
144 |
145 | 146 | handleMemberChange(member.id, e)} 150 | /> 151 |
152 | {inviteMembers.length > 1 && ( 153 | 154 | )} 155 | 156 | 157 |
158 | ))} 159 | 160 | 164 |
165 | 166 | {/** 2nd level dynamic example */} 167 | 168 |
169 | {teamsMembers.map((team) => ( 170 |
171 |
172 | 173 | handleTeamData(team.id, e)} 176 | type="text" 177 | /> 178 |

Members

179 | {team.members.map((member) => ( 180 |
181 |
182 | 183 | 187 | handleMemberInTeamData(team.id, member.id, e) 188 | } 189 | /> 190 |
191 |
192 | 193 | 197 | handleMemberInTeamData(team.id, member.id, e) 198 | } 199 | /> 200 |
201 | 202 |
203 | ))} 204 |
205 |
206 | ))} 207 |
208 | 211 |
212 |
213 | ) 214 | } 215 | 216 | export default DynamicForm 217 | -------------------------------------------------------------------------------- /src/components/InnerContent.tsx: -------------------------------------------------------------------------------- 1 | import {Outlet} from 'react-router-dom' 2 | 3 | const InnerContent=() =>{ 4 | return
5 | 6 | 7 |
; 8 | } 9 | 10 | export default InnerContent; 11 | -------------------------------------------------------------------------------- /src/components/Login.tsx: -------------------------------------------------------------------------------- 1 | import {useNavigate} from "react-router-dom" 2 | 3 | const Login = () => { 4 | const navigate = useNavigate() 5 | 6 | //ADMIN 7 | //USER 8 | 9 | const login = () => { 10 | localStorage.setItem("user", JSON.stringify({role: "ADMIN"})) 11 | navigate("/dashboard") 12 | } 13 | 14 | return ( 15 |
16 |

Welcome to login page!

17 |

Please loging to continue

18 | 19 |
20 | ) 21 | } 22 | 23 | export default Login 24 | -------------------------------------------------------------------------------- /src/components/NewUser.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Link, useLocation} from "react-router-dom" 3 | 4 | import {UserType} from "./Users" 5 | 6 | const NewUser = () => { 7 | const [newUserData, setNewUserData] = React.useState({ 8 | id: 0, 9 | name: "", 10 | phone: "", 11 | email: "", 12 | website: "", 13 | }) 14 | 15 | const location = useLocation() 16 | 17 | //location state 18 | const [locatioState, setLocationState] = React.useState({ 19 | from: "", 20 | userName: "", 21 | }) 22 | 23 | React.useEffect(() => { 24 | console.log("location from new user", location) 25 | if (location.state) { 26 | let _state = location.state as any 27 | setLocationState(_state) 28 | } 29 | }, [location]) 30 | 31 | const handleChange = (e: any) => { 32 | const _newUserData = { 33 | ...newUserData, 34 | } as any 35 | 36 | _newUserData[e.target.name] = e.target.value 37 | 38 | setNewUserData(_newUserData) 39 | } 40 | 41 | const handlePostData = () => { 42 | console.log("new user data", newUserData) 43 | 44 | //post request for API 45 | 46 | alert("Data posted successfully") 47 | } 48 | 49 | return ( 50 |
51 |

52 | Add a new user from:{locatioState.from} by {locatioState.userName} 53 |

54 | Go back 55 | 56 |
57 |
58 | 59 | 66 |
67 |
68 | 69 | 76 |
77 |
78 | 79 | 86 |
87 |
88 | 89 | 96 |
97 |
98 | 99 |
100 |
101 |
102 | ) 103 | } 104 | 105 | export default NewUser 106 | -------------------------------------------------------------------------------- /src/components/PermissionDenied.tsx: -------------------------------------------------------------------------------- 1 | const PermissionDenied = () => { 2 | return ( 3 |
4 |

Permission denied!

5 |
6 | ) 7 | } 8 | 9 | export default PermissionDenied 10 | -------------------------------------------------------------------------------- /src/components/ProtectedRoutes.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import {Navigate, Outlet} from "react-router-dom" 4 | 5 | const useAuth = () => { 6 | //get item from localstorage 7 | 8 | let user: any 9 | 10 | const _user = localStorage.getItem("user") 11 | 12 | if (_user) { 13 | user = JSON.parse(_user) 14 | console.log("user", user) 15 | } 16 | if (user) { 17 | return { 18 | auth: true, 19 | role: user.role, 20 | } 21 | } else { 22 | return { 23 | auth: false, 24 | role: null, 25 | } 26 | } 27 | } 28 | 29 | //protected Route state 30 | type ProtectedRouteType = { 31 | roleRequired?: "ADMIN" | "USER" 32 | } 33 | 34 | const ProtectedRoutes = (props: ProtectedRouteType) => { 35 | const {auth, role} = useAuth() 36 | 37 | //if the role required is there or not 38 | if (props.roleRequired) { 39 | return auth ? ( 40 | props.roleRequired === role ? ( 41 | 42 | ) : ( 43 | 44 | ) 45 | ) : ( 46 | 47 | ) 48 | } else { 49 | return auth ? : 50 | } 51 | } 52 | 53 | export default ProtectedRoutes 54 | -------------------------------------------------------------------------------- /src/components/PublicRoutes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Navigate, Outlet} from 'react-router-dom' 4 | 5 | const useAuth=()=>{ 6 | const user=localStorage.getItem('user') 7 | if(user){ 8 | return true 9 | } else { 10 | return false 11 | } 12 | } 13 | 14 | const PublicRoutes=(props:any) =>{ 15 | 16 | const auth=useAuth() 17 | 18 | return auth?: 19 | } 20 | 21 | export default PublicRoutes; 22 | -------------------------------------------------------------------------------- /src/components/Settings.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | const Settings = () => { 4 | return ( 5 |
6 |

Welcome to settings page

7 | <> 8 |
9 | ) 10 | } 11 | 12 | export default Settings 13 | -------------------------------------------------------------------------------- /src/components/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Link, useLocation, useNavigate} from "react-router-dom" 3 | 4 | import {navigationItems} from "../config" 5 | 6 | const Sidebar = () => { 7 | const useAuth = () => { 8 | const user = localStorage.getItem("user") 9 | if (user) { 10 | return true 11 | } else { 12 | return false 13 | } 14 | } 15 | const user = useAuth() 16 | const location = useLocation() 17 | const navigation = useNavigate() 18 | 19 | const logout = () => { 20 | localStorage.removeItem("user") 21 | navigation("/login") 22 | } 23 | 24 | return ( 25 |
26 |
27 | {user && ( 28 | <> 29 | {navigationItems.sidebar.map((item) => ( 30 | 36 | {item.name} 37 | 38 | ))} 39 | {location.pathname !== "/login" && ( 40 | 41 | )} 42 | 43 | )} 44 | {!user && ( 45 | 48 | Login 49 | 50 | )} 51 |
52 |
53 | ) 54 | } 55 | 56 | export default Sidebar 57 | -------------------------------------------------------------------------------- /src/components/SingleUser.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | useParams, 4 | Link, 5 | } from 'react-router-dom' 6 | 7 | import { UserType } from './Users' 8 | 9 | const SingleUser = () => { 10 | const params = useParams() 11 | 12 | const [user, setUser] = 13 | React.useState() 14 | 15 | React.useEffect(() => { 16 | const singleUserApiUrl = `https://jsonplaceholder.typicode.com/users/${params.userId}` 17 | //fetch users from json placeholder 18 | fetch(singleUserApiUrl) 19 | .then((response) => 20 | response.json(), 21 | ) 22 | .then((json) => setUser(json)) 23 | }, [params]) 24 | 25 | return ( 26 | <> 27 | Go back 28 | {user && ( 29 |
32 |

33 | Name: 34 | 35 | {user.name} 36 | 37 |

38 | 39 |

40 | Email: 41 | 42 | {user.email} 43 | 44 |

45 |

46 | Phone: 47 | 48 | {user.phone} 49 | 50 |

51 |

52 | Website: 53 | 54 | {user.website} 55 | 56 |

57 |
58 | )} 59 | 60 | ) 61 | } 62 | 63 | export default SingleUser 64 | -------------------------------------------------------------------------------- /src/components/Tab1.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Link} from "react-router-dom" 3 | 4 | const Tab1 = () => { 5 | return ( 6 |
7 |

Welcome to Tab1 page

8 | 9 | Add a new user 10 | 11 |
12 | ) 13 | } 14 | 15 | export default Tab1 16 | -------------------------------------------------------------------------------- /src/components/Tab2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Tab2=() =>{ 4 | return
5 |

Welcome to Tab2 page

6 |
; 7 | } 8 | 9 | export default Tab2; 10 | -------------------------------------------------------------------------------- /src/components/Tab3.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import WithPermission from "./WithPermission" 3 | 4 | const Tab3 = () => { 5 | return ( 6 |
7 |

Welcome to Tab3 page

8 | 9 |

Welcome Admin!

10 | 11 |
12 |
13 | ) 14 | } 15 | 16 | export default Tab3 17 | -------------------------------------------------------------------------------- /src/components/TabInner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const TabInner=() =>{ 4 | return
5 |

Welcome to TabInner page

6 |
; 7 | } 8 | 9 | export default TabInner; 10 | -------------------------------------------------------------------------------- /src/components/TabNav.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link, useLocation} from 'react-router-dom' 3 | 4 | const TabNav=() =>{ 5 | 6 | const location = useLocation() 7 | 8 | return
9 | Tab 1 10 | Tab 2 11 | Tab 3 12 |
; 13 | } 14 | 15 | export default TabNav; 16 | -------------------------------------------------------------------------------- /src/components/Tabs.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Outlet} from "react-router-dom" 3 | 4 | import TavNav from "./TabNav" 5 | 6 | const Tabs = (props: any) => { 7 | const {userName} = props.props 8 | return ( 9 |
10 |

Tabs demo page {userName}

11 | 12 | {/** Tab navigation */} 13 | 14 | {/** Tab inner content */} 15 | 16 |
17 | ) 18 | } 19 | 20 | export default Tabs 21 | -------------------------------------------------------------------------------- /src/components/Users.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Link} from "react-router-dom" 3 | 4 | export type UserType = { 5 | id: number 6 | name: string 7 | email: string 8 | phone: string 9 | website: string 10 | } 11 | type UsersType = Array 12 | 13 | const Users = (props: any) => { 14 | const [users, setUsers] = React.useState([]) 15 | 16 | React.useEffect(() => { 17 | //fetch users from json placeholder 18 | fetch("https://jsonplaceholder.typicode.com/users") 19 | .then((response) => response.json()) 20 | .then((json) => setUsers(json)) 21 | }, []) 22 | 23 | return ( 24 |
25 |

All users

26 | 27 | Add a new user 28 | 29 |
30 | {users && 31 | users.map((user) => ( 32 | //single user card 33 |
34 | 35 |

36 | Name: 37 | {user.name} 38 |

39 | 40 |
41 | ))} 42 |
43 |
44 | ) 45 | } 46 | 47 | export default Users 48 | -------------------------------------------------------------------------------- /src/components/WithPermission.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | type Props = { 4 | roleRequired: "ADMIN" | "USER" 5 | message?: string 6 | children?: React.ReactNode 7 | } 8 | 9 | const useRole = () => { 10 | let user: any 11 | 12 | const _user = localStorage.getItem("user") 13 | 14 | if (_user) { 15 | user = JSON.parse(_user) 16 | } 17 | if (user) { 18 | return user.role 19 | } else { 20 | return "USER" 21 | } 22 | } 23 | 24 | const WithPermission = (props: Props) => { 25 | const {roleRequired, message, children} = props 26 | const role = useRole() 27 | return ( 28 | <>{roleRequired === role ? children :

{message ? message : ""}

} 29 | ) 30 | } 31 | 32 | export default WithPermission 33 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './navigation' -------------------------------------------------------------------------------- /src/config/navigation.ts: -------------------------------------------------------------------------------- 1 | export const navigationItems = { 2 | sidebar: [ 3 | { 4 | name: 'Dashboard ', 5 | to: '/dashboard', 6 | text: 'dashboard', 7 | 8 | }, 9 | { 10 | name: 'Tabs Demo ', 11 | to: '/tabs', 12 | text:'tabsdemo' 13 | }, 14 | { 15 | name: 'Dynamic Form ', 16 | to: '/dynamic-form', 17 | text:'dynamicform' 18 | }, 19 | { 20 | name: 'Settings ', 21 | to: '/settings', 22 | text:'settings' 23 | }, 24 | { 25 | name: 'Users ', 26 | to: '/users', 27 | text:'users' 28 | }, 29 | { 30 | name: 'Example ', 31 | to: '/example', 32 | text:'example' 33 | }, 34 | ], 35 | footer: [], 36 | 37 | 38 | 39 | } 40 | export default navigationItems -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {BrowserRouter} from 'react-router-dom' 4 | 5 | import App from './App'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Rubik:wght@400;500&display=swap'); 2 | 3 | html, body, div, span, applet, object, iframe, 4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | a, abbr, acronym, address, big, cite, code, 6 | del, dfn, em, img, ins, kbd, q, s, samp, 7 | small, strike, strong, sub, sup, tt, var, 8 | b, u, i, center, 9 | dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, embed, 13 | figure, figcaption, footer, header, hgroup, 14 | menu, nav, output, ruby, section, summary, 15 | time, mark, audio, video { 16 | margin: 0; 17 | padding: 0; 18 | border: 0; 19 | font-size: 100%; 20 | font: inherit; 21 | vertical-align: baseline; 22 | } 23 | /* HTML5 display-role reset for older browsers */ 24 | article, aside, details, figcaption, figure, 25 | footer, header, hgroup, menu, nav, section { 26 | display: block; 27 | } 28 | body { 29 | line-height: 1; 30 | } 31 | ol, ul { 32 | list-style: none; 33 | } 34 | blockquote, q { 35 | quotes: none; 36 | } 37 | blockquote:before, blockquote:after, 38 | q:before, q:after { 39 | content: ''; 40 | content: none; 41 | } 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | 47 | 48 | body{ 49 | font-family: 'Rubik', sans-serif; 50 | } 51 | 52 | 53 | .app{ 54 | display: flex; 55 | } 56 | .sidebar{ 57 | width:200px; 58 | background-color: #365262; 59 | color: #EBF8FE; 60 | padding-top:200px; 61 | padding-left:20px; 62 | height:100vh; 63 | 64 | } 65 | .sidebar__items{ 66 | display: flex; 67 | flex-direction: column; 68 | 69 | } 70 | .sidebar__items a{ 71 | color: #EBF8FE; 72 | font-size:20px; 73 | margin-bottom:40px; 74 | text-decoration: none; 75 | } 76 | .sidebar_active{ 77 | text-decoration: underline!important; 78 | } 79 | .tab-nav{ 80 | margin-top:70px; 81 | margin-bottom:60px; 82 | } 83 | .tab-nav a{ 84 | color:#A2A2A2; 85 | font-size:20px; 86 | font-weight: 500; 87 | padding:20px; 88 | text-decoration: none; 89 | } 90 | 91 | .inner-content{ 92 | padding-top:70px; 93 | padding-left:40px; 94 | } 95 | 96 | h1{ 97 | color:#365262; 98 | font-size:30px; 99 | font-weight: 500; 100 | } 101 | h2{ 102 | color:#365262; 103 | font-size:20px; 104 | font-weight: 500; 105 | } 106 | .login{ 107 | padding-top:70px; 108 | padding-left:40px; 109 | } 110 | .login button{ 111 | margin-top:40px; 112 | } 113 | .login p{ 114 | margin-top:30px; 115 | } 116 | .tab_active{ 117 | color:#365262!important; 118 | text-decoration: underline!important; 119 | } 120 | .users h1{ 121 | margin-bottom:20px; 122 | } 123 | .users__card{ 124 | margin-top:40px; 125 | border:1px solid #365262; 126 | padding:20px; 127 | } 128 | .users__card p{ 129 | margin-bottom: 10px; 130 | } 131 | .new-user__form{ 132 | margin-top:40px; 133 | 134 | } 135 | .new-user__form-group{ 136 | margin-top:10px; 137 | padding:10px; 138 | } 139 | .new-user__form-group input{ 140 | margin-left:20px; 141 | padding:10px 10px; 142 | } 143 | .new-user__form-group button{ 144 | padding:10px; 145 | } 146 | 147 | .form-row{ 148 | display: flex; 149 | margin-top:40px; 150 | align-items: flex-end; 151 | margin-bottom:40px; 152 | } 153 | .dynamic-form button { 154 | padding:10px 10px; 155 | max-height: 40px; 156 | } 157 | .input-group{ 158 | display: flex; 159 | flex-direction: column; 160 | align-items: flex-start; 161 | margin-right:10px; 162 | 163 | } 164 | 165 | .input-group input{ 166 | padding:10px 10px; 167 | margin-top:10px; 168 | 169 | } 170 | .form-row button { 171 | padding:10px 10px; 172 | max-height: 40px; 173 | } 174 | 175 | .row-section{ 176 | margin-top:40px; 177 | } 178 | .row-section h3{ 179 | color:#365262; 180 | margin-top:30px; 181 | } 182 | .row-section__inner{ 183 | border:1px solid grey; 184 | padding:20px; 185 | margin-bottom:20px; 186 | } 187 | .row-section .form-row{ 188 | margin-top:25px; 189 | } 190 | .row-section .btn-primary{ 191 | margin-top:20px; 192 | } 193 | .btn-primary{ 194 | background-color:#365262; 195 | color:white; 196 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------