Conteúdos desta aula:
- Criando a estrutura do nosso Projeto – 00:00:47
- Instalando a Lib do Google Maps e incluindo nossa Key – 00:05:04
- Incluindo o Mapa na tela em uma localização fixa – 00:09:55
- Incluindo nossa posição atual – 00:15:19
- Conectando com a API para baixar os Cafés do Google – 00:22:48
- Colocando os marcadores no Maps – 00:27:32
- Conectando com a API para baixar as informações de um Café – 00:41:09
- Vamos instalar o Stylled Components – 00:43:25
- Criando o component para mostrar os detalhes do Café – 00:44:22
- Conectando na nossa API para baixar e criar ratings – dating local women in dallas
- Instalando a Lib React Rating Starts para a avaliação – https://onebitcode.com/free-dating-sites-las-vegas-nv/
- Setando o formulário – https://onebitcode.com/casual-dating-sites-free/
- Conectando o Form à API – dating services in buffalo ny
- Incluindo a lista de Reviews – https://onebitcode.com/how-to-meet-gay/
- Criando a sessão de cafés mais próximos – 01:43:36
- Missão – 01:52:57
Acompanhe pelo código fonte para resolver Bugs simples: https://github.com/OneBitCodeBlog/find-my-coffee-web-prod
Criando a estrutura do nosso Projeto
1 – Para começar, entre na pasta find-my-coffe, e vamos criar o nosso projeto com o seguinte comando:
1 |
npx create-react-app find_my_coffee_web |
2 – Entre no projeto rodando:
1 |
cd find_my_coffee_web |
3 – Deixe o arquivo App.js assim:
1 2 3 4 5 6 7 8 9 10 11 12 |
import React, {Fragment} from 'react'; import './App.css'; const App = () => { return ( <Fragment> </Fragment> ); } export default App; |
4 – Apague o arquivo Logo.svg e limpe o arquivo App.css
Instalando a Lib do Google Maps e incluindo nossa Key
1 – Instale a Lib do Google Maps rodando:
1 |
yarn add @react-google-maps/api |
2 – Visite https://console.cloud.google.com/marketplace/product/google/maps-backend.googleapis.com?project=<Nome Do Seu Projeto> e habilite sua credencial para usar o Maps Javascript API
3 – Crie um arquivo na raiz do projeto chamado .env e coloque nele:
1 |
REACT_APP_GOOGLE_API_KEY=<Sua Key> |
4 – Inclua o .env no arquivo .gitignore
5 – Inclua no seu arquivo App.js:
1 2 3 4 5 |
... const App = () => { ... const { REACT_APP_GOOGLE_API_KEY } = process.env; ... |
Incluindo o Mapa na tela em uma localização fixa
1 – Agora vamos habilitar o mapa no nosso app, adicionando o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... import { GoogleMap, LoadScript } from '@react-google-maps/api'; const App = () => { return ( <Fragment> <LoadScript googleMapsApiKey='SUA_GOOGLE_KEY_API'> <GoogleMap mapContainerStyle={{height: "100vh", width: "100%"}} zoom={15} center={{lat: -21.7768606, lng: -41.3109657}}> </GoogleMap> </LoadScript> </Fragment> ) } export default App; |
2 – Suba o site rodando:
1 |
yarn start |
3 – Agora substitua a parte SUA_GOOGLE_KEY_API por process.env.GOOGLE_API_KEY ficando:
1 |
<LoadScript googleMapsApiKey={REACT_APP_GOOGLE_API_KEY}> |
4 – Salve e veja o projeto atualizado no browser
Incluindo nossa posição atual
1 – Vamos setar nossa localização atual:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import React, { Fragment, useState, useEffect } from 'react'; ... const App = () => { const [latitude, setLatitude] = useState(0); const [longitude, setLongitude] = useState(0); ... useEffect(() => { setCurrentLocation(); }, []); async function setCurrentLocation() { await navigator.geolocation.getCurrentPosition(function (position) { setLatitude(position.coords.latitude); setLongitude(position.coords.longitude); loadCoffeShops(); }, function(error) { alert('Habilite a localização para utilizar o aplicativo!'); }); } return ( <div> <LoadScript googleMapsApiKey='SUA_GOOGLE_KEY_API'> <GoogleMap mapContainerStyle={mapStyles} zoom={15} center={{ lat: latitude, lng: longitude }}> </GoogleMap> </LoadScript> ... |
2 – Baixe as imagens (link1 link2 link3), crie uma pasta em public chamada images e coloque elas dentro.
3 – Inclua entre as Tags GoogleMap:
1 2 3 4 5 |
... <Marker icon='/images/my-location-pin.png' position={{ lat: latitude, lng: longitude }} /> ... |
4 – Suba o projeto e vejo o resultado
1 |
yarn start |
Conectando com a API para baixar os Cafés do Google
1 – Antes de tudo, instale o axios colando o seguinte comando no terminal:
1 |
yarn add axios |
2 – Crie uma pasta ‘/src/services’.
3 – Dentro essa pasta, crie os arquivos api.js e google_list_of_establishments.js.
4 – Cole o código abaixo no arquivo api.js:
1 2 3 4 5 |
import axios from 'axios'; const Api = axios.create({baseURL: 'http://localhost:3001/api/v1'}); export default Api; |
5 – Agora navegue no arquivo establishment_service.js e cole o seguinte código:
1 2 3 4 5 6 7 |
import Api from './api'; const EstablishmentsService = { index: (latitude, longitude) => Api.get(`/google_stores?latitude=${latitude}&longitude=${longitude}`) } export default EstablishmentsService; |
Colocando os marcadores no Maps
1 – Agora volte ao componente App.js e substitua-o pelo seguinte código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
... import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api'; ... import EstablishmentsService from './services/establishment_service'; const App = () => { ... const [locations, setLocations] = useState([]); ... async function setCurrentLocation() { try { await navigator.geolocation.getCurrentPosition(function (position) { setLatitude(position.coords.latitude); setLongitude(position.coords.longitude); }); loadCoffeShops(); } catch (error) { alert('Habilite a localização para utilizar o aplicativo!'); } } // Load all coffee shops async function loadCoffeShops() { const response = await EstablishmentsService.index(latitude, longitude); setLocations(response.data.results); } ... <GoogleMap mapContainerStyle={mapStyles} zoom={15} center={defaultCenter}> { locations.map((item, index) => { return ( <Marker key={index} icon='/images/coffee-pin.png' title={item.name} animation="4" position={{lat: item.geometry.location.lat, lng: item.geometry.location.lng}} /> ) }) } <Marker key='my location' icon='/images/my-location-pin.png' title="Seu Local" animation="2" position={{ lat: latitude, lng: longitude }} /> </GoogleMap> ... |
3 – Suba a API rodando:
1 |
rails s -p 3001 |
4 – Suba o projeto e vejo o resultado
1 |
yarn start |
Conectando com a API para baixar as informações de um Café
1 – Atualize o service establishment_service.js.
1 2 3 4 5 6 7 8 |
import Api from './api'; const EstablishmentsService = { index: (latitude, longitude) => Api.get(`/google_stores?latitude=${latitude}&longitude=${longitude}`), show: (place_id) => Api.get(`/google_stores/${place_id}`) } export default EstablishmentsService; |
Vamos instalar o Stylled Components
1 – Cole os seguintes comandos no terminal:
1 |
<span role="presentation">yarn add styled-components</span> |
Criando o component Para mostrar os detalhes do Café
1 – Vamos criar nosso componente /src/components/Establishment/index.js com a seguinte estrutura:
1 2 3 4 5 6 7 8 9 10 11 |
import React from 'react'; const Establishment = (props) => { return ( <div> </div> ) } export default Establishment; |
2 – Agora vamos puxar a API pelo EstablishmentService no nosso código. Vamos adicionar o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import React, { useEffect, useState } from 'react'; import EstablishmentService from '../../services/establishment_service'; ... const Establishment = (props) => { const [establishment, setEstablishment] = useState([]); const { REACT_APP_GOOGLE_API_KEY } = process.env; useEffect(() => { getEstablishmentInformations(); }, [props.place]); async function getEstablishmentInformations() { try { const response = await EstablishmentService.index(props.place.place_id); setEstablishment(response.data.result); } catch (error) { setEstablishment([]); } } return ( ... |
3 – Vamos criar alguns components estilizados para usar na nossa estrutura, importe o styled components:
1 |
import styled from 'styled-components' |
4 – Vamos criar um component para gerar uma barra lateral, inclua:
1 2 3 4 5 6 7 8 9 |
const LeftBar = styled.div` height: 100%; overflow-y: auto; width: 250px; position: absolute; color: white; background-color: rgba(10,10,10,0.9); padding: 20px; ` |
1 2 3 4 5 6 7 |
const Establishment = (props) => { ... return ( <LeftBar> </LeftBar> ) ... |
5 – Agora vamos criar alguns components para mostar a image, título e texto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const Title = styled.h1` font-size: 20px; color: rgba(220,110,50,0.7); ` const Paragraph = styled.p` font-size: 13px; line-height: 14px; ` const Image = styled.img` height: 150px; width: 100%; ` |
6 – Vamos começar mostrando as informações do Café: A – Uma foto default ou a foto do local
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
... <LeftBar> { (establishment.photos) ? <Image src={` https://maps.googleapis.com/maps/api/place/photo?maxwidth=400& photoreference=${establishment.photos[0].photo_reference}&sensor=false& key=${REACT_APP_GOOGLE_API_KEY}`} alt="Store perfil" /> : <Image src='/images/no_photo.jpg' alt="No perfil" /> } </LeftBar> ... |
B – O nome do estabelecimento:
1 2 3 4 5 6 |
... <LeftBar> ... <Title>{establishment.name}</Title> </LeftBar> ... |
C – Os horários de funcionamento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
... <LeftBar> ... { (establishment.opening_hours) ? <div> { (establishment.opening_hours.open_now === true) ? 'Aberto' : 'Fechado' } <hr /> { establishment.opening_hours.weekday_text.map((schedule, index) => { return (<Paragraph key={index}>{schedule}</Paragraph>) }) } </div> : 'Não há cadastros de horário de funcionamento.' } </LeftBar> |
D – O endereço do local:
1 2 3 4 5 6 7 |
... <LeftBar> ... <hr/> <Paragraph>{establishment.formatted_address}</Paragraph> </LeftBar> |
7 – Agora vamos adicionar o establishment no APP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
... import Establishment from './components/Establishment'; ... const [selected, setSelected] = useState({}); ... <GoogleMap mapContainerStyle={{ height: "100vh", width: "100%" }} zoom={15} center={(selected.geometry) ? undefined : { lat: latitude, lng: longitude }}> { locations.map((item, index) => { return ( <Marker key={index} icon='/images/coffee-pin.png' title={item.name} animation="4" position={{ lat: item.geometry.location.lat, lng: item.geometry.location.lng }} onClick={() => setSelected(item)} /> ) }) } { selected.place_id && ( <Establishment place={selected} /> ) } <Marker key='my location' icon='/images/my-location-pin.png' title="Seu Local" animation="2" position={{ lat: latitude, lng: longitude }} /> </GoogleMap> |
1 – Dentro de /src/services, crie os arquivos ‘rating.js’ e ‘store.js’.
2 – Dentro do arquivo ‘rating.js’, cole o seguinte código:
1 2 3 4 5 6 7 |
import Api from './api'; const RatingService = { create: (store, rating) => Api.post('/ratings', { store: store, rating: rating} ), } export default RatingService; |
3 – No arquivo ‘store.js’, cole o seguinte código:
1 2 3 4 5 6 7 8 |
import Api from './api'; const StoreService = { show: (google_place_id) => Api.get(`/stores/${google_place_id}`), index: (latitude, longitude) => Api.get('/stores', {params: {latitude: latitude, longitude: longitude}}), } export default StoreService; |
Instalando a Lib React Rating Starts para a avaliação
1 |
<span role="presentation">yarn add react-rating-stars-component</span> |
Setando o formulário
1 – Crie a pasta /src/components/Establishment/Ratings
2 – Nela criei o arquivo index.js, coloque nele:
1 2 3 4 5 6 7 8 9 10 11 |
import React, { Fragment, useEffect, useState } from 'react'; const Ratings = (props) => { return ( <Fragment> </Fragment> ) } export default Ratings; |
3 – No component Esblishment inclua nosso component:
1 2 3 4 5 6 7 8 |
import Ratings from './Ratings'; ... <LeftBar> ... <Ratings place={props.place} /> </LeftBar> |
4 – Dentro da pasta Ratings crie o component Form /Ratings/Form/index.js e coloque nele:
1 2 3 4 5 6 7 8 9 10 11 |
import React, { useState } from 'react'; const Form = (props) => { return ( <div> </div> ) } export default Form; |
5 – Dentro do nosso Form vamos incluir os states:
1 2 3 4 5 6 7 |
import React, { useState } from 'react'; const Form = (props) => { const [name, setName] = useState(''); const [message, setMessage] = useState(''); const [value, setValue] = useState(1); ... |
6 – Agora vamos criar a estrutura do nosso form:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const Form = (props) => { ... <div> <h4>Deixe sua Opinião</h4> <form> <input name="name" type="text" className="input" placeholder="Seu primeiro nome" onChange={(e) => setName(e.target.value)} value={name} /> <textarea name="message" className="textarea" placeholder="Sua opinião" onChange={(e) => setMessage(e.target.value)} value={message}></TextArea> <div> <ReactStars count={5} value={value} size={24} activeColor="#ffd700" onChange={(newValue) => { setValue(newValue); }} /> <button type="submit" className="button is-danger">Enviar</button> </div> </form> </div> ... |
7 – Vamos criar alguns components estilizados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
const NewRating = styled.div` padding-bottom: 50px; ` const Input = styled.input` margin-bottom: 10px; height: 20px; width: 90%; border-width: 0; ` const TextArea = styled.textarea` margin-bottom: 10px; height: 40px; width: 90%; border-width: 0; ` const Button = styled.button` color: white; background-color: #a5572f; width: 90px; height: 30px; margin-top: 10px; border-color: #a5572f; font-weight: 800; ` |
8 – Agora vamos incluir eles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import React, { useState } from 'react'; import styled from 'styled-components' import ReactStars from "react-rating-stars-component"; ... const Form = (props) => { const [name, setName] = useState(''); const [message, setMessage] = useState(''); const [value, setValue] = useState(1); return ( <NewRating> <h4>Deixe sua Opinião</h4> <form> <Input name="name" type="text" className="input" placeholder="Seu primeiro nome" onChange={(e) => setName(e.target.value)} value={name} /> <TextArea name="message" className="textarea" placeholder="Sua opinião" onChange={(e) => setMessage(e.target.value)} value={message}></TextArea> <div> <ReactStars count={5} value={value} size={24} activeColor="#ffd700" onChange={(newValue) => { setValue(newValue); }} /> <Button type="submit" className="button is-danger">Enviar</Button> </div> </form> </NewRating> ) } export default Form; |
Conectando o Form a API
1 – Vamos atualizar o form para incluir o OnSubimit:
1 2 3 |
... <form onSubmit={handleSubmit}> ... |
2 – Agora vamos adicionar a resposta do Form, à API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import RatingService from '../../services/rating'; ... const Form = (props) => { ... async function handleSubmit(e) { e.preventDefault(); const store_params = { latitude: props.place.geometry.location.lat, longitude: props.place.geometry.location.lng, name: props.place.name, address: props.place.formatted_address, google_place_id: props.place.place_id } const rating_params = { value: (value == null) ? 1 : value, opinion: message, user_name: name } await RatingService.create(store_params, rating_params); props.loadStore() setName(''); setMessage(''); } return ( ... |
7 – No component Ratings, coloque o seguinte código:
1 2 3 4 5 6 7 8 9 10 11 12 |
... import Form from '../Form'; const Ratings = (props) => { return ( <Fragment> <Form place={props.place} /> </Fragment> ) } export default Ratings; |
8 – Suba o APP e tente enviar um Review.
1 |
yarn start |
Incluindo a lista de Reviews
1 – Agora vamos puxar nossas avaliações da API, adicionando o seguinte código no component Ratings:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
... import StoreService from '../../../services/store.js'; ... const Ratings = (props) => { const [store, setStore] = useState([]); useEffect(() => { loadStore(); }, [props.place]); async function loadStore() { setStore([]); try { const response = await StoreService.show(props.place.place_id); setStore(response.data); } catch (error) { setStore([]); } } return ( ... |
2 – Feita a conexão com a API, vamos começar a estruturar nosso HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import ReactStars from "react-rating-stars-component"; ... return ( <Fragment> <h4> { store.ratings_count || 0 } Opiniões { store.ratings_average && <ReactStars edit={false} value={store.ratings_average || 0} /> } </h4> <hr /> ... </Fragment> ... |
3 – Vamos, agora, listar nossas avaliações no HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
... <hr /> </div> { store.ratings && <div> { store.ratings.map((rating, index) => { return ( <div key={index}> <strong>{rating.user_name}</strong> <ReactStars edit={false} value={rating.value} /> <p>{rating.opinion}</p> <p>{rating.date}</p> <hr /> </div> ) }) } </div> } ... |
5 – Atualize a inclusão do Form no Ratings:
1 |
<Form place={props.place} loadStore={loadStore}/> |
6 – Suba o APP e tente enviar um Review.
1 |
yarn start |
Criando a sessão de cafés mais próximos
1 – Crie a pasta /src/components/NearstCoffees.
2 – Dentro do componente NearstCoffees, crie o arquivo ‘index.js’
3 – Adicione o seguinte código no ‘index.js’:
1 2 3 4 5 6 7 8 9 10 11 |
import React, { useEffect, useState } from 'react'; const NearstCoffees = (props) => { return ( <div> </div> ) } export default NearstCoffees; |
4 – Agora vamos conectar com a nossa API de Stores:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import React, { useEffect, useState } from 'react'; import StoreService from '../../services/store'; const NearstCoffees = (props) => { const [stores, setStores] = useState([]); useEffect(() => { loadNearstStores(); }, [props.latitude]); async function loadNearstStores() { const response = await StoreService.index(props.latitude, props.longitude); setStores(response.data); } return ( ... |
5 – Vamos criar alguns components estilizados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
... import styled from 'styled-components' ... const RightBar = styled.div` width: 250px; position: absolute; color: white; right: 0; top: 0; ` const Head = styled.div` background-color: rgba(10,10,10,0.9); border-radius: 6px; padding: 2px; text-align: center; margin: 10px; ` const Body = styled.div` background-color: rgba(10,10,10,0.9); border-radius: 6px; padding: 20px; height: 450px; overflow-y: auto; margin: 10px; ` const Footer = styled.div` background-color: rgba(10,10,10,0.9); border-radius: 6px; padding: 10px 20px 20px 20px; font-size: 0.8em; margin: 10px; ` const EstablishmentItem = styled.div` cursor: pointer; ` const Title = styled.h1` font-size: 18px; color: rgba(220,110,50,0.7); ` const Paragraph = styled.p` font-size: 13px; line-height: 14px; ` |
6 – Agora vamos setar nosso HTML do componente NearstCoffees:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
... import ReactStars from "react-rating-stars-component"; ... return ( <RightBar> <Head> <h3>Find My Coffee</h3> </Head> <Body> <strong>Mais amados na região</strong> <hr /> { stores.map(store => { return ( <EstablishmentItem key={store.name}> <Title>{store.name}</Title> <Paragraph> {store.address} </Paragraph> { store.ratings_count || 0 } Opiniões <ReactStars edit={false} value={store.ratings_average || 0} /> <hr/> </EstablishmentItem> ) }) } </Body> <Footer> <h2>OneBitCode.com</h2> <Paragraph> Projeto Open Source desenvolvido na Semana Super Full Stack da escola online de programação </Paragraph> </Footer> </RightBar> ) } export default NearstCoffees; |
7 – No component App coloque:
1 2 3 4 5 6 7 |
import StoreService from '../../services/store.js'; ... {(latitude != 0 && longitude != 0) && <NearstCoffees latitude={latitude} longitude={longitude} /> } </GoogleMap> </LoadScript> |
8 – Suba o APP e tente enviar um Review.
1 |
yarn start |