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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.react-router
build
node_modules
README.md
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.env
/node_modules/

# React Router
/.react-router/
/build/
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
build
.next
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"bracketSpacing": true,
"jsxSingleQuote": false
}
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.autoSave": "onFocusChange",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM node:20-alpine AS development-dependencies-env
COPY . /app
WORKDIR /app
RUN npm ci

FROM node:20-alpine AS build-env
COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app
RUN npm run build

FROM nginx:alpine
COPY --from=build-env /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
131 changes: 84 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,89 @@
### Would you like to work with us? Apply [here](https://looqbox.gupy.io/)!
# Desafio Looqbox - Pokémon SPA

# Looqbox FrontEnd Challenge
![Looqbox](https://github.com/looqbox/looqbox-frontend-challenge/blob/master/logo.png)
Uma Single Page Application desenvolvida com ReactJS que consome a API pública PokeAPI para listar e detalhar Pokémons, incluindo visualização de estatísticas e cache de requisições com Redux.

## Challenge
In this challenge you will need to build a **S**ingle **P**age **A**pplication using ReactJS and a provided api
## Tecnologias Utilizadas

We will not use anything from your project other than evaluate your skills and you are free to use it in your portfolio

## Stack
We use:
- ReactJS
- Redux
- TypeScript
- AntDesign

## Submitting
- Make a fork of this repository
- Create your branch
- ⚠️ Do a initial Commit when you start
- ⚠️ Do a final commit when you finish
- When you're done send us a pull request

# Guidelines
You need to create a Single Page Application (SPA) that displays a list of Pokémon and allows users to search for them, using the [Pokeapi](https://pokeapi.co/docs/v2). Your app must be dynamic, meaning you **must not** reload the page to show new content.

The PokeAPI was chosen for its simplicity in making requests. Since it is an open API, please **be mindful of how many requests** you make.

## Requirements:

- On the main page, include a search bar and a preloaded list of Pokémon.
- Clicking on any Pokémon should display a card, modal, or page with that Pokémon’s information.
- Typing in the search bar and pressing Enter should display the search result instead of the list.
- Your app must include at least two different routes (e.g., /home, /details — be creative!).
- Add a README file to document your project.

You may use any libraries or dependencies you like (e.g., Axios, Bootstrap, Material UI...).

## Bonus points!
- Pagination
- Error handling
- Documentation
- Linting
- Charts
- Unit Testing
- Ant Design

## Useful links
- [React docs](https://react.dev/)
- [PokeApi docs](https://pokeapi.co/docs/v2)
- [Redux](https://redux.js.org/)
- Redux Toolkit
- React Router
- Recharts (gráficos)

## Arquitetura

O projeto segue uma estrutura baseada em Feature First, separando responsabilidades por domínio:

```
src/
├── app/
│ ├── store.ts
│ ├── hooks.ts
│ └── providers/
├── features/
│ └── pokemon/
│ ├── components/
│ ├── pages/
│ ├── pokemonSlice.ts
│ └── pokemonService.ts
├── routes/
```

- Princípios aplicados:
- Separação de responsabilidades
- Centralização de estado global com Redux
- Cache de requisições
- Persistência com Redux Persist
- Componentização reutilizável

### Instalação

Clone o repositório:

```
git clone https://github.com/M2Monteiro/looqbox-frontend-challenge.git
```

Instale as dependências:

```
npm install
```

Execute o projeto:

```
npm run dev
```

### Funcionalidades

- Listagem inicial de Pokémons
- Busca por nome
- Página de detalhes
- Cache de requisições
- Persistência do estado
- Gráfico de estatísticas
- Tratamento de erro
- Loading states
- Rotas dinâmicas

### Decisões Técnicas

- Uso de Redux Toolkit para centralizar estado
- Cache manual para evitar requisições desnecessárias à API
- Persistência do cache para melhorar performance
- Organização por features para escalabilidade
- Testes unitários para garantir estabilidade

#### Melhorias Futuras

- Paginação completa
- Comparação entre Pokémons
- Dashboard analítico avançado
- E2E com Playwright
- Deploy com CI/CD

#### Autor

Matheus Monteiro
53 changes: 53 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import js from '@eslint/js';
import reactPlugin from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import prettier from 'eslint-plugin-prettier';
import eslintConfigPrettier from 'eslint-config-prettier';
import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';

export default [
js.configs.recommended,
{
files: ['**/*.{js,jsx}'],
plugins: {
react: reactPlugin,
'react-hooks': reactHooks,
'jsx-a11y': jsxA11y,
prettier,
'@typescript-eslint': tsPlugin,
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: { jsx: true },
},
globals: {
browser: true,
es2021: true,
},
},
settings: {
react: { version: 'detect' },
},
rules: {
// React
'react/react-in-jsx-scope': 'off', // não precisa importar React no v17+
'react/prop-types': 'warn',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',

// Prettier integrado
'prettier/prettier': 'error',

// Boas práticas
'no-unused-vars': 'warn',
'no-console': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
},
},
eslintConfigPrettier, // deve ser o último para sobrescrever conflitos
];
19 changes: 19 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Looqbox</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
/>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading