mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-02-23 15:34:21 +01:00
feat: build protected route
This commit is contained in:
16
src/App.tsx
16
src/App.tsx
@@ -1,11 +1,14 @@
|
||||
import React, { Suspense, useEffect } from "react";
|
||||
import Navbar from "src/Components/Navbar/Navbar";
|
||||
import ModalsContainer from "src/Components/Modals/ModalsContainer/ModalsContainer";
|
||||
import { useAppSelector } from './utils/hooks';
|
||||
import { useAppDispatch, useAppSelector } from './utils/hooks';
|
||||
import { Wallet_Service } from "./services";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { useWrapperSetup } from "./utils/Wrapper";
|
||||
import LoadingPage from "./Components/LoadingPage/LoadingPage";
|
||||
import { useMeQuery } from "./graphql";
|
||||
import { setUser } from "./redux/features/user.slice";
|
||||
import ProtectedRoute from "./Components/ProtectedRoute/ProtectedRoute";
|
||||
|
||||
// Pages
|
||||
const FeedPage = React.lazy(() => import("./features/Posts/pages/FeedPage/FeedPage"))
|
||||
@@ -28,8 +31,17 @@ function App() {
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
}));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
useWrapperSetup()
|
||||
|
||||
useMeQuery({
|
||||
onCompleted: (data) => {
|
||||
dispatch(setUser(data.me))
|
||||
},
|
||||
onError: (error) => {
|
||||
dispatch(setUser(null))
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// if (typeof window.webln != "undefined") {
|
||||
@@ -58,7 +70,7 @@ function App() {
|
||||
|
||||
<Route path="/blog/post/:type/:id" element={<PostDetailsPage />} />
|
||||
<Route path="/blog/preview-post/:type" element={<PreviewPostPage />} />
|
||||
<Route path="/blog/create-post" element={<CreatePostPage />} />
|
||||
<Route path="/blog/create-post" element={<ProtectedRoute><CreatePostPage /></ProtectedRoute>} />
|
||||
<Route path="/blog" element={<FeedPage />} />
|
||||
|
||||
<Route path="/hackathons" element={<HackathonsPage />} />
|
||||
|
||||
@@ -52,11 +52,6 @@ export default function Navbar() {
|
||||
|
||||
const isLargeScreen = useMediaQuery(MEDIA_QUERIES.isLarge)
|
||||
|
||||
useMeQuery({
|
||||
onCompleted: (data) => {
|
||||
dispatch(setUser(data.me))
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const nav = document.querySelector("nav");
|
||||
|
||||
31
src/Components/ProtectedRoute/ProtectedRoute.tsx
Normal file
31
src/Components/ProtectedRoute/ProtectedRoute.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Navigate, Outlet } from "react-router-dom";
|
||||
import { useAppSelector } from "src/utils/hooks";
|
||||
|
||||
interface Props {
|
||||
isAllowed?: boolean;
|
||||
notAuthorizedRedirectPath?: string
|
||||
notAllowedRedirectPath?: string
|
||||
}
|
||||
|
||||
export default function ProtectedRoute({
|
||||
isAllowed = true,
|
||||
notAuthorizedRedirectPath = '/login',
|
||||
notAllowedRedirectPath = '/',
|
||||
children,
|
||||
}: PropsWithChildren<Props>) {
|
||||
|
||||
const user = useAppSelector(state => state.user.me);
|
||||
|
||||
if (user === undefined) return <></>
|
||||
|
||||
if (user === null)
|
||||
return <Navigate to={notAuthorizedRedirectPath} replace />;
|
||||
|
||||
|
||||
if (!isAllowed) {
|
||||
return <Navigate to={notAllowedRedirectPath} replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import LoadingPage from 'src/Components/LoadingPage/LoadingPage'
|
||||
import NotFoundPage from 'src/features/Shared/pages/NotFoundPage/NotFoundPage'
|
||||
import { usePostDetailsQuery } from 'src/graphql'
|
||||
import { useAppSelector, } from 'src/utils/hooks'
|
||||
import TrendingCard from '../../Components/TrendingCard/TrendingCard'
|
||||
@@ -32,7 +33,7 @@ export default function PostDetailsPage() {
|
||||
const post = postDetailsQuery.data?.getPostById;
|
||||
|
||||
if (!post)
|
||||
return <h2>404</h2>
|
||||
return <NotFoundPage />
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { ModifyArgs } from 'src/utils/storybook/utils';
|
||||
import NotFoundPage from './NotFoundPage';
|
||||
|
||||
export default {
|
||||
title: 'Shared/Pages/Not Found Page',
|
||||
component: NotFoundPage,
|
||||
argTypes: {
|
||||
backgroundColor: { control: 'color' },
|
||||
}
|
||||
} as ComponentMeta<typeof NotFoundPage>;
|
||||
|
||||
|
||||
const Template: ComponentStory<typeof NotFoundPage> = (args) => <NotFoundPage {...args as any} ></NotFoundPage>
|
||||
|
||||
export const Page = Template.bind({});
|
||||
Page.args = {
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@ export default function NotFoundPage() {
|
||||
const goBack = () => navigate(-1);
|
||||
|
||||
return (
|
||||
<div className='page-container min-h-screen flex flex-col justify-center items-center'>
|
||||
<h1 className="text-h1 font-bold mb-36">
|
||||
404 Not Found...
|
||||
<div className='page-container min-h-screen flex flex-col gap-36 justify-center items-center relative'>
|
||||
<p className='text-gray-100 absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 text-[50vmin] z-[-1]'>404</p>
|
||||
<h1 className="text-h1 font-bold">
|
||||
Not Found...
|
||||
</h1>
|
||||
<p className="text-body4 text-gray-500 font-medium mb-16">
|
||||
<p className="text-body4 text-gray-500 font-medium text-center">
|
||||
The resource you are looking for isn't here anymore, it may have been removed or the url may be invalid.
|
||||
</p>
|
||||
<Button color='primary' onClick={goBack}>
|
||||
|
||||
Reference in New Issue
Block a user