No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Curso Profesional de Next.js

Curso Profesional de Next.js

Oscar Barajas Tavares

Oscar Barajas Tavares

Obteniendo el token de la API

13/31
Recursos

Aportes 9

Preguntas 2

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Reg铆strate o inicia sesi贸n para participar.

Para los errores utilic茅 un estado 鈥渆rrorLogin鈥 para mostrar el error y otro estado 鈥渓oading鈥 para mostrar un icono en el bot贸n y desahabilitar al enviar el form

const [errorLogin, setErrorLogin] = useState(null);
const [loading, setLoading] = useState(false);

En el submit un catch y seteo el error y el estado de loading para mostrar un icono

  const submitHandler = (event) => {
    event.preventDefault();
    const email = emailRef.current.value;
    const password = passwordRef.current.value;
    setErrorLogin(null);
    setLoading(true);
    auth
      .signIn(email, password)
      .then(() => {
        route.push('/dashboard');
      })
      .catch(function (error) {
        if (error.response?.status === 401) {
          setErrorLogin('Usuario o password incorrecto.');
        } else if (error.request) {
          setErrorLogin('Tenemos un problema');
        } else {
          setErrorLogin('Algo salimal.');
        }
        setLoading(false);
      });
  };

El si hay un error muestro el div con el mensaje

{errorLogin && (
  <div class="p-3 mb-3 text-sm text-red-700 bg-red-100 rounded-lg dark:bg-red-200 dark:text-red-800" role="alert">
    <span class="font-medium">Error!</span> {errorLogin}
  </div>
)}

Si se env铆a el formulario muestro un icono dentro del button

{loading && (
  <span class="flex absolute h-4 w-4 top-0 right-0 -mt-1 -mr-1">
    <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-indigo-300 opacity-75"></span>
    <span class="relative inline-flex rounded-full h-4 w-4 bg-indigo-400"></span>
  </span>
)}

Y establezco disabled el button
disabled={loading}

驴Qu茅 son las Cookies 馃崻?

Las Cookies es un archivo con datos creado y guardado en el ordenador de un usuario cuando visita paginas web que usan Cookies.

Este peque帽o archivo contiene cierta informaci贸n de usuario y/o otros tipo para su posterior reh煤so del mismo.

驴Porque se llaman Cookies 馃崻?

El nombre tan peculiar proviene del cl谩sico cuento de hadas 鈥淗ansel y Gretel鈥 en el cuento los hermanos dejan rastros de dulces y bas谩ndose en esta analog铆a se refiere al mismo rastros que dejamos con las visitas a los sitios web que visitamos y dejan en el ordenador estos archivos.

驴Para que sirven las Cookies 馃崻?

Las Cookies almacenan distintos tipos de informaci贸n de los usuarios con el objetivo de; identificarlo, recordar ciertas preferencias, ofrecerles contenido, etc.

para los errores cree dos constantes utilizando useState en el useAuth.js

const [error, setError]=useState();

  return {
    user,
    error,
    setError,
    signIn,
  };

En el login page, en el auth.signIn

    auth.signIn(email, password).then(
      () => {
        console.log('Login success');
      },
      (reason) => {
        console.log('Login Failed');
        console.error(reason);
        auth.setError('Invalid Username or Password');
      }
    );

y antes de cerrar el form para mostrar el estado del error agrege

            {auth.error ? (
              <div className="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg dark:bg-red-200 dark:text-red-800" role="alert">
                <span className="font-medium">Login Failed!</span> {auth.error}
              </div>
            ) : null}

Utilice el modal que estaba ya en el proyecto c: quedo as铆

Lo hice con SweetAlert2 y algo del router de next.js

Ac谩 los imports

import { useRouter } from 'next/router';
import { useSate, useRef } from 'react';
import Swal from 'sweetalert2';

import { useAuth } from '@hooks/useAuth';

import { LockClosedIcon } from '@heroicons/react/solid';

Y ac谩 lo anterior al return

  const passwordRef = useRef(null);
  const emailRef = useRef(null);
  const router = useRouter();
  const auth = useAuth();

  const submitHandler = (event) => {
    event.preventDefault();
    const password = passwordRef.current.value;
    const email = emailRef.current.value;

    auth
      .singIn(email, password)
      .then(() => {
        router.push('/dashboard');
        Swal.fire({
          title: 'Welcome',
          icon: 'success',
          confirmButtonText: 'Okay',
        });
      })
      .catch((error) => {
        if (error.response?.status === 401) {
          Swal.mixin({
            toast: true,
            title: 'Error!',
            text: `This user doesn't exist, it migth be an error on your user or password`,
            icon: 'error',
            confirmButtonText: 'Okay',
          });
        } else if (error.resquest) {
          Swal.mixin({
            toast: true,
            title: 'Ups...',
            text: 'Something went wrong, we are experimenting some toubles, try it later',
            icon: 'error',
            confirmButtonText: 'Okay',
          });
        }
      });
  };

Por si a alguien le interesa, para instarlar SweetAlert, usas

npm install sweetalert2

Utiliz茅 el componente modal que nos ofrece tailwind UI y lo adapt茅 un poco

