QPAT commited on
Commit
53b191c
·
1 Parent(s): 4d16d75

Inject backend to frontend

Browse files
Files changed (40) hide show
  1. pom.xml +6 -2
  2. src/main/java/com/attendenceSystem/constant/Routes.java +6 -1
  3. src/main/java/com/attendenceSystem/constant/Views.java +8 -3
  4. src/main/java/com/attendenceSystem/module/attendance/controller/AttendanceController.java +8 -8
  5. src/main/java/com/attendenceSystem/module/attendance/repository/AttendanceRecordRepository.java +3 -0
  6. src/main/java/com/attendenceSystem/module/attendance/service/AttendanceService.java +2 -0
  7. src/main/java/com/attendenceSystem/module/attendance/service/impl/AttendanceServiceImpl.java +6 -0
  8. src/main/java/com/attendenceSystem/module/dashboard/api/DashboardApiController.java +4 -4
  9. src/main/java/com/attendenceSystem/module/dashboard/controller/DashboardController.java +12 -2
  10. src/main/java/com/attendenceSystem/module/dashboard/dto/response/{EmployeeDashboardResponse.java → StudentDashboardResponse.java} +2 -1
  11. src/main/java/com/attendenceSystem/module/dashboard/mapper/response/DashboardResponseMapper.java +29 -0
  12. src/main/java/com/attendenceSystem/module/dashboard/service/DashboardService.java +2 -2
  13. src/main/java/com/attendenceSystem/module/dashboard/service/impl/DashboardServiceImpl.java +15 -5
  14. src/main/java/com/attendenceSystem/module/dashboard/util/DashboardCalculator.java +17 -0
  15. src/main/java/com/attendenceSystem/module/faceid/controller/FaceProfileController.java +6 -5
  16. src/main/java/com/attendenceSystem/module/schedule/controller/ScheduleController.java +23 -0
  17. src/main/java/com/attendenceSystem/module/user/controller/AuthController.java +25 -8
  18. src/main/java/com/attendenceSystem/module/user/controller/UserController.java +3 -3
  19. src/main/java/com/attendenceSystem/module/user/entity/User.java +5 -1
  20. src/main/java/com/attendenceSystem/module/user/entity/UserProfile.java +5 -0
  21. src/main/java/com/attendenceSystem/module/user/entity/converter/DepartmentConverter.java +21 -0
  22. src/main/java/com/attendenceSystem/module/user/entity/enums/Department.java +21 -0
  23. src/main/java/com/attendenceSystem/module/user/entity/enums/Role.java +1 -1
  24. src/main/java/com/attendenceSystem/module/user/mapper/request/RegisterRequestMapper.java +1 -1
  25. src/main/java/com/attendenceSystem/module/user/service/UserService.java +1 -1
  26. src/main/java/com/attendenceSystem/module/user/service/impl/AuthServiceImpl.java +2 -0
  27. src/main/java/com/attendenceSystem/module/user/service/impl/UserServiceImpl.java +2 -3
  28. src/main/java/com/attendenceSystem/security/CustomUserDetails.java +44 -0
  29. src/main/java/com/attendenceSystem/security/CustomUserDetailsService.java +9 -15
  30. src/main/java/com/attendenceSystem/security/SecurityConfig.java +10 -0
  31. src/main/resources/static/js/api/auth/login.js +6 -0
  32. src/main/resources/static/js/api/auth/register.js +5 -0
  33. src/main/resources/static/js/api/method.js +30 -0
  34. src/main/resources/templates/cms/absent/absent-create.html +4 -29
  35. src/main/resources/templates/cms/absent/absent-list.html +15 -10
  36. src/main/resources/templates/cms/account/account-list.html +8 -5
  37. src/main/resources/templates/cms/dashboard/dashboard-employee.html +3 -3
  38. src/main/resources/templates/cms/sidebar/sidebar-admin.html +12 -8
  39. src/main/resources/templates/cms/sidebar/sidebar-employee.html +13 -9
  40. src/main/resources/templates/cms/sidebar/sidebar-manage.html +13 -9
pom.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0" encoding="UTF-8"?>
2
  <project xmlns="http://maven.apache.org/POM/4.0.0"
3
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
  <modelVersion>4.0.0</modelVersion>
6
  <parent>
7
  <groupId>org.springframework.boot</groupId>
@@ -35,6 +35,10 @@
35
  <groupId>org.springframework.boot</groupId>
36
  <artifactId>spring-boot-starter-data-jpa</artifactId>
37
  </dependency> -->
 
 
 
 
38
  <dependency>
39
  <groupId>org.mariadb.jdbc</groupId>
40
  <artifactId>mariadb-java-client</artifactId>
 
1
  <?xml version="1.0" encoding="UTF-8"?>
2
  <project xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
  <modelVersion>4.0.0</modelVersion>
6
  <parent>
7
  <groupId>org.springframework.boot</groupId>
 
35
  <groupId>org.springframework.boot</groupId>
36
  <artifactId>spring-boot-starter-data-jpa</artifactId>
37
  </dependency> -->
38
+ <dependency>
39
+ <groupId>org.thymeleaf.extras</groupId>
40
+ <artifactId>thymeleaf-extras-springsecurity6</artifactId>
41
+ </dependency>
42
  <dependency>
43
  <groupId>org.mariadb.jdbc</groupId>
44
  <artifactId>mariadb-java-client</artifactId>
src/main/java/com/attendenceSystem/constant/Routes.java CHANGED
@@ -15,6 +15,7 @@ public final class Routes {
15
  public static final String FORGOT_PASSWORD = "/forgot-password";
16
  public static final String VERIFY_OTP = "/verify-otp";
17
  public static final String CHANGE_PASSWORD = "/change-password";
 
18
  }
19
 
20
  public static final class User {
@@ -26,7 +27,7 @@ public final class Routes {
26
  public static final String ROOT = "/dashboard";
27
  public static final String ADMIN = "/admin";
28
  public static final String MANAGER = "/manager";
29
- public static final String EMPLOYEE = "/employee";
30
  }
31
  public static final class Attendance {
32
  public static final String ROOT = "/attendance";
@@ -40,4 +41,8 @@ public final class Routes {
40
  public static final class FaceId {
41
  public static final String ROOT = "/face-id";
42
  }
 
 
 
 
43
  }
 
15
  public static final String FORGOT_PASSWORD = "/forgot-password";
16
  public static final String VERIFY_OTP = "/verify-otp";
17
  public static final String CHANGE_PASSWORD = "/change-password";
18
+ public static final String LOGOUT = "/logout";
19
  }
20
 
21
  public static final class User {
 
27
  public static final String ROOT = "/dashboard";
28
  public static final String ADMIN = "/admin";
29
  public static final String MANAGER = "/manager";
30
+ public static final String STUDENT = "/student";
31
  }
32
  public static final class Attendance {
33
  public static final String ROOT = "/attendance";
 
41
  public static final class FaceId {
42
  public static final String ROOT = "/face-id";
43
  }
44
+
45
+ public static final class Schedule {
46
+ public static final String ROOT = "/schedule";
47
+ }
48
  }
src/main/java/com/attendenceSystem/constant/Views.java CHANGED
@@ -21,15 +21,20 @@ public final class Views {
21
 
22
  public static final class Dashboard {
23
  public static final String ADMIN = "cms/dashboard/dashboard-admin";
24
- public static final String MANAGER = "cms/dashboard/dashboard-manage";
25
  public static final String EMPLOYEE = "cms/dashboard/dashboard-employee";
26
  }
27
 
28
  public static final class Attendance {
29
- public static final String ROOT = "cms/attendance/attendance";
30
- public static final String LIST = "cms/attendance/attendance-list";
31
  public static final String LEAVE_CREATE = "cms/absent/absent-create";
32
  public static final String LEAVE_LIST = "cms/absent/absent-list";
33
  public static final String LEAVE_HISTORY = "cms/absent/absent-history";
34
  }
 
 
 
 
 
35
  }
 
21
 
22
  public static final class Dashboard {
23
  public static final String ADMIN = "cms/dashboard/dashboard-admin";
24
+ public static final String MANAGER = "cms/dashboard/dashboard-manager";
25
  public static final String EMPLOYEE = "cms/dashboard/dashboard-employee";
26
  }
27
 
28
  public static final class Attendance {
29
+ public static final String LIST = "cms/attendance/attendance";
30
+ public static final String HISTORY = "cms/attendance/attendance-history";
31
  public static final String LEAVE_CREATE = "cms/absent/absent-create";
32
  public static final String LEAVE_LIST = "cms/absent/absent-list";
33
  public static final String LEAVE_HISTORY = "cms/absent/absent-history";
34
  }
35
+
36
+ public static final class FaceId {
37
+ public static final String CREATE = "cms/face-id/faceID-create";
38
+ public static final String LIST = "cms/face-id/faceID-list";
39
+ }
40
  }
