Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Feature - filter access to add and delete doctor base on role #16

Merged
merged 4 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 19 additions & 11 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import './App.css';
import { ToastContainer } from 'react-toastify';
import { QueryClient, QueryClientProvider } from 'react-query';
import ProtectedRoute from './components/ProtectedRoute';
import ProtectedRouteAdmin from './components/ProtectedRouteAdmin';
import Home from './routes/Home';
import Login from './routes/Login';
import SignUp from './routes/SignUp';
Expand All @@ -15,33 +17,39 @@ import AppointmentUpdate from './routes/AppointmentUpdate';
import CreateAppointment from './routes/CreateAppointment';
import Layout from './components/Layout';
import NotMatch from './routes/NotMatch';
import UnAuthorize from './routes/UnAuthorize';
import Logout from './routes/Logout';

const queryClient = new QueryClient();

function App() {
return (
<>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Routes>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
<Route path="sign_up" element={<SignUp />} />
<Route path="/login" element={<Login />} />
<Route path="/sign_up" element={<SignUp />} />
<Route element={<ProtectedRoute />}>
<Route element={<Layout />}>
<Route path="appointment-list" element={<AppointmentList />} />
<Route path="appointment/:appId" element={<AppointmentUpdate />} />
<Route path="doctors" element={<Doctors />} />
<Route path="/appointment-list" element={<AppointmentList />} />
<Route path="/appointment/:appId" element={<AppointmentUpdate />} />
<Route path="/doctors" element={<Doctors />} />
<Route path="/doctor/:id" element={<DoctorDetail />} />
<Route path="create-appointment" element={<CreateAppointment />} />
<Route path="add-docs" element={<AddDoc />} />
<Route path="delete-docs" element={<DeleteDoc />} />
<Route path="/create-appointment" element={<CreateAppointment />} />
<Route element={<ProtectedRouteAdmin />}>
<Route path="/add-docs" element={<AddDoc />} />
<Route path="/delete-docs" element={<DeleteDoc />} />
</Route>
</Route>
<Route path="logout" element={<Logout />} />
<Route path="/logout" element={<Logout />} />
</Route>
<Route path="/unauthorize" element={<UnAuthorize />} />
<Route path="*" element={<NotMatch />} />
</Routes>
</BrowserRouter>
<ToastContainer />
</>
</QueryClientProvider>
);
}

Expand Down
16 changes: 16 additions & 0 deletions src/components/ProtectedRouteAdmin.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// ProtectedRoute.js
import { Navigate, Outlet } from 'react-router-dom';
import useSession from '../hooks/useSession';

const ProtectedRouteAdmin = () => {
const userInfo = useSession()[1];

if (userInfo.role === 'admin') {
return <Outlet />;
}

return (
<Navigate to="/unauthorize" />
);
};
export default ProtectedRouteAdmin;
98 changes: 59 additions & 39 deletions src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
} from 'react-icons/fa';
import { HiMenuAlt4 } from 'react-icons/hi';
import { AiOutlineCloseCircle } from 'react-icons/ai';
import useSession from '../hooks/useSession';
import logo from '../logo.png';

