Skip to content

Commit 84f875f

Browse files
committed
feature: add react
1 parent b0cdec0 commit 84f875f

9 files changed

Lines changed: 302 additions & 17 deletions

File tree

pom.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@
128128
<version>5.1.3</version>
129129
</dependency>
130130

131+
<!-- react-->
132+
<dependency>
133+
<groupId>org.webjars.npm</groupId>
134+
<artifactId>react</artifactId>
135+
<version>18.1.0</version>
136+
</dependency>
137+
<dependency>
138+
<groupId>org.webjars.npm</groupId>
139+
<artifactId>react-dom</artifactId>
140+
<version>18.1.0</version>
141+
</dependency>
142+
<dependency>
143+
<groupId>org.webjars.npm</groupId>
144+
<artifactId>babel-standalone</artifactId>
145+
<version>6.26.0</version>
146+
</dependency>
147+
<dependency>
148+
<groupId>org.webjars.npm</groupId>
149+
<artifactId>axios</artifactId>
150+
<version>0.27.2</version>
151+
</dependency>
152+
131153
<!-- extras -->
132154
<dependency>
133155
<groupId>org.thymeleaf.extras</groupId>
@@ -141,6 +163,12 @@
141163
<groupId>org.thymeleaf.extras</groupId>
142164
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
143165
</dependency>
166+
<dependency>
167+
<groupId>nz.net.ultraq.thymeleaf</groupId>
168+
<artifactId>thymeleaf-layout-dialect</artifactId>
169+
<version>3.1.0</version>
170+
</dependency>
171+
144172

145173
<dependency>
146174
<groupId>org.springframework.boot</groupId>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.github.throyer.common.springboot.controllers.app;
2+
3+
import static com.github.throyer.common.springboot.utils.Responses.ok;
4+
5+
import java.util.List;
6+
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.security.access.prepost.PreAuthorize;
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
import com.github.throyer.common.springboot.domain.role.entity.Role;
15+
import com.github.throyer.common.springboot.domain.role.repository.RoleRepository;
16+
17+
@RestController
18+
@RequestMapping("/app/roles")
19+
@PreAuthorize("hasAnyAuthority('ADM')")
20+
public class RoleController {
21+
22+
@Autowired
23+
private RoleRepository repository;
24+
25+
@GetMapping
26+
public ResponseEntity<List<Role>> index() {
27+
return ok(repository.findAll());
28+
}
29+
}

src/main/java/com/github/throyer/common/springboot/controllers/app/UserController.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
import java.util.Optional;
44

5+
import javax.validation.Valid;
6+
57
import com.github.throyer.common.springboot.domain.pagination.service.Pagination;
68
import com.github.throyer.common.springboot.domain.toast.Toasts;
79
import com.github.throyer.common.springboot.domain.toast.Type;
10+
import com.github.throyer.common.springboot.domain.user.form.CreateOrUpdateUserByAppForm;
811
import com.github.throyer.common.springboot.domain.user.repository.UserRepository;
912
import com.github.throyer.common.springboot.domain.user.service.RemoveUserService;
1013

1114
import org.springframework.beans.factory.annotation.Autowired;
1215
import org.springframework.security.access.prepost.PreAuthorize;
1316
import org.springframework.stereotype.Controller;
1417
import org.springframework.ui.Model;
18+
import org.springframework.validation.BindingResult;
1519
import org.springframework.web.bind.annotation.GetMapping;
1620
import org.springframework.web.bind.annotation.PathVariable;
1721
import org.springframework.web.bind.annotation.PostMapping;
@@ -42,6 +46,26 @@ public String index(
4246

4347
return "app/users/index";
4448
}
49+
50+
@GetMapping("/form")
51+
public String form(Model model) {
52+
model.addAttribute("user", new CreateOrUpdateUserByAppForm());
53+
return "app/users/form";
54+
}
55+
56+
@PostMapping(produces = "text/html")
57+
public String create(
58+
@Valid CreateOrUpdateUserByAppForm props,
59+
BindingResult validations,
60+
RedirectAttributes redirect,
61+
Model model
62+
) {
63+
if (validations.hasErrors()) {
64+
// return "app/users/index";
65+
}
66+
67+
return "redirect:/app/users/form";
68+
}
4569