src/main/java/com/attendenceSystem/module/attendance/controller/AttendanceController.java CHANGED
@@ -27,9 +27,9 @@ public class AttendanceController {
27
  private final AttendanceService attendanceService;
28
 
29
  @GetMapping
30
- public String toAttendancePage(@PageableDefault(size = 10) Pageable pageable, Model model) {
31
  model.addAttribute("attendanceHistory", attendanceService.getAttendanceHistory(pageable));
32
- return Views.Attendance.ROOT;
33
  }
34
 
35
  @PostMapping(Routes.Attendance.CHECK_IN)
@@ -61,33 +61,33 @@ public class AttendanceController {
61
  @GetMapping(Routes.Attendance.HISTORY)
62
  public String attendanceHistory(@PageableDefault(size = 10) Pageable pageable, Model model) {
63
  model.addAttribute("attendanceHistory", attendanceService.getAttendanceHistory(pageable));
64
- return Views.Attendance.LIST;
65
  }
66
 
67
  @GetMapping(Routes.Attendance.LEAVE)
68
  public String leaveRequestList(@PageableDefault(size = 10) Pageable pageable, Model model) {
69
- model.addAttribute("leaveRequests", attendanceService.getLeaveRequests(pageable));
70
  return Views.Attendance.LEAVE_LIST;
71
  }
72
 
73
  @GetMapping(Routes.Attendance.LEAVE_CREATE)
74
  public String toLeaveRequestPage(Model model) {
75
- model.addAttribute("leaveRequest", new CreateLeaveRequest());
76
  return Views.Attendance.LEAVE_CREATE;
77
  }
78
 
79
  @PostMapping(Routes.Attendance.LEAVE_CREATE)
80
  public String createLeaveRequest(
81
- @Valid @ModelAttribute CreateLeaveRequest leaveRequest,
82
  BindingResult result,
83
  RedirectAttributes redirectAttributes,
84
  Model model) {
85
  if (result.hasErrors()) {
86
- model.addAttribute("leaveRequest", leaveRequest);
87
  return Views.Attendance.LEAVE_CREATE;
88
  }
89
  try {
90
- attendanceService.createLeaveRequest(leaveRequest);
91
  redirectAttributes.addFlashAttribute("successMessage", "Yêu cầu nghỉ phép đã được gửi.");
92
  } catch (IllegalArgumentException e) {
93
  redirectAttributes.addFlashAttribute("errorMessage", e.getMessage());
 
27
  private final AttendanceService attendanceService;
28
 
29
  @GetMapping
30
+ public String toAttendanceListPage(@PageableDefault(size = 10) Pageable pageable, Model model) {
31
  model.addAttribute("attendanceHistory", attendanceService.getAttendanceHistory(pageable));
32
+ return Views.Attendance.LIST;
33
  }
34
 
35
  @PostMapping(Routes.Attendance.CHECK_IN)
 
61
  @GetMapping(Routes.Attendance.HISTORY)
62
  public String attendanceHistory(@PageableDefault(size = 10) Pageable pageable, Model model) {
63
  model.addAttribute("attendanceHistory", attendanceService.getAttendanceHistory(pageable));
64
+ return Views.Attendance.HISTORY;
65
  }
66
 
67
  @GetMapping(Routes.Attendance.LEAVE)
68
  public String leaveRequestList(@PageableDefault(size = 10) Pageable pageable, Model model) {
69
+ model.addAttribute("leaveRequests", attendanceService.getAllLeaveRequests(pageable));
70
  return Views.Attendance.LEAVE_LIST;
71
  }
72
 
73
  @GetMapping(Routes.Attendance.LEAVE_CREATE)
74
  public String toLeaveRequestPage(Model model) {
75
+ model.addAttribute("createLeaveRequest", new CreateLeaveRequest());
76
  return Views.Attendance.LEAVE_CREATE;
77
  }
78
 
79
  @PostMapping(Routes.Attendance.LEAVE_CREATE)
80
  public String createLeaveRequest(
81
+ @Valid @ModelAttribute CreateLeaveRequest createLeaveRequest,
82
  BindingResult result,
83
  RedirectAttributes redirectAttributes,
84
  Model model) {
85
  if (result.hasErrors()) {
86
+ model.addAttribute("createLeaveRequest", createLeaveRequest);
87
  return Views.Attendance.LEAVE_CREATE;
88
  }
89
  try {
90
+ attendanceService.createLeaveRequest(createLeaveRequest);
91
  redirectAttributes.addFlashAttribute("successMessage", "Yêu cầu nghỉ phép đã được gửi.");
92
  } catch (IllegalArgumentException e) {
93
  redirectAttributes.addFlashAttribute("errorMessage", e.getMessage());
src/main/java/com/attendenceSystem/module/attendance/repository/AttendanceRecordRepository.java CHANGED
@@ -26,4 +26,7 @@ public interface AttendanceRecordRepository extends JpaRepository<AttendanceReco
26
  long countByAttendanceDate(LocalDate attendanceDate);
27
 
28
  Page<AttendanceRecord> findAllByOrderByAttendanceDateDesc(Pageable pageable);
 
 
 
29
  }
 
26
  long countByAttendanceDate(LocalDate attendanceDate);
27
 
28
  Page<AttendanceRecord> findAllByOrderByAttendanceDateDesc(Pageable pageable);
29
+
30
+ long countByCheckInTimeNotNullAndCheckOutTimeNotNull();
31
+
32
  }
src/main/java/com/attendenceSystem/module/attendance/service/AttendanceService.java CHANGED
@@ -18,4 +18,6 @@ public interface AttendanceService {
18
  LeaveRequestResponse createLeaveRequest(CreateLeaveRequest request);
19
 
20
  Page<LeaveRequestResponse> getLeaveRequests(Pageable pageable);
 
 
21
  }
 
18
  LeaveRequestResponse createLeaveRequest(CreateLeaveRequest request);
19
 
20
  Page<LeaveRequestResponse> getLeaveRequests(Pageable pageable);
21
+
22
+ Page<LeaveRequestResponse> getAllLeaveRequests(Pageable pageable);
23
  }
src/main/java/com/attendenceSystem/module/attendance/service/impl/AttendanceServiceImpl.java CHANGED
@@ -112,6 +112,11 @@ public class AttendanceServiceImpl implements AttendanceService {
112
  .map(leaveRequestResponseMapper::fromEntity);
113
  }
114
 
 
 
 
 
 
115
  private User getCurrentUser() {
116
  String currentUser = SecurityUtil.getCurrentUserName();
117
  if (currentUser == null || currentUser.isEmpty() || currentUser.isBlank()) {
@@ -121,4 +126,5 @@ public class AttendanceServiceImpl implements AttendanceService {
121
  .findByUsername(currentUser)
122
  .orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
123
  }
 
124
  }
 
112
  .map(leaveRequestResponseMapper::fromEntity);
113
  }
114
 
115
+ @Override
116
+ public Page<LeaveRequestResponse> getAllLeaveRequests(Pageable pageable) {
117
+ return leaveRequestRepository.findAll(pageable).map(leaveRequestResponseMapper::fromEntity);
118
+ }
119
+
120
  private User getCurrentUser() {
121
  String currentUser = SecurityUtil.getCurrentUserName();
122
  if (currentUser == null || currentUser.isEmpty() || currentUser.isBlank()) {
 
126
  .findByUsername(currentUser)
127
  .orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
128
  }
129
+
130
  }
src/main/java/com/attendenceSystem/module/dashboard/api/DashboardApiController.java CHANGED
@@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RestController;
8
 
9
  import com.attendenceSystem.constant.Routes;
10
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
11
- import com.attendenceSystem.module.dashboard.dto.response.EmployeeDashboardResponse;
12
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
13
  import com.attendenceSystem.module.dashboard.service.DashboardService;
14
 
@@ -34,9 +34,9 @@ public class DashboardApiController {
34
  return ResponseEntity.ok(resp);
35
  }
36
 
37
- @GetMapping(Routes.Dashboard.EMPLOYEE)
38
- public ResponseEntity<EmployeeDashboardResponse> employee(Pageable pageable) {
39
- EmployeeDashboardResponse resp = dashboardService.getEmployeeDashboard();
40
  return ResponseEntity.ok(resp);
41
  }
42
  }
 
8
 
9
  import com.attendenceSystem.constant.Routes;
10
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
11
+ import com.attendenceSystem.module.dashboard.dto.response.StudentDashboardResponse;
12
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
13
  import com.attendenceSystem.module.dashboard.service.DashboardService;
14
 
 
34
  return ResponseEntity.ok(resp);
35
  }
36
 
37
+ @GetMapping(Routes.Dashboard.STUDENT)
38
+ public ResponseEntity<StudentDashboardResponse> employee(Pageable pageable) {
39
+ StudentDashboardResponse resp = dashboardService.getStudentDashboard();
40
  return ResponseEntity.ok(resp);
41
  }
42
  }
