Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
114 changes: 114 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Arquitetura do Projeto

## Visão Geral

O projeto segue **Clean Architecture** com camadas bem definidas: Domain, Data, UseCases e Presentation. A UI nunca conhece a API externa; os Mappers são a ponte entre o mundo real e o domínio.

```mermaid
flowchart TB
subgraph Presentation [Presentation]
Pages[Pages]
Components[Components]
Hooks[Hooks]
end

subgraph UseCases [Use Cases]
GetList[GetPokemonListUseCase]
GetDetails[GetPokemonDetailsUseCase]
end

subgraph Data [Data]
Repository[PokeApiPokemonRepository]
Mapper[PokemonMapper]
end

subgraph Domain [Domain]
Entities[Entities]
Interfaces[Repository Interfaces]
end

subgraph External [External]
PokeAPI[PokéAPI]
end

Pages --> Hooks
Hooks --> GetList
Hooks --> GetDetails
GetList --> Interfaces
GetDetails --> Interfaces
Interfaces --> Repository
Repository --> Mapper
Repository --> PokeAPI
Mapper --> Entities
```

## Estrutura de Camadas

### Domain

- **Entidades puras**: `IPokemon`, `IPokemonListItem`, `IEvolutionChainNode`, etc.
- **Interfaces de repositório**: `IPokemonRepository` define o contrato sem implementação.
- **Sem dependências externas**: o domínio não conhece HTTP, React ou qualquer framework.

### Data

- **Repositório**: `PokeApiPokemonRepository` implementa `IPokemonRepository` e consome a PokéAPI.
- **Mappers**: transformam DTOs da API em entidades do domínio (`mapPokemonListResponse`, `mapPokemonDetails`, `mapEvolutionChain`).
- **UseCases**: orquestram a lógica de negócio e delegam ao repositório.

### Presentation

- **Componentes React**: páginas, listas, cards, modais, filtros.
- **Hooks**: `usePokemonList`, `usePokemonDetails`, `usePokemonFilters`, `useEvolutionChain`.
- **Consome apenas UseCases** via injeção de dependência (`di.ts`).

## SOLID na Prática

### S — Single Responsibility

- **PokemonMapper**: única responsabilidade de transformar DTOs em entidades. Cada função mapeia um aspecto específico (sprites, stats, abilities, flavor texts).
- **Cada UseCase**: uma única ação (`GetPokemonListUseCase` lista, `GetPokemonDetailsUseCase` busca detalhes).

### D — Dependency Inversion

- UseCases dependem de `IPokemonRepository` (interface), não da implementação concreta.
- Injeção via `di.ts`: o repositório é instanciado uma vez e passado aos UseCases.
- Testes usam `MockPokemonRepository` sem alterar os UseCases.

### O — Open/Closed

- **pokemon-type-colors** e **pokemon-color-gradients**: extensíveis por novos tipos sem alterar código existente. Novos tipos são adicionados em objetos de configuração.
- **FilterPanel**: novos filtros podem ser adicionados sem modificar a estrutura existente.

## Padrões de Projeto

### Mapper

Isola a UI da "sujeira" da API externa. A PokéAPI retorna estruturas aninhadas, nomes em snake_case e formatos inconsistentes. O Mapper:

- Normaliza nomes (`front_default` → `frontDefault`)
- Aplica fallbacks (imagem padrão, descrição padrão)
- Extrai e deduplica flavor texts
- Transforma evolution-chain em árvore recursiva

### UseCase

Centraliza a regra de negócio. O `GetPokemonDetailsUseCase` orquestra a busca: o repositório internamente chama `getPokemon` e `getPokemonSpecies` em paralelo via `Promise.all`, e o Mapper unifica os dados.

## Por trás dos panos

### Otimização com Promise.all

- **Listagem com filtros**: busca em paralelo por tipo (OR entre tipos), geração, cor e habitat. Depois, batch-fetch dos Pokémon da página em paralelo.
- **Detalhes**: `pokemon` e `species` em paralelo (`Promise.all([getPokemon, getPokemonSpecies])`).
- **Busca por nome**: fetch da lista completa (`limit=10000`), filtro em memória, depois batch-fetch da página.

### URL como Single Source of Truth

- `usePokemonFilters` lê `searchParams` (page, search, types, minAtk, maxAtk, minExp, maxExp, generation, color, habitat).
- Qualquer alteração de filtro atualiza a URL com `router.replace(..., { scroll: false })`.
- Compartilhamento de estado: refresh da página mantém os filtros. Navegação para detalhes e volta preserva busca e página.

### Cache

- TanStack Query com `staleTime: 30_000` (30 segundos) em `query-client.ts`, atendendo ao requisito de cache mínimo.
56 changes: 56 additions & 0 deletions TESTE_DINHEIROW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Teste Dinheirow
Olá Dev! Tudo bem?

Nós estamos sempre em busca de profissionais interessantes e interessados, com boa capacidade de aprendizado, adaptação e principalmente bom senso! Este teste tem como objetivo avaliar e desafiar você. Não é obrigatório realizá-lo completamente, queremos apenas reconhecer seu esforço e potencial para aprender, se adaptar e tomar decisões.

Vamos ao teste!

## Desafio Pokémon
Crie um aplicativo front-end conectado ao back-end Pokemon desenvolvido usando dados e rotas obtidos pela PokéAPI.

API DOCS: https://pokeapi.co/docs/v2

### 💻 Requisitos

- A aplicação deverá conter uma página inicial com uma listagem de Pokémons com paginação e deverá possuir um filtro que busca os dados pelo nome do Pokémon.
- Ao clicar em um Pokémon deverá ser exibida a página de detalhes com (nome, descrição e sprites/imagens do Pokémon)
- Na página de detalhes deverá conter um botão que volta para página inicial de listagem dos Pokémons
- Ao voltar para a página da listagem dos Pokémons o filtro por nome e a página da listagem deverão ser os mesmos que estavam antes do usuário entrar na página de detalhes.
- Caso o Pokémon não tenha imagem deverá ser exibida uma imagem padrão de Pokémon. (atenção: não será permitido colocar essa validação nos componentes visuais)
- Caso o Pokémon não tenha descrição exibir o texto "descrição não informada". (atenção: não será permitido colocar essa validação nos componentes visuais)
- Todas as rotas usadas deverão ter cacheamento de pelo menos 30 segundos.

### Critérios de avaliação
O cumprimento de todos os requisitos é obrigatório e acarretará a eliminação do candidato que não os cumprir.
Além dos requisitos levantados acima, iremos avaliar os seguintes critérios:
- Gerenciamento de estado
- Componentização
- Responsividade
- Testes automatizados
- UI design

### Tecnologias Obrigatórias
- NextJs (versão 15+)
- react (versão 18+)
- Typescript
- Tailwind css
- shadcn
- Tanstack/react-query

### Tecnologias opcionais
- Tanstack/form
- zustand
- Jest
- playwright

### Difencial
- UI design agradável
- Conhecimento de AI
- conhecimento em Vue.js (básico)

### Por onde começo?
Primeiramente, você pode fazer um fork desse repositório aqui, para sua conta do Github, depois disso crie uma branch nova com o seu nome (ex: nome_sobrenome), para podermos indentificá-lo.

Após terminar o desafio, você pode solicitar um pull request para a branch master do nosso repositório. Vamos receber e fazer a avaliação de todos.

Boa sorte! :)
17 changes: 17 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/presentation/components",
"utils": "@/lib/utils"
}
}
15 changes: 15 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "raw.githubusercontent.com",
pathname: "/PokeAPI/**",
},
],
},
};

export default nextConfig;
Loading