Skip to content

Commit e8d8c59

Browse files
authored
Merge pull request #2 from letsdevapps/enhancement/issue-1
Criar frontend demo para pagination
2 parents c59d9a5 + 691a988 commit e8d8c59

File tree

10 files changed

+285
-34
lines changed

10 files changed

+285
-34
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ name: Build and Test Spring Boot HATEOAS
44
on:
55
push:
66
branches: [ main ]
7+
pull_request:
8+
branches: [ main ]
79
workflow_dispatch:
810

911
jobs:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ target/
66
.mvn/
77
mvnw
88
mvnw.cmd
9+
bin/
910

1011
### STS ###
1112
.apt_generated

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
Demonstração do uso de HATEOAS para um projeto Springboot REST
44

5+
## Frontend
6+
7+
### Home Message
8+
9+
* Indice
10+
11+
http://localhost:8080/
12+
513
## Endpoints API
614

715
### Home Message

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@
5757
<scope>runtime</scope>
5858
</dependency>
5959

60+
<!-- Fronend Thymeleaf - Para demonstrar paginação (opcional) -->
61+
<dependency>
62+
<groupId>org.springframework.boot</groupId>
63+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
64+
</dependency>
65+
6066
<dependency>
6167
<groupId>org.projectlombok</groupId>
6268
<artifactId>lombok</artifactId>

src/main/java/com/pro/api/HomeApi.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class HomeApi {
3838

3939
@GetMapping({ "", "/", "/index" })
4040
public EntityModel<HomeDTO> index() {
41-
HomeDTO homeDto = new HomeDTO("Springboot HATEOAS Home | Index");
41+
HomeDTO homeDto = new HomeDTO("Springboot HATEOAS API Home | Index");
4242

4343
return EntityModel.of(homeDto,
4444
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).index()).withSelfRel());
@@ -69,10 +69,10 @@ public CollectionModel<EntityModel<HomeDTO>> getAllMessagesAssembler() {
6969
}
7070

7171
@GetMapping("/messages/pag")
72-
public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam int page, @RequestParam int size) {
72+
public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam String searchText, @RequestParam int page, @RequestParam int size) {
7373
Pageable pageable = PageRequest.of(page, size);
7474

75-
Page<HomeDTO> pageResult = homeService.getAllMessages(pageable);
75+
Page<HomeDTO> pageResult = homeService.getAllMessages(pageable, searchText);
7676

7777
List<EntityModel<HomeDTO>> userResources = pageResult.stream().map(homeDTOAssembler::toModel)
7878
.collect(Collectors.toList());
@@ -81,15 +81,15 @@ public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam int pa
8181

8282
//lembrar de adicionar o self a paginação tambem
8383
collectionModel.add(WebMvcLinkBuilder.linkTo(
84-
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page, size)).withSelfRel());
84+
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page, size)).withSelfRel());
8585

8686
if (pageResult.hasNext()) {
8787
collectionModel.add(WebMvcLinkBuilder.linkTo(
88-
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page + 1, size)).withRel("next"));
88+
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page + 1, size)).withRel("next"));
8989
}
9090
if (pageResult.hasPrevious()) {
9191
collectionModel.add(WebMvcLinkBuilder.linkTo(
92-
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page - 1, size)).withRel("prev"));
92+
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page - 1, size)).withRel("prev"));
9393
}
9494

9595
return collectionModel;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.pro.controller;
2+
3+
import java.util.Map;
4+
5+
import org.springframework.boot.web.client.RestTemplateBuilder;
6+
import org.springframework.core.ParameterizedTypeReference;
7+
import org.springframework.http.HttpMethod;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.stereotype.Controller;
10+
import org.springframework.ui.Model;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.RequestMethod;
13+
import org.springframework.web.bind.annotation.RequestParam;
14+
import org.springframework.web.client.RestTemplate;
15+
16+
import lombok.extern.slf4j.Slf4j;
17+
18+
@Slf4j
19+
@Controller
20+
@RequestMapping({ "", "/" })
21+
public class HomeController {
22+
23+
private final RestTemplate restTemplate;
24+
25+
public HomeController(RestTemplateBuilder builder) {
26+
this.restTemplate = builder.build();
27+
}
28+
29+
@RequestMapping(value = { "", "/", "/home", "/index" }, method = { RequestMethod.GET, RequestMethod.POST })
30+
public String index(Model model,
31+
@RequestParam(defaultValue = "") String searchText,
32+
@RequestParam(defaultValue = "0") int page,
33+
@RequestParam(defaultValue = "3") int size) {
34+
log.info("----- Springboot HATEOAS Controller Home | Index -----");
35+
36+
String url = "http://localhost:8080/api/messages/pag?page="+ page + "&size=" + size + "&searchText=" + searchText;
37+
38+
// if (searchText != null && !searchText.isEmpty()) {
39+
// url += "&searchText=" + searchText;
40+
// }
41+
42+
ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {};
43+
44+
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(url, HttpMethod.GET ,null ,typeRef);
45+
46+
Map<String, Object> body = response.getBody();
47+
48+
model.addAttribute("apiResponse", body);
49+
model.addAttribute("searchText", searchText);
50+
model.addAttribute("index", true);
51+
return "web/home/home";
52+
}
53+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ public HomeCollectionAssembler(HomeDTOAssembler homeDTOAssembler) {
2222
this.homeDTOAssembler = homeDTOAssembler;
2323
}
2424

25-
public CollectionModel<EntityModel<HomeDTO>> toCollectionModel(Iterable<? extends HomeDTO> entities, int page, int size) {
25+
public CollectionModel<EntityModel<HomeDTO>> toCollectionModel(Iterable<? extends HomeDTO> entities, String searchText, int page, int size) {
2626
List<EntityModel<HomeDTO>> entityModels = StreamSupport.stream(entities.spliterator(), false)
2727
.map(homeDTOAssembler::toModel) // Usa o assembler do item para cada item
2828
.collect(Collectors.toList());
2929

3030
// Link 'self' para a coleção de recursos
3131
return CollectionModel.of(entityModels,
32-
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page, size)).withSelfRel());
32+
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page, size)).withSelfRel());
3333
}
3434
}

