diff --git a/src/main/java/com/primefactorsolutions/model/TimeOffRequest.java b/src/main/java/com/primefactorsolutions/model/TimeOffRequest.java index b0daea9..6d0046e 100644 --- a/src/main/java/com/primefactorsolutions/model/TimeOffRequest.java +++ b/src/main/java/com/primefactorsolutions/model/TimeOffRequest.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.Date; +import java.time.LocalDate; @Data @Entity @@ -20,24 +20,11 @@ public class TimeOffRequest extends BaseEntity { @Enumerated(EnumType.STRING) private TimeOffRequestType category; @Enumerated(EnumType.STRING) - private Status state; + private TimeOffRequestStatus state; private Double availableDays; - private Date expiration; - private Date startDate; - private Date endDate; + private LocalDate expiration; + private LocalDate startDate; + private LocalDate endDate; private Double daysToBeTake; private Double daysBalance; - public enum Status { - ALL, - TAKEN, - REQUESTED, - APPROVED, - IN_USE, - UNDER_REVIEW, - PENDING, - REJECTED, - COMPLETED, - CANCELLED, - EXPIRED, - } } diff --git a/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java b/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java new file mode 100644 index 0000000..e549c4e --- /dev/null +++ b/src/main/java/com/primefactorsolutions/model/TimeOffRequestStatus.java @@ -0,0 +1,15 @@ +package com.primefactorsolutions.model; + +public enum TimeOffRequestStatus { + ALL, + TAKEN, + REQUESTED, + APPROVED, + IN_USE, + UNDER_REVIEW, + PENDING, + REJECTED, + COMPLETED, + CANCELLED, + EXPIRED +} diff --git a/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java new file mode 100644 index 0000000..8af34ea --- /dev/null +++ b/src/main/java/com/primefactorsolutions/views/RequestEmployeeView.java @@ -0,0 +1,185 @@ +package com.primefactorsolutions.views; + +import com.primefactorsolutions.model.*; +import com.primefactorsolutions.service.EmployeeService; +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.Grid; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +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 java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@SpringComponent +@PermitAll +@Scope("prototype") +@PageTitle("RequestEmployee") +@Route(value = "/requests", layout = MainLayout.class) +public class RequestEmployeeView extends Div implements HasUrlParameter { + + private final TimeOffRequestService requestService; + private final EmployeeService employeeService; + private final Grid requestGrid = new Grid<>(TimeOffRequest.class); + private List requests = Collections.emptyList(); + private ComboBox categoryFilter; + private ComboBox stateFilter; + private UUID employeeId; + private TimeOffRequest request; + + public RequestEmployeeView(final TimeOffRequestService requestService, final EmployeeService employeeService) { + this.requestService = requestService; + this.employeeService = employeeService; + initializeView(); + } + + private void initializeView() { + setupFilters(); + setupGrid(); + add(requestGrid, createSummaryLayout(), createActionButtons()); + refreshRequestGrid(null, null); + } + + private void setupFilters() { + categoryFilter = createCategoryFilter(); + stateFilter = createStateFilter(); + add(categoryFilter, stateFilter); + } + + private ComboBox createCategoryFilter() { + categoryFilter = new ComboBox<>("Category"); + categoryFilter.setItems(TimeOffRequestType.values()); + categoryFilter.setValue(TimeOffRequestType.values()[0]); + categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue())); + return categoryFilter; + } + + private ComboBox createStateFilter() { + stateFilter = new ComboBox<>("State"); + stateFilter.setItems(TimeOffRequestStatus.values()); + stateFilter.setValue(TimeOffRequestStatus.values()[0]); + stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue())); + return stateFilter; + } + + private void setupGrid() { + requestGrid.setColumns( + "category", + "state", + "availableDays", + "expiration", + "startDate", + "endDate", + "daysToBeTake", + "daysBalance" + ); + requestGrid.setAllRowsVisible(true); + requestGrid.asSingleSelect().addValueChangeListener(event -> { + TimeOffRequest selectedRequest = event.getValue(); + if (selectedRequest != null) { + request = selectedRequest; + } + }); + } + + private VerticalLayout createSummaryLayout() { + int totalVacations = 15; + int totalTimeOff = 2; + int totalAvailableDays = totalVacations + totalTimeOff; + return new VerticalLayout( + new Span("TOTAL HOLIDAYS: " + totalVacations), + new Span("TOTAL TIME OFF: " + totalTimeOff), + new Span("TOTAL AVAILABLE DAYS: " + totalAvailableDays) + ); + } + + private HorizontalLayout createActionButtons() { + Button viewButton = new Button("View", event -> { + if (request != null) { + navigateToViewRequest(request); + } else { + Notification.show("Please select a request to view.", 3000, Notification.Position.MIDDLE); + } + }); + Button editButton = new Button("Edit", event -> { + if (request != null) { + navigateToEditRequest(request); + } else { + Notification.show("Please select a request to view.", 3000, Notification.Position.MIDDLE); + } + }); + Button closeButton = new Button("Close", event -> navigateToRequestsListView()); + return new HorizontalLayout(viewButton, editButton, closeButton); + } + + private void navigateToRequestsListView() { + getUI().ifPresent(ui -> ui.navigate(RequestsListView.class)); + } + + private void navigateToEditRequest(final TimeOffRequest request) { + navigateToRequestView(request, "edit"); + } + + private void navigateToViewRequest(final TimeOffRequest request) { + navigateToRequestView(request, "view"); + } + + private void navigateToRequestView(final TimeOffRequest request, final String action) { + getUI().ifPresent(ui -> ui.navigate(RequestView.class, request.getId().toString() + "/" + action)); + } + + private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequestStatus state) { + List filteredRequests = allFiltersAreNull(category, state) + ? requestService.findRequestsByEmployeeId(employeeId) + : fetchFilteredTimeOffRequests(category, state); + requestGrid.setItems(filteredRequests); + } + + private boolean allFiltersAreNull(final TimeOffRequestType category, final TimeOffRequestStatus state) { + return category == null && state == null; + } + + private List fetchFilteredTimeOffRequests(final TimeOffRequestType category, + final TimeOffRequestStatus state) { + requests = requestService.findRequestsByEmployeeId(employeeId); + if (category != null && !"ALL".equals(category.name())) { + requests = requests.stream() + .filter(req -> req.getCategory().equals(category)) + .collect(Collectors.toList()); + } + if (state != null && !"ALL".equals(state.name())) { + requests = requests.stream() + .filter(req -> req.getState().equals(state)) + .collect(Collectors.toList()); + } + return requests; + } + + @Override + public void setParameter(final BeforeEvent event, final String parameter) { + employeeId = UUID.fromString(parameter); + Employee employee = employeeService.getEmployee(employeeId); + requests = requestService.findRequestsByEmployeeId(employeeId); + setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName()); + requestGrid.setItems(requests); + } + + private void setViewTitle(final String employeeName, final String employeeTeam) { + addComponentAsFirst(new H3("Name: " + employeeName)); + addComponentAtIndex(1, new H3("Team: " + employeeTeam)); + } +} diff --git a/src/main/java/com/primefactorsolutions/views/RequestView.java b/src/main/java/com/primefactorsolutions/views/RequestView.java index 7bc4af9..45b1ea4 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestView.java @@ -1,156 +1,169 @@ package com.primefactorsolutions.views; -import com.primefactorsolutions.model.Employee; -import com.primefactorsolutions.model.TimeOffRequest; -import com.primefactorsolutions.model.TimeOffRequestType; +import com.primefactorsolutions.model.*; import com.primefactorsolutions.service.EmployeeService; +import com.primefactorsolutions.service.TeamService; import com.primefactorsolutions.service.TimeOffRequestService; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.html.H3; -import com.vaadin.flow.component.html.Span; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.HasUrlParameter; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.textfield.NumberField; +import com.vaadin.flow.router.*; import com.vaadin.flow.spring.annotation.SpringComponent; import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Scope; +import org.vaadin.firitin.form.BeanValidationForm; -import java.util.Collections; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; @SpringComponent @PermitAll @Scope("prototype") @PageTitle("Request") -@Route(value = "/requests", layout = MainLayout.class) -public class RequestView extends Div implements HasUrlParameter { +@Route(value = "/requests/:requestId?/:action?", layout = MainLayout.class) +public class RequestView extends BeanValidationForm implements HasUrlParameter { + + private final ComboBox state = new ComboBox<>("State"); + private final DatePicker expiration = new DatePicker("Expiration"); + private final DatePicker startDate = new DatePicker("Start Date"); + private final DatePicker endDate = new DatePicker("End Date"); + private final NumberField availableDays = new NumberField("Available Days"); + private final NumberField daysToBeTake = new NumberField("Days To Be Take"); private final TimeOffRequestService requestService; private final EmployeeService employeeService; - private final Grid requestGrid = new Grid<>(TimeOffRequest.class); - private List requests = Collections.emptyList(); - private ComboBox categoryFilter; - private ComboBox stateFilter; - private UUID employeeId; + private TimeOffRequest request; + private Employee employee; - public RequestView(final TimeOffRequestService requestService, final EmployeeService employeeService) { + private Button saveButton; + + public RequestView(final TimeOffRequestService requestService, + final EmployeeService employeeService, + final TeamService teamService) { + super(TimeOffRequest.class); this.requestService = requestService; this.employeeService = employeeService; - initializeView(); - } - - private void initializeView() { - setupFilters(); - setupGrid(); - add(requestGrid, createSummaryLayout(), createActionButtons()); - refreshRequestGrid(null, null); - } - - private void setupFilters() { - categoryFilter = createCategoryFilter(); - stateFilter = createStateFilter(); - add(categoryFilter, stateFilter); - } - - private ComboBox createCategoryFilter() { - categoryFilter = new ComboBox<>("Category"); - categoryFilter.setItems(TimeOffRequestType.values()); - categoryFilter.setValue(TimeOffRequestType.values()[0]); - categoryFilter.addValueChangeListener(event -> refreshRequestGrid(event.getValue(), stateFilter.getValue())); - return categoryFilter; - } - - private ComboBox createStateFilter() { - stateFilter = new ComboBox<>("State"); - stateFilter.setItems(TimeOffRequest.Status.values()); - stateFilter.setValue(TimeOffRequest.Status.values()[0]); - stateFilter.addValueChangeListener(event -> refreshRequestGrid(categoryFilter.getValue(), event.getValue())); - return stateFilter; - } - - private void setupGrid() { - requestGrid.setColumns( - "category", - "state", - "availableDays", - "expiration", - "startDate", - "endDate", - "daysToBeTake", - "daysBalance" - ); - requestGrid.setAllRowsVisible(true); - } - - private VerticalLayout createSummaryLayout() { - int totalVacations = 15; - int totalTimeOff = 2; - int totalAvailableDays = totalVacations + totalTimeOff; - return new VerticalLayout( - new Span("TOTAL HOLIDAYS: " + totalVacations), - new Span("TOTAL TIME OFF: " + totalTimeOff), - new Span("TOTAL AVAILABLE DAYS: " + totalAvailableDays) - ); - } - - private HorizontalLayout createActionButtons() { - Button viewButton = new Button("View"); - Button editButton = new Button("Edit"); - Button saveButton = new Button("Save"); - Button closeButton = new Button("Close", event -> navigateToRequestsListView()); - return new HorizontalLayout(viewButton, editButton, saveButton, closeButton); - } - - private void navigateToRequestsListView() { - getUI().ifPresent(ui -> ui.navigate(RequestsListView.class)); - } - - private void refreshRequestGrid(final TimeOffRequestType category, final TimeOffRequest.Status state) { - List filteredRequests = allFiltersAreNull(category, state) - ? requestService.findRequestsByEmployeeId(employeeId) - : fetchFilteredTimeOffRequests(category, state); - requestGrid.setItems(filteredRequests); - } - - private boolean allFiltersAreNull(final TimeOffRequestType category, final TimeOffRequest.Status state) { - return category == null && state == null; - } - - private List fetchFilteredTimeOffRequests(final TimeOffRequestType category, - final TimeOffRequest.Status state) { - requests = requestService.findRequestsByEmployeeId(employeeId); - if (category != null && !"ALL".equals(category.name())) { - requests = requests.stream() - .filter(req -> req.getCategory().equals(category)) - .collect(Collectors.toList()); - } - if (state != null && !"ALL".equals(state.name())) { - requests = requests.stream() - .filter(req -> req.getState().equals(state)) - .collect(Collectors.toList()); - } - return requests; + state.setItems(List.of(TimeOffRequestStatus.values())); } @Override - public void setParameter(final BeforeEvent event, final String parameter) { - employeeId = UUID.fromString(parameter); - Employee employee = employeeService.getEmployee(employeeId); - requests = requestService.findRequestsByEmployeeId(employeeId); - setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName()); - requestGrid.setItems(requests); + public void setParameter(final BeforeEvent beforeEvent, final String action) { + final RouteParameters params = beforeEvent.getRouteParameters(); + final String requestIdString = params.get("requestId").orElse(null); + + if ("new".equals(action)) { + setEntityWithEnabledSave(new TimeOffRequest()); + } else { + assert requestIdString != null; + UUID requestId = UUID.fromString(requestIdString); + request = requestService.findTimeOffRequest(requestId); + UUID employeeId = request.getEmployee().getId(); + employee = employeeService.getEmployee(employeeId); + setEntity(request); + configureViewOrEditAction(action); + } } - private void setViewTitle(final String employeeName, final String employeeTeam) { - addComponentAsFirst(new H3("Name: " + employeeName)); - addComponentAtIndex(1, new H3("Team: " + employeeTeam)); + @Override + protected List getFormComponents() { + return List.of( + createEmployeeHeader(), + createTeamHeader(), + createCategoryHeader(), + state, + expiration, + startDate, + endDate, + availableDays, + daysToBeTake, + createCloseButton() + ); + } + + protected Button createSaveButton() { + saveButton = new Button("Save"); + saveButton.addClickListener(event -> saveRequest()); + return saveButton; + } + + protected Button createCloseButton() { + Button closeButton = new Button("Close"); + closeButton.addClickListener(event -> closeForm()); + return closeButton; + } + + private void setFieldsReadOnly(final boolean option) { + state.setReadOnly(option); + expiration.setReadOnly(option); + startDate.setReadOnly(option); + endDate.setReadOnly(option); + availableDays.setReadOnly(option); + daysToBeTake.setReadOnly(option); + } + + private void saveRequest() { + if (isFormValid()) { + TimeOffRequest request = getEntity(); + setRequestFieldValues(request); + requestService.saveTimeOffRequest(request); + Notification.show("Request saved successfully."); + clearForm(); + } + } + + private void setRequestFieldValues(TimeOffRequest request) { + request.setState(state.getValue()); + request.setExpiration(expiration.getValue()); + request.setStartDate(startDate.getValue()); + request.setEndDate(endDate.getValue()); + request.setAvailableDays(availableDays.getValue()); + request.setDaysToBeTake(daysToBeTake.getValue()); + } + + private void closeForm() { + getUI().ifPresent(ui -> ui.navigate("requests/" + employee.getId().toString())); + } + + private boolean isFormValid() { + return !state.isEmpty() + && expiration.getValue() != null + && startDate.getValue() != null + && endDate.getValue() != null + && availableDays.getValue() != null + && daysToBeTake.getValue() != null; + } + + private void clearForm() { + state.clear(); + expiration.clear(); + startDate.clear(); + endDate.clear(); + availableDays.clear(); + daysToBeTake.clear(); + } + + private void configureViewOrEditAction(final String action) { + if ("edit".equals(action) && !request.getId().toString().isEmpty()) { + setFieldsReadOnly(false); + } else if ("view".equals(action) && !request.getId().toString().isEmpty()) { + setFieldsReadOnly(true); + saveButton.setEnabled(false); + } + } + + private H3 createEmployeeHeader() { + return new H3("Employee: " + employee.getFirstName() + " " + employee.getLastName()); + } + + private H3 createTeamHeader() { + return new H3("Team: " + employee.getTeam().getName()); + } + + private H3 createCategoryHeader() { + return new H3("Category: " + request.getCategory()); } } diff --git a/src/main/java/com/primefactorsolutions/views/RequestsListView.java b/src/main/java/com/primefactorsolutions/views/RequestsListView.java index 8fbb619..71216d2 100644 --- a/src/main/java/com/primefactorsolutions/views/RequestsListView.java +++ b/src/main/java/com/primefactorsolutions/views/RequestsListView.java @@ -37,7 +37,7 @@ public class RequestsListView extends Main { private ComboBox employeeFilter; private ComboBox teamFilter; private ComboBox categoryFilter; - private ComboBox stateFilter; + private ComboBox stateFilter; private UUID selectedEmployeeId; public RequestsListView(final TimeOffRequestService requestService, @@ -96,16 +96,14 @@ public class RequestsListView extends Main { Notification.show("Please select a request to view.", 3000, Notification.Position.MIDDLE); } }); - Button editButton = new Button("Edit"); - Button saveButton = new Button("Save"); Button closeButton = new Button("Close", event -> navigateToMainView()); - return new HorizontalLayout(viewButton, editButton, saveButton, closeButton); + return new HorizontalLayout(viewButton, closeButton); } private void refreshRequestGrid(final Employee employee, final Team team, final TimeOffRequestType category, - final TimeOffRequest.Status state) { + final TimeOffRequestStatus state) { requestGrid.setPagingDataProvider((page, pageSize) -> { requests = requestService.findAllTimeOffRequests(); @@ -122,7 +120,7 @@ public class RequestsListView extends Main { private boolean allFiltersAreNull(final Employee employee, final Team team, final TimeOffRequestType category, - final TimeOffRequest.Status state) { + final TimeOffRequestStatus state) { return employee == null && team == null && category == null && state == null; } @@ -139,7 +137,7 @@ public class RequestsListView extends Main { final Employee employee, final Team team, final TimeOffRequestType category, - final TimeOffRequest.Status state) { + final TimeOffRequestStatus state) { if (employee != null && !"ALL".equals(employee.getFirstName())) { requests = requests.stream() .filter(request -> request.getEmployee().getId().equals(employee.getId())) @@ -228,10 +226,10 @@ public class RequestsListView extends Main { return categoryFilter; } - private ComboBox createStateFilter() { + private ComboBox createStateFilter() { stateFilter = new ComboBox<>("State"); - stateFilter.setItems(TimeOffRequest.Status.values()); - stateFilter.setValue(TimeOffRequest.Status.values()[0]); + stateFilter.setItems(TimeOffRequestStatus.values()); + stateFilter.setValue(TimeOffRequestStatus.values()[0]); stateFilter.addValueChangeListener(event -> refreshRequestGrid( employeeFilter.getValue(),