Autenticação

Supabase Auth

Supabase Auth:

Banco de Dados

Coluna uid na tabela investments:

create table
  public.investments (
    name character varying null,
    created_at timestamp with time zone not null default now(),
    value bigint null,
    origin character varying null,
    category character varying null,
    interest character varying null,
    id uuid not null default gen_random_uuid (),
    uid uuid null,
    constraint investments_pkey primary key (id),
    constraint investments_uid_fkey foreign key (uid) references auth.users (id)
  ) tablespace pg_default;
create table
  public.investments (
    name character varying null,
    created_at timestamp with time zone not null default now(),
    value bigint null,
    origin character varying null,
    category character varying null,
    interest character varying null,
    id uuid not null default gen_random_uuid (),
    uid uuid null,
    constraint investments_pkey primary key (id),
    constraint investments_uid_fkey foreign key (uid) references auth.users (id)
  ) tablespace pg_default;

Permitir Acesso Anônimo

Row Level Security (Authentication > Configuration > Policies) - authenticated users:

-- Turn on security
alter table "investments"
enable row level security;

-- Allow anonymous access
CREATE POLICY "Allow anonymous access"
  ON investments
  FOR SELECT
  TO anon, authenticated
  USING (true);
-- Turn on security
alter table "investments"
enable row level security;

-- Allow anonymous access
CREATE POLICY "Allow anonymous access"
  ON investments
  FOR SELECT
  TO anon, authenticated
  USING (true);

Google Provider

InvestApp (Auth)

Código Fonte

Arquivos
invest-app
├── README.md
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│   ├── next.svg
│   └── vercel.svg
├── src
│   ├── app
│   │   ├── favicon.ico
│   │   ├── globals.css
│   │   ├── investments
│   │   │   └── [slug]
│   │   │       └── page.jsx
│   │   ├── layout.jsx
│   │   ├── page.jsx
│   │   ├── signin
│   │   │   └── page.jsx
│   │   └── signup
│   │       └── page.jsx
│   ├── components
│   │   ├── InvestmentCard.jsx
│   │   ├── InvestmentForm.jsx
│   │   ├── Modal.jsx
│   │   ├── NavBar.jsx
│   │   └── ProtectedPage.jsx
│   ├── contexts
│   │   ├── InvestmentContext.jsx
│   │   └── UserAuthContext.jsx
│   ├── lib
│   │   └── format.js
│   └── services
│       ├── storage.js
│       └── supabase.js
└── tailwind.config.js
Arquivos
invest-app
├── README.md
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│   ├── next.svg
│   └── vercel.svg
├── src
│   ├── app
│   │   ├── favicon.ico
│   │   ├── globals.css
│   │   ├── investments
│   │   │   └── [slug]
│   │   │       └── page.jsx
│   │   ├── layout.jsx
│   │   ├── page.jsx
│   │   ├── signin
│   │   │   └── page.jsx
│   │   └── signup
│   │       └── page.jsx
│   ├── components
│   │   ├── InvestmentCard.jsx
│   │   ├── InvestmentForm.jsx
│   │   ├── Modal.jsx
│   │   ├── NavBar.jsx
│   │   └── ProtectedPage.jsx
│   ├── contexts
│   │   ├── InvestmentContext.jsx
│   │   └── UserAuthContext.jsx
│   ├── lib
│   │   └── format.js
│   └── services
│       ├── storage.js
│       └── supabase.js
└── tailwind.config.js
/codes/react/supabase-auth/invest-app/src/services/supabase.js
import { createClient } from '@supabase/supabase-js';
 
const API_KEY = process.env.NEXT_PUBLIC_SUPABASE_KEY;
const API_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;
 
export const supabase = createClient(API_URL, API_KEY);
 
/codes/react/supabase-auth/invest-app/src/services/supabase.js
import { createClient } from '@supabase/supabase-js';
 
const API_KEY = process.env.NEXT_PUBLIC_SUPABASE_KEY;
const API_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;
 
export const supabase = createClient(API_URL, API_KEY);
 
/codes/react/supabase-auth/invest-app/src/contexts/UserAuthContext.jsx
'use client';
 
import { createContext, useContext, useEffect, useState } from 'react';
 
import { supabase } from '@/services/supabase';
 
const UserAuthContext = createContext({});
 