src/main/java/com/pro/service/HomeService.java

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5+
import java.util.stream.Collectors;
56

67
import org.springframework.data.domain.Page;
78
import org.springframework.data.domain.PageImpl;
@@ -15,15 +16,22 @@ public class HomeService {
1516

1617
private List<HomeDTO> homeDtoList;
1718

18-
public HomeService() {
19-
init();
20-
}
19+
// public HomeService() {
20+
// init();
21+
// }
2122

2223
public List<HomeDTO> getAllMessages() {
2324
return homeDtoList;
2425
}
2526

26-
public Page<HomeDTO> getAllMessages(Pageable pageable) {
27+
public Page<HomeDTO> getAllMessages(Pageable pageable, String searchText) {
28+
init();
29+
if (searchText != null && !searchText.isEmpty()) {
30+
homeDtoList = homeDtoList.stream()
31+
.filter(homeDTO -> homeDTO.getMessage().toLowerCase().contains(searchText.toLowerCase()))
32+
.collect(Collectors.toList());
33+
}
34+
2735
int start = (int) pageable.getOffset();
2836
int end = Math.min((start + pageable.getPageSize()), homeDtoList.size());
2937

@@ -34,27 +42,10 @@ public Page<HomeDTO> getAllMessages(Pageable pageable) {
3442

3543
private void init() {
3644
homeDtoList = new ArrayList<HomeDTO>();
37-
38-
HomeDTO m1 = new HomeDTO("mensagem 1");
39-
HomeDTO m2 = new HomeDTO("mensagem 2");
40-
HomeDTO m3 = new HomeDTO("mensagem 3");
41-
HomeDTO m4 = new HomeDTO("mensagem 4");
42-
HomeDTO m5 = new HomeDTO("mensagem 5");
43-
HomeDTO m6 = new HomeDTO("mensagem 6");
44-
HomeDTO m7 = new HomeDTO("mensagem 7");
45-
HomeDTO m8 = new HomeDTO("mensagem 8");
46-
HomeDTO m9 = new HomeDTO("mensagem 9");
47-
HomeDTO m10 = new HomeDTO("mensagem 10");
48-
49-
homeDtoList.add(m1);
50-
homeDtoList.add(m2);
51-
homeDtoList.add(m3);
52-
homeDtoList.add(m4);
53-
homeDtoList.add(m5);
54-
homeDtoList.add(m6);
55-
homeDtoList.add(m7);
56-
homeDtoList.add(m8);
57-
homeDtoList.add(m9);
58-
homeDtoList.add(m10);
45+
46+
for (int i=1; i<=10; i++) {
47+
HomeDTO m = new HomeDTO("mensagem " + i);
48+
homeDtoList.add(m);
49+
}
5950
}
6051
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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>Home | Início</h2>
31+
</div>
32+
33+
<div class="w3-container w3-margin-top" style="min-height: 400px;">
34+
<!-- Card principal -->
35+
<div class="w3-card w3-white w3-padding w3-round-large">
36+
<h3 class="w3-text-dark-grey">Lista de Mensagens</h3>
37+
38+
<!-- 🔎 Formulário de pesquisa -->
39+
<form class="w3-row w3-margin-bottom"
40+
th:action="@{/home}" method="get">
41+
<div class="w3-col s9">
42+
<input class="w3-input w3-border w3-round"
43+
type="text"
44+
name="searchText"
45+
th:value="${searchText}"
46+
placeholder="Digite o texto para buscar..."/>
47+
</div>
48+
<div class="w3-col s3">
49+
<button class="w3-button w3-blue w3-round w3-block"
50+
type="submit">
51+
Buscar
52+
</button>
53+
</div>
54+
</form>
55+
56+
<!-- 📄 Lista de mensagens -->
57+
<ul class="w3-ul w3-hoverable w3-card-4 w3-round">
58+
<li class="w3-padding"
59+
th:each="msg : ${apiResponse['_embedded']['homeDTOList']}">
60+
<span class="w3-text-grey"
61+
th:text="${msg['message']}">
62+
</span>
63+
</li>
64+
</ul>
65+
66+
<!-- ⚠ Caso não tenha mensagens -->
67+
<div class="w3-panel w3-yellow w3-round w3-margin-top"
68+
th:if="${#lists.isEmpty(apiResponse['_embedded']['homeDTOList'])}">
69+
Nenhuma mensagem encontrada.
70+
</div>
71+
72+
<!-- 🔁 Paginação -->
73+
<div class="w3-center w3-margin-top">
74+
<a class="w3-button w3-light-grey w3-round w3-margin-right"
75+
th:if="${apiResponse['_links']['prev'] != null}"
76+
th:href="@{/home(page=${apiResponse['_links']['prev']['href'].split('page=')[1].split('&')[0]}, searchText=${searchText})}">
77+
◀ Anterior
78+
</a>
79+
80+
<a class="w3-button w3-blue w3-round"
81+
th:if="${apiResponse['_links']['next'] != null}"
82+
th:href="@{/home(page=${apiResponse['_links']['next']['href'].split('page=')[1].split('&')[0]}, searchText=${searchText})}">
83+
Próxima ▶
84+
</a>
85+
</div>
86+
</div>
87+
</div>
88+
</body>
89+
</html>

0 commit comments

Comments
 (0)