Skip to content

Commit 549d545

Browse files
authored
Aceito
Criar frontend para product crud
2 parents e8d8c59 + 535fba8 commit 549d545

File tree

6 files changed

+216
-9
lines changed

6 files changed

+216
-9
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Demonstração do uso de HATEOAS para um projeto Springboot REST
1010

1111
http://localhost:8080/
1212

13+
### Products
14+
15+
* CRUD Products
16+
17+
http://localhost:8080/products
18+
1319
## Endpoints API
1420

1521
### Home Message

src/main/java/com/pro/api/ProductController.java renamed to src/main/java/com/pro/api/ProductApi.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.springframework.beans.factory.annotation.Autowired;
77
import org.springframework.hateoas.CollectionModel;
88
import org.springframework.hateoas.EntityModel;
9+
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
910
import org.springframework.http.ResponseEntity;
1011
import org.springframework.web.bind.annotation.DeleteMapping;
1112
import org.springframework.web.bind.annotation.GetMapping;
@@ -23,7 +24,7 @@
2324

2425
@RestController
2526
@RequestMapping("/api/products")
26-
public class ProductController {
27+
public class ProductApi {
2728

2829
@Autowired
2930
private ProductRepository productRepository;
@@ -46,7 +47,13 @@ public CollectionModel<EntityModel<ProductDTO>> getAllProducts() {
4647
List<EntityModel<ProductDTO>> products = productRepository.findAll().stream().map(productDTOAssembler::toModel)
4748
.collect(Collectors.toList());
4849

49-
return CollectionModel.of(products);
50+
CollectionModel<EntityModel<ProductDTO>> collection = CollectionModel.of(products);
51+
52+
collection.add(WebMvcLinkBuilder.linkTo(
53+
WebMvcLinkBuilder.methodOn(
54+
ProductApi.class).getAllProducts()).withSelfRel());
55+
56+
return collection;
5057
}
5158

5259
// READ - Obter um produto específico
@@ -79,7 +86,8 @@ public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
7986
.orElseThrow(() -> new RuntimeException("Produto não encontrado"));
8087
productRepository.delete(product);
8188

82-
//return ResponseEntity.ok("Produto excluído com sucesso");
83-
return null;
89+
//return ResponseEntity.ok("Produto excluído com sucesso"); alterar para ResponseEntity<String>
90+
//return null;
91+
return ResponseEntity.noContent().build(); // Retorna 204 sem conteúdo
8492
}
8593
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.pro.controller;
2+
3+
import java.util.Map;
4+
5+
import org.springframework.boot.web.client.RestTemplateBuilder;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.stereotype.Controller;
8+
import org.springframework.ui.Model;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.ModelAttribute;
11+
import org.springframework.web.bind.annotation.PostMapping;
12+
import org.springframework.web.bind.annotation.RequestMapping;
13+
import org.springframework.web.bind.annotation.RequestParam;
14+
import org.springframework.web.client.RestTemplate;
15+
16+
import com.pro.model.Product;
17+
18+
@Controller
19+
@RequestMapping("/products")
20+
public class ProductController {
21+
22+
private final RestTemplate restTemplate;
23+
24+
public ProductController(RestTemplateBuilder builder) {
25+
this.restTemplate = builder.build();
26+
}
27+
28+
@GetMapping({"", "/"})
29+
public String listProducts(Model model) {
30+
31+
String apiUrl = "http://localhost:8080/api/products";
32+
33+
ResponseEntity<Map> response = restTemplate.getForEntity(apiUrl, Map.class);
34+
35+
model.addAttribute("apiResponse", response.getBody());
36+
37+
return "web/product/list";
38+
}
39+
40+
@GetMapping("/edit")
41+
public String editProduct(@RequestParam String href, Model model) {
42+
43+
ResponseEntity<Map> response = restTemplate.getForEntity(href, Map.class);
44+
45+
model.addAttribute("product", response.getBody());
46+
47+
return "web/product/form";
48+
}
49+
50+
@PostMapping("/edit")
51+
public String updateProduct(@RequestParam String href, @ModelAttribute Product product) {
52+
restTemplate.put(href, product); // Atualiza o produto via PUT na API
53+
return "redirect:/products"; // Redireciona para a lista de produtos
54+
}
55+
56+
@GetMapping("/delete")
57+
public String deleteProduct(@RequestParam String href) {
58+
59+
restTemplate.delete(href);
60+
61+
return "redirect:/products";
62+
}
63+
}

src/main/java/com/pro/dto/assembler/ProductDTOAssembler.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
66
import org.springframework.stereotype.Component;
77

8-
import com.pro.api.ProductController;
8+
import com.pro.api.ProductApi;
99
import com.pro.dto.ProductDTO;
1010
import com.pro.model.Product;
1111

@@ -24,18 +24,18 @@ public EntityModel<ProductDTO> toModel(Product product) {
2424

2525
// Link self (auto referenciado)
2626
productResource.add(WebMvcLinkBuilder
27-
.linkTo(WebMvcLinkBuilder.methodOn(ProductController.class).getProduct(product.getId())).withSelfRel());
27+
.linkTo(WebMvcLinkBuilder.methodOn(ProductApi.class).getProduct(product.getId())).withSelfRel());
2828

2929
// Link para todos os produtos
3030
productResource.add(WebMvcLinkBuilder
31-
.linkTo(WebMvcLinkBuilder.methodOn(ProductController.class).getAllProducts()).withRel("all-products"));
31+
.linkTo(WebMvcLinkBuilder.methodOn(ProductApi.class).getAllProducts()).withRel("all-products"));
3232