export function UserAuthContextProvider({ children }) {
  const [user, setUser] = useState({});
  const [loading, setLoading] = useState(true);
 
  function logIn(email, password) {
    return supabase.auth.signInWithPassword({ email, password });
  }
 
  function signUp(email, password) {
    return supabase.auth.signUp({ email, password });
  }
 
  function logOut() {
    setUser(null);
 
    return supabase.auth.signOut();
  }
 
  async function googleSignIn() {
    return await supabase.auth.signInWithOAuth({
      provider: 'google',
    });
  }
 
  useEffect(() => {
    const {
      data: { subscription: authListener },
    } = supabase.auth.onAuthStateChange((event, session) => {
      console.log('session', event, session);
 
      if (session) {
        setUser(session?.user);
      } else {
        setUser(null);
      }
 
      setLoading(false);
    });
 
    return () => {
      authListener?.unsubscribe();
    };
  }, []);
 
  return (
    <UserAuthContext.Provider
      value={{ user, logIn, signUp, logOut, googleSignIn }}
    >
      {loading ? <div>Loading...</div> : children}
    </UserAuthContext.Provider>
  );
}
 
export function useUserAuth() {
  return useContext(UserAuthContext);
}
 
/codes/react/supabase-auth/invest-app/src/contexts/UserAuthContext.jsx
'use client';
 
import { createContext, useContext, useEffect, useState } from 'react';
 
import { supabase } from '@/services/supabase';
 
const UserAuthContext = createContext({});
 
export function UserAuthContextProvider({ children }) {
  const [user, setUser] = useState({});
  const [loading, setLoading] = useState(true);
 
  function logIn(email, password) {
    return supabase.auth.signInWithPassword({ email, password });
  }
 
  function signUp(email, password) {
    return supabase.auth.signUp({ email, password });
  }
 
  function logOut() {
    setUser(null);
 
    return supabase.auth.signOut();
  }
 
  async function googleSignIn() {
    return await supabase.auth.signInWithOAuth({
      provider: 'google',
    });
  }
 
  useEffect(() => {
    const {
      data: { subscription: authListener },
    } = supabase.auth.onAuthStateChange((event, session) => {
      console.log('session', event, session);
 
      if (session) {
        setUser(session?.user);
      } else {
        setUser(null);
      }
 
      setLoading(false);
    });
 
    return () => {
      authListener?.unsubscribe();
    };
  }, []);
 
  return (
    <UserAuthContext.Provider
      value={{ user, logIn, signUp, logOut, googleSignIn }}
    >
      {loading ? <div>Loading...</div> : children}
    </UserAuthContext.Provider>
  );
}
 
export function useUserAuth() {
  return useContext(UserAuthContext);
}
 
/codes/react/supabase-auth/invest-app/src/components/ProtectedPage.jsx
'use client';
 
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useUserAuth } from '@/contexts/UserAuthContext';
 
export default function ProtectedPage({ children }) {
  const { user } = useUserAuth();
  const router = useRouter();
 
  if (user == null) {
    router.push('/signin');
  }
 
  useEffect(() => {
    if (user == null) {
      router.push('/signin');
    }
  }, [user]);
 
  return <>{children}</>;
}
 
/codes/react/supabase-auth/invest-app/src/components/ProtectedPage.jsx
'use client';
 
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useUserAuth } from '@/contexts/UserAuthContext';
 
export default function ProtectedPage({ children }) {
  const { user } = useUserAuth();
  const router = useRouter();
 
  if (user == null) {
    router.push('/signin');
  }
 
  useEffect(() => {
    if (user == null) {
      router.push('/signin');
    }
  }, [user]);
 
  return <>{children}</>;
}
 
/codes/react/supabase-auth/invest-app/src/components/NavBar.jsx
'use client';
 
