QPAT commited on
Commit
1e37e16
·
1 Parent(s): f640a1c

add change role in admin function

Browse files
pom.xml CHANGED
@@ -35,11 +35,6 @@
35
  <groupId>org.springframework.boot</groupId>
36
  <artifactId>spring-boot-starter-data-jpa</artifactId>
37
  </dependency> -->
38
- <dependency>
39
- <groupId>me.paulschwarz</groupId>
40
- <artifactId>spring-dotenv</artifactId>
41
- <version>4.0.0</version>
42
- </dependency>
43
  <dependency>
44
  <groupId>com.fasterxml.jackson.datatype</groupId>
45
  <artifactId>jackson-datatype-jsr310</artifactId>
 
35
  <groupId>org.springframework.boot</groupId>
36
  <artifactId>spring-boot-starter-data-jpa</artifactId>
37
  </dependency> -->
 
 
 
 
 
38
  <dependency>
39
  <groupId>com.fasterxml.jackson.datatype</groupId>
40
  <artifactId>jackson-datatype-jsr310</artifactId>
src/main/java/com/attendenceSystem/constant/Routes.java CHANGED
@@ -16,6 +16,7 @@ public final class Routes {
16
  public static final String ACTIVATE = "/activate";
17
  public static final String ACCEPT = "/accept";
18
  public static final String REJECT = "/reject";
 
19
  }
20
 
21
  public static final class Role {
 
16
  public static final String ACTIVATE = "/activate";
17
  public static final String ACCEPT = "/accept";
18
  public static final String REJECT = "/reject";
19
+ public static final String CHANGE_ROLE = "/change-role";
20
  }
21
 
22
  public static final class Role {
src/main/java/com/attendenceSystem/module/user/controller/AdminAccountController.java CHANGED
@@ -9,10 +9,15 @@ import org.springframework.web.bind.annotation.GetMapping;
9
  import org.springframework.web.bind.annotation.PathVariable;
10
  import org.springframework.web.bind.annotation.PostMapping;
11
  import org.springframework.web.bind.annotation.RequestMapping;
 
12
 
13
  import com.attendenceSystem.constant.Routes;
14
  import com.attendenceSystem.constant.Views;
 
 
 
15
  import com.attendenceSystem.module.user.dto.response.UserResponse;
 
16
  import com.attendenceSystem.module.user.service.AccountService;
17
  import com.attendenceSystem.util.SecurityUtil;
18
 
@@ -30,6 +35,10 @@ public class AdminAccountController {
30
  model.addAttribute("users", accountService.getUsers(pageable));
31
  model.addAttribute("currentUsername", SecurityUtil.getCurrentUserName());
32
  model.addAttribute("currentRole", SecurityUtil.getCurrentUserRole());
 
 
 
 
33
  return Views.User.LIST;
34
  }
35
 
@@ -51,4 +60,10 @@ public class AdminAccountController {
51
  accountService.activateUser(id);
52
  return Routes.REDIRECT + Routes.Role.ADMIN + Routes.Account.ROOT;
53
  }
 
 
 
 
 
 
54
  }
 
9
  import org.springframework.web.bind.annotation.PathVariable;
10
  import org.springframework.web.bind.annotation.PostMapping;
11
  import org.springframework.web.bind.annotation.RequestMapping;
12
+ import org.springframework.web.bind.annotation.RequestParam;
13
 
14
  import com.attendenceSystem.constant.Routes;
15
  import com.attendenceSystem.constant.Views;
16
+ import java.util.Arrays;
17
+ import java.util.List;
18
+
19
  import com.attendenceSystem.module.user.dto.response.UserResponse;
20
+ import com.attendenceSystem.module.user.entity.enums.Role;
21
  import com.attendenceSystem.module.user.service.AccountService;
22
  import com.attendenceSystem.util.SecurityUtil;
23
 
 
35
  model.addAttribute("users", accountService.getUsers(pageable));
36
  model.addAttribute("currentUsername", SecurityUtil.getCurrentUserName());
37
  model.addAttribute("currentRole", SecurityUtil.getCurrentUserRole());
38
+ List<Role> availableRoles = Arrays.stream(Role.values())
39
+ .filter(role -> role != Role.ADMIN)
40
+ .toList();
41
+ model.addAttribute("availableRoles", availableRoles);
42
  return Views.User.LIST;
43
  }
44
 
 
60
  accountService.activateUser(id);
61
  return Routes.REDIRECT + Routes.Role.ADMIN + Routes.Account.ROOT;
62
  }
63
+
64
+ @PostMapping(Routes.Account.ROOT + Routes.Action.CHANGE_ROLE + "/{id}")
65
+ public String changeRole(@PathVariable("id") Long id, @RequestParam("newRole") Role newRole) {
66
+ accountService.changeRole(id, newRole);
67
+ return Routes.REDIRECT + Routes.Role.ADMIN + Routes.Account.ROOT;
68
+ }
69
  }