src/main/java/com/attendenceSystem/module/dashboard/controller/DashboardController.java CHANGED
@@ -1,5 +1,7 @@
1
  package com.attendenceSystem.module.dashboard.controller;
2
 
 
 
3
  import org.springframework.stereotype.Controller;
4
  import org.springframework.ui.Model;
5
  import org.springframework.web.bind.annotation.GetMapping;
@@ -29,9 +31,17 @@ public class DashboardController {
29
  return Views.Dashboard.MANAGER;
30
  }
31
 
32
- @GetMapping(Routes.Dashboard.EMPLOYEE)
33
  public String toEmployeeDashboardPage(Model model) {
34
- model.addAttribute("dashboard", dashboardService.getEmployeeDashboard());
 
 
 
 
 
 
 
 
35
  return Views.Dashboard.EMPLOYEE;
36
  }
37
  }
 
1
  package com.attendenceSystem.module.dashboard.controller;
2
 
3
+ import org.springframework.security.core.Authentication;
4
+ import org.springframework.security.core.context.SecurityContextHolder;
5
  import org.springframework.stereotype.Controller;
6
  import org.springframework.ui.Model;
7
  import org.springframework.web.bind.annotation.GetMapping;
 
31
  return Views.Dashboard.MANAGER;
32
  }
33
 
34
+ @GetMapping(Routes.Dashboard.STUDENT)
35
  public String toEmployeeDashboardPage(Model model) {
36
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
37
+ System.out.println("=== DASHBOARD AUTH ===");
38
+ System.out.println(auth);
39
+ if (auth != null) {
40
+ System.out.println("name = " + auth.getName());
41
+ System.out.println("authorities = " + auth.getAuthorities());
42
+ System.out.println("authenticated = " + auth.isAuthenticated());
43
+ }
44
+ model.addAttribute("dashboard", dashboardService.getStudentDashboard());
45
  return Views.Dashboard.EMPLOYEE;
46
  }
47
  }
src/main/java/com/attendenceSystem/module/dashboard/dto/response/{EmployeeDashboardResponse.java → StudentDashboardResponse.java} RENAMED
@@ -4,11 +4,12 @@ import org.springframework.data.domain.Page;
4
 
5
  import com.attendenceSystem.module.attendance.dto.response.AttendanceResponse;
6
 