3333
// Link para atualizar o produto (update)
34-
productResource.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ProductController.class)
34+
productResource.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ProductApi.class)
3535
.updateProduct(product.getId(), product)).withRel("update-product"));
3636

3737
// Link para deletar o produto (delete)
38-
productResource.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ProductController.class)
38+
productResource.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ProductApi.class)
3939
.deleteProduct(product.getId())).withRel("delete-product"));
4040

4141
return productResource;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!DOCTYPE html>
2+
<html xmlns="http://www.w3.org/1999/xhtml"
3+
xmlns:th="http://www.thymeleaf.org"
4+
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
5+
<head>
6+
<meta name="description" content="Pagina Inicial da aplicação">
7+
<meta name="keywords" content="pagina-inicial, pagina-home, inicio, home, inicial, index, ...">
8+
<meta name="author" content="Dev Academy">
9+
<meta name="revisit-after" content="60 days">
10+
<meta name="ROBOTS" content="INDEX, FOLLOW">
11+
<link rel="icon" th:href="@{/ico/favicon.ico}">
12+
<meta charset="UTF-8">
13+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
14+
<title>Home | Inicio</title>
15+
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
16+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
17+
<style>
18+
.body-align {
19+
/* border: 2px solid #000000; */
20+
padding-top: 2%;
21+
padding-left: 5%;
22+
padding-right: 5%;
23+
text-align: center;
24+
}
25+
</style>
26+
</head>
27+
<body class="w3-light-grey">
28+
29+
<div class="w3-container w3-blue w3-padding">
30+
<h2>Product | Início</h2>
31+
</div>
32+
33+
<div class="w3-container w3-margin-top" style="min-height: 400px;">
34+
35+
<!-- Card principal -->
36+
<div class="w3-card w3-white w3-padding w3-round-large">
37+
38+
<h3 class="w3-text-dark-grey">Editar Produto</h3>
39+
40+
<!-- Formulário de edição -->
41+
<div class="w3-card w3-white w3-padding w3-round-large">
42+
43+
<h3 class="w3-text-dark-grey">Detalhes do Produto</h3>
44+
45+
<!-- Formulário de edição do produto -->
46+
<form th:action="@{/products/edit(href=${product['_links']['self']['href']})}" method="post">
47+
48+
<div class="w3-margin-bottom">
49+
<label for="name">Nome do Produto:</label>
50+
<input class="w3-input w3-border w3-round" type="text" id="name" name="name" th:value="${product['name']}" required/>
51+
</div>
52+
53+
<div class="w3-margin-bottom">
54+
<label for="price">Preço:</label>
55+
<input class="w3-input w3-border w3-round" type="number" id="price" name="price" th:value="${product['price']}" required/>
56+
</div>
57+
58+
<div class="w3-center">
59+
<button class="w3-button w3-blue w3-round" type="submit">Salvar alterações</button>
60+
</div>
61+
</form>
62+
63+
</div>
64+
65+
</div>
66+
</div>
67+
</body>
68+
</html>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!DOCTYPE html>
2+
<html xmlns="http://www.w3.org/1999/xhtml"
3+
xmlns:th="http://www.thymeleaf.org"
4+
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
5+
<head>
6+
<meta name="description" content="Pagina Inicial da aplicação">
7+
<meta name="keywords" content="pagina-inicial, pagina-home, inicio, home, inicial, index, ...">
8+
<meta name="author" content="Dev Academy">
9+
<meta name="revisit-after" content="60 days">
10+
<meta name="ROBOTS" content="INDEX, FOLLOW">
11+
<link rel="icon" th:href="@{/ico/favicon.ico}">
12+
<meta charset="UTF-8">
13+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
14+
<title>Home | Inicio</title>
15+
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
16+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
17+
<style>
18+
.body-align {
19+
/* border: 2px solid #000000; */
20+
padding-top: 2%;
21+
padding-left: 5%;
22+
padding-right: 5%;
23+
text-align: center;
24+
}
25+
</style>
26+
</head>
27+
<body class="w3-light-grey">
28+
29+
<div class="w3-container w3-blue w3-padding">
30+
<h2>Product | Início</h2>
31+
</div>
32+
33+
<div class="w3-container w3-margin-top" style="min-height: 400px;">
34+
35+
<!-- Card principal -->
36+
<div class="w3-card w3-white w3-padding w3-round-large">
37+
38+
<h3 class="w3-text-dark-grey">Lista de Produtos</h3>
39+
40+
<div class="w3-panel w3-yellow w3-round w3-margin-top"
41+
th:if="${#lists.isEmpty(apiResponse['_embedded'])}">
42+
Nenhum produto encontrado.
43+
</div>
44+
45+
<div th:unless="${#lists.isEmpty(apiResponse['_embedded'])}">
46+
<ul class="w3-ul w3-card-4">
47+
<li th:each="prod : ${apiResponse['_embedded']['productDTOList']}">
48+
<span th:text="${prod['name']}"></span> - <span th:text="${prod['price']}"></span>
49+
50+
<!-- Link de edição via HATEOAS -->
51+
<a th:href="@{/products/edit(href=${prod['_links']['self']['href']})}">Editar</a>
52+
53+
<!-- Link de exclusão via HATEOAS -->
54+
<a th:href="@{/products/delete(href=${prod['_links']['self']['href']})}">Excluir</a>
55+
</li>
56+
</ul>
57+
</div>
58+
59+
</div>
60+
</div>
61+
</body>
62+
</html>

0 commit comments

Comments
 (0)