Spaces:
Configuration error
Configuration error
Inject backend to frontend
Browse files- pom.xml +6 -2
- src/main/java/com/attendenceSystem/constant/Routes.java +6 -1
- src/main/java/com/attendenceSystem/constant/Views.java +8 -3
- src/main/java/com/attendenceSystem/module/attendance/controller/AttendanceController.java +8 -8
- src/main/java/com/attendenceSystem/module/attendance/repository/AttendanceRecordRepository.java +3 -0
- src/main/java/com/attendenceSystem/module/attendance/service/AttendanceService.java +2 -0
- src/main/java/com/attendenceSystem/module/attendance/service/impl/AttendanceServiceImpl.java +6 -0
- src/main/java/com/attendenceSystem/module/dashboard/api/DashboardApiController.java +4 -4
- src/main/java/com/attendenceSystem/module/dashboard/controller/DashboardController.java +12 -2
- src/main/java/com/attendenceSystem/module/dashboard/dto/response/{EmployeeDashboardResponse.java → StudentDashboardResponse.java} +2 -1
- src/main/java/com/attendenceSystem/module/dashboard/mapper/response/DashboardResponseMapper.java +29 -0
- src/main/java/com/attendenceSystem/module/dashboard/service/DashboardService.java +2 -2
- src/main/java/com/attendenceSystem/module/dashboard/service/impl/DashboardServiceImpl.java +15 -5
- src/main/java/com/attendenceSystem/module/dashboard/util/DashboardCalculator.java +17 -0
- src/main/java/com/attendenceSystem/module/faceid/controller/FaceProfileController.java +6 -5
- src/main/java/com/attendenceSystem/module/schedule/controller/ScheduleController.java +23 -0
- src/main/java/com/attendenceSystem/module/user/controller/AuthController.java +25 -8
- src/main/java/com/attendenceSystem/module/user/controller/UserController.java +3 -3
- src/main/java/com/attendenceSystem/module/user/entity/User.java +5 -1
- src/main/java/com/attendenceSystem/module/user/entity/UserProfile.java +5 -0
- src/main/java/com/attendenceSystem/module/user/entity/converter/DepartmentConverter.java +21 -0
- src/main/java/com/attendenceSystem/module/user/entity/enums/Department.java +21 -0
- src/main/java/com/attendenceSystem/module/user/entity/enums/Role.java +1 -1
- src/main/java/com/attendenceSystem/module/user/mapper/request/RegisterRequestMapper.java +1 -1
- src/main/java/com/attendenceSystem/module/user/service/UserService.java +1 -1
- src/main/java/com/attendenceSystem/module/user/service/impl/AuthServiceImpl.java +2 -0
- src/main/java/com/attendenceSystem/module/user/service/impl/UserServiceImpl.java +2 -3
- src/main/java/com/attendenceSystem/security/CustomUserDetails.java +44 -0
- src/main/java/com/attendenceSystem/security/CustomUserDetailsService.java +9 -15
- src/main/java/com/attendenceSystem/security/SecurityConfig.java +10 -0
- src/main/resources/static/js/api/auth/login.js +6 -0
- src/main/resources/static/js/api/auth/register.js +5 -0
- src/main/resources/static/js/api/method.js +30 -0
- src/main/resources/templates/cms/absent/absent-create.html +4 -29
- src/main/resources/templates/cms/absent/absent-list.html +15 -10
- src/main/resources/templates/cms/account/account-list.html +8 -5
- src/main/resources/templates/cms/dashboard/dashboard-employee.html +3 -3
- src/main/resources/templates/cms/sidebar/sidebar-admin.html +12 -8
- src/main/resources/templates/cms/sidebar/sidebar-employee.html +13 -9
- 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 |
-
|
| 4 |
-
|
| 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
|
| 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-
|
| 25 |
public static final String EMPLOYEE = "cms/dashboard/dashboard-employee";
|
| 26 |
}
|
| 27 |
|
| 28 |
public static final class Attendance {
|
| 29 |
-
public static final String
|
| 30 |
-
public static final String
|
| 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
|
| 31 |
model.addAttribute("attendanceHistory", attendanceService.getAttendanceHistory(pageable));
|
| 32 |
-
return Views.Attendance.
|
| 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.
|
| 65 |
}
|
| 66 |
|
| 67 |
@GetMapping(Routes.Attendance.LEAVE)
|
| 68 |
public String leaveRequestList(@PageableDefault(size = 10) Pageable pageable, Model model) {
|
| 69 |
-
model.addAttribute("leaveRequests", attendanceService.
|
| 70 |
return Views.Attendance.LEAVE_LIST;
|
| 71 |
}
|
| 72 |
|
| 73 |
@GetMapping(Routes.Attendance.LEAVE_CREATE)
|
| 74 |
public String toLeaveRequestPage(Model model) {
|
| 75 |
-
model.addAttribute("
|
| 76 |
return Views.Attendance.LEAVE_CREATE;
|
| 77 |
}
|
| 78 |
|
| 79 |
@PostMapping(Routes.Attendance.LEAVE_CREATE)
|
| 80 |
public String createLeaveRequest(
|
| 81 |
-
@Valid @ModelAttribute CreateLeaveRequest
|
| 82 |
BindingResult result,
|
| 83 |
RedirectAttributes redirectAttributes,
|
| 84 |
Model model) {
|
| 85 |
if (result.hasErrors()) {
|
| 86 |
-
model.addAttribute("
|
| 87 |
return Views.Attendance.LEAVE_CREATE;
|
| 88 |
}
|
| 89 |
try {
|
| 90 |
-
attendanceService.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());
|
|
|
|
| 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.
|
| 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.
|
| 38 |
-
public ResponseEntity<
|
| 39 |
-
|
| 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.
|
| 33 |
public String toEmployeeDashboardPage(Model model) {
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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.
|
| 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 |
-
|
| 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.
|
| 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
|
|
|
|
| 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
|
|
|
|
| 52 |
}
|
| 53 |
|
| 54 |
@Override
|
| 55 |
-
public
|
| 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
|
|
|
|
| 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(
|
| 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
|
| 33 |
}
|
| 34 |
|
| 35 |
@GetMapping("/create")
|
| 36 |
public String createForm(Model model) {
|
| 37 |
model.addAttribute("createFaceProfileRequest", new CreateFaceProfileRequest());
|
| 38 |
-
return
|
| 39 |
}
|
| 40 |
|
| 41 |
@PostMapping("/create")
|
| 42 |
public String create(@Valid @ModelAttribute CreateFaceProfileRequest request,
|
| 43 |
BindingResult result, Model model) {
|
| 44 |
if (result.hasErrors()) {
|
| 45 |
-
return
|
| 46 |
}
|
| 47 |
faceProfileService.createFaceProfile(request);
|
| 48 |
-
return Routes.REDIRECT +
|
| 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 "
|
| 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 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 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("
|
| 26 |
return Views.User.LIST;
|
| 27 |
}
|
| 28 |
|
| 29 |
@GetMapping("/{id}")
|
| 30 |
public String toDetailPage(@PathVariable("id") Long id, Model model) {
|
| 31 |
-
UserResponse
|
| 32 |
-
model.addAttribute("
|
| 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),
|
| 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.
|
| 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>
|
| 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>
|
| 40 |
-
|
| 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 |
-
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
throws UsernameNotFoundException {
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 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="@{/
|
| 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"
|
| 73 |
</div>
|
| 74 |
|
| 75 |
<div class="form-group">
|
| 76 |
<label>Đến ngày</label>
|
| 77 |
-
<input type="date"
|
| 78 |
</div>
|
| 79 |
|
| 80 |
</div>
|
|
@@ -82,7 +57,7 @@
|
|
| 82 |
<!-- Lý do -->
|
| 83 |
<div class="form-group">
|
| 84 |
<label>Lý do</label>
|
| 85 |
-
<textarea
|
| 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>
|
| 115 |
-
|
| 116 |
-
<
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
<td></td>
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
<td>
|
| 122 |
-
<span class="status pending">
|
| 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
|
| 221 |
</div>
|
| 222 |
|
| 223 |
<div class="profile-item">
|
| 224 |
<label>Số điện thoại</label>
|
| 225 |
-
<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="/
|
| 20 |
<span>📊</span>
|
| 21 |
Dashboard
|
| 22 |
</a>
|
| 23 |
</li>
|
| 24 |
|
| 25 |
<li class="sidebar-option">
|
| 26 |
-
<a href="/
|
| 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">
|
| 53 |
|
| 54 |
<div>
|
| 55 |
-
<h3
|
| 56 |
-
<div class="header-description"
|
|
|
|
|
|
|
| 57 |
</div>
|
| 58 |
</div>
|
| 59 |
|
| 60 |
-
<
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
| 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="/
|
| 20 |
<span>📊</span>
|
| 21 |
Dashboard
|
| 22 |
</a>
|
| 23 |
</li>
|
| 24 |
|
| 25 |
<li class="sidebar-option">
|
| 26 |
-
<a href="/
|
| 27 |
<span>🕒</span>
|
| 28 |
Lịch sử điểm danh
|
| 29 |
</a>
|
| 30 |
</li>
|
| 31 |
|
| 32 |
<li class="sidebar-option">
|
| 33 |
-
<a href="/
|
| 34 |
<span>📅</span>
|
| 35 |
Xin nghỉ phép
|
| 36 |
</a>
|
| 37 |
</li>
|
| 38 |
|
| 39 |
<li class="sidebar-option">
|
| 40 |
-
<a href="/
|
| 41 |
<span>👤</span>
|
| 42 |
Đăng kí Face ID
|
| 43 |
</a>
|
|
@@ -72,14 +72,18 @@
|
|
| 72 |
<div class="avatar"></div>
|
| 73 |
|
| 74 |
<div>
|
| 75 |
-
<h3
|
| 76 |
-
<div class="header-description"
|
|
|
|
|
|
|
| 77 |
</div>
|
| 78 |
</div>
|
| 79 |
|
| 80 |
-
<
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
| 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="/
|
| 20 |
<span>📊</span>
|
| 21 |
Dashboard
|
| 22 |
</a>
|
| 23 |
</li>
|
| 24 |
|
| 25 |
<li class="sidebar-option">
|
| 26 |
-
<a href="/
|
| 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="/
|
| 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">
|
| 63 |
|
| 64 |
<div>
|
| 65 |
-
<h3
|
| 66 |
-
<div class="header-description"
|
|
|
|
|
|
|
| 67 |
</div>
|
| 68 |
</div>
|
| 69 |
|
| 70 |
-
<
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
| 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 |
|