├── .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 |
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 |
--------------------------------------------------------------------------------