Added Private route and global store

This commit is contained in:
Manik Maity
2024-11-08 18:53:34 +05:30
parent 81657315f5
commit 700b0136fc
9 changed files with 138 additions and 41 deletions

View File

@@ -15,7 +15,8 @@
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.27.0"
"react-router-dom": "^6.27.0",
"zustand": "^5.0.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
@@ -1399,14 +1400,14 @@
"version": "15.7.13",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -2081,7 +2082,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/culori": {
@@ -5955,6 +5956,35 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zustand": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
"integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=18.0.0",
"immer": ">=9.0.6",
"react": ">=18.0.0",
"use-sync-external-store": ">=1.2.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
}
}
}
}

View File

@@ -17,7 +17,8 @@
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.27.0"
"react-router-dom": "^6.27.0",
"zustand": "^5.0.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",

View File

@@ -10,25 +10,27 @@ import Bookmark from "./pages/Bookmark";
import VotingPage from "./pages/VotingPage";
import CreatePollForm from "./pages/CreatePollForm";
import { QueryClient, QueryClientProvider } from "react-query";
import PrivateRoute from "./components/PrivateRoute/PrivateRoute";
function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<Register />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="bookmark" element={<Bookmark />} />
<Route path="/voting/:pollId" element={<VotingPage />} />
<Route path="/create" element={<CreatePollForm />} />
</Routes>
</BrowserRouter>
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<Register />} />
<Route element={<PrivateRoute />}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="bookmark" element={<Bookmark />} />
<Route path="/voting/:pollId" element={<VotingPage />} />
<Route path="/create" element={<CreatePollForm />} />
</Route>
</Routes>
</BrowserRouter>
</QueryClientProvider>
);
}

View File

@@ -1,8 +1,12 @@
import React from 'react'
import { Link } from 'react-router-dom'
import useUserStore from '../../store/useStore'
import ProfileImage from './ProfileImage';
function Header() {
const {user} = useUserStore();
return (
<div className="navbar bg-base-100">
<div className="flex-1">
@@ -10,32 +14,11 @@ function Header() {
</div>
<div className="flex-none">
<ul className="menu menu-horizontal px-1">
<li><Link to={"/login"}>Login</Link></li>
{user.username ? <li><Link to={"/dashboard"}>Dashboard</Link></li> : <li><Link to={"/login"}>Login</Link></li>}
<li><Link to={'/bookmark'}>Bookmarks</Link></li>
</ul>
</div>
<div className="flex-none">
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button" className="btn btn-ghost btn-circle avatar">
<div className="w-10 rounded-full">
<img
alt="Tailwind CSS Navbar component"
src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" />
</div>
</div>
<ul
tabIndex={0}
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li>
<Link to={'/dashboard'} className="justify-between" >
Profile
<span className="badge">New</span>
</Link>
</li>
<li><a>Logout</a></li>
</ul>
</div>
</div>
{user.username && <ProfileImage userData={user}/>}
</div>
)
}

View File

@@ -0,0 +1,41 @@
import React from 'react'
import { Link } from 'react-router-dom'
import useUserStore from '../../store/useStore';
function ProfileImage({userData}) {
const {setUser} = useUserStore();
const handleLogout = () => {
setUser({});
}
return (
<div className="flex-none">
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button" className="btn btn-ghost btn-circle avatar">
<div className="w-10 rounded-full">
<img
alt="Tailwind CSS Navbar component"
src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" />
</div>
</div>
<ul
tabIndex={0}
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li className='font-bold'><a >{userData?.username || "User"}</a></li>
<li>
<Link to={'/dashboard'} className="justify-between" >
Profile
<span className="badge">New</span>
</Link>
</li>
<li><a onClick={handleLogout}>Logout</a></li>
</ul>
</div>
</div>
)
}
export default ProfileImage

View File

@@ -0,0 +1,16 @@
import React from 'react'
import useUserStore from '../../store/useStore';
import { Navigate, Outlet } from 'react-router-dom';
function PrivateRoute() {
const {user} = useUserStore();
return (
<div>
{user.username ? <Outlet/> : <Navigate to={'/login'}/>}
</div>
)
}
export default PrivateRoute

View File

@@ -1,10 +1,12 @@
import React from 'react'
import { useNavigate } from 'react-router-dom';
import useStore from '../store/useStore';
function Home() {
const navigator = useNavigate();
return (
<div className="flex bg-base-200 min-h-screen flex-col items-center text-white p-8">
<h1 className="text-4xl mt-2 md:text-5xl font-bold mb-6 text-center flex flex-col gap-2 md:block">Welcome to <span className='bg-slate-800 px-4 rounded-md relative'>LivePoll</span></h1>

View File

@@ -5,6 +5,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { loginService } from '../services/loginService';
import SpinnerLoader from '../components/Loaders/SpinnerLoader';
import InlineTextError from '../components/Errors/InlineTextError';
import useUserStore from '../store/useStore';
const LoginPage = () => {
@@ -12,8 +13,11 @@ const LoginPage = () => {
const [password, setPassword] = useState('');
const navigator = useNavigate();
let {setUser} = useUserStore()
const mutation = useMutation(loginService, {
onSuccess: (data) => {
setUser(data?.user);
setEmail('');
setPassword('');
navigator('/');

View File

@@ -0,0 +1,18 @@
import {create} from "zustand"
import {createJSONStorage, persist} from "zustand/middleware"
const useUserStore = create(persist((set) => {
return {
user : {},
setUser : (user) => set(() => {
return {
user : user
}
}),
}
}, {
name: "livepoll",
storage: createJSONStorage(() => localStorage),
}));
export default useUserStore;