Componente LoginError

A帽ad铆 un catch en LoginPage.jsx

si quieren ver su cookie actual en cualquier navegador ctrl+shit+c, ejecutar un
alert(document.cookie)

Resultado

Para la retroalimentaci贸n de errores use los toast de tailwind y el hook de useState

En loginPage

import { useRef, useState } from 'react';
import { LockClosedIcon } from '@heroicons/react/solid';
// import custom hook
import { useAuth } from '@hooks/useAuth';
// import components
import Toast from '@components/Toast';
import Spinner from '@components/Spinner';

export default function LoginPage() {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);
  // In this case auth return the user and a function to sign in
  const auth = useAuth();

  // state of spinner
  const [loading, setLoading] = useState(false);
  // state of error
  const [error, setError] = useState(null);

  const submitHandler = (e) => {
    // prevent the form from submitting
    e.preventDefault();
    setLoading(true);
    setError(false);
    const email = emailRef.current.value;
    const password = passwordRef.current.value;

    // do something with the data, in this case the user signIn
    auth
      .signIn(email, password)
      .then(() => {
        console.log('signed in');
        setLoading(false);
      })
      .catch((err) => {
        setError(true);
        setLoading(false);
        console.log('Error signing: ', err);
      });
  };

  return (
    <>
      <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
        <div className="max-w-md w-full space-y-8">
          <div>
            <img className="mx-auto h-12 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="Workflow" />
            <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">Sign in to your account</h2>
          </div>
          <form className="mt-8 space-y-6" onSubmit={submitHandler}>
            <input type="hidden" name="remember" defaultValue="true" />
            <div className="rounded-md shadow-sm -space-y-px">
              <div>
                <label htmlFor="email-address" className="sr-only">
                  Email address
                </label>
                <input
                  id="email-address"
                  name="email"
                  type="email"
                  autoComplete="email"
                  required
                  className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
                  placeholder="Email address"
                  ref={emailRef}
                />
              </div>
              <div>
                <label htmlFor="password" className="sr-only">
                  Password
                </label>
                <input
                  id="password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                  required
                  className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
                  placeholder="Password"
                  ref={passwordRef}
                />
              </div>
            </div>

            <div className="flex items-center justify-between">
              <div className="flex items-center">
                <input id="remember-me" name="remember-me" type="checkbox" className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" />
                <label htmlFor="remember-me" className="ml-2 block text-sm text-gray-900">
                  Remember me
                </label>
              </div>

              <div className="text-sm">
                <a href="/reset" className="font-medium text-indigo-600 hover:text-indigo-500">
                  Forgot your password?
                </a>
              </div>
            </div>

            <div>
              <button
                type="submit"
                className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
              >
                <span className="absolute left-0 inset-y-0 flex items-center pl-3">
                  <LockClosedIcon className="h-5 w-5 text-indigo-500 group-hover:text-indigo-400" aria-hidden="true" />
                </span>
                Sign in
              </button>
            </div>
          </form>
          {loading && <Spinner />}
          {error && <Toast type="error" title="Error" message="Your email or password is incorrect, try again" time="now" />}
        </div>
      </div>
    </>
  );
}

El toast

import React from 'react';