7
- public record EmployeeDashboardResponse(
8
 
9
  Long totalReports,
10
  Long acceptedReports,
11
  Long rejectedReports,
 
12
  Page<AttendanceResponse> attendanceHistory
13
 
14
  ) {
 
4
 
5
  import com.attendenceSystem.module.attendance.dto.response.AttendanceResponse;
6
 
7
+ public record StudentDashboardResponse(
8
 
9
  Long totalReports,
10
  Long acceptedReports,
11
  Long rejectedReports,
12
+ String attendanceRate,
13
  Page<AttendanceResponse> attendanceHistory
14
 
15
  ) {
src/main/java/com/attendenceSystem/module/dashboard/mapper/response/DashboardResponseMapper.java ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.dashboard.mapper.response;
2
+
3
+ import org.springframework.data.domain.Page;
4
+ import org.springframework.stereotype.Component;
5
+
6
+ import com.attendenceSystem.module.attendance.dto.response.AttendanceResponse;
7
+ import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
8
+ import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
9
+ import com.attendenceSystem.module.dashboard.dto.response.StudentDashboardResponse;
10
+
11
+ @Component
12
+ public class DashboardResponseMapper {
13
+
14
+ public AdminDashboardResponse toAdminDashboardResponse(long totalAccounts, long activeAccounts,
15
+ long inactiveAccounts, long pendingAccounts) {
16
+ return new AdminDashboardResponse(totalAccounts, activeAccounts, inactiveAccounts, pendingAccounts);
17
+ }
18
+
19
+ public ManagerDashboardResponse toManagerDashboardResponse(long totalEmployees, long attendedEmployees,
20
+ long absentEmployees, Page<AttendanceResponse> attendanceHistory) {
21
+ return new ManagerDashboardResponse(totalEmployees, attendedEmployees, absentEmployees, attendanceHistory);
22
+ }
23
+
24
+ public StudentDashboardResponse toStudentDashboardResponse(long totalReports, long acceptedReports,
25
+ long rejectedReports, String attendanceRate, Page<AttendanceResponse> attendanceHistory) {
26
+ return new StudentDashboardResponse(totalReports, acceptedReports, rejectedReports, attendanceRate,
27
+ attendanceHistory);
28
+ }
29
+ }
src/main/java/com/attendenceSystem/module/dashboard/service/DashboardService.java CHANGED
@@ -1,7 +1,7 @@
1
  package com.attendenceSystem.module.dashboard.service;
2
 
3
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
4
- import com.attendenceSystem.module.dashboard.dto.response.EmployeeDashboardResponse;
5
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
6
 
7
  public interface DashboardService {
@@ -9,7 +9,7 @@ public interface DashboardService {
9
 
10
  ManagerDashboardResponse getManagerDashboard();
11
 
12
- EmployeeDashboardResponse getEmployeeDashboard();
13
 
14
  }
15
 
 
1
  package com.attendenceSystem.module.dashboard.service;
2
 
3
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
4
+ import com.attendenceSystem.module.dashboard.dto.response.StudentDashboardResponse;
5
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
6
 
7
  public interface DashboardService {
 
9
 
10
  ManagerDashboardResponse getManagerDashboard();
11
 
12
+ StudentDashboardResponse getStudentDashboard();
13
 
14
  }
15
 
src/main/java/com/attendenceSystem/module/dashboard/service/impl/DashboardServiceImpl.java CHANGED
@@ -9,10 +9,13 @@ import org.springframework.stereotype.Service;
9
  import com.attendenceSystem.module.attendance.dto.response.AttendanceResponse;
10
  import com.attendenceSystem.module.attendance.mapper.response.AttendanceResponseMapper;
11
  import com.attendenceSystem.module.attendance.repository.AttendanceRecordRepository;
 
12
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
13
- import com.attendenceSystem.module.dashboard.dto.response.EmployeeDashboardResponse;
14
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
 
15
  import com.attendenceSystem.module.dashboard.service.DashboardService;
 
16
  import com.attendenceSystem.module.report.entity.enums.ReportStatus;
17
  import com.attendenceSystem.module.report.repository.ReportRepository;
18
  import com.attendenceSystem.module.user.entity.User;
@@ -29,6 +32,7 @@ public class DashboardServiceImpl implements DashboardService {
29
  private final AttendanceRecordRepository attendanceRecordRepository;
30
  private final ReportRepository reportRepository;
31
  private final AttendanceResponseMapper attendanceResponseMapper;
 
32
 
33
  @Override
34
  public AdminDashboardResponse getAdminDashboard() {
@@ -36,7 +40,8 @@ public class DashboardServiceImpl implements DashboardService {
36
  long activeAccounts = userRepository.countByIsActiveTrue();
37
  long inactiveAccounts = userRepository.countByIsActiveFalse();
38
  long pendingAccounts = userRepository.countByMustChangePasswordTrue();
39
- return new AdminDashboardResponse(totalAccounts, activeAccounts, inactiveAccounts, pendingAccounts);
 
40
  }
41
 
42
  @Override
@@ -48,19 +53,24 @@ public class DashboardServiceImpl implements DashboardService {
48
  Page<AttendanceResponse> attendanceHistory = attendanceRecordRepository
49
  .findAllByOrderByAttendanceDateDesc(PageRequest.of(0, 10))
50
  .map(attendanceResponseMapper::fromEntity);
51
- return new ManagerDashboardResponse(totalEmployees, attendedEmployees, absentEmployees, attendanceHistory);
 
52
  }
53
 
54
  @Override
55
- public EmployeeDashboardResponse getEmployeeDashboard() {
56
  User user = getCurrentUser();
57
  long totalReports = reportRepository.countByEmployee(user);
58
  long acceptedReports = reportRepository.countByEmployeeAndStatus(user, ReportStatus.ACCEPTED);
59
  long rejectedReports = reportRepository.countByEmployeeAndStatus(user, ReportStatus.REJECTED);
 
 
 
60
  Page<AttendanceResponse> attendanceHistory = attendanceRecordRepository
61
  .findByUser(user, PageRequest.of(0, 10))
62
  .map(attendanceResponseMapper::fromEntity);
63
- return new EmployeeDashboardResponse(totalReports, acceptedReports, rejectedReports, attendanceHistory);
 
64
  }
65
 
66
  private User getCurrentUser() {
 
9
  import com.attendenceSystem.module.attendance.dto.response.AttendanceResponse;
10
  import com.attendenceSystem.module.attendance.mapper.response.AttendanceResponseMapper;
11
  import com.attendenceSystem.module.attendance.repository.AttendanceRecordRepository;
12
+ import com.attendenceSystem.module.attendance.util.AttendanceCalculator;
13
  import com.attendenceSystem.module.dashboard.dto.response.AdminDashboardResponse;
14
+ import com.attendenceSystem.module.dashboard.dto.response.StudentDashboardResponse;
15
  import com.attendenceSystem.module.dashboard.dto.response.ManagerDashboardResponse;
16
+ import com.attendenceSystem.module.dashboard.mapper.response.DashboardResponseMapper;
17
  import com.attendenceSystem.module.dashboard.service.DashboardService;
18
+ import com.attendenceSystem.module.dashboard.util.DashboardCalculator;
19
  import com.attendenceSystem.module.report.entity.enums.ReportStatus;
20
  import com.attendenceSystem.module.report.repository.ReportRepository;
21
  import com.attendenceSystem.module.user.entity.User;
 
32
  private final AttendanceRecordRepository attendanceRecordRepository;
33
  private final ReportRepository reportRepository;
34
  private final AttendanceResponseMapper attendanceResponseMapper;
35
+ private final DashboardResponseMapper dashboardResponseMapper;
36
 
37
  @Override
38
  public AdminDashboardResponse getAdminDashboard() {
 
40
  long activeAccounts = userRepository.countByIsActiveTrue();
41
  long inactiveAccounts = userRepository.countByIsActiveFalse();
42
  long pendingAccounts = userRepository.countByMustChangePasswordTrue();
43
+ return dashboardResponseMapper.toAdminDashboardResponse(totalAccounts, activeAccounts, inactiveAccounts,
44
+ pendingAccounts);
45
  }
46
 
47
  @Override
 
53
  Page<AttendanceResponse> attendanceHistory = attendanceRecordRepository
54
  .findAllByOrderByAttendanceDateDesc(PageRequest.of(0, 10))
55
  .map(attendanceResponseMapper::fromEntity);
56
+ return dashboardResponseMapper.toManagerDashboardResponse(totalEmployees, attendedEmployees, absentEmployees,
57
+ attendanceHistory);
58
  }
59
 
60
  @Override
61
+ public StudentDashboardResponse getStudentDashboard() {
62
  User user = getCurrentUser();
63
  long totalReports = reportRepository.countByEmployee(user);
64
  long acceptedReports = reportRepository.countByEmployeeAndStatus(user, ReportStatus.ACCEPTED);
65
  long rejectedReports = reportRepository.countByEmployeeAndStatus(user, ReportStatus.REJECTED);
66
+ long totalDays = attendanceRecordRepository.count();
67
+ long attendedDays = attendanceRecordRepository.countByCheckInTimeNotNullAndCheckOutTimeNotNull();
68
+ String attendenceRate = DashboardCalculator.showResultStr(attendedDays, totalDays);
69
  Page<AttendanceResponse> attendanceHistory = attendanceRecordRepository
70
  .findByUser(user, PageRequest.of(0, 10))
71
  .map(attendanceResponseMapper::fromEntity);
72
+ return dashboardResponseMapper.toStudentDashboardResponse(totalReports, acceptedReports, rejectedReports,
73
+ attendenceRate, attendanceHistory);
74
  }
75
 
76
  private User getCurrentUser() {
src/main/java/com/attendenceSystem/module/dashboard/util/DashboardCalculator.java ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.dashboard.util;
2
+
3
+ import lombok.experimental.UtilityClass;
4
+
5
+ @UtilityClass
6
+ public class DashboardCalculator {
7
+ public double calculatePercent(long onTime, long total) {
8
+ if (total <= 0) {
9
+ return 0;
10
+ }
11
+ return (double) onTime / total * 100;
12
+ }
13
+
14
+ public String showResultStr(long onTime, long total) {
15
+ return String.format("%.2f%%", calculatePercent(onTime, total));
16
+ }
17
+ }
src/main/java/com/attendenceSystem/module/faceid/controller/FaceProfileController.java CHANGED
@@ -11,6 +11,7 @@ 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.module.faceid.dto.request.CreateFaceProfileRequest;
15
  import com.attendenceSystem.module.faceid.service.FaceProfileService;
16
 
@@ -19,7 +20,7 @@ import lombok.RequiredArgsConstructor;
19
 
20
  @Controller
21
  @RequiredArgsConstructor
22
- @RequestMapping("/faceid")
23
  public class FaceProfileController {
24
 
25
  private final FaceProfileService faceProfileService;
@@ -29,22 +30,22 @@ public class FaceProfileController {
29
  @PageableDefault(size = 10) Pageable pageable,
30
  Model model) {
31
  model.addAttribute("faceProfiles", faceProfileService.searchFaceProfiles(query, pageable));
32
- return "cms/face-id/faceID-list";
33
  }
34
 
35
  @GetMapping("/create")
36
  public String createForm(Model model) {
37
  model.addAttribute("createFaceProfileRequest", new CreateFaceProfileRequest());
38
- return "cms/face-id/faceID-create";
39
  }
40
 
41
  @PostMapping("/create")
42
  public String create(@Valid @ModelAttribute CreateFaceProfileRequest request,
43
  BindingResult result, Model model) {
44
  if (result.hasErrors()) {
45
- return "cms/face-id/faceID-create";
46
  }
47
  faceProfileService.createFaceProfile(request);
48
- return Routes.REDIRECT + "/faceid";
49
  }
50
  }
 
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.faceid.dto.request.CreateFaceProfileRequest;
16
  import com.attendenceSystem.module.faceid.service.FaceProfileService;
17
 
 
20
 
21
  @Controller
22
  @RequiredArgsConstructor
23
+ @RequestMapping(Routes.FaceId.ROOT)
24
  public class FaceProfileController {
25
 
26
  private final FaceProfileService faceProfileService;
 
30
  @PageableDefault(size = 10) Pageable pageable,
31
  Model model) {
32
  model.addAttribute("faceProfiles", faceProfileService.searchFaceProfiles(query, pageable));
33
+ return Views.FaceId.LIST;
34
  }
35
 
36
  @GetMapping("/create")
37
  public String createForm(Model model) {
38
  model.addAttribute("createFaceProfileRequest", new CreateFaceProfileRequest());
39
+ return Views.FaceId.CREATE;
40
  }
41
 
42
  @PostMapping("/create")
43
  public String create(@Valid @ModelAttribute CreateFaceProfileRequest request,
44
  BindingResult result, Model model) {
45
  if (result.hasErrors()) {
46
+ return Views.FaceId.CREATE;
47
  }
48
  faceProfileService.createFaceProfile(request);
49
+ return Routes.REDIRECT + Routes.FaceId.ROOT;
50
  }
51
  }
src/main/java/com/attendenceSystem/module/schedule/controller/ScheduleController.java ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.schedule.controller;
2
+
3
+ import org.springframework.stereotype.Controller;
4
+ import org.springframework.ui.Model;
5
+ import org.springframework.web.bind.annotation.RequestMapping;
6
+
7
+ import com.attendenceSystem.constant.Routes;
8
+
9
+ import lombok.RequiredArgsConstructor;
10
+ import org.springframework.web.bind.annotation.GetMapping;
11
+ import org.springframework.web.bind.annotation.RequestParam;
12
+
13
+
14
+ @RequiredArgsConstructor
15
+ @Controller
16
+ @RequestMapping(Routes.Schedule.ROOT)
17
+ public class ScheduleController {
18
+ @GetMapping
19
+ public String toSchedulePage(Model model) {
20
+ return new String();
21
+ }
22
+
23
+ }
src/main/java/com/attendenceSystem/module/user/controller/AuthController.java CHANGED
@@ -1,7 +1,9 @@
1
  package com.attendenceSystem.module.user.controller;
2
 
 
3
  import org.springframework.security.core.AuthenticationException;
4
  import org.springframework.security.core.context.SecurityContextHolder;
 
5
  import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
6
  import org.springframework.stereotype.Controller;
7
  import org.springframework.ui.Model;
@@ -10,12 +12,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
10
 
11
  import com.attendenceSystem.constant.Routes;
12
  import com.attendenceSystem.constant.Views;
 
 
13
  import com.attendenceSystem.module.user.dto.request.LoginRequest;
14
  import com.attendenceSystem.module.user.dto.request.RegisterRequest;
15
  import com.attendenceSystem.module.user.dto.response.UserResponse;
16
  import com.attendenceSystem.module.user.service.AuthService;
17
 
18
  import jakarta.servlet.http.HttpServletRequest;
 
19
  import jakarta.validation.Valid;
20
  import lombok.RequiredArgsConstructor;
21
  import org.springframework.web.bind.annotation.GetMapping;
@@ -73,7 +78,7 @@ public class AuthController {
73
  return switch (user.role()) {
74
  case "ADMIN" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.ADMIN;
75
  case "MANAGER" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.MANAGER;
76
- case "STAFF" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.EMPLOYEE;
77
  default -> Routes.REDIRECT + Routes.Dashboard.ROOT;
78
  };
79
  } catch (AuthenticationException e) {
@@ -84,17 +89,17 @@ public class AuthController {
84
 
85
  @PostMapping(Routes.Auth.REGISTER)
86
  public String register(
87
- @Valid @ModelAttribute RegisterRequest request,
88
- BindingResult result,
89
- Model model,
90
- RedirectAttributes redirectAttributes) {
91
- if(result.hasErrors()){
92
  return Views.Auth.REGISTER;
93
  }
94
  try {
95
  authService.register(request);
96
  redirectAttributes.addFlashAttribute("successMessage", "Đăng kí thành công");
97
- return Routes.REDIRECT + Routes.Auth.ROOT+ Routes.Auth.LOGIN;
98
  } catch (IllegalArgumentException e) {
99
  request.setPassword("");
100
  request.setRePassword("");
@@ -102,5 +107,17 @@ public class AuthController {
102
  return Views.Auth.REGISTER;
103
  }
104
  }
105
-
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
 
1
  package com.attendenceSystem.module.user.controller;
2
 
3
+ import org.springframework.security.core.Authentication;
4
  import org.springframework.security.core.AuthenticationException;
5
  import org.springframework.security.core.context.SecurityContextHolder;
6
+ import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
7
  import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
8
  import org.springframework.stereotype.Controller;
9
  import org.springframework.ui.Model;
 
12
 
13
  import com.attendenceSystem.constant.Routes;
14
  import com.attendenceSystem.constant.Views;
15
+ import com.attendenceSystem.module.log.entity.enums.LogAction;
16
+ import com.attendenceSystem.module.log.entity.enums.LogEntityType;
17
  import com.attendenceSystem.module.user.dto.request.LoginRequest;
18
  import com.attendenceSystem.module.user.dto.request.RegisterRequest;
19
  import com.attendenceSystem.module.user.dto.response.UserResponse;
20
  import com.attendenceSystem.module.user.service.AuthService;
21
 
22
  import jakarta.servlet.http.HttpServletRequest;
23
+ import jakarta.servlet.http.HttpServletResponse;
24
  import jakarta.validation.Valid;
25
  import lombok.RequiredArgsConstructor;
26
  import org.springframework.web.bind.annotation.GetMapping;
 
78
  return switch (user.role()) {
79
  case "ADMIN" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.ADMIN;
80
  case "MANAGER" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.MANAGER;
81
+ case "STUDENT" -> Routes.REDIRECT + Routes.Dashboard.ROOT + Routes.Dashboard.STUDENT;
82
  default -> Routes.REDIRECT + Routes.Dashboard.ROOT;
83
  };
84
  } catch (AuthenticationException e) {
 
89
 
90
  @PostMapping(Routes.Auth.REGISTER)
91
  public String register(
92
+ @Valid @ModelAttribute RegisterRequest request,
93
+ BindingResult result,
94
+ Model model,
95
+ RedirectAttributes redirectAttributes) {
96
+ if (result.hasErrors()) {
97
  return Views.Auth.REGISTER;
98
  }
99
  try {
100
  authService.register(request);
101
  redirectAttributes.addFlashAttribute("successMessage", "Đăng kí thành công");
102
+ return Routes.REDIRECT + Routes.Auth.ROOT + Routes.Auth.LOGIN;
103
  } catch (IllegalArgumentException e) {
104
  request.setPassword("");
105
  request.setRePassword("");
 
107
  return Views.Auth.REGISTER;
108
  }
109
  }
110
+
111
+ @PostMapping(Routes.Auth.LOGOUT)
112
+ public String logout(
113
+ HttpServletRequest request,
114
+ HttpServletResponse response) {
115
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
116
+ if (authentication != null) {
117
+ new SecurityContextLogoutHandler()
118
+ .logout(request, response, authentication);
119
+ }
120
+
121
+ return Routes.REDIRECT+"/";
122
+ }
123
  }
src/main/java/com/attendenceSystem/module/user/controller/UserController.java CHANGED
@@ -22,14 +22,14 @@ public class UserController {
22
 
23
  @GetMapping
24
  public String toListPage(@PageableDefault(size = 10) Pageable pageable, Model model) {
25
- model.addAttribute("accounts", userService.getAccounts(pageable));
26
  return Views.User.LIST;
27
  }
28
 
29
  @GetMapping("/{id}")
30
  public String toDetailPage(@PathVariable("id") Long id, Model model) {
31
- UserResponse account = userService.getById(id);
32
- model.addAttribute("account", account);
33
  return Views.User.DETAIL;
34
  }
35
 
 
22
 
23
  @GetMapping
24
  public String toListPage(@PageableDefault(size = 10) Pageable pageable, Model model) {
25
+ model.addAttribute("users", userService.getUsers(pageable));
26
  return Views.User.LIST;
27
  }
28
 
29
  @GetMapping("/{id}")
30
  public String toDetailPage(@PathVariable("id") Long id, Model model) {
31
+ UserResponse user = userService.getById(id);
32
+ model.addAttribute("user", user);
33
  return Views.User.DETAIL;
34
  }
35
 
src/main/java/com/attendenceSystem/module/user/entity/User.java CHANGED
@@ -5,6 +5,7 @@ import java.time.Instant;
5
  import org.hibernate.annotations.CreationTimestamp;
6
  import org.hibernate.annotations.UpdateTimestamp;
7
 
 
8
  import com.attendenceSystem.module.user.entity.enums.Role;
9
 
10
  import jakarta.persistence.Column;
@@ -35,6 +36,8 @@ public class User {
35
  private String username;
36
  @Column(name = "email", unique = true, nullable = false, length = 255)
37
  private String email;
 
 
38
  @Column(name = "password", nullable = false, length = 255)
39
  private String password;
40
  @Column(name = "role", nullable = false)
@@ -44,13 +47,14 @@ public class User {
44
  private boolean isActive = true;
45
  @Column(name = "full_name", nullable = false, length = 255)
46
  private String fullName;
 
 
47
  @CreationTimestamp
48
  @Column(name = "created_at", nullable = false, updatable = false)
49
  private Instant createdAt;
50
  @UpdateTimestamp
51
  @Column(name = "updated_at", nullable = false)
52
  private Instant updatedAt;
53
-
54
  @Column(name = "must_change_password", nullable = false)
55
  @Builder.Default
56
  private boolean mustChangePassword = false;
 
5
  import org.hibernate.annotations.CreationTimestamp;
6
  import org.hibernate.annotations.UpdateTimestamp;
7
 
8
+ import com.attendenceSystem.module.user.entity.enums.Department;
9
  import com.attendenceSystem.module.user.entity.enums.Role;
10
 
11
  import jakarta.persistence.Column;
 
36
  private String username;
37
  @Column(name = "email", unique = true, nullable = false, length = 255)
38
  private String email;
39
+ @Column(name = "phone", unique = true)
40
+ private String phone;
41
  @Column(name = "password", nullable = false, length = 255)
42
  private String password;
43
  @Column(name = "role", nullable = false)
 
47
  private boolean isActive = true;
48
  @Column(name = "full_name", nullable = false, length = 255)
49
  private String fullName;
50
+ @Column(name = "department")
51
+ private Department department;
52
  @CreationTimestamp
53
  @Column(name = "created_at", nullable = false, updatable = false)
54
  private Instant createdAt;
55
  @UpdateTimestamp
56
  @Column(name = "updated_at", nullable = false)
57
  private Instant updatedAt;
 
58
  @Column(name = "must_change_password", nullable = false)
59
  @Builder.Default
60
  private boolean mustChangePassword = false;
src/main/java/com/attendenceSystem/module/user/entity/UserProfile.java ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.user.entity;
2
+
3
+ public class UserProfile {
4
+
5
+ }
src/main/java/com/attendenceSystem/module/user/entity/converter/DepartmentConverter.java ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.user.entity.converter;
2
+
3
+ import com.attendenceSystem.module.user.entity.enums.Department;
4
+
5
+ import jakarta.persistence.AttributeConverter;
6
+ import jakarta.persistence.Converter;
7
+
8
+ @Converter(autoApply = true)
9
+ public class DepartmentConverter implements AttributeConverter<Department, Integer> {
10
+
11
+ @Override
12
+ public Integer convertToDatabaseColumn(Department attribute) {
13
+ return attribute == null ? null : attribute.value;
14
+ }
15
+
16
+ @Override
17
+ public Department convertToEntityAttribute(Integer dbData) {
18
+ return dbData == null ? null : Department.fromValue(dbData);
19
+ }
20
+
21
+ }
src/main/java/com/attendenceSystem/module/user/entity/enums/Department.java ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.module.user.entity.enums;
2
+
3
+ import lombok.Getter;
4
+ import lombok.RequiredArgsConstructor;
5
+
6
+ @Getter
7
+ @RequiredArgsConstructor
8
+ public enum Department {
9
+ SE(0);
10
+
11
+ public final int value;
12
+
13
+ public static Department fromValue(int value) {
14
+ for (Department d : Department.values()) {
15
+ if (d.value == value) {
16
+ return d;
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+ }
src/main/java/com/attendenceSystem/module/user/entity/enums/Role.java CHANGED
@@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor;
6
  @Getter
7
  @RequiredArgsConstructor
8
  public enum Role {
9
- ADMIN(1), MANAGER(2), STAFF(3), ;
10
 
11
  public final int value;
12
  public static Role fromValue(int value){
 
6
  @Getter
7
  @RequiredArgsConstructor
8
  public enum Role {
9
+ ADMIN(1), MANAGER(2), STUDENT(3), ;
10
 
11
  public final int value;
12
  public static Role fromValue(int value){
src/main/java/com/attendenceSystem/module/user/mapper/request/RegisterRequestMapper.java CHANGED
@@ -15,7 +15,7 @@ public final class RegisterRequestMapper {
15
  .email(request.getEmail())
16
  .password(hashPassword)
17
  .fullName(request.getFullName())
18
- .role(Role.STAFF)
19
  .mustChangePassword(false)
20
  .build();
21
  }
 
15
  .email(request.getEmail())
16
  .password(hashPassword)
17
  .fullName(request.getFullName())
18
+ .role(Role.STUDENT)
19
  .mustChangePassword(false)
20
  .build();
21
  }
src/main/java/com/attendenceSystem/module/user/service/UserService.java CHANGED
@@ -6,7 +6,7 @@ import org.springframework.data.domain.Pageable;
6
  import com.attendenceSystem.module.user.dto.response.UserResponse;
7
 
8
  public interface UserService {
9
- Page<UserResponse> getAccounts(Pageable pageable);
10
  UserResponse getById(Long id);
11
  void deleteUser(Long id);
12
  }
 
6
  import com.attendenceSystem.module.user.dto.response.UserResponse;
7
 
8
  public interface UserService {
9
+ Page<UserResponse> getUsers(Pageable pageable);
10
  UserResponse getById(Long id);
11
  void deleteUser(Long id);
12
  }
src/main/java/com/attendenceSystem/module/user/service/impl/AuthServiceImpl.java CHANGED
@@ -29,6 +29,7 @@ public class AuthServiceImpl implements AuthService {
29
  private final UserRepository userRepository;
30
  private final AuthenticationManager authenticationManager;
31
  private final PasswordEncoder passwordEncoder;
 
32
 
33
  @Transactional
34
  @Override
@@ -44,6 +45,7 @@ public class AuthServiceImpl implements AuthService {
44
  userRepository.save(savedUser);
45
  }
46
 
 
47
  @Override
48
  public UserResponse login(LoginRequest request) {
49
 
 
29
  private final UserRepository userRepository;
30
  private final AuthenticationManager authenticationManager;
31
  private final PasswordEncoder passwordEncoder;
32
+
33
 
34
  @Transactional
35
  @Override
 
45
  userRepository.save(savedUser);
46
  }
47
 
48
+ @Transactional
49
  @Override
50
  public UserResponse login(LoginRequest request) {
51
 
src/main/java/com/attendenceSystem/module/user/service/impl/UserServiceImpl.java CHANGED
@@ -36,8 +36,7 @@ public class UserServiceImpl implements UserService {
36
  }
37
 
38
  @Override
39
- public Page<UserResponse> getAccounts(Pageable pageable) {
40
- // TODO Auto-generated method stub
41
- throw new UnsupportedOperationException("Unimplemented method 'getAccounts'");
42
  }
43
  }
 
36
  }
37
 
38
  @Override
39
+ public Page<UserResponse> getUsers(Pageable pageable) {
40
+ return userRepository.findAll(pageable).map(userResponseMapper::fromEntity);
 
41
  }
42
  }
src/main/java/com/attendenceSystem/security/CustomUserDetails.java ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.attendenceSystem.security;
2
+
3
+ import java.util.Collection;
4
+ import java.util.List;
5
+
6
+ import org.springframework.security.core.GrantedAuthority;
7
+ import org.springframework.security.core.authority.SimpleGrantedAuthority;
8
+ import org.springframework.security.core.userdetails.UserDetails;
9
+
10
+ import com.attendenceSystem.module.user.entity.User;
11
+ import com.attendenceSystem.module.user.entity.enums.Department;
12
+
13
+ import lombok.Getter;
14
+ import lombok.RequiredArgsConstructor;
15
+
16
+ @Getter
17
+ @RequiredArgsConstructor
18
+ public class CustomUserDetails implements UserDetails {
19
+ private final Long id;
20
+ private final String username;
21
+ private final String password;
22
+ private final String fullName;
23
+ private final String email;
24
+ private final String phone;
25
+ private final String department;
26
+ private final String role;
27
+
28
+ public static CustomUserDetails fromUser(User user) {
29
+ return new CustomUserDetails(
30
+ user.getId(),
31
+ user.getUsername(),
32
+ user.getPassword(),
33
+ user.getFullName(),
34
+ user.getEmail(),
35
+ user.getPhone(),
36
+ user.getDepartment().name(),
37
+ user.getRole().name());
38
+ }
39
+
40
+ @Override
41
+ public Collection<? extends GrantedAuthority> getAuthorities() {
42
+ return List.of(new SimpleGrantedAuthority("ROLE_"+role));
43
+ }
44
+ }
src/main/java/com/attendenceSystem/security/CustomUserDetailsService.java CHANGED
@@ -12,23 +12,17 @@ import lombok.RequiredArgsConstructor;
12
 
13
  @Service
14
  @RequiredArgsConstructor
15
- public class CustomUserDetailsService
16
- implements UserDetailsService {
17
 
18
- private final UserRepository userRepository;
19
 
20
- @Override
21
- public UserDetails loadUserByUsername(String username)
22
- throws UsernameNotFoundException {
23
 
24
- User user = userRepository
25
- .findUserByUsernameOrEmail(username, username)
26
- .orElseThrow(() -> new UsernameNotFoundException("Không tìm thấy User"));
27
 
28
- return org.springframework.security.core.userdetails.User
29
- .withUsername(user.getUsername())
30
- .password(user.getPassword())
31
- .roles(user.getRole().name())
32
- .build();
33
- }
34
  }
 
12
 
13
  @Service
14
  @RequiredArgsConstructor
15
+ public class CustomUserDetailsService implements UserDetailsService {
 
16
 
17
+ private final UserRepository userRepository;
18
 
19
+ @Override
20
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 
21
 
22
+ User user = userRepository
23
+ .findUserByUsernameOrEmail(username, username)
24
+ .orElseThrow(() -> new UsernameNotFoundException("Không tìm thấy User"));
25
 
26
+ return CustomUserDetails.fromUser(user);
27
+ }
 
 
 
 
28
  }
src/main/java/com/attendenceSystem/security/SecurityConfig.java CHANGED
@@ -13,6 +13,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13
  import org.springframework.security.crypto.password.PasswordEncoder;
14
  import org.springframework.security.web.SecurityFilterChain;
15
  import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
 
16
 
17
  // import com.attendenceSystem.constant.Routes;
18
  // import com.attendenceSystem.entity.enums.Role;
@@ -46,7 +48,10 @@ public class SecurityConfig {
46
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
47
  http
48
  .authorizeHttpRequests(auth -> auth
 
 
49
  .anyRequest().permitAll())
 
50
  .csrf(csrf -> csrf.disable());
51
  return http.build();
52
  }
@@ -55,6 +60,11 @@ public class SecurityConfig {
55
  public AuthenticationManager authenticationManager() throws Exception {
56
  return authenticationConfiguration.getAuthenticationManager();
57
  }
 
 
 
 
 
58
  // Chỉ dùng để test admin
59
  // @Bean
60
  // public UserDetailsService userDetailsService(
 
13
  import org.springframework.security.crypto.password.PasswordEncoder;
14
  import org.springframework.security.web.SecurityFilterChain;
15
  import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
16
+ import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
17
+ import org.springframework.security.web.context.SecurityContextRepository;
18
 
19
  // import com.attendenceSystem.constant.Routes;
20
  // import com.attendenceSystem.entity.enums.Role;
 
48
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
49
  http
50
  .authorizeHttpRequests(auth -> auth
51
+ .requestMatchers("/auth/**", "/login/**", "/css/**", "/js/**").permitAll()
52
+ .requestMatchers("/dashboard/**").authenticated()
53
  .anyRequest().permitAll())
54
+
55
  .csrf(csrf -> csrf.disable());
56
  return http.build();
57
  }
 
60
  public AuthenticationManager authenticationManager() throws Exception {
61
  return authenticationConfiguration.getAuthenticationManager();
62
  }
63
+
64
+ @Bean
65
+ public SecurityContextRepository securityContextRepository() {
66
+ return new HttpSessionSecurityContextRepository();
67
+ }
68
  // Chỉ dùng để test admin
69
  // @Bean
70
  // public UserDetailsService userDetailsService(
src/main/resources/static/js/api/auth/login.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import {post} from "../method.js"
2
+ export async function login(request) {
3
+ return post(
4
+ "api/auth/login", request
5
+ );
6
+ }
src/main/resources/static/js/api/auth/register.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import {post} from "../method.js"
2
+
3
+ export async function register(request) {
4
+ return post("api/auth/register",request);
5
+ }
src/main/resources/static/js/api/method.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function post(url, body) {
2
+ const response = await fetch(url, {
3
+ method: "POST",
4
+ headers: {
5
+ "Content-Type": "application/json",
6
+ },
7
+ body: JSON.stringify(body),
8
+ });
9
+ if (!response.ok) {
10
+ const err = await response.text();
11
+ throw new Error(err);
12
+ }
13
+ return await response.json();
14
+ }
15
+
16
+ async function get(url) {
17
+ const response = await fetch(
18
+ url, {
19
+ method: "GET",
20
+ headers: {
21
+ "Content-Type": "application/json"
22
+ }
23
+ }
24
+ );
25
+ if(!response.ok){
26
+ const err = await response.text();
27
+ throw new Error(err);
28
+ }
29
+ return response.json();
30
+ }
src/main/resources/templates/cms/absent/absent-create.html CHANGED
@@ -38,43 +38,18 @@
38
 
39
  <div class="card-body">
40
 
41
- <form class="absent-form" method="post" th:action="@{/absent/create}">
42
- <!-- Phòng ban -->
43
- <div class="form-group">
44
- <label>Phòng ban</label>
45
-
46
- <select name="departmentId" required>
47
- <option value="" disabled selected>Chọn phòng ban</option>
48
-
49
- <option th:each="d : ${departments}" th:value="${d.id}" th:text="${d.name}">
50
- </option>
51
-
52
- </select>
53
- </div>
54
- <!-- Loại nghỉ -->
55
- <div class="form-group">
56
- <label>Loại nghỉ phép</label>
57
-
58
- <select name="type" required>
59
- <option value="" disabled selected>Chọn loại nghỉ phép</option>
60
- <option value="PAID">Nghỉ có lương</option>
61
- <option value="UNPAID">Nghỉ không lương</option>
62
- <option value="SICK">Nghỉ bệnh</option>
63
- <option value="ANNUAL">Nghỉ phép năm</option>
64
- </select>
65
- </div>
66
-
67
  <!-- Date -->
68
  <div class="form-grid">
69
 
70
  <div class="form-group">
71
  <label>Từ ngày</label>
72
- <input type="date" name="startDate" required>
73
  </div>
74
 
75
  <div class="form-group">
76
  <label>Đến ngày</label>
77
- <input type="date" name="endDate" required>
78
  </div>
79
 
80
  </div>
@@ -82,7 +57,7 @@
82
  <!-- Lý do -->
83
  <div class="form-group">
84
  <label>Lý do</label>
85
- <textarea name="reason" rows="5" placeholder="Nhập lý do xin nghỉ..."
86
  required></textarea>
87
  </div>
88
 
 
38
 
39
  <div class="card-body">
40
 
41
+ <form class="absent-form" method="post" th:action="@{/attendance/leave/create}" th:object="${createLeaveRequest}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  <!-- Date -->
43
  <div class="form-grid">
44
 
45
  <div class="form-group">
46
  <label>Từ ngày</label>
47
+ <input type="date" th:field="*{startDate}" required>
48
  </div>
49
 
50
  <div class="form-group">
51
  <label>Đến ngày</label>
52
+ <input type="date" th:field="*{endDate}" required>
53
  </div>
54
 
55
  </div>
 
57
  <!-- Lý do -->
58
  <div class="form-group">
59
  <label>Lý do</label>
60
+ <textarea th:field="*{reason}" rows="5" placeholder="Nhập lý do xin nghỉ..."
61
  required></textarea>
62
  </div>
63
 
src/main/resources/templates/cms/absent/absent-list.html CHANGED
@@ -109,17 +109,22 @@
109
  </thead>
110
 
111
  <tbody>
112
-
113
- <tr>
114
- <td></td>
115
- <td></td>
116
- <td></td>
117
- <td></td>
118
- <td></td>
119
- <td></td>
120
-
 
 
 
 
 
121
  <td>
122
- <span class="status pending">Chờ duyệt</span>
123
  </td>
124
 
125
  <td>
 
109
  </thead>
110
 
111
  <tbody>
112
+ <!-- Không có dữ liệu -->
113
+ <tr th:if="${leaveRequests == null or leaveRequests.content.isEmpty()}">
114
+ <td colspan="7" class="text-center">
115
+ Chưa có đơn nghỉ phép nào.
116
+ </td>
117
+ </tr>
118
+ <tr th:each="leave : ${leaveRequests.content}">
119
+ <td th:text="${leave.fullName()}">Nhân viên</td>
120
+ <td>Phòng ban</td>
121
+ <td>CỘT NÀY SAI NGHIỆP VỤ. NÓ NÊN NẰM Ở DETAILS</td>
122
+ <td th:text="${leave.startDate()}">Từ ngày</td>
123
+ <td th:text="${leave.endDate()}">Đến ngày</td>
124
+ <td>Cột này nên nhét vào hành động. Phê duyệt và từ chối nên nhét vào details</td>
125
+
126
  <td>
127
+ <span class="status pending">Trạng thái</span>
128
  </td>
129
 
130
  <td>
src/main/resources/templates/cms/account/account-list.html CHANGED
@@ -45,16 +45,19 @@
45
  </thead>
46
 
47
  <tbody>
48
- <tr>
 
 
 
49
  <td>
50
  <div class="user">
51
  <div class="avatar">A</div>
52
- <strong>Nguyễn Văn A</strong>
53
  </div>
54
  </td>
55
- <td>nguyenvana@company.com</td>
56
- <td><span class="role">Nhân viên</span></td>
57
- <td><span class="status active">Hoạt động</span></td>
58
  <td>
59
  <div class="actions">
60
  <button class="icon-btn btn-role">Đổi quyền</button>
 
45
  </thead>
46
 
47
  <tbody>
48
+ <tr th:if="${users == null or users.content.isEmpty()}">
49
+ <td colspan="5">Chưa có tài khoản nào.</td>
50
+ </tr>
51
+ <tr th:each="users : ${users.content}">
52
  <td>
53
  <div class="user">
54
  <div class="avatar">A</div>
55
+ <strong th:text="${users.fullName()}">Nguyễn Văn A</strong>
56
  </div>
57
  </td>
58
+ <td th:text="${users.email()}">nguyenvana@company.com</td>
59
+ <td ><span th:text="${users.role()}" class="role">Nhân viên</span></td>
60
+ <td><span th:text="${users.isActive()} == true?'Hoạt động':'Ngừng hoạt động'" class="status active">Hoạt động</span></td>
61
  <td>
62
  <div class="actions">
63
  <button class="icon-btn btn-role">Đổi quyền</button>
src/main/resources/templates/cms/dashboard/dashboard-employee.html CHANGED
@@ -217,17 +217,17 @@
217
 
218
  <div class="profile-item">
219
  <label>Email</label>
220
- <span>nguyenvana@company.com</span>
221
  </div>
222
 
223
  <div class="profile-item">
224
  <label>Số điện thoại</label>
225
- <span>0123456789</span>
226
  </div>
227
 
228
  <div class="profile-item">
229
  <label>Phòng ban</label>
230
- <span>IT Department</span>
231
  </div>
232
 
233
  <div class="profile-item">
 
217
 
218
  <div class="profile-item">
219
  <label>Email</label>
220
+ <span th:text="${#authentication.principal.email}"></span>
221
  </div>
222
 
223
  <div class="profile-item">
224
  <label>Số điện thoại</label>
225
+ <span th:text="${#authentication.principal.phone}"></span>
226
  </div>
227
 
228
  <div class="profile-item">
229
  <label>Phòng ban</label>
230
+ <span th:text="${#authentication.principal.department}">IT Department</span>
231
  </div>
232
 
233
  <div class="profile-item">
src/main/resources/templates/cms/sidebar/sidebar-admin.html CHANGED
@@ -16,14 +16,14 @@
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
- <a href="/dashboardAdmin" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
- <a href="/accountList" class="sidebar-link">
27
  <span>👥</span>
28
  Quản lý tài khoản
29
  </a>
@@ -49,17 +49,21 @@
49
  <div class="sidebar-footer">
50
 
51
  <div class="footer-user">
52
- <div class="avatar">N</div>
53
 
54
  <div>
55
- <h3>Nguyễn Thị B</h3>
56
- <div class="header-description">Quản lý</div>
 
 
57
  </div>
58
  </div>
59
 
60
- <a href="/" class="logout">
61
- 🚪 Đăng xuất
62
- </a>
 
 
63
 
64
  </div>
65
  </div>
 
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
+ <a href="/dashboard/admin" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
+ <a href="/user" class="sidebar-link">
27
  <span>👥</span>
28
  Quản lý tài khoản
29
  </a>
 
49
  <div class="sidebar-footer">
50
 
51
  <div class="footer-user">
52
+ <div class="avatar"></div>
53
 
54
  <div>
55
+ <h3 th:text="${#authentication.principal.fullName}"></h3>
56
+ <div class="header-description" th:text="${#authentication.principal.role}">
57
+ Vai trò
58
+ </div>
59
  </div>
60
  </div>
61
 
62
+ <form th:action="@{/auth/logout}" method="post" class="logout-form">
63
+ <button type="submit" class="logout">
64
+ 🚪 Đăng xuất
65
+ </button>
66
+ </form>
67
 
68
  </div>
69
  </div>
src/main/resources/templates/cms/sidebar/sidebar-employee.html CHANGED
@@ -16,28 +16,28 @@
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
- <a href="/dashboardEmployee" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
- <a href="/attendanceHistory" class="sidebar-link">
27
  <span>🕒</span>
28
  Lịch sử điểm danh
29
  </a>
30
  </li>
31
 
32
  <li class="sidebar-option">
33
- <a href="/absentCreate" class="sidebar-link">
34
  <span>📅</span>
35
  Xin nghỉ phép
36
  </a>
37
  </li>
38
 
39
  <li class="sidebar-option">
40
- <a href="/faceIDCreate" class="sidebar-link">
41
  <span>👤</span>
42
  Đăng kí Face ID
43
  </a>
@@ -72,14 +72,18 @@
72
  <div class="avatar"></div>
73
 
74
  <div>
75
- <h3>Nguyễn Thị B</h3>
76
- <div class="header-description">Quản lý</div>
 
 
77
  </div>
78
  </div>
79
 
80
- <a href="/" class="logout">
81
- 🚪 Đăng xuất
82
- </a>
 
 
83
 
84
  </div>
85
  </div>
 
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
+ <a href="/dashboard/student" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
+ <a href="/attendance/history" class="sidebar-link">
27
  <span>🕒</span>
28
  Lịch sử điểm danh
29
  </a>
30
  </li>
31
 
32
  <li class="sidebar-option">
33
+ <a href="/attendance/leave/create" class="sidebar-link">
34
  <span>📅</span>
35
  Xin nghỉ phép
36
  </a>
37
  </li>
38
 
39
  <li class="sidebar-option">
40
+ <a href="/face-id/create" class="sidebar-link">
41
  <span>👤</span>
42
  Đăng kí Face ID
43
  </a>
 
72
  <div class="avatar"></div>
73
 
74
  <div>
75
+ <h3 th:text="${#authentication.principal.fullName}"></h3>
76
+ <div class="header-description" th:text="${#authentication.principal.role}">
77
+ Vai trò
78
+ </div>
79
  </div>
80
  </div>
81
 
82
+ <form th:action="@{/auth/logout}" method="post" class="logout-form">
83
+ <button type="submit" class="logout">
84
+ 🚪 Đăng xuất
85
+ </button>
86
+ </form>
87
 
88
  </div>
89
  </div>
src/main/resources/templates/cms/sidebar/sidebar-manage.html CHANGED
@@ -16,14 +16,14 @@
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
- <a href="/dashboardManager" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
- <a href="/attendances" class="sidebar-link">
27
  <span>🕒</span>
28
  Quản lý điểm danh
29
  </a>
@@ -37,7 +37,7 @@
37
  </li>
38
 
39
  <li class="sidebar-option">
40
- <a href="/absentList" class="sidebar-link">
41
  <span>📅</span>
42
  Duyệt nghỉ phép
43
  </a>
@@ -59,17 +59,21 @@
59
  <div class="sidebar-footer">
60
 
61
  <div class="footer-user">
62
- <div class="avatar">N</div>
63
 
64
  <div>
65
- <h3>Nguyễn Thị B</h3>
66
- <div class="header-description">Quản lý</div>
 
 
67
  </div>
68
  </div>
69
 
70
- <a href="/" class="logout">
71
- 🚪 Đăng xuất
72
- </a>
 
 
73
 
74
  </div>
75
 
 
16
  <ul class="sidebar-menu">
17
 
18
  <li class="sidebar-option">
19
+ <a href="/dashboard/manager" class="sidebar-link">
20
  <span>📊</span>
21
  Dashboard
22
  </a>
23
  </li>
24
 
25
  <li class="sidebar-option">
26
+ <a href="/attendance" class="sidebar-link">
27
  <span>🕒</span>
28
  Quản lý điểm danh
29
  </a>
 
37
  </li>
38
 
39
  <li class="sidebar-option">
40
+ <a href="/attendance/leave" class="sidebar-link">
41
  <span>📅</span>
42
  Duyệt nghỉ phép
43
  </a>
 
59
  <div class="sidebar-footer">
60
 
61
  <div class="footer-user">
62
+ <div class="avatar"></div>
63
 
64
  <div>
65
+ <h3 th:text="${#authentication.principal.fullName}"></h3>
66
+ <div class="header-description" th:text="${#authentication.principal.role}">
67
+ Vai trò
68
+ </div>
69
  </div>
70
  </div>
71
 
72
+ <form th:action="@{/auth/logout}" method="post" class="logout-form">
73
+ <button type="submit" class="logout">
74
+ 🚪 Đăng xuất
75
+ </button>
76
+ </form>
77
 
78
  </div>
79