Merge pull request '#37, #41, #42, #43, #44 Perfil de Personal Administrativo - Listado General de Vacaciones y Filtrado' (#48) from Listado-General-Vacaciones into main
All checks were successful
Builder / Build-Project (push) Successful in 2m32s

Reviewed-on: #48
This commit is contained in:
alex 2024-10-10 19:56:03 +00:00
commit 2a59bc4e7e
10 changed files with 444 additions and 9 deletions

View File

@ -0,0 +1,14 @@
package com.primefactorsolutions.model;
import jakarta.persistence.*;
import lombok.*;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Team extends BaseEntity {
private String name;
}

View File

@ -1,21 +1,46 @@
package com.primefactorsolutions.model;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Date;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TimeOffRequest extends BaseEntity {
@OneToMany(fetch = FetchType.EAGER, mappedBy = "request", cascade = {CascadeType.ALL})
private List<TimeOffRequestEntry> entries;
private TimeOffRequestType type;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@ManyToOne
@JoinColumn(name = "team_id", nullable = false)
private Team team;
@Enumerated(EnumType.STRING)
private TimeOffRequestType category;
@Enumerated(EnumType.STRING)
private Status state;
private Double availableDays;
private Date expiration;
private Date startDate;
private Date endDate;
private Double daysToBeTake;
private Double daysBalance;
public enum Status {
ALL,
TAKEN,
REQUESTED,
APPROVED,
IN_USE,
UNDER_REVIEW,
PENDING,
REJECTED,
COMPLETED,
CANCELLED,
EXPIRED,
}
}

View File

@ -1,7 +1,10 @@
package com.primefactorsolutions.model;
public enum TimeOffRequestType {
ALL,
VACATION,
MATERNITY,
BIRTHDAY,
FIXED_HOLIDAY,
OTHER
}

View File

@ -0,0 +1,9 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Team;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface TeamRepository extends JpaRepository<Team, UUID> {
}

View File

@ -0,0 +1,9 @@
package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.TimeOffRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface TimeOffRequestRepository extends JpaRepository<TimeOffRequest, UUID> {
}

View File

@ -0,0 +1,27 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.repositories.TeamRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class TeamService {
private final TeamRepository teamRepository;
public void saveTeam(final Team newTeam) {
teamRepository.save(newTeam);
}
public void deleteTeam(final UUID id) {
teamRepository.deleteById(id);
}
public List<Team> findAllTeams() {
return teamRepository.findAll();
}
}

View File

@ -0,0 +1,88 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.repositories.TimeOffRequestRepository;
import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class TimeOffRequestService {
private final TimeOffRequestRepository timeOffRequestRepository;
public void saveTimeOffRequest(final TimeOffRequest newTimeOffRequest) {
timeOffRequestRepository.save(newTimeOffRequest);
}
public void deleteTimeOffRequest(final UUID id) {
timeOffRequestRepository.deleteById(id);
}
public List<TimeOffRequest> getAllTimeOffRequests() {
return timeOffRequestRepository.findAll();
}
public TimeOffRequest getTimeOffRequest(final UUID id) {
Optional<TimeOffRequest> timeOffRequest = timeOffRequestRepository.findById(id);
return timeOffRequest.orElse(null);
}
public List<TimeOffRequest> findTimeOffRequests(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<TimeOffRequest> timeOffRequests = timeOffRequestRepository.findAll();
int end = Math.min(start + pageSize, timeOffRequests.size());
timeOffRequests.sort(new BeanComparator<>(sortProperty));
if (!asc) {
Collections.reverse(timeOffRequests);
}
return timeOffRequests.subList(start, end);
}
public List<TimeOffRequest> findTimeOffRequests(final int start, final int pageSize) {
List<TimeOffRequest> timeOffRequests = timeOffRequestRepository.findAll();
int end = Math.min(start + pageSize, timeOffRequests.size());
return timeOffRequests.subList(start, end);
}
public List<TimeOffRequest> findTimeOffRequestBy(final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state,
final int start,
final int pageSize) {
List<TimeOffRequest> timeOffRequests = timeOffRequestRepository.findAll();
if (employee != null && !"ALL".equals(employee.getFirstName())) {
timeOffRequests = timeOffRequests.stream()
.filter(req -> req.getEmployee().equals(employee))
.collect(Collectors.toList());
}
if (team != null && !"ALL".equals(team.getName())) {
timeOffRequests = timeOffRequests.stream()
.filter(req -> req.getTeam().equals(team))
.collect(Collectors.toList());
}
if (category != null && !"ALL".equals(category.name())) {
timeOffRequests = timeOffRequests.stream()
.filter(req -> req.getCategory().equals(category))
.collect(Collectors.toList());
}
if (state != null && !"ALL".equals(state.name())) {
timeOffRequests = timeOffRequests.stream()
.filter(req -> req.getState().equals(state))
.collect(Collectors.toList());
}
int end = Math.min(start + pageSize, timeOffRequests.size());
return timeOffRequests.subList(start, end);
}
}

View File

@ -107,6 +107,8 @@ public class MainLayout extends AppLayout {
SideNavItem timeOff = new SideNavItem("My Time-off", TimeoffView.class,
LineAwesomeIcon.PLANE_DEPARTURE_SOLID.create());
timeOff.addItem(new SideNavItem("Vacations", RequestsListView.class,
LineAwesomeIcon.SUN.create()));
SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class,
LineAwesomeIcon.HOURGLASS_START_SOLID.create());
timesheet.addItem(new SideNavItem("Hours Worked", HoursWorkedView.class,

View File

@ -1,11 +1,24 @@
package com.primefactorsolutions.views;
import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid;
import java.util.List;
@SpringComponent
@Scope("prototype")
@ -13,4 +26,211 @@ import org.springframework.context.annotation.Scope;
@Route(value = "/requests", layout = MainLayout.class)
@PermitAll
public class RequestsListView extends Main {
}
private final TimeOffRequestService requestService;
private final EmployeeService employeeService;
private final TeamService teamService;
private final PagingGrid<TimeOffRequest> requestGrid;
private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<TimeOffRequest.Status> stateFilter;
public RequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService,
final TeamService teamService) {
this.requestService = requestService;
this.employeeService = employeeService;
this.teamService = teamService;
this.requestGrid = new PagingGrid<>(TimeOffRequest.class);
initializeView();
refreshRequestGrid(null, null, null, null);
}
private void initializeView() {
setupFilters();
setupRequestGrid();
add(requestGrid);
add(createActionButtons());
}
private void setupFilters() {
add(createEmployeeFilter());
add(createTeamFilter());
add(createCategoryFilter());
add(createStateFilter());
}
private void setupRequestGrid() {
requestGrid.setColumns(
"category",
"state",
"availableDays",
"expiration",
"startDate",
"endDate",
"daysToBeTake",
"daysBalance"
);
requestGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee");
requestGrid.addComponentColumn(this::createTeamSpan).setHeader("Team");
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(5);
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("View");
Button editButton = new Button("Edit");
Button saveButton = new Button("Save");
Button closeButton = new Button("Close", event -> closeView());
return new HorizontalLayout(viewButton, editButton, saveButton, closeButton);
}
private void refreshRequestGrid(final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
requestGrid.setPagingDataProvider((page, pageSize) -> {
if (allFiltersAreNull(employee, team, category, state)) {
return fetchTimeOffRequests((int) page, pageSize);
} else {
return fetchFilteredTimeOffRequests((int) page, pageSize, employee, team, category, state);
}
});
requestGrid.getDataProvider().refreshAll();
}
private boolean allFiltersAreNull(final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
return employee == null && team == null && category == null && state == null;
}
private List<TimeOffRequest> fetchTimeOffRequests(final int page, final int pageSize) {
int start = page * pageSize;
if (requestGrid.getSortOrder().isEmpty()) {
return requestService.findTimeOffRequests(start, pageSize);
} else {
return fetchSortedTimeOffRequests(start, pageSize);
}
}
private List<TimeOffRequest> fetchSortedTimeOffRequests(final int start, final int pageSize) {
GridSortOrder<TimeOffRequest> sortOrder = requestGrid.getSortOrder().getFirst();
return requestService.findTimeOffRequests(
start,
pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING
);
}
private List<TimeOffRequest> fetchFilteredTimeOffRequests(final int page,
final int pageSize,
final Employee employee,
final Team team,
final TimeOffRequestType category,
final TimeOffRequest.Status state) {
return requestService.findTimeOffRequestBy(employee, team, category, state, page, pageSize);
}
private Span createEmployeeSpan(final TimeOffRequest timeOffRequest) {
Employee employee = timeOffRequest.getEmployee();
return new Span(employee.getFirstName() + " " + employee.getLastName());
}
private Span createTeamSpan(final TimeOffRequest timeOffRequest) {
return new Span(timeOffRequest.getTeam().getName());
}
private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Employee");
List<Employee> employees = employeeService.findAllEmployees();
employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeLabel);
employeeFilter.setValue(employees.getFirst());
employeeFilter.addValueChangeListener(event ->
refreshRequestGrid(
event.getValue(),
teamFilter.getValue(),
categoryFilter.getValue(),
stateFilter.getValue()
)
);
return employeeFilter;
}
private ComboBox<Team> createTeamFilter() {
teamFilter = new ComboBox<>("Team");
List<Team> teams = teamService.findAllTeams();
teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
event.getValue(),
categoryFilter.getValue(),
stateFilter.getValue()
)
);
return teamFilter;
}
private ComboBox<TimeOffRequestType> createCategoryFilter() {
categoryFilter = new ComboBox<>("Category");
categoryFilter.setItems(TimeOffRequestType.values());
categoryFilter.setValue(TimeOffRequestType.values()[0]);
categoryFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
event.getValue(),
stateFilter.getValue()
)
);
return categoryFilter;
}
private ComboBox<TimeOffRequest.Status> createStateFilter() {
stateFilter = new ComboBox<>("State");
stateFilter.setItems(TimeOffRequest.Status.values());
stateFilter.setValue(TimeOffRequest.Status.values()[0]);
stateFilter.addValueChangeListener(event ->
refreshRequestGrid(
employeeFilter.getValue(),
teamFilter.getValue(),
categoryFilter.getValue(),
event.getValue()
)
);
return stateFilter;
}
private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("ALL");
return allEmployeesOption;
}
private Team createAllTeamsOption() {
Team allTeamsOption = new Team();
allTeamsOption.setName("ALL");
return allTeamsOption;
}
private String getEmployeeLabel(final Employee employee) {
return "ALL".equals(employee.getFirstName()) ? "ALL" : employee.getFirstName() + " " + employee.getLastName();
}
private String getTeamLabel(final Team team) {
return "ALL".equals(team.getName()) ? "ALL" : team.getName();
}
private void closeView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
}

