Tatiana Inama 4 years atrás
parent
commit
437766db42
10 changed files with 156 additions and 77 deletions
  1. 14 0
      app.d.ts
  2. 0 7
      context/UserContext.tsx
  3. 20 30
      hooks/useForm.ts
  4. 2 2
      pages/api/signup.ts
  5. 19 0
      pages/api/users.ts
  6. 3 6
      pages/login.tsx
  7. 8 6
      pages/settings.tsx
  8. 9 5
      pages/signup.tsx
  9. 1 1
      tsconfig.json
  10. 80 20
      utils/api.ts

+ 14 - 0
app.d.ts

@@ -0,0 +1,14 @@
+// Declaring global types for HomeChallengeTypes
+enum Team {
+  Admins = "Admins",
+  Users = "Users",
+  Viewers = "Viewers",
+}
+
+interface User {
+  id: number;
+  name: string;
+  email: string;
+  password: string;
+  team: Team;
+}

+ 0 - 7
context/UserContext.tsx

@@ -1,12 +1,5 @@
 import React, { useState, FC } from "react";
 
-interface User {
-  email: string;
-  id: number;
-  name: string;
-  team: string;
-}
-
 interface UserContextInterface {
   user?: User;
   logged: boolean;

+ 20 - 30
hooks/useForm.ts

@@ -1,57 +1,47 @@
-import fetchApi, { ApiMethods, ApiPaths } from "./../utils/api";
+import { ApiResulState, FetchApiInterface } from "./../utils/api";
 import { FormEventHandler, useState } from "react";
 
-type useFormProps = {
-  path: ApiPaths;
-  query?: string;
-  method: ApiMethods;
-};
-
 type FormMessage = {
   message: string;
 };
 
-type SuccessFormData = {
-  [k: string]: FormDataEntryValue;
+type Success = FormMessage & {
+  result: Partial<User>;
 };
 
 type SubmitHandler = FormEventHandler<HTMLFormElement>;
 
 type UseForm = {
-  error?: FormMessage;
   loading: boolean;
   submitHandler: SubmitHandler;
-  success?: FormMessage;
-  successFormData?: SuccessFormData;
+  error?: FormMessage;
+  success?: Success;
 };
 
-const useForm = ({ path, method, query }: useFormProps): UseForm => {
+const useForm = <T>(fetchApi: FetchApiInterface<T>): UseForm => {
   const [error, setError] = useState<FormMessage>();
   const [loading, setLoading] = useState(false);
-  const [success, setSuccess] = useState<FormMessage>();
-  const [successFormData, setSuccessFormData] = useState<SuccessFormData>();
+  const [success, setSuccess] = useState<Success>();
 
   const submitHandler: FormEventHandler<HTMLFormElement> = async (event) => {
     event.preventDefault();
     setLoading(true);
     const formData = new FormData(event.currentTarget);
-    const body = Object.fromEntries(formData.entries());
-    const { error, message } = await fetchApi({
-      path,
-      query,
-      method,
-      body,
-    });
-    if (error) {
-      setLoading(false);
-      setError({ message: error });
-    } else {
-      setSuccessFormData(body);
-      setLoading(false);
-      setSuccess({ message: message || "Success!" });
+    const body = Object.fromEntries(formData.entries()) as unknown as T;
+    const result = await fetchApi(body);
+    setLoading(false);
+    switch (result.state) {
+      case ApiResulState.SUCCESS: {
+        setSuccess({ message: result.message, result: result.user });
+        break;
+      }
+      case ApiResulState.ERROR: {
+        setError({ message: result.error });
+        break;
+      }
     }
   };
-  return { error, loading, success, submitHandler, successFormData };
+  return { error, loading, success, submitHandler };
 };
 
 export default useForm;

+ 2 - 2
pages/api/signup.ts

@@ -1,7 +1,7 @@
 import axios from "axios";
 import type { NextApiHandler } from "next";
 
-const signUpHandler: NextApiHandler = async (req, res) => {
+const registerHandler: NextApiHandler = async (req, res) => {
   try {
     const response = await axios.post(
       `${process.env.PRISMA_API}/register`,
@@ -23,4 +23,4 @@ const signUpHandler: NextApiHandler = async (req, res) => {
   }
 };
 
-export default signUpHandler;
+export default registerHandler;

+ 19 - 0
pages/api/users.ts

@@ -0,0 +1,19 @@
+import axios from "axios";
+import type { NextApiHandler } from "next";
+
+const usersHandler: NextApiHandler = async (req, res) => {
+  try {
+    const response = await axios.get(`${process.env.PRISMA_API}/users`);
+    res.status(response.status).json(response.data);
+  } catch (e) {
+    if (axios.isAxiosError(e)) {
+      res.status(e.response?.status || 500).json(e.response?.data);
+    } else {
+      res.status(500).json({
+        message: "There was an error processing the request, please try again.",
+      });
+    }
+  }
+};
+
+export default usersHandler;

+ 3 - 6
pages/login.tsx

@@ -3,19 +3,16 @@ import { useContext, useEffect } from "react";
 import Layout from "../components/Layout";
 import { UserContext } from "../context/UserContext";
 import useForm from "../hooks/useForm";
-import { ApiMethods, ApiPaths } from "../utils/api";
+import api from "../utils/api";
 
 const Login: NextPage = () => {
   const context = useContext(UserContext);
 
-  const { error, submitHandler, success } = useForm({
-    path: ApiPaths.Login,
-    method: ApiMethods.POST,
-  });
+  const { error, submitHandler, success } = useForm(api.login);
 
   useEffect(() => {
     if (success && context) {
-      context.setLogged(true);
+      console.log(success.message, success.result, context);
     }
   }, [success, context]);
 

+ 8 - 6
pages/settings.tsx

@@ -1,7 +1,8 @@
 import { NextPage } from "next/types";
+import { useEffect } from "react";
 import Layout from "../components/Layout";
 import useForm from "../hooks/useForm";
-import { ApiPaths, ApiMethods } from "../utils/api";
+import api from "../utils/api";
 
 const Settings: NextPage = () => {
   const user = {
@@ -12,12 +13,13 @@ const Settings: NextPage = () => {
     team: "Admins",
   };
 
-  const { error, submitHandler } = useForm({
-    path: ApiPaths.Settings,
-    query: JSON.stringify(user.id),
-    method: ApiMethods.PUT,
-  });
+  const { error, submitHandler, success } = useForm(api.updateUser);
 
+  useEffect(() => {
+    if (success) {
+      console.log(success);
+    }
+  }, [success]);
   return (
     <Layout>
       <form className="form" onSubmit={submitHandler}>

+ 9 - 5
pages/signup.tsx

@@ -1,13 +1,17 @@
 import { NextPage } from "next";
+import { useEffect } from "react";
 import Layout from "../components/Layout";
 import useForm from "../hooks/useForm";
-import { ApiMethods, ApiPaths } from "../utils/api";
+import api from "../utils/api";
 
 const SignUp: NextPage = () => {
-  const { submitHandler, error } = useForm({
-    path: ApiPaths.SignUp,
-    method: ApiMethods.POST,
-  });
+  const { submitHandler, error, success } = useForm(api.register);
+
+  useEffect(() => {
+    if (success) {
+      console.log(success);
+    }
+  }, [success]);
 
   return (
     <Layout>

+ 1 - 1
tsconfig.json

@@ -16,6 +16,6 @@
     "jsx": "preserve",
     "incremental": true
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+  "include": ["next-env.d.ts", "app.d.ts", "**/*.ts", "**/*.tsx"],
   "exclude": ["node_modules", "*.js"]
 }

+ 80 - 20
utils/api.ts

@@ -1,44 +1,104 @@
+import assert from "assert";
+const API = process.env.NEXT_PUBLIC_API || "/api";
+
 export enum ApiMethods {
   GET = "GET",
   POST = "POST",
   PUT = "PUT",
 }
 
-export enum ApiPaths {
-  SignUp = "signup",
-  Login = "login",
-  Settings = "user",
+export enum ApiResulState {
+  SUCCESS,
+  ERROR,
 }
 
-type FetchApiProps<T> = {
-  method: ApiMethods;
-  path: ApiPaths;
-  query?: string;
-  body: T;
+type SuccessApiResult = {
+  state: ApiResulState.SUCCESS;
+  message: string;
+  user: User;
 };
 
-type FetchApiResult = { error?: string; message?: string };
+type FailureApiResult = { state: ApiResulState.ERROR; error: string };
+
+export type FetchApiResult = SuccessApiResult | FailureApiResult;
+
+type FetchApi = <T>(
+  path: string,
+  method: ApiMethods,
+  body: T
+) => Promise<FetchApiResult>;
 
-const fetchApi = async <T>({
-  method,
-  path,
-  query,
-  body,
-}: FetchApiProps<T>): Promise<FetchApiResult> => {
-  const response = await fetch(`/api/${path}/${query || ""}`, {
+const fetchApi: FetchApi = async (path, method, body) => {
+  const response = await fetch(`${API}${path}`, {
     method: method,
     body: JSON.stringify(body),
   });
 
-  const { message } = await response.json();
+  const { message, ...result } = await response.json();
 
   if (response.ok) {
-    return { message };
+    return { state: ApiResulState.SUCCESS, message, user: result };
   } else {
     return {
+      state: ApiResulState.ERROR,
       error: message.replace("Incorect", "Incorrect"),
     };
   }
 };
 
-export default fetchApi;
+export interface FetchApiInterface<T> {
+  (body: T): Promise<FetchApiResult>;
+}
+
+export const login: FetchApiInterface<
+  Pick<User, "email" | "password">
+> = async (body) => {
+  const response = await fetchApi("/login", ApiMethods.POST, body);
+  switch (response.state) {
+    case ApiResulState.SUCCESS: {
+      const user = await fetchUserData(body.email);
+      return {
+        ...response,
+        user: user,
+      };
+    }
+    default:
+      return response;
+  }
+};
+
+export const register: FetchApiInterface<
+  Pick<User, "email" | "password">
+> = async (body) => {
+  return fetchApi("/register", ApiMethods.POST, body);
+};
+
+export const updateUser: FetchApiInterface<
+  Partial<User> & Pick<User, "id">
+> = async ({ id, ...body }) => {
+  return fetchApi(`/user/${JSON.stringify(id)}`, ApiMethods.PUT, body);
+};
+
+const fetchUsers = async (): Promise<Array<User>> => {
+  const response = await fetch("/api/users");
+  if (response.ok) {
+    return response.json();
+  } else {
+    return Promise.resolve([]);
+  }
+};
+
+const fetchUserData = async (email: string): Promise<User> => {
+  const users = await fetchUsers();
+  const user = users.find((user) => user.email === email);
+  assert(user);
+  return user;
+};
+
+const apiPaths = {
+  login,
+  register,
+  updateUser,
+};
+
+export default apiPaths;