const Sidebar = () => {
const userInfo = useSession()[1];
const [isMenuOpen, setIsMenuOpen] = useState(false);
const navigate = useNavigate();
const toggleMenu = () => {
Expand All @@ -18,7 +20,6 @@ const Sidebar = () => {
toggleMenu();
navigate(href);
};

return (
<>
<div className="inline-block h-auto p-2 m-2 border-2 border-black rounded-md hover:border-green-400 md:hidden">
Expand All @@ -30,11 +31,16 @@ const Sidebar = () => {
<HiMenuAlt4 />
</button>
</div>
<div className={`no-scrollbar fixed left-0 top-0 w-full h-screen border-r bg-black opacity-65 border-r-gray-900 overflow-y-auto text-white transition-transform ease-in-out z-10 duration-500 ${isMenuOpen ? 'translate-x-0' : '-translate-x-full'}`}>
<ul className="pt-24 uppercase">
<div className={`no-scrollbar flex flex-col justify-around fixed left-0 top-0 w-full h-screen border-r bg-black opacity-65 border-r-gray-900 overflow-y-auto text-white transition-transform ease-in-out z-10 duration-500 ${isMenuOpen ? 'translate-x-0' : '-translate-x-full'}`}>
<ul className="uppercase">
<li className="px-8 py-4 transition-colors cursor-pointer hover:text-green-400">
<AiOutlineCloseCircle onClick={toggleMenu} className="text-3xl" />
</li>
{ userInfo.role === 'admin' ? (
<li className="px-8 py-4 transition-colors cursor-pointer hover:text-green-400">
<p className="text-center font-bold">You are an admin</p>
</li>
) : (<></>)}
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
Expand Down Expand Up @@ -62,24 +68,28 @@ const Sidebar = () => {
Schedule a new Appointment
</button>
</li>
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
onClick={() => directLink('/add-docs')}
className="block w-full"
>
Add a new Doctor
</button>
</li>
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
onClick={() => directLink('/delete-docs')}
className="block w-full"
>
Delete a Doctor
</button>
</li>
{ userInfo.role === 'admin' ? (
<>
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
onClick={() => directLink('/add-docs')}
className="block w-full"
>
Add a new Doctor
</button>
</li>
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
onClick={() => directLink('/delete-docs')}
className="block w-full"
>
Delete a Doctor
</button>
</li>
</>
) : (<></>)}
<li className="px-8 py-4 transition-colors border-b cursor-pointer border-white-700 hover:text-green-400">
<button
type="button"
Expand All @@ -90,7 +100,7 @@ const Sidebar = () => {
</button>
</li>
</ul>
<ul className="flex flex-row self-end justify-center py-24 align-center">
<ul className="flex flex-row justify-center align-center">
<li className="p-[5px]"><FaTwitter className="transition-colors cursor-pointer hover:text-green-400" /></li>
<li className="p-[5px]">
<FaFacebookF className="transition-colors cursor-pointer hover:text-green-400" />
Expand All @@ -106,7 +116,13 @@ const Sidebar = () => {
</div>
<div className="sticky top-0 hidden w-5/12 h-screen max-w-[375px] overflow-y-auto md:block no-scrollbar">
<div className="text-[#181818] w-full h-screen py-2 border-r-2 border-r-[#f3f3f3] overflow-x-hidden hidden md:flex flex-col justify-between">
<Link className="px-4" to="/"><img className="w-[80%] m-auto" src={logo} alt="logo" /></Link>
<div className="w-full">
<Link className="px-4" to="/"><img className="w-[80%] m-auto" src={logo} alt="logo" /></Link>
{ userInfo.role === 'admin' ? (
<p className="text-center font-bold">You are an admin</p>
) : (<></>)}
</div>

<ul className="flex flex-col items-start justify-center gap-2 px-4 text-base sm:text-lg md:text-lg lg:text-xl xl:text-2xl">
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
Expand All @@ -132,22 +148,26 @@ const Sidebar = () => {
Schedule a new Appointment
</Link>
</li>
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
to="/add-docs"
className="block w-full"
>
Add a new Doctor
</Link>
</li>
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
to="/delete-docs"
className="block w-full"
>
Delete a Doctor
</Link>
</li>
{ userInfo.role === 'admin' ? (
<>
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
to="/add-docs"
className="block w-full"
>
Add a new Doctor
</Link>
</li>
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
to="/delete-docs"
className="block w-full"
>
Delete a Doctor
</Link>
</li>
</>
) : (<></>)}
<li className="w-full px-4 py-4 transition-all rounded-xl text-start hover:text-white hover:bg-green-400">
<Link
to="/logout"
Expand Down
13 changes: 5 additions & 8 deletions src/hooks/useSession.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useQuery } from 'react-query';
import { selectAuth } from '../redux/store';
import { getCurrentUser } from '../redux/auth/authActions';

const useSession = () => {
const {
currentUser,
userInfo,
} = useSelector(selectAuth);
const dispatch = useDispatch();

Expand All @@ -22,13 +23,9 @@ const useSession = () => {

const userSignedIn = token && Math.abs((Date.now() - tokenTime)) < 1_800_000; // 30 minutes

useEffect(() => {
if (!currentUser && userSignedIn) {
dispatch(getCurrentUser());
}
}, [dispatch, currentUser, userSignedIn]);
useQuery('currentUser', () => dispatch(getCurrentUser()));

return [userSignedIn, currentUser];
return [userSignedIn, userInfo];
};

export default useSession;
14 changes: 3 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,11 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import { Provider } from 'react-redux';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { store } from './redux/store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
<Provider store={store}>
<App />
</Provider>,
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
17 changes: 7 additions & 10 deletions src/routes/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ const Login = () => {

const login = (data) => {
toast.promise(
dispatch(loginUser(data)),
dispatch(
loginUser(data),
).then(() => {
if (error) {
toast.error(`Oops, something went wrong: ${error}`);
}
}),
{
pending: 'loading...',
error,
Expand All @@ -48,10 +54,6 @@ const Login = () => {
);
};

if (error) {
toast.error(`Oops, something went wrong: ${error}`);
}

if (loggedIn || userSignedIn) {
return <Navigate to="/appointment-list" />;
}
Expand Down Expand Up @@ -80,11 +82,6 @@ const Login = () => {
{errors.password?.message}
</div>
</div>
<div className="mb-4 flex justify-center">
<a className="inline-block align-baseline font-bold text-sm text-green-500 hover:text-green-800" href="/">
Forgot Password?
</a>
</div>
<div className="mb-4">
<Button className="w-full" text="Sign In" type="submit" />
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/routes/NotMatch.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Link } from 'react-router-dom';

const NotMatch = () => (
<>
<p>Not found!</p>
<p>Try another page</p>
<Link to="/" className="flex flex-row items-center px-8 py-4 mt-8 bg-green-400 rounded-full cursor-pointer justify-evenly hover:bg-green-500 transition-bg">Go to Homepage</Link>
</>
<div className="flex flex-col items-center justify-center h-screen w-screen">
<p className="text-center">Page not Found</p>
<p className="text-center">Try another page</p>
<Link to="/" className="flex flex-row items-center px-8 py-4 mt-8 bg-green-400 rounded-full cursor-pointer justify-evenly hover:bg-green-500 transition-bg">Go to your Homepage</Link>
</div>
);
export default NotMatch;
16 changes: 9 additions & 7 deletions src/routes/SignUp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ const SignUp = () => {

const signup = (data) => {
toast.promise(
dispatch(registerUser(data)),
dispatch(
registerUser(data),
).then(() => {
if (error) {
toast.error(`Oops, something went wrong: ${error}`);
}
}),
{
pending: 'loading...',
error,
Expand All @@ -50,10 +56,6 @@ const SignUp = () => {
);
};

if (error) {
toast.error(`Oops, something went wrong: ${error}`);
}

if (needsConfirmation) {
return <Navigate to="/login" />;
}
Expand Down Expand Up @@ -118,11 +120,11 @@ const SignUp = () => {
<label htmlFor="checked-checkbox" className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">
I agree with
{' '}
<a className="underline" href="https://google.com">Terms</a>
<span className="underline">Terms</span>
{' '}
and
{' '}
<a className="underline" href="https://google.com">Privacy</a>
<span className="underline">Privacy</span>
</label>
<div className="text-green-700 text-xs ml-2">
{errors.accept_terms?.message}
Expand Down
Loading