https://dsmovie-danielreis.netlify.app/
DevSuperior - Escola de programação
- Criar projetos backend e frontend
- Salvar os projeto no Github em monorepo
- Montar o visual estático do front end
- Implementar o back end
- Modelo de domínio
- Acesso a banco de dados
- Estruturar o back end no padrão camadas
- Criar endpoints da API REST
- Implantação na nuvem
- Integrar back end e front end
- Três pilares do React
- Componentes
- Props
- Estado
- React Hooks
- useState
- useEffect
- useParams
- useNavigate
- Curl
- Git
- Java JDK 11 ou 17
- Maven
- STS
- Postman
- Postgresql e pgAdmin
- Heroku CLI
- NodeJS 16.x (https://nodejs.org/en/download/)
- VS Code
https://www.youtube.com/playlist?list=PLNuUvBZGBA8mcAF-YX7RJhA26TBLdG5yk
- Instalar o curl
sudo apt-get install -y curl
- Conferir a instalação:
curl
- Instalar:
sudo apt-get install -y git
- Conferir a instalação:
git
- Instalar Java:
sudo apt install openjdk-11-jdk
sudo apt install openjdk-17-jdk
- Verificar a instalação:
java -version
- Configurar JAVA_HOME:
- Verificar caminho Java:
sudo update-alternatives --config java
- Edite o arquivo .bashrc:
sudo gedit ~/.bashrc
- Copie o código abaixo no final do arquivo (observe a versão do seu JDK). Salve o arquivo.
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export JAVA_HOME export PATH=$PATH:$JAVA_HOME
- Abra um novo terminal e teste:
echo $JAVA_HOME
- Instalar Maven:
sudo apt-get install maven
- Verificar a instalação:
mvn -v
- Google: STS
- Baixar
- Descompactar (exemplo: /home/user/apps)
- Iniciar STS
- Selecione um workspace (exemplo: /home/user/Workspaces/ws-sts)
- Liberar permissão na pasta do workspace:
sudo chmod -R ugo+rw /home/user/Workspaces/ws-sts
- Instalar com snap:
snap install postman
https://www.postgresql.org/download/linux/ubuntu/
https://www.pgadmin.org/download/pgadmin-4-apt/
https://devcenter.heroku.com/articles/heroku-cli
sudo apt update
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install nodejs
No Debian e no Ubuntu:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
Caso tenha problemas de instalação a própria DOC do Yarn é muito útil: https://classic.yarnpkg.com/pt-BR/docs/install/#debian-stable
https://code.visualstudio.com/download
sudo snap install code --classic
Guia de instalação das ferramentas
node -v
yarn -v
Caso precise instalar o Yarn, faça o comando:
npm install --global yarn
https://www.figma.com/file/hpQuzpGHq2MmrI87xnfMoT/DSMovie1
- ATENÇÃO: esta será a estrutura de pastas que vamos criar:
yarn create react-app frontend --template typescript
OU:
npx create-react-app frontend --template typescript
IMPORTANTE: deletar subpasta .git
- Lembrete: ver extensões e arquivos ocultos
-
Criar projeto Spring Boot no
Spring Initializr
com as seguintes dependências:- Web
- JPA
- H2
- Postgres
- Security
-
Ajuste no arquivo pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
- Botão direito no projeto -> Maven -> Update project (force update)
Caso não tenha configurado ainda seu Github:
git init
git add .
git commit -m "Projeto criado"
git branch -M main
git remote add origin [email protected]:seuusuario/seurepositorio.git
git push -u origin main
-
Deletar arquivos não usados
-
COMMIT: Project clean
- Bootstrap
yarn add [email protected]
- Arquivo index.css
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap');
:root {
--color-primary: #4D41C0;
}
* {
box-sizing: border-box;
font-family: 'Open Sans', sans-serif;
}
html, body {
background-color: #E5E5E5;
}
a, a:hover {
text-decoration: none;
color: unset;
}
- Arquivo index.tsx
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
- COMMIT: Bootstrap
ATENÇÃO: no arquivo tsconfig.json: "baseUrl": "./src"
(reinicie o app)
<header>
<nav className="container">
<div className="dsmovie-nav-content">
<h1>DSMovie</h1>
<a href="https://github.com/devsuperior" target="_blank" rel="noreferrer">
<div className="dsmovie-contact-container">
<GithubIcon />
<p className="dsmovie-contact-link">/devsuperior</p>
</div>
</a>
</div>
</nav>
</header>
header {
height: 60px;
background-color: var(--color-primary);
display: flex;
align-items: center;
}
.dsmovie-nav-content {
display: flex;
align-items: center;
justify-content: space-between;
color: #fff;
}
.dsmovie-nav-content h1 {
font-size: 24px;
margin: 0;
font-weight: 700;
}
.dsmovie-contact-container {
display: flex;
align-items: center;
}
.dsmovie-contact-link {
margin: 0 0 0 10px;
font-size: 12px;
}
- COMMIT: Navbar
- Instalar React Router DOM
yarn add [email protected] @types/[email protected]
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import Listing from 'pages/Listing';
import Form from 'pages/Form';
import Navbar from "components/Navbar";
function App() {
return (
<BrowserRouter>
<Navbar />
<Routes>
<Route path="/" element={<Listing />} />
<Route path="/form">
<Route path=":movieId" element={<Form />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
- COMMIT: Routes
const movie = {
id: 1,
image: "https://www.themoviedb.org/t/p/w533_and_h300_bestv2/jBJWaqoSCiARWtfV0GlqHrcdidd.jpg",
title: "The Witcher",
count: 2,
score: 4.5
};
<div className="dsmovie-form-container">
<img className="dsmovie-movie-card-image" src="url" alt="The Witcher" />
<div className="dsmovie-card-bottom-container">
<h3>"The Witcher"</h3>
<form className="dsmovie-form">
<div className="form-group dsmovie-form-group">
<label htmlFor="email">Informe seu email</label>
<input type="email" className="form-control" id="email" />
</div>
<div className="form-group dsmovie-form-group">
<label htmlFor="score">Informe sua avaliação</label>
<select className="form-control" id="score">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div className="dsmovie-form-btn-container">
<button type="submit" className="btn btn-primary dsmovie-btn">Salvar</button>
</div>
</form >
<button className="btn btn-primary dsmovie-btn mt-3">Cancelar</button>
</div >
</div >
.dsmovie-form-container {
max-width: 480px;
margin: 40px auto;
padding: 20px;
}
.dsmovie-form {
width: calc(100% - 20px);
}
.dsmovie-form-group {
margin-bottom: 20px;
}
.dsmovie-form-group label {
font-size: 12px;
color: #aaa;
}
.dsmovie-form-btn-container {
display: flex;
justify-content: center;
}
.dsmovie-movie-card-image {
width: 100%;
border-radius: 8px 8px 0 0;
}
.dsmovie-card-bottom-container {
background-color: #fff;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 10px 20px 10px;
border-radius: 0 0 8px 8px;
}
.dsmovie-card-bottom-container h3 {
color: #4A4A4A;
text-align: center;
font-size: 14px;
font-weight: 700;
margin-bottom: 10px;
min-height: 40px;
}
.dsmovie-btn {
background-color: var(--color-primary);
font-size: 14px;
font-weight: 700;
height: 40px;
width: 180px;
display: flex;
align-items: center;
justify-content: center;
}
- COMMIT: Form layout
<div className="dsmovie-pagination-container">
<div className="dsmovie-pagination-box">
<button className="dsmovie-pagination-button" disabled={true} >
<Arrow />
</button>
<p>{`${1} de ${3}`}</p>
<button className="dsmovie-pagination-button" disabled={false} >
<Arrow className="dsmovie-flip-horizontal" />
</button>
</div>
</div>
.dsmovie-pagination-container {
padding: 15px 0;
display: flex;
justify-content: center;
align-items: center;
}
.dsmovie-pagination-box {
width: 180px;
display: flex;
justify-content: space-between;
align-items: center;
}
.dsmovie-pagination-box form {
width: 100%;
display: flex;
}
.dsmovie-pagination-button {
width: 40px;
height: 40px;
border-radius: 4px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--color-primary);
cursor: pointer;
}
.dsmovie-pagination-button svg {
filter: brightness(0) saturate(100%) invert(26%) sepia(19%) saturate(7395%) hue-rotate(234deg) brightness(89%) contrast(92%);
}
.dsmovie-pagination-button:disabled {
border: 1px solid #c2c2c2;
cursor: unset;
}
.dsmovie-pagination-button:disabled svg {
filter: none;
}
.dsmovie-pagination-container p {
margin: 0;
font-size: 12px;
color: var(--color-primary);
}
.dsmovie-flip-horizontal {
transform: rotate(180deg);
}
- COMMIT: Pagination layout
<div className="dsmovie-stars-container">
<StarFull />
<StarFull />
<StarFull />
<StarHalf />
<StarEmpty />
</div>
.dsmovie-stars-container {
width: 130px;
display: flex;
justify-content: space-between;
}
.dsmovie-stars-container svg {
width: 22px;
height: auto;
}
<div className="dsmovie-score-container">
<p className="dsmovie-score-value">{score > 0 ? score.toFixed(1) : '-'}</p>
<MovieStars />
<p className="dsmovie-score-count">{count} avaliações</p>
</div>
.dsmovie-score-container {
display: flex;
flex-direction: column;
align-items: center;
}
.dsmovie-score-value {
margin: 0;
color: #FFBB3A;
font-size: 16px;
font-weight: 700;
}
.dsmovie-score-count {
font-size: 12px;
color: #989898;
margin: 4px 0 10px 0;
}
<div>
<img className="dsmovie-movie-card-image" src={movie.image} alt={movie.title} />
<div className="dsmovie-card-bottom-container">
<h3>{movie.title}</h3>
<MovieScore />
<div className="btn btn-primary dsmovie-btn">Avaliar</div>
</div>
</div>
- COMMIT: MovieCard
- COMMIT: Navigation buttons
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private Environment env;
@Override
protected void configure(HttpSecurity http) throws Exception {
if (Arrays.asList(env.getActiveProfiles()).contains("test")) {
http.headers().frameOptions().disable();
}
http.cors().and().csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().anyRequest().permitAll();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE", "OPTIONS"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
- COMMIT: Security config
spring.profiles.active=test
spring.jpa.open-in-view=false
# Dados de conexão com o banco H2
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
# Configuração do cliente web do banco H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# Configuração para mostrar o SQL no console
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
INSERT INTO tb_user(email) VALUES ('[email protected]');
INSERT INTO tb_user(email) VALUES ('[email protected]');
INSERT INTO tb_user(email) VALUES ('[email protected]');
INSERT INTO tb_user(email) VALUES ('[email protected]');
INSERT INTO tb_user(email) VALUES ('[email protected]');
INSERT INTO tb_movie(score, count, title, image) VALUES (4.5, 2, 'The Witcher', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/jBJWaqoSCiARWtfV0GlqHrcdidd.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (3.3, 3, 'Venom: Tempo de Carnificina', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/vIgyYkXkg6NC2whRbYjBD7eb3Er.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'O Espetacular Homem-Aranha 2: A Ameaça de Electro', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/u7SeO6Y42P7VCTWLhpnL96cyOqd.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Matrix Resurrections', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/hv7o3VgfsairBoQFAawgaQ4cR1m.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Shang-Chi e a Lenda dos Dez Anéis', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/cinER0ESG0eJ49kXlExM0MEWGxW.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Django Livre', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/2oZklIzUbvZXXzIFzv7Hi68d6xf.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Titanic', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/yDI6D5ZQh67YU4r2ms8qcSbAviZ.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'O Lobo de Wall Street', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/cWUOv3H7YFwvKeaQhoAQTLLpo9Z.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Aves de Rapina: Arlequina e sua Emancipação Fantabulosa', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/jiqD14fg7UTZOT6qgvzTmfRYpWI.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Rogue One: Uma História Star Wars', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/6t8ES1d12OzWyCGxBeDYLHoaDrT.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Star Wars: A Guerra dos Clones', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/uK15I3sGd8AudO9z6J6vi0HH1UU.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Star Wars: Episódio I - A Ameaça Fantasma', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/36LnijfQCOC89rCMOhn2OINXROI.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Vingadores: Ultimato', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/7RyHsO4yDXtBv1zUU3mTpHeQ0d5.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Thor', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/cDJ61O1STtbWNBwefuqVrRe3d7l.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Cisne Negro', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/hqh5O4KssfJWI62HGAgrjHXbxhD.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'O Silêncio dos Inocentes', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/mfwq2nMBzArzQ7Y9RKE8SKeeTkg.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Clube da Luta', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/hZkgoQYus5vegHoetLkCJzb17zJ.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Guerra Mundial Z', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/31VpBgUX5O4Z3dn5ZbX8HLqoXH3.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Harry Potter e as Relíquias da Morte - Parte 1', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/vcrgU0KaNj5mKUCIQSUdiQwTE9y.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Harry Potter e a Pedra Filosofal', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/lvOLivVeX3DVVcwfVkxKf0R22D8.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Alice no País das Maravilhas', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/qNdlZgz9yoSJ8f0YxQWfKGPoVV.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Animais Fantásticos e Onde Habitam', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/8Qsr8pvDL3s1jNZQ4HK1d1Xlvnh.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'A Teoria de Tudo', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/kq2MHrRfH6RTfkvyDEmYLmGHE6U.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'O Livro de Boba Fett', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/sjx6zjQI2dLGtEL0HGWsnq6UyLU.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'O Último Duelo', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/4LrL40XecjGLRpX5I2gzMTUt04l.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Interestelar', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/rAiYTfKGqDCRIIqo664sY9XZIvQ.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Contato', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/yFkUPqBuUnbhYbQL8VFpTrAT9za.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Duna', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/jYEW5xZkZk2WTrdbMGAPFuBqbDc.jpg');
INSERT INTO tb_movie(score, count, title, image) VALUES (0, 0, 'Aquaman', 'https://www.themoviedb.org/t/p/w533_and_h300_bestv2/2cUsDz4TzFYHrKktT1bKHHQ7Cgm.jpg');
INSERT INTO tb_score(movie_id, user_id, value) VALUES (1, 1, 5.0);
INSERT INTO tb_score(movie_id, user_id, value) VALUES (1, 2, 4.0);
INSERT INTO tb_score(movie_id, user_id, value) VALUES (2, 1, 3.0);
INSERT INTO tb_score(movie_id, user_id, value) VALUES (2, 2, 3.0);
INSERT INTO tb_score(movie_id, user_id, value) VALUES (2, 3, 4.0);
- COMMIT: Domain model, database seed
- Criar repository
- Criar DTO
- Criar service
- Criar controller
- COMMIT: Find movies
-
Informar email, id do filme e valor da avaliação (1 a 5).
-
Recuperar usuário do banco de dados pelo email. Se o usuário não existir, insira no banco.
-
Salvar a avaliação do usuário para o dado filme.
-
Recalcular a avaliação média do filme e salvar no banco de dados.
- COMMIT: Save score
- Criar três perfis de projeto: test, dev, prod
- Gerar script SQL no perfil dev
- Testar projeto no banco Postgres local
#spring.jpa.properties.javax.persistence.schema-generation.create-source=metadata
#spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
#spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql
#spring.jpa.properties.hibernate.hbm2ddl.delimiter=;
spring.datasource.url=jdbc:postgresql://localhost:5432/dsmovie
spring.datasource.username=postgres
spring.datasource.password=1234567
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=${DATABASE_URL}
java.runtime.version=17
- COMMIT: First homolog
- Criar app no Heroku
- Provisionar banco Postgres
- Definir variável APP_PROFILE=prod
- Conectar ao banco via pgAdmin
- Criar seed do banco
heroku -v
heroku login
heroku git:remote -a <nome-do-app>
git remote -v
git subtree push --prefix backend heroku main
-
Deploy básico
- Base directory: frontend
- Build command: yarn build
- Publish directory: frontend/build
-
Arquivo _redirects
/* /index.html 200
- Configurações adicionais
- Site settings -> Domain Management: (colocar o nome que você quiser)
- Deploys -> Trigger deploy
- Instalar Axios
yarn add [email protected]
- Definir BASE_URL
- Definir os tipos Movie e MoviePage
- Fazer a requisição
export type Movie = {
id: number;
title: string;
score: number;
count: number;
image: string;
}
export type MoviePage = {
content: Movie[];
last: boolean;
totalPages: number;
totalElements: number;
size: number;
number: number;
first: boolean;
numberOfElements: number;
empty: boolean;
}
- COMMIT: First request
Hooks são funções cujo comportamento está vinculado ao estado e ao ciclo de vida do React a partir de componentes funcionais.
https://pt-br.reactjs.org/docs/hooks-overview.html
Hook: useState
Manter estado no componente
Hook: useEffect
Executar algo na instanciação ou destruição do componente, observar estado
- COMMIT: useState, useEffect
Props podem ser entendidas como argumentos do componente React.
https://pt-br.reactjs.org/docs/components-and-props.html
NOTA: em uma renderização dinâmica de coleção, cada elemento renderizado DEVE possuir um atributo key
.
- COMMIT: Props
- COMMIT: useParams
// EX:
// getFills(3.5) => [1, 1, 1, 0.5, 0]
// getFills(4.1) => [1, 1, 1, 1, 0.5]
function getFills(score: number) {
const fills = [0, 0, 0, 0, 0];
const integerPart = Math.floor(score);
for (let i = 0; i < integerPart; i++) {
fills[i] = 1;
}
const diff = score - integerPart;
if (diff > 0) {
fills[integerPart] = 0.5;
}
return fills;
}
- COMMIT: Show score
- Controlar botão habilitado/desabilitado
- Trocar página ao clique do botão
const handlePageChange = (newNumber: number) => {
setPageNumber(newNumber);
}
- COMMIT: Pagination
useEffect(() => {
axios.get(`${BASE_URL}/movies/${movieId}`)
.then(response => {
setMovie(response.data);
});
}, [movieId]);
Função para validar email
// https://stackoverflow.com/questions/46155/whats-the-best-way-to-validate-an-email-address-in-javascript
export function validateEmail(email: any) {
return String(email)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
}
Objeto de configuração da requisição Axios
const config: AxiosRequestConfig = {
baseURL: BASE_URL,
method: 'PUT',
url: '/scores',
data: {
email: email,
movieId: movieId,
score: score
}
}
- COMMIT: Save, useNavigate