Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
af5b2ea
Initiate project with boilerplate configured
wesleymoliveira Mar 30, 2021
249fefe
Add PokemonCard Component
wesleymoliveira Mar 30, 2021
19dc31c
Add TextInputField Component
wesleymoliveira Mar 30, 2021
c38e1c7
Add libs to fetch api and mock data
wesleymoliveira Mar 30, 2021
1e0d3ed
Add Pokemon page and fetch data
wesleymoliveira Mar 31, 2021
d4fefa7
Add dynamic Pakemon route
wesleymoliveira Mar 31, 2021
cd9b1ac
Add CheckBox Component
wesleymoliveira Mar 31, 2021
d1b917d
Add Statsbar Component
wesleymoliveira Mar 31, 2021
c9b3cf8
Add Pokemon Dynamic Static Page with mock data
wesleymoliveira Mar 31, 2021
1c174dc
Add Component PokemonStats to use on Page Pokemon
wesleymoliveira Mar 31, 2021
94ffe4b
Modify Pokemon Card to deal with new situations
wesleymoliveira Mar 31, 2021
eab51da
Fix field name
wesleymoliveira Mar 31, 2021
94fc3e0
Initiate Backend with basic get route - Mongodb - Mongoose- Express -…
wesleymoliveira Mar 31, 2021
d5fc8a6
Fix border-radius only in Stats
wesleymoliveira Mar 31, 2021
28f5056
Fix props on OnStatsModifier
wesleymoliveira Mar 31, 2021
c6bcb5d
fix typying on pokemonType electric
wesleymoliveira Mar 31, 2021
7d26fab
Fixed Card width
wesleymoliveira Apr 1, 2021
a2163e5
change api url to fetch localhost
wesleymoliveira Apr 1, 2021
66fd540
Add POST route to create pokemon
wesleymoliveira Apr 1, 2021
e1cce5b
Add Delete route and Controller
wesleymoliveira Apr 1, 2021
cf6294d
Fix fieldName
wesleymoliveira Apr 1, 2021
e1da3f1
Add Link to /pokemons on Main Compo
wesleymoliveira Apr 1, 2021
ce946db
Add Documentation with Swagger on route /api-docs
wesleymoliveira Apr 1, 2021
f9b45ea
Add component Button
wesleymoliveira Apr 1, 2021
cae6d39
Add Logic to integrate delete route with frontend
wesleymoliveira Apr 1, 2021
90db870
Add context to handle Modal Add Pokemon
wesleymoliveira Apr 1, 2021
14a9705
Add Select Component
wesleymoliveira Apr 1, 2021
25ee464
Change api to use local path
wesleymoliveira Apr 1, 2021
9a61a3d
Add count to start with pagination
wesleymoliveira Apr 1, 2021
fe44b29
Add react-icons and react-tabs
wesleymoliveira Apr 1, 2021
1bf63a2
change types accepted by checkbox
wesleymoliveira Apr 1, 2021
c4afaa8
Add FormAdd Pokemon and integrate with other components
wesleymoliveira Apr 1, 2021
db3b044
Fix field name
wesleymoliveira Apr 1, 2021
2965b14
Add redirect when page not found
wesleymoliveira Apr 1, 2021
17c6147
Fix a mix of style issues
wesleymoliveira Apr 1, 2021
b5c77ac
Fix stories default args
wesleymoliveira Apr 1, 2021
a22302a
Create a link at Pokemon name on PokeMonCard
wesleymoliveira Apr 1, 2021
31811d8
Add another color to statsBar
wesleymoliveira Apr 1, 2021
b289fc9
yarn lock updates and add image to use
wesleymoliveira Apr 1, 2021
0262616
Add funcionalities to PokemonStats Comp
wesleymoliveira Apr 1, 2021
9bd5f5d
Add files
wesleymoliveira Apr 1, 2021
08952b0
Add Select COmponent
wesleymoliveira Apr 1, 2021
42134cd
Add images
wesleymoliveira Apr 1, 2021
22b4461
Add README
wesleymoliveira Apr 1, 2021
382c4d8
update mongoose
wesleymoliveira Apr 1, 2021
005c575
remove react toastify
wesleymoliveira Apr 1, 2021
171559b
Remove toastify
wesleymoliveira Apr 1, 2021
c05c91d
chamge rules to field row
wesleymoliveira Apr 1, 2021
c2584b3
Fix README
wesleymoliveira Apr 1, 2021
a509799
Fix README
wesleymoliveira Apr 1, 2021
657b55c
Fix flow of checkbox 0 or 1
wesleymoliveira Apr 5, 2021
718e7f6
Initiate image upload flow
wesleymoliveira Apr 6, 2021
3aaadca
Delete test images
wesleymoliveira Apr 7, 2021
1551202
Add Multer to file upload an it's config
wesleymoliveira Apr 7, 2021
791c13b
Add Image Upload flow
wesleymoliveira Apr 7, 2021
8bd53b4
Add Joi to validation
wesleymoliveira Apr 7, 2021
b977dc8
Fix image path to use the backend path
wesleymoliveira Apr 7, 2021
a99edf2
Add validation Util to use on AddPokemon
wesleymoliveira Apr 7, 2021
9c84e09
General adjustments to get the error from form and show to user after…
wesleymoliveira Apr 7, 2021
9c3e232
Style and typing adjusts
wesleymoliveira Apr 7, 2021
bc699cb
Component complete Refactor to use validations and get errors
wesleymoliveira Apr 7, 2021
e3dac45
push back to pokemons after add Pokemon
wesleymoliveira Apr 7, 2021
cb52bdd
Merge branch 'wesley-oliveira' of https://github.com/wesleymoliveira/…
wesleymoliveira Apr 7, 2021
fcf6bac
REadme update
wesleymoliveira Apr 7, 2021
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
.env