src/main/java/com/attendenceSystem/module/user/service/AccountService.java CHANGED
@@ -4,6 +4,7 @@ import org.springframework.data.domain.Page;
4
  import org.springframework.data.domain.Pageable;
5
 
6
  import com.attendenceSystem.module.user.dto.response.UserResponse;
 
7
 
8
  public interface AccountService {
9
  Page<UserResponse> getUsers(Pageable pageable);
@@ -12,4 +13,5 @@ public interface AccountService {
12
  void deactivateUser(Long id);
13
  void activateUser(Long id);
14
  void changeDepartment(Long id, String departmentCode);
 
15
  }
 
4
  import org.springframework.data.domain.Pageable;
5
 
6
  import com.attendenceSystem.module.user.dto.response.UserResponse;
7
+ import com.attendenceSystem.module.user.entity.enums.Role;
8
 
9
  public interface AccountService {
10
  Page<UserResponse> getUsers(Pageable pageable);
 
13
  void deactivateUser(Long id);
14
  void activateUser(Long id);
15
  void changeDepartment(Long id, String departmentCode);
16
+ void changeRole(Long id, Role newRole);
17
  }
src/main/java/com/attendenceSystem/module/user/service/impl/AccountServiceImpl.java CHANGED
@@ -96,6 +96,26 @@ public class AccountServiceImpl implements AccountService {
96
  userRepository.save(user);
97
  }
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  private User findById(final Long id) {
100
  return userRepository.findById(id)
101
  .orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng với id: " + id));
 
96
  userRepository.save(user);
97
  }
98
 
99
+ @Transactional
100
+ @Override
101
+ public void changeRole(final Long id, final Role newRole) {
102
+ User user = findById(id);
103
+ validateAdminAction(user);
104
+
105
+ // Không cho đổi thành ADMIN
106
+ if (newRole == Role.ADMIN) {
107
+ throw new IllegalStateException("Không thể đổi quyền thành ADMIN");
108
+ }
109
+
110
+ // Không cho đổi role của admin
111
+ if (user.getRole() == Role.ADMIN) {
112
+ throw new IllegalStateException("Không thể đổi quyền của admin");
113
+ }
114
+
115
+ user.setRole(newRole);
116
+ userRepository.save(user);
117
+ }
118
+
119
  private User findById(final Long id) {
120
  return userRepository.findById(id)
121
  .orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng với id: " + id));
src/main/resources/application.properties CHANGED
@@ -64,3 +64,5 @@ app.timezone=Asia/Ho_Chi_Minh
64
  # Face ID Configuration
65
  face-id.api-key=${FACE_ID_API_KEY:dev-secret-key-change-in-production}
66
  face-id.confidence-threshold=0.90
 
 
 
64
  # Face ID Configuration
65
  face-id.api-key=${FACE_ID_API_KEY:dev-secret-key-change-in-production}
66
  face-id.confidence-threshold=0.90
67
+
68
+ spring.config.import=optional:file:.env[.properties]
src/main/resources/static/css/account/account-list.css CHANGED
@@ -226,12 +226,53 @@ tbody tr:hover {
226
  }
227
 
228
  /* ===================================
229
- ACTION BUTTONS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  =================================== */
231
 
232
  .actions {
233
  display: flex;
234
  gap: 8px;
 
235
  }
236
 
237
  .icon-btn {
 
226
  }
227
 
228
  /* ===================================
229
+ ROLE CHANGE DROPDOWN
230
+ =================================== */
231
+
232
+ .role-change {
233
+ display: flex;
234
+ align-items: center;
235
+ }
236
+
237
+ .role-change-form {
238
+ display: flex;
239
+ gap: 6px;
240
+ align-items: center;
241
+ }
242
+
243
+ .role-select {
244
+ height: 38px;
245
+ padding: 0 10px;
246
+ border: 1px solid var(--border);
247
+ border-radius: 10px;
248
+ background: var(--bg-form);
249
+ color: var(--text-white);
250
+ font-size: .85rem;
251
+ font-weight: 500;
252
+ cursor: pointer;
253
+ outline: none;
254
+ transition: .3s;
255
+ min-width: 110px;
256
+ }
257
+
258
+ .role-select:focus {
259
+ border-color: var(--indigo-06);
260
+ box-shadow: 0 0 0 4px var(--shadow-box);
261
+ }
262
+
263
+ .role-select option {
264
+ background: var(--bg-card);
265
+ color: var(--text-white);
266
+ }
267
+
268
+ /* ===================================
269
+ ACTION BUTTONS
270
  =================================== */
271
 
272
  .actions {
273
  display: flex;
274
  gap: 8px;
275
+ flex-wrap: wrap;
276
  }
277
 
278
  .icon-btn {
src/main/resources/templates/cms/account/account-list.html CHANGED
@@ -65,7 +65,18 @@
65
  <div class="actions"
66
  th:with="canManage=${users.username() != currentUsername and users.role() != 'ADMIN'}">
67
 
68
- <button class="icon-btn btn-role" th:if="${canManage}">Đổi quyền</button>
 
 
 
 
 
 
 
 
 
 
 
69
  <div th:if="${canManage and users.status() == 'ACTIVE'}">
70
  <form th:action="@{/admin/accounts/deactivate/{id}(id=${users.id()})}" method="post">
71
  <button class="icon-btn lock">🔒</button>
@@ -115,6 +126,23 @@
115
  </div>
116
 
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  <div id="accountModal" class="detail-modal">
119
 
120
  <div class="detail-content">
@@ -169,6 +197,42 @@
169
 
170
  </div>
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  </body>
173
 
174
  </html>
 
65
  <div class="actions"
66
  th:with="canManage=${users.username() != currentUsername and users.role() != 'ADMIN'}">
67
 
68
+ <div class="role-change" th:if="${canManage}">
69
+ <form th:action="@{/admin/accounts/change-role/{id}(id=${users.id()})}" method="post" class="role-change-form">
70
+ <select name="newRole" class="role-select">
71
+ <option th:each="role : ${availableRoles}"
72
+ th:value="${role.name()}"
73
+ th:text="${role.name()}"
74
+ th:selected="${role.name() == users.role()}">
75
+ </option>
76
+ </select>
77
+ <button type="button" class="icon-btn btn-role" onclick="confirmRoleChange(this)">Đổi quyền</button>
78
+ </form>
79
+ </div>
80
  <div th:if="${canManage and users.status() == 'ACTIVE'}">
81
  <form th:action="@{/admin/accounts/deactivate/{id}(id=${users.id()})}" method="post">
82
  <button class="icon-btn lock">🔒</button>
 
126
  </div>
127
 
128
 
129
+ <!-- CONFIRMATION MODAL FOR ROLE CHANGE -->
130
+ <div id="roleConfirmModal" class="detail-modal">
131
+ <div class="detail-content" style="max-width: 480px;">
132
+ <div class="detail-header">
133
+ <h2>Xác nhận đổi quyền</h2>
134
+ <button class="close-btn" onclick="closeRoleConfirm()">✕</button>
135
+ </div>
136
+ <div class="detail-body" style="grid-template-columns: 1fr;">
137
+ <p id="roleConfirmMessage" style="color: #e2e8f0; font-size: 1rem; line-height: 1.6;"></p>
138
+ </div>
139
+ <div class="detail-header" style="border-top: 1px solid var(--border); border-bottom: none; justify-content: flex-end; gap: 12px;">
140
+ <button class="icon-btn" style="background: var(--bg-form);" onclick="closeRoleConfirm()">Hủy</button>
141
+ <button class="icon-btn btn-role" id="roleConfirmSubmit" style="min-width: 100px;">Xác nhận</button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
  <div id="accountModal" class="detail-modal">
147
 
148
  <div class="detail-content">
 
197
 
198
  </div>
199
 
200
+ <script>
201
+ let pendingForm = null;
202
+
203
+ function confirmRoleChange(button) {
204
+ const form = button.closest('.role-change-form');
205
+ const select = form.querySelector('.role-select');
206
+ const selectedRole = select.options[select.selectedIndex].text;
207
+ const row = form.closest('tr');
208
+ const userName = row.querySelector('strong').textContent;
209
+
210
+ pendingForm = form;
211
+
212
+ document.getElementById('roleConfirmMessage').textContent =
213
+ 'Bạn có chắc chắn muốn đổi quyền của "' + userName + '" thành ' + selectedRole + '?';
214
+
215
+ document.getElementById('roleConfirmModal').style.display = 'flex';
216
+ document.getElementById('roleConfirmSubmit').onclick = function() {
217
+ if (pendingForm) {
218
+ pendingForm.submit();
219
+ }
220
+ };
221
+ }
222
+
223
+ function closeRoleConfirm() {
224
+ document.getElementById('roleConfirmModal').style.display = 'none';
225
+ pendingForm = null;
226
+ }
227
+
228
+ // Close modal when clicking outside
229
+ document.getElementById('roleConfirmModal').addEventListener('click', function(e) {
230
+ if (e.target === this) {
231
+ closeRoleConfirm();
232
+ }
233
+ });
234
+ </script>
235
+
236
  </body>
237
 
238
  </html>