import Link from 'next/link';
import { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { useUserAuth } from '@/contexts/UserAuthContext';
 
const initialMenu = [
  { title: 'Entrar', path: '/signin' },
  { title: 'Cadastrar', path: '/signup' },
];
 
const userMenu = [
  { title: 'Investimentos', path: '/' },
  // { title: 'Extrato', path: '/report' },
];
 
export default function NavBar() {
  const [menu, setMenu] = useState(initialMenu);
 
  const pathname = usePathname();
 
  const router = useRouter();
 
  const { logOut, user } = useUserAuth();
 
  const handleLogout = async () => {
    try {
      router.push('/signin');
 
      await logOut();
    } catch (error) {
      console.log(error.message);
    }
  };
 
  useEffect(() => {
    if (user) {
      setMenu(userMenu);
    } else {
      setMenu(initialMenu);
    }
  }, [user]);
 
  return (
    <nav className="bg-white border-gray-200 dark:bg-gray-900">
      <div className="max-w-screen-lg flex flex-wrap items-center justify-between mx-auto p-4">
        <Link
          href="/"
          className="text-2xl font-semibold whitespace-nowrap dark:text-white"
        >
          InvestApp
        </Link>
        <button
          data-collapse-toggle="navbar-default"
          type="button"
          className="inline-flex items-center p-2 ml-3 text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
          aria-controls="navbar-default"
          aria-expanded="false"
        >
          <span className="sr-only">Abrir menu</span>
          <svg
            className="w-6 h-6"
            aria-hidden="true"
            fill="currentColor"
            viewBox="0 0 20 20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              fillRule="evenodd"
              d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
              clipRule="evenodd"
            ></path>
          </svg>
        </button>
        <div className="hidden w-full md:block md:w-auto" id="navbar-default">
          <ul className="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
            {menu.map((item, index) => (
              <li key={index}>
                {pathname === item.path ? (
                  <Link
                    href={item.path}
                    className="block py-2 pl-3 pr-4 text-white bg-gray-600 rounded md:bg-transparent md:text-gray-800 md:hover:text-gray-500 md:p-0 dark:text-white md:dark:text-gray-500"
                    aria-current="page"
                  >
                    {item.title}
                  </Link>
                ) : (
                  <Link
                    href={item.path}
                    className="block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-gray-700 md:p-0 dark:text-white md:dark:hover:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent"
                  >
                    {item.title}
                  </Link>
                )}
              </li>
            ))}
            {user && (
              <li>
                <button
                  type="button"
                  className="block py-2 pl-3 pr-4 text-gray-500 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-gray-700 md:p-0 dark:text-white md:dark:hover:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent"
                  onClick={handleLogout}
                >
                  Sair
                </button>
              </li>
            )}
          </ul>
        </div>
      </div>
    </nav>
  );
}
 
/codes/react/supabase-auth/invest-app/src/components/NavBar.jsx
'use client';
 
import Link from 'next/link';
import { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { useUserAuth } from '@/contexts/UserAuthContext';
 
const initialMenu = [
  { title: 'Entrar', path: '/signin' },
  { title: 'Cadastrar', path: '/signup' },
];
 
const userMenu = [
  { title: 'Investimentos', path: '/' },
  // { title: 'Extrato', path: '/report' },
];
 
export default function NavBar() {
  const [menu, setMenu] = useState(initialMenu);
 
  const pathname = usePathname();
 
  const router = useRouter();
 
  const { logOut, user } = useUserAuth();
 
  const handleLogout = async () => {
    try {
      router.push('/signin');
 
      await logOut();
    } catch (error) {
      console.log(error.message);
    }
  };
 
  useEffect(() => {
    if (user) {
      setMenu(userMenu);
    } else {
      setMenu(initialMenu);
    }
  }, [user]);
 
  return (
    <nav className="bg-white border-gray-200 dark:bg-gray-900">
      <div className="max-w-screen-lg flex flex-wrap items-center justify-between mx-auto p-4">
        <Link
          href="/"
          className="text-2xl font-semibold whitespace-nowrap dark:text-white"
        >
          InvestApp
        </Link>
        <button
          data-collapse-toggle="navbar-default"
          type="button"
          className="inline-flex items-center p-2 ml-3 text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
          aria-controls="navbar-default"
          aria-expanded="false"
        >
          <span className="sr-only">Abrir menu</span>
          <svg
            className="w-6 h-6"
            aria-hidden="true"
            fill="currentColor"
            viewBox="0 0 20 20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              fillRule="evenodd"
              d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
              clipRule="evenodd"
            ></path>
          </svg>
        </button>
        <div className="hidden w-full md:block md:w-auto" id="navbar-default">
          <ul className="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
            {menu.map((item, index) => (
              <li key={index}>
                {pathname === item.path ? (
                  <Link
                    href={item.path}
                    className="block py-2 pl-3 pr-4 text-white bg-gray-600 rounded md:bg-transparent md:text-gray-800 md:hover:text-gray-500 md:p-0 dark:text-white md:dark:text-gray-500"
                    aria-current="page"
                  >
                    {item.title}
                  </Link>
                ) : (
                  <Link
                    href={item.path}
                    className="block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-gray-700 md:p-0 dark:text-white md:dark:hover:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent"
                  >
                    {item.title}
                  </Link>
                )}
              </li>
            ))}
            {user && (
              <li>
                <button
                  type="button"
                  className="block py-2 pl-3 pr-4 text-gray-500 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-gray-700 md:p-0 dark:text-white md:dark:hover:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent"
                  onClick={handleLogout}
                >
                  Sair
                </button>
              </li>
            )}
          </ul>
        </div>
      </div>
    </nav>
  );
}
 
/codes/react/supabase-auth/invest-app/src/app/signup/page.jsx
'use client';
 
import { useUserAuth } from '@/contexts/UserAuthContext';
import { useState } from 'react';
 
export default function Signup() {
  const [email, setEmail] = useState('');
 
  const [password, setPassword] = useState('');
 
  const [confirmPassword, setConfirmPassword] = useState('');
 
  const [message, setMessage] = useState('');
 
  const { signUp } = useUserAuth();
 
  const handleSubmit = async (event) => {
    event.preventDefault();
 
    setMessage('');
 
    if (password !== confirmPassword) {
      setMessage('As senhas não conferem.');
    }
 
    try {
      if (!message) {
        await signUp(email, password);
 
        setMessage('Acess sua caixa de email para confirmar o cadastro.');
      }
    } catch (err) {
      setMessage('Erro no cadastro, tente novamente.');
    }
  };
 
  return (
    <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm h-full px-6 py-12 lg:px-8">
      <h1 className="mb-8 text-3xl text-center">Cadastro</h1>
      {message && (
        <div class="bg-gray-300 text-gray-700 p-4 my-4" role="alert">
          <p>{message}</p>
        </div>
      )}
      <form onSubmit={handleSubmit}>
        <div className="mb-4">
          <label
            htmlFor="email"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Email
          </label>
          <input
            id="email"
            type="email"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="password"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Senha
          </label>
          <input
            id="password"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="confirmPassword"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Confirmar Senha
          </label>
          <input
            id="confirmPassword"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={confirmPassword}
            onChange={(e) => setConfirmPassword(e.target.value)}
            required
          />
        </div>
 
        <button
          type="submit"
          className="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          Cadastrar
        </button>
      </form>
    </div>
  );
}
 
/codes/react/supabase-auth/invest-app/src/app/signup/page.jsx
'use client';
 
import { useUserAuth } from '@/contexts/UserAuthContext';
import { useState } from 'react';
 
export default function Signup() {
  const [email, setEmail] = useState('');
 
  const [password, setPassword] = useState('');
 
  const [confirmPassword, setConfirmPassword] = useState('');
 
  const [message, setMessage] = useState('');
 
  const { signUp } = useUserAuth();
 
  const handleSubmit = async (event) => {
    event.preventDefault();
 
    setMessage('');
 
    if (password !== confirmPassword) {
      setMessage('As senhas não conferem.');
    }
 
    try {
      if (!message) {
        await signUp(email, password);
 
        setMessage('Acess sua caixa de email para confirmar o cadastro.');
      }
    } catch (err) {
      setMessage('Erro no cadastro, tente novamente.');
    }
  };
 
  return (
    <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm h-full px-6 py-12 lg:px-8">
      <h1 className="mb-8 text-3xl text-center">Cadastro</h1>
      {message && (
        <div class="bg-gray-300 text-gray-700 p-4 my-4" role="alert">
          <p>{message}</p>
        </div>
      )}
      <form onSubmit={handleSubmit}>
        <div className="mb-4">
          <label
            htmlFor="email"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Email
          </label>
          <input
            id="email"
            type="email"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="password"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Senha
          </label>
          <input
            id="password"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="confirmPassword"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Confirmar Senha
          </label>
          <input
            id="confirmPassword"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={confirmPassword}
            onChange={(e) => setConfirmPassword(e.target.value)}
            required
          />
        </div>
 
        <button
          type="submit"
          className="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          Cadastrar
        </button>
      </form>
    </div>
  );
}
 
/codes/react/supabase-auth/invest-app/src/app/signin/page.jsx
'use client';
 
import { useUserAuth } from '@/contexts/UserAuthContext';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
 
export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
 
  const [error, setError] = useState('');
 
  const { logIn, googleSignIn } = useUserAuth();
 
  const router = useRouter();
 
  const handleLogin = async (event) => {
    event.preventDefault();
 
    setError('');
 
    try {
      await logIn(email, password);
 
      router.push('/');
    } catch (err) {
      setError('Erro no login, tente novamente.');
    }
  };
 
  const handleGoogleSignIn = async (event) => {
    event.preventDefault();
 
    try {
      await googleSignIn();
 
      router.push('/');
    } catch (error) {
      setError('Erro no login, tente novamente.');
    }
  };
 
  return (
    <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm h-full px-6 py-12 lg:px-8">
      <h1 className="mb-8 text-3xl text-center">Entrar</h1>
      {error && (
        <div className="bg-orange-100 text-orange-700 p-4 my-4" role="alert">
          <p>{error}</p>
        </div>
      )}
      <form onSubmit={handleLogin}>
        <div className="mb-4">
          <label
            htmlFor="email"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Email
          </label>
          <input
            id="email"
            type="email"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="password"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Senha
          </label>
          <input
            id="password"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button
          type="submit"
          className="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          Entrar
        </button>
 
        <div className="my-4 flex items-center before:mt-0.5 before:flex-1 before:border-t before:border-neutral-300 after:mt-0.5 after:flex-1 after:border-t after:border-neutral-300">
          <p className="mx-4 mb-0 text-center font-semibold dark:text-neutral-200">
            ou
          </p>
        </div>
 
        <button
          type="button"
          className="text-white w-full mt-4 bg-[#4285F4] hover:bg-[#4285F4]/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center justify-between dark:focus:ring-[#4285F4]/55 mr-2 mb-2"
          onClick={handleGoogleSignIn}
        >
          <svg
            className="mr-2 -ml-1 w-4 h-4"
            aria-hidden="true"
            focusable="false"
            data-prefix="fab"
            data-icon="google"
            role="img"
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 488 512"
          >
            <path
              fill="currentColor"
              d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"
            ></path>
          </svg>
          Entrar com Google<div></div>
        </button>
      </form>
    </div>
  );
}
 
/codes/react/supabase-auth/invest-app/src/app/signin/page.jsx
'use client';
 
import { useUserAuth } from '@/contexts/UserAuthContext';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
 
export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
 
  const [error, setError] = useState('');
 
  const { logIn, googleSignIn } = useUserAuth();
 
  const router = useRouter();
 
  const handleLogin = async (event) => {
    event.preventDefault();
 
    setError('');
 
    try {
      await logIn(email, password);
 
      router.push('/');
    } catch (err) {
      setError('Erro no login, tente novamente.');
    }
  };
 
  const handleGoogleSignIn = async (event) => {
    event.preventDefault();
 
    try {
      await googleSignIn();
 
      router.push('/');
    } catch (error) {
      setError('Erro no login, tente novamente.');
    }
  };
 
  return (
    <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm h-full px-6 py-12 lg:px-8">
      <h1 className="mb-8 text-3xl text-center">Entrar</h1>
      {error && (
        <div className="bg-orange-100 text-orange-700 p-4 my-4" role="alert">
          <p>{error}</p>
        </div>
      )}
      <form onSubmit={handleLogin}>
        <div className="mb-4">
          <label
            htmlFor="email"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Email
          </label>
          <input
            id="email"
            type="email"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
 
        <div className="mb-4">
          <label
            htmlFor="password"
            className="block text-sm font-medium leading-6 text-gray-900 mb-2"
          >
            Senha
          </label>
          <input
            id="password"
            type="password"
            className="block p-2 w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button
          type="submit"
          className="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          Entrar
        </button>
 
        <div className="my-4 flex items-center before:mt-0.5 before:flex-1 before:border-t before:border-neutral-300 after:mt-0.5 after:flex-1 after:border-t after:border-neutral-300">
          <p className="mx-4 mb-0 text-center font-semibold dark:text-neutral-200">
            ou
          </p>
        </div>
 
        <button
          type="button"
          className="text-white w-full mt-4 bg-[#4285F4] hover:bg-[#4285F4]/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center justify-between dark:focus:ring-[#4285F4]/55 mr-2 mb-2"
          onClick={handleGoogleSignIn}
        >
          <svg
            className="mr-2 -ml-1 w-4 h-4"
            aria-hidden="true"
            focusable="false"
            data-prefix="fab"
            data-icon="google"
            role="img"
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 488 512"
          >
            <path
              fill="currentColor"
              d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"
            ></path>
          </svg>
          Entrar com Google<div></div>
        </button>
      </form>
    </div>
  );
}
 

Editar esta página