export default function Toast({ type, title, message, time }) {
  return (
    <div className="flex flex-col justify-center">
      {/* info toast */}
      {type === 'info' && (
        <div
          className="bg-blue-600 shadow-lg mx-auto w-96 max-w-full text-sm pointer-events-auto bg-clip-padding rounded-lg block mb-3"
          id="static-example"
          role="alert"
          aria-live="assertive"
          aria-atomic="true"
          data-mdb-autohide="false"
        >
          <div className="bg-blue-600 flex justify-between items-center py-2 px-3 bg-clip-padding border-b border-blue-500 rounded-t-lg">
            <p className="font-bold text-white flex items-center">
              <svg
                aria-hidden="true"
                focusable="false"
                data-prefix="fas"
                data-icon="info-circle"
                className="w-4 h-4 mr-2 fill-current"
                role="img"
                
                viewBox="0 0 512 512"
              >
                <path
                  fill="currentColor"
                  d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"
                ></path>
              </svg>
              MDBootstrap
            </p>
            <div className="flex items-center">
              <p className="text-white opacity-90 text-xs">11 mins ago</p>
              <button
                type="button"
                className="btn-close btn-close-white box-content w-4 h-4 ml-2 text-white border-none rounded-none opacity-50 focus:shadow-none focus:outline-none focus:opacity-100 hover:text-white hover:opacity-75 hover:no-underline"
                data-mdb-dismiss="toast"
                aria-label="Close"
              ></button>
            </div>
          </div>
          <div className="p-3 bg-blue-600 rounded-b-lg break-words text-white">Hello, world! This is a toast message.</div>
        </div>
      )}

      {/* success toast */}
      {type === 'success' && (
        <div
          className="bg-green-500 shadow-lg mx-auto w-96 max-w-full text-sm pointer-events-auto bg-clip-padding rounded-lg block mb-3"
          id="static-example"
          role="alert"
          aria-live="assertive"
          aria-atomic="true"
          data-mdb-autohide="false"
        >
          <div className="bg-green-500 flex justify-between items-center py-2 px-3 bg-clip-padding border-b border-green-400 rounded-t-lg">
            <p className="font-bold text-white flex items-center">
              <svg
                aria-hidden="true"
                focusable="false"
                data-prefix="fas"
                data-icon="check-circle"
                className="w-4 h-4 mr-2 fill-current"
                role="img"
                
                viewBox="0 0 512 512"
              >
                <path
                  fill="currentColor"
                  d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
                ></path>
              </svg>
              MDBootstrap
            </p>
            <div className="flex items-center">
              <p className="text-white opacity-90 text-xs">11 mins ago</p>
              <button
                type="button"
                className="btn-close btn-close-white box-content w-4 h-4 ml-2 text-white border-none rounded-none opacity-50 focus:shadow-none focus:outline-none focus:opacity-100 hover:text-white hover:opacity-75 hover:no-underline"
                data-mdb-dismiss="toast"
                aria-label="Close"
              ></button>
            </div>
          </div>
          <div className="p-3 bg-green-500 rounded-b-lg break-words text-white">Hello, world! This is a toast message.</div>
        </div>
      )}
      {/* danger toast */}

      {type === 'danger' && (
        <div
          className="bg-yellow-500 shadow-lg mx-auto w-96 max-w-full text-sm pointer-events-auto bg-clip-padding rounded-lg block mb-3"
          id="static-example"
          role="alert"
          aria-live="assertive"
          aria-atomic="true"
          data-mdb-autohide="false"
        >
          <div className="bg-yellow-500 flex justify-between items-center py-2 px-3 bg-clip-padding border-b border-yellow-400 rounded-t-lg">
            <p className="font-bold text-white flex items-center">
              <svg
                aria-hidden="true"
                focusable="false"
                data-prefix="fas"
                data-icon="exclamation-triangle"
                className="w-4 h-4 mr-2 fill-current"
                role="img"
               
                viewBox="0 0 576 512"
              >
                <path
                  fill="currentColor"
                  d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
                ></path>
              </svg>
              MDBootstrap
            </p>
            <div className="flex items-center">
              <p className="text-white opacity-90 text-xs">11 mins ago</p>
              <button
                type="button"
                className="btn-close btn-close-white box-content w-4 h-4 ml-2 text-white border-none rounded-none opacity-50 focus:shadow-none focus:outline-none focus:opacity-100 hover:text-white hover:opacity-75 hover:no-underline"
                data-mdb-dismiss="toast"
                aria-label="Close"
              ></button>
            </div>
          </div>
          <div className="p-3 bg-yellow-500 rounded-b-lg break-words text-white">Hello, world! This is a toast message.</div>
        </div>
      )}
      {/* error toast */}
      {type === 'error' && (
        <div
          className="bg-red-600 shadow-lg mx-auto w-96 max-w-full text-sm pointer-events-auto bg-clip-padding rounded-lg block mb-3"
          id="static-example"
          role="alert"
          aria-live="assertive"
          aria-atomic="true"
          data-mdb-autohide="false"
        >
          <div className="bg-red-600 flex justify-between items-center py-2 px-3 bg-clip-padding border-b border-red-500 rounded-t-lg">
            <p className="font-bold text-white flex items-center">
              <svg
                aria-hidden="true"
                focusable="false"
                data-prefix="fas"
                data-icon="times-circle"
                className="w-4 h-4 mr-2 fill-current"
                role="img"
               
                viewBox="0 0 512 512"
              >
                <path
                  fill="currentColor"
                  d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
                ></path>
              </svg>
              {title != null ? title : 'MDBootstrap'}
            </p>
            <div className="flex items-center">
              <p className="text-white opacity-90 text-xs">{time != null ? time : '11 mins ago'}</p>
              <button
                type="button"
                className="btn-close btn-close-white box-content w-4 h-4 ml-2 text-white border-none rounded-none opacity-50 focus:shadow-none focus:outline-none focus:opacity-100 hover:text-white hover:opacity-75 hover:no-underline"
                data-mdb-dismiss="toast"
                aria-label="Close"
              ></button>
            </div>
          </div>
          <div className="p-3 bg-red-600 rounded-b-lg break-words text-white">{message != null ? message : 'Hello, world! This is a toast message.'}</div>
        </div>
      )}
    </div>
  );
}

Y el spinner

const Spinner = () => {
  return (
    <div class="flex justify-center items-center">
      <div class="spinner-border animate-spin inline-block w-8 h-8 border-4 rounded-full" role="status">
        <span class="visually-hidden">.</span>
      </div>
    </div>
  );
};

export default Spinner;

Mientras estemos trabajando con el login, me parece util tener quedamos los valores que iran en email y password para evitar estar escribiendolo repetidas veces. 馃槂

const [value,setValue] = useState({email:'admin@mail.com',password:'admin123'})

As铆 que cree ese estado, y se los pase a los inputs.

Obviamente es algo que se debe de borrar antes de subir a producci贸n