View File

@ -27,3 +27,41 @@ insert into employee (id, version, username, first_name, last_name, status) valu
insert into employee (id, version, username, first_name, last_name, status) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE');
insert into employee (id, version, username, first_name, last_name, status) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE');
insert into employee (id, version, username, first_name, last_name, status) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE');
INSERT INTO team (id, version, name) VALUES ('b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 1, 'ABC');
INSERT INTO team (id, version, name) VALUES ('6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 1, 'XYZ');
INSERT INTO team (id, version, name) VALUES ('c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 1, 'DEF');
INSERT INTO team (id, version, name) VALUES ('8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 1, 'GHI');
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('9d6f12ba-e341-4e7a-b8a6-cab0982bd8c1', 1, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'VACATION', 'TAKEN', 15, '2025-12-31', '2024-10-01', '2024-10-10', 5, 10);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('2fa314bc-f547-4b12-a8b6-bb789feabc12', 1, '19b5a76e-d7b1-4b76-8b02-4d0748e85809', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'BIRTHDAY', 'APPROVED', 15, '2025-12-31', '2024-12-01', '2024-12-15', 7, 8);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('d5f6341a-913d-4e7f-a0b2-cfe0786acd34', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FIXED_HOLIDAY', 'IN_USE', 20, '2025-11-30', '2024-11-10', '2024-11-20', 10, 10);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('4f913b23-ff23-4527-bcd6-adfe01234567', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MATERNITY', 'TAKEN', 18, '2025-06-30', '2024-07-01', '2024-07-15', 10, 8);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('8c653f2a-f9a3-4d67-b3b6-12ad98fe0983', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'VACATION', 'REQUESTED', 10, '2025-10-31', '2024-09-15', '2024-09-20', 5, 5);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('fb9d9d75-b2ab-4ea4-b8b3-0a8f89e5c123', 1, '2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', '6d63bc15-3f8b-46f7-9cf1-7e9b0b9a2b28', 'FIXED_HOLIDAY', 'IN_USE', 12, '2025-08-31', '2024-08-05', '2024-08-15', 6, 6);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('1c913a12-46e9-47b7-9e31-ab903fedc789', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'BIRTHDAY', 'TAKEN', 14, '2025-12-31', '2024-10-20', '2024-10-25', 5, 9);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('b1249d3a-cc34-4954-88d9-1e4f67fe2436', 1, 'afc5c741-f70a-4394-853b-39d51b118927', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'MATERNITY', 'APPROVED', 20, '2025-11-30', '2024-11-05', '2024-11-12', 7, 13);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('6fdc47a8-127b-41c4-8d12-7fc12098ab12', 1, 'b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'VACATION', 'TAKEN', 18, '2025-06-30', '2024-07-10', '2024-07-20', 8, 10);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'FIXED_HOLIDAY', 'PENDING', 15, '2025-12-31', '2024-09-01', '2024-09-05', 4, 11);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('89bc4b2a-943f-487c-a9f3-bacf78145e67', 1, 'cba3efb7-32bc-44be-9fdc-fc5e4f211254', '8f6b61e7-efb2-4de7-b8ed-7438c9d8babe', 'VACATION', 'APPROVED', 20, '2025-11-30', '2024-10-25', '2024-11-05', 9, 11);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('37adfc2a-7463-4b2d-a7c1-fae04567cdef', 1, 'e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'BIRTHDAY', 'TAKEN', 18, '2025-06-30', '2024-06-01', '2024-06-10', 6, 12);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('2bc138ea-12db-4b89-a0b4-78e045e34b4e', 1, 'f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'MATERNITY', 'REQUESTED', 10, '2025-10-31', '2024-10-01', '2024-10-10', 3, 7);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('14de1a56-6893-4e12-90f3-4faec457f002', 1, 'cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa', 'FIXED_HOLIDAY', 'PENDING', 22, '2025-08-31', '2024-07-15', '2024-07-25', 8, 14);
insert into time_off_request (id, version, employee_id, team_id, category, state, available_days, expiration, start_date, end_date, days_to_be_take, days_balance)
values ('fb08a6c9-cd17-42e8-b9e2-734ec834cae2', 1, '4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 'c3a8a7b1-f2d9-48c0-86ea-f215c2e6b3a3', 'VACATION', 'TAKEN', 16, '2025-12-31', '2024-09-30', '2024-10-05', 4, 12);