backend/node_modules

front-end/node_modules

85 changes: 53 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,79 @@
# Teste de Desenvolvimento Web
# [](https://github.com/wesleymoliveira)👨‍💻 Wesley Moreira Oliveira

Olá Dev! Tudo bem?
[![GitHub Badge](https://img.shields.io/badge/%3E-GitHub-black?style=flat&logo=github)](https://github.com/wesleymoliveira) [![Linkedin Badge](https://img.shields.io/badge/%3E-Linkedin-blue?style=flat&logo=linkedin)](https://www.linkedin.com/in/wesleymoliveira/) [![Gmail Badge](https://img.shields.io/badge/%3E-Gmail-red?style=flat&logo=gmail)](mailto:[email protected]) [![Whatsapp Badge](https://img.shields.io/badge/%3E-Whatsapp-green?style=flat&logo=whatsapp)](https://api.whatsapp.com/send?phone=5522999130259&text=Ol%C3%A1!)

A RedFox está sempre em busca de profissionais interessantes e interessados, com boa capacidade de aprendizado, adaptação e principalmente motivação!
### Leiam por favor.

Este teste tem como objetivo avaliar e desafiar você. Não é obrigatório realizá-lo completamente, queremos apenas conhecer você, seu esforço e potencial para aprender, se adaptar e tomar decisões.
Os ajustes informados anteriormente foram devidamente feitos. Muito obrigado pela compreensão.

Agora vamos ao teste!
# - Frontend -

## Abordagem

## Desafio Pokémon
Comecei criando um mock com os dados necessários e usei o mocky.io junto com o storybook para fazer o Frontend.
Procurei desenvolver os componentes de forma mais isolada possível, permitindo a sua reutilização ampla.

Nós temos um problema, atualmente nosso sistema é só um excel, cheio de informações sobre Pokémon. Nós usamos ele como banco de dados e ao mesmo tempo interface de gerenciamento, inserindo, editando, deletando e filtrando os dados.
## Instruções

A missão é criar um sistema para substituir este excel, pois queremos expandir e acrescentar funcionalidades. Queremos manter o básico, mas principalmente queremos uma forma prática e agradável de buscar os dados, com listagem, filtros, paginação e detalhes sobre cada Pokémon.
- Instale as dependências `$ yarn`
- Navegue até a pasta /frontend e execute - `$ yarn dev`
- Para visualizar o protótipo isolado de cada componente e suas diferentes propriedades, utilize o comando: `$ yarn storybook`

Fique à vontade com o layout, precisamos de uma interface que consiga entregar as funcionalidades principais e substituir o excel, só isso.
### O que foi utilizado?

- Typescript
- Styled Components
- NextJS
- Joi
- Plop - `$ yarn generate ComponentName` (automatizar a criação dos components)
- Storybook - O Storybook é uma excelente ferramenta para prototipação da UI e visualização isolada da aplicação.

## Consigo fazer tudo isso?
<img src="frontend/public/img/storybook.gif"/>
<img src="frontend/public/img/add-remove-flow.gif"/>

Consegue sim!
- [x] Joi - Validation
<img src="frontend/public/img/fields-validation.gif"/>

O teste é flexível, você pode escolher alguma parte específica dele para fazer, em que se sinta mais confortável e confiante, por exemplo: a interface, as funcionalidades, o banco de dados, etc...O importante é tentar atingir o objetivo de alguma forma.
- [x] Typescript
- [x] NextJs
- [x] Styled Components

Aqui na RedFox queremos aproveitar ao máximo suas habilidades e aptidões, mas também desafiar você a adquirir novas, então nossa equipe tem a liberdade de trasitar entre frontend, backend, infraestrutura, etc...Sem se restringir, tudo depende do esforço e vontade de cada um.
## Importante

- [x] Utilizei o NextJs com recursos de criação dinâmica de páginas estáticas para as rotas /pokemon/nomedopokemon. Ou seja a aplicação tem uma página estática para cada pokemon do banco de dados.

## 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, para podermos indentificá-lo.
# - Backend -

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.
## Instruções

- Por favor certifique-se que tem o MongoDB Instalado.
- inicie o serviço do Mongo - `$ mongod`
- Navegue até a pasta /backend e execute - `$ yarn dev`

## E o Layout??
- Para visualizar a documentação da API, visite a rota: `/api-docs/`. Exemplo : `http://localhost:3333/api-docs/`

Fique a vontade quanto a isso, não vamos avaliar o design da sua interface. Se quiser desenhar algo bacana, diferente, pensar até em UI/UX, etc...é claro que vamos valorizar o seu esforço e considerar como um diferencial, mas não se preocupe.
<img src="backend/src/api-doc.png"/>

### O que foi utilizado?

## Regras
- Typescript
- Nodemon
- Cors
- Multer
- MongoDB
- Mongoose
- Express
- Swagger

Para o desafio ficar mais interessante, decidimos criar algumas regras:
- No layout, deve utilizar algum framework CSS (ex: Bootstrap, MaterializeCSS, Bulma...)
- No frontend, deve utilizar algum framework JS (ex: VueJS, ReactJS, Angular...tente não usar jQuery)
- No backend, deve utilizar NodeJS
- Documentar um pouco o projeto, o que você fez e de que forma devemos executar-lo
## Rotas disponíveis

- [x] GET - http://localhost:3333/pokemons
- [x] GET - http://localhost:3333/pokemons/{pokemonname}
- [x] DELETE - http://localhost:3333/pokemons/{id}
- [x] POST - http://localhost:3333/pokemons/ (passando JSON no corpo da requisição)
Extra:
- [x] GET - http://localhost:3333/api-docs

## Só isso?

Só!...mas se quiser ir além, tente preparar o projeto para ser executado de maneira simples e prática, se coloque no lugar de alguém com menos conhecimentos, que precisa ver o que você desenvolveu.

ps: Se fizer deploy em algum servidor ou utilizar alguma ferramenta que facilite a execução (ex: docker), será um diferencial.


Boa sorte! (^_^)
### Por favor, fiquem a vontade para críticas e feedbacks de melhoria. Eu ficaria muito feliz em saber como progredir.
174 changes: 174 additions & 0 deletions backend/app/controllers/PokemonController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { Response, Request } from "express";
import Pokemon, { PokemonInterface } from "../models/Pokemon";

const getPokemons = async (req: Request, res: Response): Promise<void> => {
try {
const pokemons: PokemonInterface[] = await Pokemon.find({});

const count = await Pokemon.countDocuments();
console.log(count);
res.json(pokemons);
} catch (err) {
res.status(500);
res.end();
console.error("Error message:", err);
}
};

const getPokemonByName = async (req: Request, res: Response): Promise<void> => {
try {
const name = req.params.name.toString();

//const namesearch = new RegExp(name, "i");
//const namesearch = { $regex: name, $options: "i" };

//console.log(namesearch);

const pokemon: PokemonInterface[] = await Pokemon.find({
Name: name,
});

if (pokemon[0]) {
res.status(200);
res.json(pokemon);
} else {
res.status(404);
res.json({ erro: "Pokemon não encontrado" });
}
} catch (err) {
res.status(500);
res.end();
console.error("Error message:", err);
}
};

const createPokemon = async (req: Request, res: Response): Promise<void> => {
try {
const body = req.body as Pick<
PokemonInterface,
| "Row"
| "Name"
| "Pokedex Number"
| "Img name"
| "Generation"
| "Evolution Stage"
| "Evolved"
| "FamilyID"
| "Cross Gen"
| "Type 1"
| "Type 2"
| "Weather 1"
| "Weather 2"
| "STAT TOTAL"
| "ATK"
| "DEF"
| "STA"
| "Legendary"
| "Aquireable"
| "Spawns"
| "Regional"
| "Raidable"
| "Hatchable"
| "Shiny"
| "Nest"
| "New"
| "Not-Gettable"
| "Future Evolve"
| "100% CP @ 40"
| "100% CP @ 39"
>;

const pokemon: PokemonInterface = new Pokemon({
Row: body.Row,
Name: body.Name,
"Pokedex Number": body["Pokedex Number"],
"Img name": req.file.filename,
Generation: body.Generation,
"Evolution Stage": body["Evolution Stage"],
Evolved: body.Evolved,
FamilyID: body.FamilyID,
"Cross Gen": body["Cross Gen"],
"Type 1": body["Type 1"],
"Type 2": body["Type 2"],
"Weather 1": body["Weather 1"],
"Weather 2": body["Weather 2"],
"STAT TOTAL": body["STAT TOTAL"],
ATK: body.ATK,
DEF: body.DEF,
STA: body.STA,
Legendary: body.Legendary,
Aquireable: body.Aquireable,
Spawns: body.Spawns,
Regional: body.Regional,
Raidable: body.Raidable,
Hatchable: body.Hatchable,
Shiny: body.Shiny,
Nest: body.Nest,
New: body.New,
"Not-Gettable": body["Not-Gettable"],
"Future Evolve": body["Future Evolve"],
"100% CP @ 40": body["100% CP @ 40"],
"100% CP @ 39": body["100% CP @ 39"],
});

await pokemon.save();

res.status(201).json({
Row: pokemon.Row,
Name: pokemon.Name,
"Pokedex Number": pokemon["Pokedex Number"],
"Img name": pokemon["Img name"],
Generation: pokemon.Generation,
"Evolution Stage": pokemon["Evolution Stage"],
Evolved: pokemon.Evolved,
FamilyID: pokemon.FamilyID,
"Cross Gen": pokemon["Cross Gen"],
"Type 1": pokemon["Type 1"],
"Type 2": pokemon["Type 2"],
"Weather 1": pokemon["Weather 1"],
"Weather 2": pokemon["Weather 2"],
"STAT TOTAL": pokemon["STAT TOTAL"],
ATK: pokemon.ATK,
DEF: pokemon.DEF,
STA: pokemon.STA,
Legendary: pokemon.Legendary,
Aquireable: pokemon.Aquireable,
Spawns: pokemon.Spawns,
Regional: pokemon.Regional,
Raidable: pokemon.Raidable,
Hatchable: pokemon.Hatchable,
Shiny: pokemon.Shiny,
Nest: pokemon.Nest,
New: pokemon.New,
"Not-Gettable": pokemon["Not-Gettable"],
"Future Evolve": pokemon["Future Evolve"],
"100% CP @ 40": pokemon["100% CP @ 40"],
"100% CP @ 39": pokemon["100% CP @ 39"],
});
} catch (err) {
res.status(500);
res.end();
console.error("Error message:", err);
}
};

const deletePokemon = async (req: Request, res: Response): Promise<void> => {
try {
const id = req.params.id.toString();

if (Pokemon.findById(id)) {
await Pokemon.findByIdAndDelete(id);
res.status(204);
res.end();
} else {
res.status(404);
res.json({ erro: "ID não encontrada" });
}
} catch (err) {
res.status(500);
res.end();
console.error("Error message:", err);
}
};

export { getPokemons, getPokemonByName, createPokemon, deletePokemon };
73 changes: 73 additions & 0 deletions backend/app/models/Pokemon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import mongoose, { Schema, Document } from "mongoose";

export interface PokemonInterface extends Document {
Row: Number;
Name: String;
"Pokedex Number": Number;
"Img name": Number;
Generation: Number;
"Evolution Stage": Number;
Evolved: Boolean;
FamilyID: Number;
"Cross Gen": Boolean;
"Type 1": String;
"Type 2"?: String;
"Weather 1": String;
"Weather 2"?: String;
"STAT TOTAL": Number;
ATK: Number;
DEF: Number;
STA: Number;
Legendary: Boolean;
Aquireable: Number;
Spawns: Boolean;
Regional: Boolean;
Raidable: Boolean;
Hatchable: Number;
Shiny: Boolean;
Nest: Boolean;
New: Boolean;
"Not-Gettable": Boolean;
"Future Evolve": Boolean;
"100% CP @ 40": Number;
"100% CP @ 39": Number;
}

const PokemonSchema: Schema = new Schema({
Row: { type: String },
Name: { type: String, required: true },
"Pokedex Number": { type: String, required: true },
"Img name": { type: String, required: true },
Generation: { type: String, required: true },
"Evolution Stage": { type: String, required: true },
FamilyID: { type: String, required: true },
"Type 1": { type: String, required: true },
"Type 2": { type: String },
"Weather 1": { type: String, required: true },
"Weather 2": { type: String },
"STAT TOTAL": { type: String, required: true },
ATK: { type: String, required: true },
DEF: { type: String, required: true },
STA: { type: String, required: true },
Hatchable: { type: String, required: true },
"100% CP @ 40": { type: String, required: true },
"100% CP @ 39": { type: String, required: true },
Evolved: {
type: String,
required: true,
enum: ["0", "1"],
},
"Cross Gen": { type: String, required: true, enum: ["0", "1"] },
Legendary: { type: String, required: true, enum: ["0", "1"] },
Aquireable: { type: String, required: true, enum: ["0", "1"] },
Spawns: { type: String, required: true, enum: ["0", "1"] },
Regional: { type: String, required: true, enum: ["0", "1"] },
Raidable: { type: String, required: true, enum: ["0", "1"] },
Shiny: { type: String, required: true, enum: ["0", "1"] },
Nest: { type: String, required: true, enum: ["0", "1"] },
New: { type: String, required: true, enum: ["0", "1"] },
"Not-Gettable": { type: String, required: true, enum: ["0", "1"] },
"Future Evolve": { type: String, required: true, enum: ["0", "1"] },
});

export default mongoose.model<PokemonInterface>("Pokemon", PokemonSchema);
18 changes: 18 additions & 0 deletions backend/config/multerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import path from "path";
import crypto from "crypto";
import multer from "multer";

export default {
dest: path.resolve(__dirname, "..", "images"),
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.resolve(__dirname, "..", "images"));
},
filename: (req, file, cb) => {
const fileHash = crypto.randomBytes(10).toString("hex");
const filename = `${fileHash}-${file.originalname}`;

return cb(null, filename);
},
}),
};
Loading