Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project requirements finished. #14

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
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
58 changes: 6 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,11 @@
# Desafio de Frontend
Nesta aplicação, o usuário pode adicionar, editar e deletar cards contendo informações do país, local e a meta para visitar o local.

<img src="./img/logo-clubpetro.png"
alt="Clubpetro" width="300">

- [Descrição](#descrição)
- [O Desafio](#o-desafio)
- [Requisitos Obrigatórios](#requisitos-obrigatórios)
- [Bônus](#bônus)
- [Submissão e Prazo de Entrega](#submissão-e-prazo-de-entrega)
## Para executar o projeto siga os passos a seguir
### `yarn` ou `npm install`

## Descrição
- Depois de clonar o repositório na sua máquina, já dentro do editor e no diretório do projeto, execute no seu terminal o comando `yarn` ou `npm install`, de acordo com o gerenciador de pacotes que estiver usando. Esse comando instalará todas as dependências necessárias para o projeto ser executado.

Este desafio tem como objetivo avaliar as habilidades técnicas do canditado a vaga de desenvolvedor frontend no Clubpetro.
- Após isso, execute o comando `yarn json` ou `npm run json` para gerar a mock api que providenciará o uso de dados.

#### O Desafio

O desafio consiste em desenvolver um sistema que permita o CRUD de lugares para se conhecer ao redor do mundo. Como na imagem a seguir:

<img src="./img/challenge.png" alt="Desafio" >

O Sistema deverá conter um formulário com 3 campos:

- País: um select contendo a lista de todos os países existentes;
- Local: um input para que o usuário digite o local que ele deseja conhecer no país selecionado;
- Meta: um input para que o usuário digite a o mês e o ano que ele pretende visitar o local em questão.

Quando o usuário clicar em "Adicionar", o formulário deverá ser resetado e o local deverá aparecer na listagem dos cards, como mostrado na imagem acima.

#### Requisitos Obrigatórios

> Requisitos que serão avaliados no desafio.

- O Sistema deverá ser desenvolvido em typescript utilizando a biblioteca [React](https://pt-br.reactjs.org/);
- O Layout apresentado na imagem acima deverá ser fielmente seguido e pode ser encontrado no [Figma](https://www.figma.com/file/IC0xt3K3X21rLEfLRQ3mpl/Lugares-que-quero-conhecer?node-id=0%3A1);
- O CRUD poderá ser gerenciado pelo estado no React;
- Apenas o Local e Meta poderão ser editados e a edição do card deverá ser feita de acordo com a criatividade do canditado, não tendo um layout específico para esta ação;
- O Sistema deverá ser desenvolvido utilizando [React Hooks](https://pt-br.reactjs.org/docs/hooks-intro.html);
- O Sistema deverá ser integrado à API [Rest Countries](https://restcountries.eu/rest/v2/all) para a listagem dos países. Esta conta com a imagem da bandeira e a tradução do nome do país para Português;
- A biblioteca [react-input-mask](https://www.npmjs.com/package/react-input-mask) deverá ser utilizada para colocar uma mascara no input de "Meta" no formato mm/aaaa;
- O Sistema deverá ser responsivo;
- O candidato deverá adicionar ao projeto uma explicação de como executar a aplicação.

#### Bônus

> Requisitos que não são obrigatórios mas podem te deixar em vantagem com relação aos outros candidatos.

- [Material-UI](https://material-ui.com/pt/);
- [Styled Components](https://styled-components.com/);
- Testes automatizados;
- Utilização da biblioteca [json-server](https://www.npmjs.com/package/json-server) para o CRUD.

### Submissão e Prazo de entrega

- O canditado deverá realizar um fork deste repositório e submeter o código no mesmo;
- O prazo de entrega para este desafio é de 2 (duas) semanas, contando a partir do dia em que o candidato recebeu o email com o link do repositório;
- Ao finalizar o desafio, o candidato deverá enviar um email para [email protected] contendo o link do seu PR.
- Por fim, execute o comando `yarn start` ou `npm start` para abrir o projeto no navegador.
1 change: 1 addition & 0 deletions web/.eslintcache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"/home/georgesantos/Desktop/frontend-challenge/web/src/index.tsx":"1","/home/georgesantos/Desktop/frontend-challenge/web/src/components/Header/index.tsx":"2","/home/georgesantos/Desktop/frontend-challenge/web/src/components/Header/styles.ts":"3","/home/georgesantos/Desktop/frontend-challenge/web/src/App.tsx":"4","/home/georgesantos/Desktop/frontend-challenge/web/src/styles/global.ts":"5","/home/georgesantos/Desktop/frontend-challenge/web/src/components/SelectInput/index.tsx":"6","/home/georgesantos/Desktop/frontend-challenge/web/src/components/SelectInput/customStyles.ts":"7","/home/georgesantos/Desktop/frontend-challenge/web/src/routes/index.tsx":"8","/home/georgesantos/Desktop/frontend-challenge/web/src/components/Input/index.tsx":"9","/home/georgesantos/Desktop/frontend-challenge/web/src/services/api.ts":"10","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/PlaceCard/index.tsx":"11","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/index.tsx":"12","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/EditPlace/styles.ts":"13","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/EditPlace/index.tsx":"14","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/styles.ts":"15","/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/PlaceCard/styles.ts":"16"},{"size":199,"mtime":1610988525916,"results":"17","hashOfConfig":"18"},{"size":263,"mtime":1610988525916,"results":"19","hashOfConfig":"18"},{"size":217,"mtime":1610988525916,"results":"20","hashOfConfig":"18"},{"size":450,"mtime":1611426207748,"results":"21","hashOfConfig":"18"},{"size":616,"mtime":1611428767324,"results":"22","hashOfConfig":"18"},{"size":1307,"mtime":1611252943396,"results":"23","hashOfConfig":"18"},{"size":835,"mtime":1611166691439,"results":"24","hashOfConfig":"18"},{"size":429,"mtime":1611276836858,"results":"25","hashOfConfig":"18"},{"size":686,"mtime":1611429854476,"results":"26","hashOfConfig":"18"},{"size":121,"mtime":1611078260397,"results":"27","hashOfConfig":"18"},{"size":1611,"mtime":1611333473549,"results":"28","hashOfConfig":"18"},{"size":4145,"mtime":1611428527249,"results":"29","hashOfConfig":"18"},{"size":1519,"mtime":1611339403987,"results":"30","hashOfConfig":"18"},{"size":2047,"mtime":1611415149051,"results":"31","hashOfConfig":"18"},{"size":2038,"mtime":1611424668478,"results":"32","hashOfConfig":"18"},{"size":1420,"mtime":1611245373416,"results":"33","hashOfConfig":"18"},{"filePath":"34","messages":"35","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},"y1ww2r",{"filePath":"37","messages":"38","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"39"},{"filePath":"40","messages":"41","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"39"},{"filePath":"42","messages":"43","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"44","messages":"45","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"46","messages":"47","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"48","messages":"49","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"39"},{"filePath":"50","messages":"51","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"52","messages":"53","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"54","messages":"55","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"56","messages":"57","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"58","messages":"59","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"60","messages":"61","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"36"},{"filePath":"62","messages":"63","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"64","messages":"65","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"66","messages":"67","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/home/georgesantos/Desktop/frontend-challenge/web/src/index.tsx",[],["68","69"],"/home/georgesantos/Desktop/frontend-challenge/web/src/components/Header/index.tsx",[],["70","71"],"/home/georgesantos/Desktop/frontend-challenge/web/src/components/Header/styles.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/App.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/styles/global.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/components/SelectInput/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/components/SelectInput/customStyles.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/routes/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/components/Input/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/services/api.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/PlaceCard/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/EditPlace/styles.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/EditPlace/index.tsx",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/styles.ts",[],"/home/georgesantos/Desktop/frontend-challenge/web/src/pages/PlaceCardList/PlaceCard/styles.ts",[],{"ruleId":"72","replacedBy":"73"},{"ruleId":"74","replacedBy":"75"},{"ruleId":"72","replacedBy":"76"},{"ruleId":"74","replacedBy":"77"},"no-native-reassign",["78"],"no-negated-in-lhs",["79"],["78"],["79"],"no-global-assign","no-unsafe-negation"]
23 changes: 23 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
8 changes: 8 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Para executar o projeto siga os passos a seguir
### `yarn` ou `npm install`

- Depois de clonar o repositório na sua máquina, já dentro do editor e no diretório do projeto, execute no seu terminal o comando `yarn` ou `npm install`, de acordo com o gerenciador de pacotes que estiver usando. Esse comando instalará todas as dependências necessárias para o projeto ser executado.

- Após isso, execute o comando `yarn json` ou `npm run json` para gerar a mock api que providenciará o uso de dados.

- Por fim, execute o comando `yarn start` ou `npm start` para abrir o projeto no navegador.
64 changes: 64 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "web",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"@unform/core": "^2.1.3",
"@unform/web": "^2.1.3",
"axios": "^0.21.1",
"json-server": "^0.16.3",
"polished": "^4.0.5",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-icons": "^4.1.0",
"react-input-mask": "^2.0.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"react-select": "^3.2.0",
"react-toastify": "^6.2.0",
"styled-components": "^5.2.1",
"typescript": "^4.0.3",
"uuid": "^8.3.2",
"web-vitals": "^0.2.4",
"yup": "^0.32.8"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"json": "yarn json-server -p 3333 src/services/db.json"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-input-mask": "^3.0.0",
"@types/react-router-dom": "^5.1.7",
"@types/react-select": "^3.1.2",
"@types/styled-components": "^5.1.7",
"@types/uuid": "^8.3.0"
}
}
17 changes: 17 additions & 0 deletions web/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#4F9419" />

<title>React App</title>

<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
3 changes: 3 additions & 0 deletions web/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
21 changes: 21 additions & 0 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import GlobaStyle from './styles/global';
import { ToastContainer } from 'react-toastify';

import Routes from './routes';
import Header from './components/Header';

const App: React.FC = () => {

return (
<Router>
<Header />
<Routes/>
<GlobaStyle />
<ToastContainer autoClose={5000} />
</Router>
);
}

export default App;
Binary file added web/src/assets/lugares.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions web/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import logo from '../../assets/lugares.png';

import { Container } from './styles';

const Header: React.FC = () => {
return (
<Container>
<img src={logo} alt="Planet Earth"/>
</Container>
);
}

export default Header;
13 changes: 13 additions & 0 deletions web/src/components/Header/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from 'styled-components';

export const Container = styled.div`
display: flex;
align-items: center;
width: 100%;
height: 85px;
background-color: #000000;

img {
margin-left: 53px;
}
`;
35 changes: 35 additions & 0 deletions web/src/components/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, {
InputHTMLAttributes,
useEffect,
useRef,
} from 'react';
import { useField } from '@unform/core';

interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
name: string;
}

const Input: React.FC<InputProps> = ({ name,...rest }) => {
const inputRef = useRef<HTMLInputElement>(null);


const { fieldName, defaultValue,registerField } = useField(name);

useEffect(() => {
registerField({
name: fieldName,
ref: inputRef.current,
path: 'value',
});
}, [fieldName, registerField]);

return (
<input
defaultValue={defaultValue}
ref={inputRef}
{...rest}
/>
);
};

export default Input;
44 changes: 44 additions & 0 deletions web/src/components/SelectInput/customStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { StylesConfig, OptionTypeBase } from 'react-select';

const customStyles: StylesConfig<OptionTypeBase, boolean> = {
container: provided => ({
...provided,
width: '303px',
minHeight: '1px',
textAlign: 'left',
alignItems: 'center',
color: '#333333',
border: 0,
}),
control: provided => ({
...provided,
borderRadius: '7px',
minHeight: '1px',
height: '48px',
width: '303px',
border: 0,
backgroundColor: '#fff',
paddingLeft: '8px'

}),

input: (provided) => ({
...provided,
textAlign: 'center',
color: '#868686',
}),

placeholder: provided => ({
...provided,
minHeight: '1px',
textAlign: 'center',
color: '#868686',
}),

singleValue: provided => ({
...provided,
color: '#868686',
}),
};

export default customStyles;
51 changes: 51 additions & 0 deletions web/src/components/SelectInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useRef, useEffect } from 'react';
import Select, {
OptionTypeBase,
Props as SelectProps,
} from 'react-select';
import { useField } from '@unform/core';
import customStyles from './customStyles';

interface Props extends SelectProps<OptionTypeBase> {
name: string;
}
const SelectInput: React.FC<Props> = ({ name, ...rest }) => {
const selectRef = useRef(null);
const { fieldName, defaultValue, registerField} = useField(name);
useEffect(() => {
registerField({
name: fieldName,
ref: selectRef.current,
getValue: (ref: any) => {
if (rest.isMulti) {
if (!ref.state.value) {
return [];
}
return ref.state.value.map((option: OptionTypeBase) => option.value);
}
if (!ref.state.value) {
return '';
}
return ref.state.value.value;
},
});
}, [fieldName, registerField, rest.isMulti]);
return (
<Select
defaultValue={defaultValue}
ref={selectRef}
{...rest}
styles={customStyles}
theme={theme => ({
...theme,
borderRadius: 0,
colors: {
...theme.colors,
primary25: 'rgba(79, 148, 25, 0.5)',
primary: '#4f9419',
},
})}
/>
);
};
export default SelectInput;
12 changes: 12 additions & 0 deletions web/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);


Loading