4670
@PostMapping("/delete/{id}")
4771
public String delete(@PathVariable Long id, RedirectAttributes redirect) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.github.throyer.common.springboot.domain.user.form;
2+
3+
import static com.github.throyer.common.springboot.domain.mail.validation.EmailValidations.validateEmailUniqueness;
4+
5+
import java.util.List;
6+
7+
import javax.validation.constraints.Email;
8+
import javax.validation.constraints.NotEmpty;
9+
import javax.validation.constraints.NotNull;
10+
11+
import org.springframework.validation.BindingResult;
12+
13+
import com.github.throyer.common.springboot.domain.mail.model.Addressable;
14+
import com.github.throyer.common.springboot.domain.role.entity.Role;
15+
import com.github.throyer.common.springboot.domain.user.entity.User;
16+
import com.github.throyer.common.springboot.utils.JSON;
17+
18+
import lombok.Data;
19+
import lombok.NoArgsConstructor;
20+
21+
@Data
22+
@NoArgsConstructor
23+
public class CreateOrUpdateUserByAppForm implements Addressable {
24+
25+
private Long id;
26+
27+
@NotEmpty(message = "${user.name.not-empty}")
28+
private String name;
29+
30+
@NotEmpty(message = "{user.email.not-empty}")
31+
@Email(message = "{user.email.is-valid}")
32+
private String email;
33+
34+
@NotNull
35+
private List<Role> roles;
36+
37+
public void validate(BindingResult result) {
38+
validateEmailUniqueness(this, result);
39+
}
40+
41+
public void validate() {
42+
validateEmailUniqueness(this);
43+
}
44+
45+
public User user() {
46+
var user = new User(name, email, null, roles);
47+
user.setId(id);
48+
return user;
49+
}
50+
51+
@Override
52+
public String toString() {
53+
return JSON.stringify(this);
54+
}
55+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const App = () => {
2+
3+
const [roles, setRoles] = React.useState([])
4+
5+
const findAllRoles = React.useCallback(async () => {
6+
const { data: roles } = await axios
7+
.get('app/roles');
8+
9+
setRoles(roles);
10+
}, []);
11+
12+
13+
React.useEffect(() => {
14+
findAllRoles();
15+
}, [])
16+
17+
18+
return (
19+
<div className="col-md-4 shadow-sm p-3 mb-5 bg-body rounded">
20+
<div className="row">
21+
<div className="col-md-12">
22+
<div className="form-group">
23+
<label>
24+
<small>
25+
Nome
26+
<span className="text-danger">
27+
<strong>*</strong>
28+
</span>
29+
</small>
30+
</label>
31+
<input
32+
required
33+
name="name"
34+
type="text"
35+
className="form-control form-control-sm"
36+
/>
37+
</div>
38+
</div>
39+
<div className="col-md-12 mt-3">
40+
<div className="form-group">
41+
<label>
42+
<small>
43+
Email
44+
<span className="text-danger">
45+
<strong>*</strong>
46+
</span>
47+
</small>
48+
</label>
49+
<input
50+
name="email"
51+
type="email"
52+
className="form-control form-control-sm"
53+
required
54+
/>
55+
56+
<div className="invalid-feedback">
57+
<small>Por favor, informe o numero de andares.</small>
58+
</div>
59+
</div>
60+
</div>
61+
<div className="col-md-12 mt-3">
62+
<label>
63+
<small>
64+
Role
65+
<span className="text-danger">
66+
<strong>*</strong>
67+
</span>
68+
</small>
69+
</label>
70+
<select
71+
required
72+
name="roles"
73+
className="form-select form-select-sm"
74+
>
75+
<option value="">Selecione</option>
76+
{roles.map(({ id, name }) => (
77+
<option value={id}>{name}</option>
78+
))}
79+
</select>
80+
81+
<div className="invalid-feedback">
82+
<small>Por favor, informe a fonte da coleta.</small>
83+
</div>
84+
</div>
85+
</div>
86+
<hr />
87+
<div className="grid">
88+
<div className="g-col-6">
89+
<a href="app/users" className="btn btn-sm btn-outline-primary">
90+
Voltar
91+
</a>
92+
</div>
93+
<div className="g-col-6">
94+
<button className="btn btn-sm btn-success" type="submit">
95+
Salvar
96+
</button>
97+
</div>
98+
</div>
99+
</div>
100+
);
101+
}
102+
103+
ReactDOM.render(<App />, document.querySelector('#app'))

src/main/resources/templates/app/fragments/imports.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,11 @@
1010
<script language="javascript" th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.min.js}"></script>
1111
<script language="javascript" th:src="@{/js/main.js}"></script>
1212
</th:block>
13+
14+
<!-- REACT JAVASCRIPT -->
15+
<th:block th:fragment="react-javascript">
16+
<script language="javascript" th:src="@{/webjars/react/18.1.0/umd/react.production.min.js}"></script>
17+
<script language="javascript" th:src="@{/webjars/react-dom/18.1.0/umd/react-dom.production.min.js}"></script>
18+
<script language="javascript" th:src="@{/webjars/babel-standalone/6.26.0/babel.min.js}"></script>
19+
<script language="javascript" th:src="@{/webjars/axios/0.27.2/dist/axios.min.js}"></script>
20+
</th:block>

