Tatiana Inama vor 4 Jahren
Ursprung
Commit
73d9cc203c

+ 1 - 1
components/FormResponse.tsx

@@ -10,7 +10,7 @@ const FormResponse: FC<{
   if (loading) {
     return null;
   }
-  return error ? (
+  return error && !success ? (
     <div role="alert" className="form-alert-msg">
       {error.message}
     </div>

+ 10 - 7
components/Nav.tsx

@@ -16,19 +16,22 @@ const Nav: FC = () => {
             </Link>
           </li>
         </ul>
-        <ul className="flex">
+        <ul className="flex min-w-0">
           {context.user ? (
-            <li>
-              <span className="py-7 px-4 block">
+            pathname === "/" ? null : (
+              <li
+                className="py-7 px-4 overflow-hidden whitespace-nowrap text-ellipsis"
+                title={`Hello ${context.user.name}`}
+              >
                 Hello <b>{context.user.name}</b>
-              </span>
-            </li>
+              </li>
+            )
           ) : (
             <>
               <li>
                 <Link href="/signup">
                   <a
-                    className={`py-7 px-4 block ${
+                    className={`py-7 px-2 sm:px-4 block ${
                       pathname === "/signup" ? "font-bold" : ""
                     }`}
                   >
@@ -39,7 +42,7 @@ const Nav: FC = () => {
               <li className="">
                 <Link href="/login">
                   <a
-                    className={`py-7 px-4 block ${
+                    className={`py-7 px-4 pl-2 sm:px-4 block ${
                       pathname === "/login" ? "font-bold" : ""
                     }`}
                   >

+ 6 - 1
context/UserContext.tsx

@@ -1,6 +1,6 @@
 import React, { useState, FC, useContext } from "react";
 
-interface UserContextInterface {
+export interface UserContextInterface {
   user?: User;
   isAuthenticated: boolean;
   login: (user: User) => void;
@@ -8,6 +8,11 @@ interface UserContextInterface {
   updateUser: (user: User) => void;
 }
 
+export interface UserContextAuthenticatedInterface
+  extends UserContextInterface {
+  user: User;
+}
+
 export const UserContext = React.createContext<UserContextInterface>({
   isAuthenticated: false,
   login: () => {},

+ 9 - 1
hooks/useForm.ts

@@ -18,7 +18,10 @@ type UseForm = {
   success?: Success;
 };
 
-const useForm = <T>(fetchApi: FetchApiInterface<T>): UseForm => {
+const useForm = <T>(
+  fetchApi: FetchApiInterface<T>,
+  resetAfterSubmit?: boolean
+): UseForm => {
   const [error, setError] = useState<FormMessage>();
   const [loading, setLoading] = useState(false);
   const [success, setSuccess] = useState<Success>();
@@ -33,10 +36,15 @@ const useForm = <T>(fetchApi: FetchApiInterface<T>): UseForm => {
     switch (result.state) {
       case ApiResulState.SUCCESS: {
         setSuccess({ message: result.message, result: result.user });
+        setError(undefined);
+        if (resetAfterSubmit) {
+          (event.target as HTMLFormElement).reset();
+        }
         break;
       }
       case ApiResulState.ERROR: {
         setError({ message: result.error });
+        setSuccess(undefined);
         break;
       }
     }

+ 44 - 23
pages/index.tsx

@@ -1,7 +1,45 @@
 import type { NextPage } from "next";
 import Link from "next/link";
+import { FC } from "react";
 import Layout from "../components/Layout";
-import { useAuth } from "../context/UserContext";
+import {
+  useAuth,
+  UserContextAuthenticatedInterface,
+} from "../context/UserContext";
+
+const AuthenticatedView: FC<
+  Pick<UserContextAuthenticatedInterface, "user" | "logout">
+> = ({ user, logout }) => (
+  <div>
+    <h1 className="text-2xl font-bold mb-4 text-center">Welcome back!</h1>
+    <div className="rounded w-full mx-auto border p-4 flex flex-col gap-4 xs:max-w-md xs:gap-6 xs:py-6">
+      <header className="flex flex-col items-center text-center gap-2 xs:flex-row xs:gap-4 xs:text-left">
+        <div className="h-12 w-12 bg-gray-300 rounded-full"></div>
+        <div>
+          <h2 className="font-bold">{user.name}</h2>
+          <p className="text-sm">{user.email}</p>
+          <p>
+            <span className="bg-green-200 font-semibold font-mono rounded text-xs inline-block px-1 leading-5">
+              {user.team}
+            </span>
+          </p>
+        </div>
+      </header>
+      <div className="flex flex-col gap-2 xs:flex-row">
+        <Link href="/settings">
+          <a className="default-button">Edit profile</a>
+        </Link>
+        <button
+          type="button"
+          onClick={() => logout()}
+          className="outline-button"
+        >
+          Logout
+        </button>
+      </div>
+    </div>
+  </div>
+);
 
 const Home: NextPage = () => {
   const { user, logout } = useAuth();
@@ -9,29 +47,12 @@ const Home: NextPage = () => {
     <Layout>
       <div className="">
         {user ? (
-          <>
-            <h1>
-              Welcome back, <em>{user.name}</em>!
-            </h1>
-            <p>
-              You can change your settings in{" "}
-              <Link href="/settings">
-                <a>here</a>
-              </Link>
-            </p>
-            <button
-              type="button"
-              onClick={() => logout()}
-              className="form-button"
-            >
-              Logout
-            </button>
-          </>
+          <AuthenticatedView user={user} logout={logout} />
         ) : (
-          <>
-            <h1>Welcome!</h1>
-            <p>Log in to get started</p>
-          </>
+          <div>
+            <h1 className="text-2xl font-bold mb-4 text-center">Welcome!</h1>
+            <p className="text-center">Please, sign up or log in to continue</p>
+          </div>
         )}
       </div>
     </Layout>

+ 6 - 2
pages/login.tsx

@@ -22,7 +22,7 @@ const Login: NextPage = () => {
 
   return (
     <Layout>
-      <form className="form" onSubmit={submitHandler}>
+      <form className="form" onSubmit={submitHandler} autoComplete="true">
         <div className="form-group">
           <label htmlFor="email" className="form-label">
             Email
@@ -50,7 +50,11 @@ const Login: NextPage = () => {
             required
           />
         </div>
-        <button type="submit" className="form-button" disabled={loading}>
+        <button
+          type="submit"
+          className="default-button col-start-2 w-max"
+          disabled={loading}
+        >
           {loading ? <Spinner /> : "Log in"}
         </button>
         {error && !success && (

+ 6 - 2
pages/settings.tsx

@@ -66,7 +66,7 @@ const Settings: NextPage = () => {
             Team
           </label>
           <select
-            className="form-input"
+            className="form-input appearance-none bg-dropdown-arrow bg-no-repeat bg-[right_8px_center]"
             id="team"
             name="team"
             defaultValue={user.team}
@@ -78,7 +78,11 @@ const Settings: NextPage = () => {
           </select>
         </div>
         <input type="hidden" id="id" name="id" value={user.id} />
-        <button type="submit" className="form-button" disabled={loading}>
+        <button
+          type="submit"
+          className="default-button col-start-2 w-max"
+          disabled={loading}
+        >
           {loading ? <Spinner /> : "Save"}
         </button>
         <FormResponse

+ 9 - 2
pages/signup.tsx

@@ -6,7 +6,10 @@ import useForm from "../hooks/useForm";
 import api from "../utils/api";
 
 const SignUp: NextPage = () => {
-  const { submitHandler, error, success, loading } = useForm(api.register);
+  const { submitHandler, error, success, loading } = useForm(
+    api.register,
+    true
+  );
 
   return (
     <Layout>
@@ -38,7 +41,11 @@ const SignUp: NextPage = () => {
             required
           />
         </div>
-        <button type="submit" className="form-button" disabled={loading}>
+        <button
+          type="submit"
+          className="default-button col-start-2 w-max"
+          disabled={loading}
+        >
           {loading ? <Spinner /> : "Sign up"}
         </button>
         <FormResponse

BIN
public/triangle.png


+ 9 - 5
styles/globals.css

@@ -22,23 +22,27 @@
 
 @layer components {
   .form {
-    @apply w-full max-w-screen-xs mx-auto flex flex-col xs:grid xs:grid-cols-form xs:items-center gap-4;
+    @apply w-full max-w-screen-xs mx-auto flex flex-col sm:grid sm:grid-cols-form sm:items-center gap-4;
   }
 
   .form-group {
-    @apply xs:contents;
+    @apply sm:contents;
   }
 
   .form-label {
-    @apply font-bold flex-1 xs:text-right;
+    @apply font-bold flex-1 sm:text-right;
   }
 
   .form-input {
     @apply bg-prisma-50 w-full rounded py-2 px-3 text-sm;
   }
 
-  .form-button {
-    @apply col-start-2 w-max py-2 px-4 bg-prisma-900 text-white font-bold rounded text-sm hover:bg-prisma-800 active:bg-prisma-700;
+  .default-button {
+    @apply py-2 px-4 bg-prisma-900 text-white font-bold rounded text-sm text-center hover:bg-prisma-800 active:bg-prisma-700 border border-prisma-800;
+  }
+
+  .outline-button {
+    @apply py-2 px-4 bg-white text-prisma-900 font-bold rounded text-sm text-center hover:bg-gray-100 active:bg-gray-200 border border-gray-100;
   }
 
   .form-alert-msg {

+ 4 - 0
tailwind.config.js

@@ -14,6 +14,7 @@ module.exports = {
       black: colors.black,
       red: colors.red,
       green: colors.green,
+      gray: colors.gray,
       prisma: {
         50: "#ebebeb",
         100: "#d2d2d2",
@@ -37,6 +38,9 @@ module.exports = {
       xs: "475px",
       ...defaultTheme.screens,
     },
+    backgroundImage: {
+      "dropdown-arrow": "url('/triangle.png')",
+    },
     extend: {},
   },
   plugins: [],

+ 0 - 32
utils/apiErrorHandler.ts

@@ -1,32 +0,0 @@
-import axios from "axios";
-
-enum HttpError {
-  Unauthorized = 401,
-  ServerError = 500,
-}
-
-type ErrorHandlerResponse = {
-  status: HttpError;
-  message: string;
-};
-
-export const errorHandler = (error: unknown): ErrorHandlerResponse => {
-  if (axios.isAxiosError(error) && error.response) {
-    const { status, data } = error.response;
-    console.log(status, data);
-    if (
-      status === HttpError.ServerError &&
-      data?.message.includes("Incorect")
-    ) {
-      return {
-        status: HttpError.Unauthorized,
-        message: "The email or password was incorrect",
-      };
-    }
-    return { status, message: data };
-  }
-  return {
-    status: HttpError.ServerError,
-    message: "There was an error, please try again",
-  };
-};