1 / 10
🎮
Disciplina de Desenvolvimento Web
TaTaKaZim Jogos

Vaporzão

Clone didático da plataforma Steam desenvolvido com React 19 + Vite, aplicando os conceitos ensinados nas aulas 07 a 16.

React 19 React Router v6 Axios Context API JWT Auth SweetAlert2
use as setas ou ← → para navegar
Visão Geral

O que construímos

🏠

Home

Feed com hero banner, seções Recentes, Mais Avaliados e Populares, sidebar de gêneros e atalhos.

🔍

Catálogo

Busca por título e filtro por gênero em tempo real. Listagem de todos os jogos disponíveis.

🎮

Detalhes do Jogo

Galeria de imagens, conquistas, reviews e controles de biblioteca e wishlist.

Reviews

Criar, editar e excluir avaliações com nota. Média calculada dinamicamente.

🏆

Conquistas

Desenvolvedor cria conquistas por jogo, exibidas na página de detalhes.

CRUD de Jogos

Criação de jogos com gêneros e upload de imagens, edição e gerenciamento completo.

Aulas 07 · 08 · 09

Integração com API & async/await

Aula 08 — api.js
Instância Axios configurada
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://alunos-ads-api...',
});

// Token injetado automaticamente
api.interceptors.request.use(config => {
  const token = localStorage
    .getItem('token');
  if (token)
    config.headers.token = token;
  return config;
});
baseURL centralizadainterceptor automático
Aula 09 — async/await
Promise.all para performance
// Home — 2 chamadas paralelas
const [destaquesRes, generosRes] =
  await Promise.all([
    api.get('/jogos/destaques'),
    api.get('/generos'),
  ]);

// GameDetails — 3 chamadas paralelas
const [gameRes, statusRes, userRes] =
  await Promise.all([
    api.get(`/jogos/${id}`),
    api.get(`/jogos/${id}/status`),
    api.get('/auth/me'),
  ]);
requisições em paralelotry/catch/finally
Aula 13

Formulários React

Login
Componente controlado
const [matricula,
  setMatricula] = useState("");
const [senha,
  setSenha] = useState("");

<input
  value={matricula}
  onChange={e =>
    setMatricula(
      e.target.value
    )}
/>
Criar Jogo
Seleção múltipla de gêneros
function handleGenero(id) {
  setGeneroIds(prev =>
    prev.includes(id)
      ? prev.filter(
          g => g !== id)
      : [...prev, id]
  );
}
// Validação antes do POST
if (generoIds.length === 0) {
  swal.fire({
    icon: 'warning'
  });
  return;
}
Primeiro Acesso
Submit com redirect
async function
  handleSubmit(e) {
  e.preventDefault();

  await api.patch(
    '/usuarios/me',
    { nome }
  );

  // Atualiza Context global
  login(token, novoUser);
  navigate('/');
}
Controlled ComponentsonSubmit + preventDefaultvalidação client-sidefeedback via SweetAlert2
Aula 11

React Router

Rotas definidas — App.jsx
<Routes>
  <Route path="/login"
    element={<Login />} />

  <Route path="/"
    element={
      <ProtectedRoute>
        <Home />
      </ProtectedRoute>
    } />

  <Route
    path="/jogos/:id"
    element={<GameDetails />}
  />

  <Route
    path="/perfil/:matricula"
    element={<PublicProfile />}
  />
</Routes>
Hooks de navegação
useNavigateRedirecionar após login / logout
useParamsCapturar :id e :matricula
useLocationPassar gênero ativo ao catálogo
ProtectedRoute
function ProtectedRoute({ children }) {
  const { token, loading }
    = useAuth();
  if (loading) return <Spinner />;
  if (!token)
    return <Navigate to="/login" />;
  return children;
}
Aula 12

Autenticação JWT

Fluxo completo
1
Usuário envia matrícula + senha
2
API retorna token JWT + dados do usuário
3
Token salvo no localStorage
4
Interceptor Axios injeta o token em toda requisição
5
ProtectedRoute bloqueia rotas sem token válido
Login.jsx
handleLogin
async function handleLogin(e) {
  e.preventDefault();

  const res = await api.post(
    '/auth/login',
    { matricula, senha }
  );

  const { token, usuario } = res.data;

  // Salva no AuthContext global
  login(token, usuario);

  if (!usuario.nome) {
    navigate('/first-access');
  } else {
    navigate('/');
  }
}
Aulas 14 · 15 · 16

Estado Global com Context API

AuthContext.jsx
const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [token, setToken] = useState(null);
  const [user,  setUser]  = useState(null);

  useEffect(() => {
    async function init() {
      const stored =
        localStorage.getItem('token');
      if (stored) {
        const res =
          await api.get('/auth/me');
        setToken(stored);
        setUser(res.data);
      }
    }
    init();
  }, []);

  return (
    <AuthContext.Provider
      value={{ token, user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

Por que Context?

Sem Context, cada página teria que ler o localStorage manualmente e não reagiria a mudanças. Com o AuthProvider no topo da árvore, qualquer componente acessa o usuário logado instantaneamente.

Estado compartilhado
tokenJWT da sessão ativa
userDados do usuário logado
loadingAguardando restauração
login()Persiste token e atualiza estado
logout()Limpa token e redireciona
Aula 10

Custom Hook useAuth

Definição — AuthContext.jsx
export function useAuth() {
  const context =
    useContext(AuthContext);

  if (!context) {
    throw new Error(
      "useAuth must be used"
      + " within AuthProvider"
    );
  }

  return context;
  // { token, user, loading,
  //   login, logout }
}
Usado em toda a aplicação
// ProtectedRoute.jsx
const { token, loading } = useAuth();

// Navbar.jsx
const { user, logout } = useAuth();

// Login.jsx
const { login } = useAuth();

// GameDetails.jsx
const { user } = useAuth();

Benefício do Custom Hook

Encapsula o useContext(AuthContext) + validação de escopo em um único lugar. Qualquer componente obtém o estado global com uma linha — sem repetição de código.

Produto Final

Telas implementadas

Clique em qualquer tela para ver como ela funciona.

🏠

Home

Hero, recentes, populares

clique →
🔍

Catálogo

Busca + filtro por gênero

clique →
🎮

Detalhes

Info, galeria, conquistas

clique →
📚

Biblioteca

Jogos adquiridos

clique →

Wishlist

Lista de desejos

clique →

Reviews

Criar, editar, excluir

clique →

Criar Jogo

Form + gêneros + imagens

clique →
👤

Perfil Público

Biblioteca de outro user

clique →
Conclusão

Tudo que aprendemos
em prática

Cada aula do semestre foi aplicada diretamente no Vaporzão — da integração básica com APIs até o estado global com Context API.

joaovictorcascardo
Aulas 07-09HTTPS, integração de APIs e async/await
Aula 10Custom Hook useAuth
Aula 11React Router + ProtectedRoute + hooks
Aula 12Autenticação JWT completa
Aula 13Formulários controlados e validação
Aulas 14-16Estado Global com Context API