src/main/resources/templates/app/fragments/layout.html

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
<!-- head -->
44
<head>
5-
6-
<title th:replace="${title}">
7-
Common API
8-
</title>
5+
<th:block th:replace="${title}">
6+
Spring Boot example
7+
</th:block>
98

109
<meta charset="UTF-8">
1110
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -33,16 +32,16 @@
3332
</th:block>
3433

3534
<!-- navbar default -->
36-
<nav
35+
<th:block
3736
th:replace="~{app/fragments/navbar :: navbar}">
38-
</nav>
37+
</th:block>
3938

4039
<!-- content container -->
4140
<th:block th:replace="${content}"></th:block>
4241

43-
<footer
42+
<th:block
4443
th:replace="~{app/fragments/footer :: footer}">
45-
</footer>
44+
</th:block>
4645

4746
<!-- modals section -->
4847
<th:block
@@ -54,9 +53,9 @@
5453
</th:block>
5554

5655
<!-- default scripts -->
57-
<script
56+
<th:block
5857
th:replace="~{app/fragments/imports :: default-javascript}">
59-
</script>
58+
</th:block>
6059

6160
<!-- custom scripts -->
6261
<th:block
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<layout th:replace="~{app/fragments/layout :: layout(~{:: title}, ~{:: link}, ~{:: section}, ~{:: #scripts}, ~{})}">
2+
<title>Users</title>
3+
<link rel="stylesheet" type="text/css" th:href="@{/css/users.css}">
4+
<section class="container mt-4">
5+
<div class="row">
6+
<div class="col-md-12">
7+
<h3 class="font-weight-light">
8+
<i class="fas fa-user"></i>
9+
<span th:text="${user.id == null ? 'Create' : 'Edit'}"></span>
10+
User
11+
</h3>
12+
<hr>
13+
</div>
14+
</div>
15+
<form
16+
id="app"
17+
novalidate
18+
method="POST"
19+
th:action="@{/app/users}"
20+
th:data-user="${user}"
21+
class="row d-flex justify-content-center needs-validation">
22+
</form>
23+
</section>
24+
<th:block id="scripts">
25+
<th:block th:replace="~{app/fragments/imports :: react-javascript}"></th:block>
26+
<script language="javascript" th:src="@{/js/forms.js}"></script>
27+
<script language="javascript" type="text/babel" th:src="@{/js/forms/users-form.js}"></script>
28+
</th:block>
29+
</layout>

0 commit comments

Comments
 (0)