Selaa lähdekoodia

Add PrivatePages Component

Tatiana Inama 4 vuotta sitten
vanhempi
commit
d54d36b128
8 muutettua tiedostoa jossa 100 lisäystä ja 46 poistoa
  1. 1 1
      app.d.ts
  2. 30 0
      components/PrivatePage.tsx
  3. 38 6
      context/UserContext.tsx
  4. 5 1
      pages/_app.tsx
  5. 9 3
      pages/index.tsx
  6. 5 5
      pages/login.tsx
  7. 11 16
      pages/settings.tsx
  8. 1 14
      utils/api.ts

+ 1 - 1
app.d.ts

@@ -6,7 +6,7 @@ enum Team {
 }
 
 interface User {
-  id: number;
+  id: number | string;
   name: string;
   email: string;
   password: string;

+ 30 - 0
components/PrivatePage.tsx

@@ -0,0 +1,30 @@
+import { FC, useEffect } from "react";
+import { useRouter } from "next/router";
+import { useAuth } from "../context/UserContext";
+
+const PrivatePage: FC<{ protectedRoutes: string[] }> = ({
+  protectedRoutes,
+  children,
+}) => {
+  const { pathname, push } = useRouter();
+  const { isAuthenticated } = useAuth();
+
+  const pathIsProtected = protectedRoutes.includes(pathname);
+
+  useEffect(() => {
+    if (!isAuthenticated && pathIsProtected) {
+      push("/");
+    }
+  }, [isAuthenticated, pathIsProtected, push]);
+
+  if (!isAuthenticated && pathIsProtected) {
+    return (
+      <div className="form-alert-msg">
+        There was an error, please reload the page
+      </div>
+    );
+  }
+  return <>{children}</>;
+};
+
+export default PrivatePage;

+ 38 - 6
context/UserContext.tsx

@@ -1,23 +1,55 @@
-import React, { useState, FC } from "react";
+import React, { useState, FC, useContext } from "react";
 
 interface UserContextInterface {
   user?: User;
-  setUser:
-    | React.Dispatch<React.SetStateAction<User | undefined>>
-    | ((value: React.SetStateAction<User | undefined>) => void);
+  isAuthenticated: boolean;
+  login: (user: User) => void;
+  logout: () => void;
+  updateUser: (user: User) => void;
 }
 
 export const UserContext = React.createContext<UserContextInterface>({
-  setUser: () => {},
+  isAuthenticated: false,
+  login: () => {},
+  logout: () => {},
+  updateUser: () => {},
 });
 
 const UserContextProvider: FC = ({ children }) => {
   const [user, setUser] = useState<User>();
+  const [isAuthenticated, setIsAuthenticated] = useState(false);
+
+  const login = (user: User) => {
+    setIsAuthenticated(true);
+    setUser(user);
+  };
+
+  const logout = () => {
+    setIsAuthenticated(false);
+    setUser(undefined);
+  };
+
+  const updateUser = (newUserData: User) => {
+    if (user) {
+      setUser({
+        ...newUserData,
+        id:
+          typeof newUserData.id === "string"
+            ? parseInt(newUserData.id)
+            : newUserData.id,
+      });
+    }
+  };
+
   return (
-    <UserContext.Provider value={{ user, setUser }}>
+    <UserContext.Provider
+      value={{ user, login, logout, updateUser, isAuthenticated }}
+    >
       {children}
     </UserContext.Provider>
   );
 };
 
+export const useAuth = () => useContext(UserContext);
+
 export default UserContextProvider;

+ 5 - 1
pages/_app.tsx

@@ -1,12 +1,16 @@
 import type { AppProps } from "next/app";
+import PrivatePage from "../components/PrivatePage";
 import UserContextProvider from "../context/UserContext";
 
 import "../styles/globals.css";
 
 function MyApp({ Component, pageProps }: AppProps) {
+  const protectedRoutes = ["/settings"];
   return (
     <UserContextProvider>
-      <Component {...pageProps} />
+      <PrivatePage protectedRoutes={protectedRoutes}>
+        <Component {...pageProps} />
+      </PrivatePage>
     </UserContextProvider>
   );
 }

+ 9 - 3
pages/index.tsx

@@ -1,11 +1,10 @@
 import type { NextPage } from "next";
 import Link from "next/link";
-import { useContext } from "react";
 import Layout from "../components/Layout";
-import { UserContext } from "../context/UserContext";
+import { useAuth } from "../context/UserContext";
 
 const Home: NextPage = () => {
-  const { user } = useContext(UserContext);
+  const { user, logout } = useAuth();
   return (
     <Layout>
       <div className="">
@@ -20,6 +19,13 @@ const Home: NextPage = () => {
                 <a>here</a>
               </Link>
             </p>
+            <button
+              type="button"
+              onClick={() => logout()}
+              className="form-button"
+            >
+              Logout
+            </button>
           </>
         ) : (
           <>

+ 5 - 5
pages/login.tsx

@@ -1,25 +1,25 @@
 import { NextPage } from "next";
-import { useContext, useEffect } from "react";
+import { useEffect } from "react";
 import Layout from "../components/Layout";
-import { UserContext } from "../context/UserContext";
+import { useAuth } from "../context/UserContext";
 import useForm from "../hooks/useForm";
 import { useRouter } from "next/router";
 import api from "../utils/api";
 
 const Login: NextPage = () => {
-  const { setUser } = useContext(UserContext);
+  const { login } = useAuth();
   const router = useRouter();
 
   const { error, submitHandler, success } = useForm(api.login);
 
   useEffect(() => {
     if (success) {
-      setUser(success.result);
+      login(success.result);
       setTimeout(() => {
         router.push("/");
       }, 1000);
     }
-  }, [success, setUser, router]);
+  }, [success, login, router]);
 
   return (
     <Layout>

+ 11 - 16
pages/settings.tsx

@@ -1,31 +1,22 @@
 import { NextPage } from "next/types";
-import { useContext, useEffect } from "react";
+import { useEffect } from "react";
 import Layout from "../components/Layout";
-import { UserContext } from "../context/UserContext";
+import { useAuth } from "../context/UserContext";
 import useForm from "../hooks/useForm";
 import api from "../utils/api";
 
 const Settings: NextPage = () => {
-  const { user, setUser } = useContext(UserContext);
+  const { updateUser, user } = useAuth();
 
   const { error, submitHandler, success } = useForm(api.updateUser);
 
   useEffect(() => {
     if (success) {
-      setUser(success.result);
+      updateUser(success.result);
     }
-  }, [success, setUser]);
+  }, [success, updateUser]);
 
-  if (!user) {
-    return (
-      <Layout>
-        <div className="form-alert-msg">
-          There was an error, please reload the page
-        </div>
-      </Layout>
-    );
-  }
-  return (
+  return user ? (
     <Layout>
       <form className="form" onSubmit={submitHandler}>
         <div className="form-group">
@@ -38,6 +29,7 @@ const Settings: NextPage = () => {
             id="name"
             className="form-input"
             defaultValue={user.name}
+            required
           />
         </div>
         <div className="form-group">
@@ -50,6 +42,7 @@ const Settings: NextPage = () => {
             id="email"
             className="form-input"
             defaultValue={user.email}
+            required
           />
         </div>
         <div className="form-group">
@@ -63,6 +56,7 @@ const Settings: NextPage = () => {
             className="form-input"
             defaultValue={user.password}
             minLength={6}
+            required
           />
         </div>
         <div className="form-group">
@@ -74,6 +68,7 @@ const Settings: NextPage = () => {
             id="team"
             name="team"
             defaultValue={user.team}
+            required
           >
             <option value="Admins">Admins</option>
             <option value="Users">Users</option>
@@ -96,7 +91,7 @@ const Settings: NextPage = () => {
         )}
       </form>
     </Layout>
-  );
+  ) : null;
 };
 
 export default Settings;

+ 1 - 14
utils/api.ts

@@ -76,20 +76,7 @@ export const register: FetchApiInterface<
 export const updateUser: FetchApiInterface<
   Partial<User> & Pick<User, "id">
 > = async ({ id, ...body }) => {
-  const response = await fetchApi(`/user/${id}`, ApiMethods.PUT, body);
-  switch (response.state) {
-    case ApiResulState.SUCCESS: {
-      return {
-        ...response,
-        user: {
-          ...response.user,
-          id: id,
-        },
-      };
-    }
-    default:
-      return response;
-  }
+  return fetchApi(`/user/${id}`, ApiMethods.PUT, body);
 };
 
 const fetchUsers = async (): Promise<Array<User>> => {