From d49eee8988858d85fecf5045582aabb52f473e06 Mon Sep 17 00:00:00 2001 From: Melina Gutierrez Date: Fri, 15 Nov 2024 07:03:31 -0400 Subject: [PATCH] Formulario de registro de horas trabajadas --- .../model/HoursWorked.java | 27 ++- .../service/HoursWorkedService.java | 9 +- .../views/HoursWorkedListView.java | 217 +++++++++++++----- .../views/HoursWorkedView.java | 124 ++++++++-- .../views/MainLayout.java | 2 +- src/main/resources/data.sql | 3 + 6 files changed, 293 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/primefactorsolutions/model/HoursWorked.java b/src/main/java/com/primefactorsolutions/model/HoursWorked.java index 3f203b4..519f99c 100644 --- a/src/main/java/com/primefactorsolutions/model/HoursWorked.java +++ b/src/main/java/com/primefactorsolutions/model/HoursWorked.java @@ -15,22 +15,21 @@ import java.util.UUID; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) public class HoursWorked extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private UUID id; - @ManyToOne - @JoinColumn(name = "employee_id", nullable = false) + @JoinColumn(name = "employee_id", nullable = true) private Employee employee; @ManyToOne - @JoinColumn(name = "team_id", nullable = false) + @JoinColumn(name = "team_id", nullable = true) private Team team; private int weekNumber; private LocalDate date; private String actividad; + private String tareasEspecificas; private double hours; + private double horasTareasEspecificas; + private double horaspendientes; private double totalHours; public Employee getEmployee() { @@ -88,5 +87,21 @@ public class HoursWorked extends BaseEntity { public void setTeam(Team team) { this.team = team; } + + public String getTareasEspecificas() { + return tareasEspecificas; + } + + public void setTareasEspecificas(String tareasEspecificas) { + this.tareasEspecificas = tareasEspecificas; + } + + public double getHorasTareasEspecificas() { + return horasTareasEspecificas; + } + + public void setHorasTareasEspecificas(double horasTareasEspecificas) { + this.tareasEspecificas = tareasEspecificas; + } } diff --git a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java index 25d7235..ed77e2e 100644 --- a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java +++ b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java @@ -72,13 +72,8 @@ public class HoursWorkedService { } public HoursWorked getHoursWorked(final UUID id) { - Optional registro = hoursWorkedRepository.findById(id); - HoursWorked hw = registro.orElse(null); - if (hw == null) { - System.out.println("No se encontró el registro de horas trabajadas"); - return null; - } - return hw; + final Optional hoursWorked = hoursWorkedRepository.findById(id); + return hoursWorked.get(); } } \ No newline at end of file diff --git a/src/main/java/com/primefactorsolutions/views/HoursWorkedListView.java b/src/main/java/com/primefactorsolutions/views/HoursWorkedListView.java index dd526c9..fb20d2b 100644 --- a/src/main/java/com/primefactorsolutions/views/HoursWorkedListView.java +++ b/src/main/java/com/primefactorsolutions/views/HoursWorkedListView.java @@ -1,11 +1,19 @@ package com.primefactorsolutions.views; -import com.primefactorsolutions.model.HoursWorked; +import com.primefactorsolutions.model.Employee; +import com.primefactorsolutions.model.Team; +import com.primefactorsolutions.model.TimeOffRequest; +import com.primefactorsolutions.model.TimeOffRequestStatus; +import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.HoursWorkedService; +import com.primefactorsolutions.service.TeamService; 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.H2; import com.vaadin.flow.component.html.Main; +import com.vaadin.flow.component.notification.Notification; +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; @@ -14,40 +22,125 @@ import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.PagingGrid; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; @SpringComponent @PermitAll @Scope("prototype") -@PageTitle("HoursWorkedList") +@PageTitle("Registro de Horas Trabajadas") @Route(value = "/hours-worked-list", layout = MainLayout.class) +public class HoursWorkedListView extends Main { -public class HoursWorkedListView extends Main { private final HoursWorkedService hoursWorkedService; - private final PagingGrid table = new PagingGrid<>(HoursWorked.class); + private final EmployeeService employeeService; + private final TeamService teamService; + private final PagingGrid hoursWorkedGrid = new PagingGrid<>(); + private final PagingGrid table = new PagingGrid<>(Employee.class); + private List employees = Collections.emptyList(); + private ComboBox employeeFilter; + private ComboBox teamFilter; + private UUID selectedEmployeeId; - public HoursWorkedListView (final HoursWorkedService hoursWorkedService) { + + public HoursWorkedListView(final HoursWorkedService hoursWorkedService, + final EmployeeService employeeService, + final TeamService teamService) { this.hoursWorkedService = hoursWorkedService; - setupView(); - refreshGrid(); + this.employeeService = employeeService; + this.teamService = teamService; + this.employees = employeeService.findAllEmployees(); + + initializeView(); + refreshGridListHoursWorked(null, null); } - private void setupView() { - add(new H2("Lista de Horas trabajadas")); - configureTable(); - add(createAddHoursWorkedButton()); - add(table); + private void refreshGridListHoursWorked(final Employee employee, + final Team team) { + hoursWorkedGrid.setPagingDataProvider((page, pageSize) -> { + int start = (int) (page * hoursWorkedGrid.getPageSize()); + return fetchFilteredEmployees(start, pageSize, employee, team); + }); + hoursWorkedGrid.getDataProvider().refreshAll(); } - private void configureTable() { - table.setColumns("Actividad", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"); - addEditButtonColumn("View", this::navigateToHoursWorkedView); - addEditButtonColumn("Edit", this::navigateToEditView); - setupPagingGrid(); + private List fetchFilteredEmployees(final int start, + final int pageSize, + final Employee employee, + final Team team) { + List filteredEmployees = employeeService.findAllEmployees(); + + if (employee != null && !"TODOS".equals(employee.getFirstName())) { + filteredEmployees = filteredEmployees.stream() + .filter(emp -> emp.getId().equals(employee.getId())) + .collect(Collectors.toList()); + } + + if (team != null && !"TODOS".equals(team.getName())) { + filteredEmployees = filteredEmployees.stream() + .filter(emp -> emp.getTeam() != null && emp.getTeam().getId().equals(team.getId())) + .collect(Collectors.toList()); + } + + int end = Math.min(start + pageSize, filteredEmployees.size()); + return filteredEmployees.subList(start, end); } - private void addEditButtonColumn( final String label, final ButtonClickHandler handler) { - table.addComponentColumn(hoursWorked -> createButton(label, () -> handler.handle(hoursWorked))); + private void initializeView() { + setupFilters(); + add(createAddHoursWorked()); + setupListHoursWorkedGrid(); + add(hoursWorkedGrid); + add(createActionButtons()); + } + + private void setupFilters() { + add(createEmployeeFilter()); + add(createTeamFilter()); + } + + private void setupListHoursWorkedGrid() { + hoursWorkedGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); + hoursWorkedGrid.addColumn(this::getTeamName).setHeader("Equipo"); + + hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); + hoursWorkedGrid.setPageSize(5); + hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> { + Employee selectedRequest = event.getValue(); + if (selectedRequest != null) { + selectedEmployeeId = selectedRequest.getId(); + } + }); + } + + private String getTeamName(final Employee employee) { + Team team = employee.getTeam(); + return team != null ? team.getName() : "Sin asignar"; + } + + private String getTeamLabel(final Team team) { + return "TODOS".equals(team.getName()) ? "TODOS" : team.getName(); + } + + + private HorizontalLayout createActionButtons() { + Button viewButton = new Button("Ver", event -> { + if (selectedEmployeeId != null) { + navigateToHoursWorkedView(selectedEmployeeId); + } else { + Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE); + } + }); + Button closeButton = new Button("Salir", event -> navigateToListView()); + return new HorizontalLayout(viewButton, closeButton); + } + + private void navigateToListView() { + getUI().ifPresent(ui -> ui.navigate(MainView.class)); + } + + private void navigateToHoursWorkedView(final UUID idEmployee) { + getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + idEmployee.toString())); } private Button createButton(final String label, final Runnable onClickAction) { @@ -56,46 +149,66 @@ public class HoursWorkedListView extends Main { return button; } - private Button createAddHoursWorkedButton() { return createButton("Crear Actividad", this::navigateToAddHoursWorkedView);} - - private void navigateToEditView(final HoursWorked hoursWorked) { - getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, hoursWorked.getId().toString() + "/edit")); + private Button createAddHoursWorked() { + return createButton("Agregar Actividad", this::navigateToHours); } - private void navigateToHoursWorkedView(final HoursWorked hoursWorked) { - getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, hoursWorked.getId().toString() + "/view")); + private void navigateToHours() { getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));} + + private ComboBox createEmployeeFilter() { + employeeFilter = new ComboBox<>("Empleado"); + List employees = new ArrayList<>(employeeService.findAllEmployees()); + employees.addFirst(createAllEmployeesOption()); + employeeFilter.setItems(employees); + employeeFilter.setItemLabelGenerator(this::getEmployeeFullName); + employeeFilter.setValue(employees.getFirst()); + employeeFilter.addValueChangeListener(event -> + refreshGridListHoursWorked( + event.getValue(), + teamFilter.getValue() + ) + ); + return employeeFilter; + } + + private String getEmployeeFullName(final Employee employee) { + return "TODOS".equals(employee.getFirstName()) + ? "TODOS" : employee.getFirstName() + " " + employee.getLastName(); + } + + private ComboBox createTeamFilter() { + teamFilter = new ComboBox<>("Equipo"); + List teams = new ArrayList<>(teamService.findAllTeams()); + teams.addFirst(createAllTeamsOption()); + teamFilter.setItems(teams); + teamFilter.setItemLabelGenerator(this::getTeamLabel); + teamFilter.setValue(teams.getFirst()); + teamFilter.addValueChangeListener(event -> + refreshGridListHoursWorked( + employeeFilter.getValue(), + event.getValue() + ) + ); + return teamFilter; + } + + + private Employee createAllEmployeesOption() { + Employee allEmployeesOption = new Employee(); + allEmployeesOption.setFirstName("TODOS"); + return allEmployeesOption; + } + + private Team createAllTeamsOption() { + Team allTeamsOption = new Team(); + allTeamsOption.setName("TODOS"); + return allTeamsOption; } - private void navigateToAddHoursWorkedView() { getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class, "new"));} private void setupPagingGrid() { table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); table.setPageSize(5); } - private void refreshGrid() { - table.setPagingDataProvider((page, pageSize) -> fetchHoursWorkeds((int) page, pageSize)); - } - - private List fetchHoursWorkeds(final int page, final int pageSize) { - int start = page * pageSize; - if (hasSortOrder()) { - return fetchSortedHoursWorkeds(start, pageSize); - } - return hoursWorkedService.findHoursWorkeds(start, pageSize); - } - - private boolean hasSortOrder() { return !table.getSortOrder().isEmpty(); } - - private List fetchSortedHoursWorkeds(final int start, final int pageSize) { - GridSortOrder sortOrder = table.getSortOrder().getFirst(); - return hoursWorkedService.findHoursWorkeds(start, pageSize, - sortOrder.getSorted().getKey(), - sortOrder.getDirection() == SortDirection.ASCENDING); - } - - @FunctionalInterface - private interface ButtonClickHandler { - void handle(HoursWorked hoursWorked); - } } diff --git a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java index 2a3cbdb..2c03fed 100644 --- a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java +++ b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java @@ -5,10 +5,9 @@ import com.primefactorsolutions.model.Team; import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.HoursWorkedService; import com.primefactorsolutions.service.TeamService; -import com.primefactorsolutions.service.TimeOffRequestService; import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.datepicker.DatePicker; -import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.button.Button; @@ -17,9 +16,12 @@ 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.components.datepicker.VDatePicker; import org.vaadin.firitin.form.BeanValidationForm; -import java.awt.*; +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -27,33 +29,42 @@ import java.util.UUID; @PermitAll @Scope("prototype") @PageTitle("Horas Trabajadas") -@Route(value = "/timesheets/listhw/:hours-worked?/me", layout = MainLayout.class) +@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class) public class HoursWorkedView extends BeanValidationForm implements HasUrlParameter { - private final DatePicker dateField = new DatePicker("Fecha"); + private final VDatePicker dateField = new VDatePicker("Fecha"); private final ComboBox teamField = new ComboBox<>("Equipo"); - private final ComboBox employeeField = new ComboBox<>("Empleado"); + private ComboBox employeeField; + private final ComboBox tareasEspecificasDropdown = new ComboBox<>("Tarea Específica"); + private final TextField tareaEspecificaInput = new TextField("Otra Tarea Específica"); + private final TextField horasTareaEspecificaField = new TextField("Horas Tarea Específica"); private final TextField activityField = new TextField("Actividad"); private final TextField hoursField = new TextField("Horas"); + private final H2 equipoLabel = new H2("Tareas del Cliente/Equipo"); + private final H2 empresaLabel = new H2("Tareas de la Empresa"); + private final Label totalCompletadoLabel = new Label(); + private final HoursWorkedService hoursWorkedService; private final EmployeeService employeeService; private final TeamService teamService; - private HoursWorked request; + private HoursWorked hoursWorked; private Employee employee; private Button saveButton; public HoursWorkedView(HoursWorkedService hoursWorkedService, EmployeeService employeeService, - TeamService teamService, TimeOffRequestService timeOffRequestService) { + TeamService teamService) { super(HoursWorked.class); this.hoursWorkedService = hoursWorkedService; this.employeeService = employeeService; this.teamService = teamService; - // Initialize combo boxes + initializeDateField(); initializeTeamField(); initializeEmployeeField(); + configureTareasEspecificas(); + } @Override @@ -71,7 +82,6 @@ public class HoursWorkedView extends BeanValidationForm implements if ("edit".equals(action) && !s.isEmpty()) { saveButton.setVisible(true); } else if ("view".equals(action) && !s.isEmpty()) { - saveButton.setVisible(false); } } @@ -83,12 +93,35 @@ public class HoursWorkedView extends BeanValidationForm implements dateField, teamField, employeeField, + equipoLabel, activityField, hoursField, + empresaLabel, + tareasEspecificasDropdown, + tareaEspecificaInput, + horasTareaEspecificaField, createCloseButton() ); } + private void configureTareasEspecificas() { + tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros"); + tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); + + tareasEspecificasDropdown.addValueChangeListener(event -> { + String selected = event.getValue(); + boolean isOtros = "Otros".equals(selected); + tareaEspecificaInput.setVisible(isOtros); + horasTareaEspecificaField.setVisible(true); + if (!isOtros) { + tareaEspecificaInput.clear(); + horasTareaEspecificaField.clear(); + } + }); + tareaEspecificaInput.setVisible(false); + horasTareaEspecificaField.setVisible(false); + } + protected Button createSaveButton() { saveButton = new Button("Guardar"); saveButton.addClickListener(event -> saveHoursWorked()); @@ -102,23 +135,53 @@ public class HoursWorkedView extends BeanValidationForm implements } private void initializeTeamField() { + List teams = new ArrayList<>(teamService.findAllTeams()); teamField.setItems(teamService.findAllTeams()); teamField.setItemLabelGenerator(Team::getName); + teamField.setValue(teams.getFirst()); teamField.addValueChangeListener(event -> { - teamField.getValue(); - }); + if (teams != null) { + employeeField.getValue(); + event.getValue(); + } + } + ); } - private void initializeEmployeeField() { - employeeField.setItems(employeeService.findAllEmployees()); - employeeField.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName()); + private ComboBox initializeEmployeeField() { + employeeField = new ComboBox<>("Empleado"); + List employees = new ArrayList<>(employeeService.findAllEmployees()); + employeeField.setItems(employees); + employeeField.setItemLabelGenerator(this::getEmployeeFullName); + employeeField.setValue(employees.getFirst()); + return employeeField; + } + + private String getEmployeeFullName(final Employee employee) { + return "TODOS".equals(employee.getFirstName()) + ? "TODOS" : employee.getFirstName() + " " + employee.getLastName(); + } + + private void initializeDateField() { + LocalDate today = LocalDate.now(); + YearMonth currentMonth = YearMonth.of(today.getYear(), today.getMonth()); + + LocalDate startOfMonth = currentMonth.atDay(1); + + LocalDate maxSelectableDate = today; + + dateField.setMin(startOfMonth); + dateField.setMax(maxSelectableDate); + dateField.setValue(today); + + dateField.setLabel("Fecha"); } private void saveHoursWorked() { if (isFormValid()) { HoursWorked hoursWorked = getEntity(); setFieldValues(hoursWorked); - hoursWorkedService.saveHoursWorked(hoursWorked); + hoursWorkedService.save(hoursWorked); Notification.show("Horas trabajadas guardadas correctamente."); closeForm(); } @@ -129,11 +192,26 @@ public class HoursWorkedView extends BeanValidationForm implements hoursWorked.setTeam(teamField.getValue()); hoursWorked.setEmployee(employeeField.getValue()); hoursWorked.setActividad(activityField.getValue()); - //hoursWorked.setHours(hoursField.; + try { + double hours = Double.parseDouble(hoursField.getValue()); + hoursWorked.setHours(hours); + } catch (NumberFormatException e) { + Notification.show("Por favor, ingrese un número válido para las horas."); + } + if ("Otros".equals(tareasEspecificasDropdown.getValue())) { + // Maneja horas y actividad específica adicional + hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue()); + try { + double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue()); + hoursWorked.setHorasTareasEspecificas(horasEspecifica); + } catch (NumberFormatException e) { + Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica."); + } + } } private void closeForm() { - getUI().ifPresent(ui -> ui.navigate("hours-workedId")); + getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString())); } private boolean isFormValid() { @@ -141,12 +219,13 @@ public class HoursWorkedView extends BeanValidationForm implements teamField.getValue() != null && employeeField.getValue() != null && !activityField.isEmpty(); + } private void configureViewOrEditAction(String action) { - if ("edit".equals(action) && request != null) { + if ("edit".equals(action) && hoursWorked != null) { setFieldsReadOnly(false); - } else if ("view".equals(action) && request != null) { + } else if ("view".equals(action) && hoursWorked != null) { setFieldsReadOnly(true); saveButton.setEnabled(false); } @@ -159,6 +238,5 @@ public class HoursWorkedView extends BeanValidationForm implements activityField.setReadOnly(readOnly); } - private H3 createEmployeeHeader() { - return new H3("Empleado: " + (employee != null ? employee.getFirstName() + " " + employee.getLastName() : ""));} + } \ No newline at end of file diff --git a/src/main/java/com/primefactorsolutions/views/MainLayout.java b/src/main/java/com/primefactorsolutions/views/MainLayout.java index 420a9e4..8b23940 100644 --- a/src/main/java/com/primefactorsolutions/views/MainLayout.java +++ b/src/main/java/com/primefactorsolutions/views/MainLayout.java @@ -153,7 +153,7 @@ public class MainLayout extends AppLayout { LineAwesomeIcon.LIST_ALT.create())); SideNavItem timesheet = new SideNavItem("My Timesheet", TimesheetView.class, LineAwesomeIcon.HOURGLASS_START_SOLID.create()); - timesheet.addItem(new SideNavItem("Hours Worked List View", HoursWorkedListView.class, + timesheet.addItem(new SideNavItem("Registro de Horas Trabajadas", HoursWorkedListView.class, LineAwesomeIcon.ID_CARD_SOLID.create())); timesheet.addItem(new SideNavItem("Reporte Horas Trabajadas", ReporteView.class, LineAwesomeIcon.ID_CARD_SOLID.create())); diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 5cb7a98..cb9a79f 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -90,3 +90,6 @@ insert into time_off_request (id, version, employee_id, category, state, availab values ('12ec8b74-983d-4a17-b67e-134f45ae904c', 1, '5c1a7b82-832d-4f24-8377-54b77b91b6a8', 'AÑO_NUEVO', 'SOLICITADO', 1, '2025-01-01', '2025-01-01', '2025-01-01', 1, 0); insert into time_off_request (id, version, employee_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', 'LUNES_CARNAVAL', 'APROBADO', 1, '2024-02-12', '2024-02-12', '2024-02-12', 1, 0); + +INSERT INTO HOURS_WORKED (ID, VERSION, ACTIVIDAD, DATE, HORAS_TAREAS_ESPECIFICAS, HORASPENDIENTES, HOURS, TAREAS_ESPECIFICAS, TOTAL_HOURS, WEEK_NUMBER, EMPLOYEE_ID, TEAM_ID) +VALUES ('6d6b3a71-9b11-4526-8335-b089627a8cd6', 0, 'Scrum Meeting', '2024-11-15', 0.0, 0.0, 2.0, NULL, 0.0, 0, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');