diff --git a/package.json b/package.json index 3395e11..209a580 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "type": "module", "dependencies": { "@f0rce/ace-widget": "1.0.2", - "@polymer/polymer": "3.5.1", + "@polymer/polymer": "3.5.2", "@vaadin-component-factory/vcf-pdf-viewer": "2.0.1", "@vaadin/bundles": "24.5.1", "@vaadin/common-frontend": "0.0.19", @@ -19,29 +19,30 @@ "@vaadin/vaadin-usage-statistics": "2.1.3", "construct-style-sheets-polyfill": "3.1.0", "date-fns": "2.29.3", - "lit": "3.1.4", + "lit": "3.2.1", "print-js": "1.6.0", "proj4": "2.12.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-router-dom": "6.23.1" + "react-router-dom": "6.26.2" }, "devDependencies": { - "@babel/preset-react": "7.24.7", - "@rollup/plugin-replace": "5.0.7", - "@rollup/pluginutils": "5.1.0", - "@types/react": "18.3.3", - "@types/react-dom": "18.3.0", - "@vitejs/plugin-react": "4.3.1", - "async": "3.2.5", - "glob": "10.4.1", + "@babel/preset-react": "7.25.7", + "@preact/signals-react-transform": "0.4.0", + "@rollup/plugin-replace": "6.0.1", + "@rollup/pluginutils": "5.1.2", + "@types/react": "18.3.11", + "@types/react-dom": "18.3.1", + "@vitejs/plugin-react": "4.3.3", + "async": "3.2.6", + "glob": "10.4.5", "rollup-plugin-brotli": "3.1.0", "rollup-plugin-visualizer": "5.12.0", "strip-css-comments": "5.0.0", "transform-ast": "2.4.4", - "typescript": "5.4.5", - "vite": "5.3.3", - "vite-plugin-checker": "0.6.4", + "typescript": "5.6.3", + "vite": "5.4.9", + "vite-plugin-checker": "0.8.0", "workbox-build": "7.1.1", "workbox-core": "7.1.0", "workbox-precaching": "7.1.0" @@ -49,7 +50,7 @@ "vaadin": { "dependencies": { "@f0rce/ace-widget": "1.0.2", - "@polymer/polymer": "3.5.1", + "@polymer/polymer": "3.5.2", "@vaadin-component-factory/vcf-pdf-viewer": "2.0.1", "@vaadin/bundles": "24.5.1", "@vaadin/common-frontend": "0.0.19", @@ -64,34 +65,35 @@ "@vaadin/vaadin-usage-statistics": "2.1.3", "construct-style-sheets-polyfill": "3.1.0", "date-fns": "2.29.3", - "lit": "3.1.4", + "lit": "3.2.1", "print-js": "1.6.0", "proj4": "2.12.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-router-dom": "6.23.1" + "react-router-dom": "6.26.2" }, "devDependencies": { - "@babel/preset-react": "7.24.7", - "@rollup/plugin-replace": "5.0.7", - "@rollup/pluginutils": "5.1.0", - "@types/react": "18.3.3", - "@types/react-dom": "18.3.0", - "@vitejs/plugin-react": "4.3.1", - "async": "3.2.5", - "glob": "10.4.1", + "@babel/preset-react": "7.25.7", + "@preact/signals-react-transform": "0.4.0", + "@rollup/plugin-replace": "6.0.1", + "@rollup/pluginutils": "5.1.2", + "@types/react": "18.3.11", + "@types/react-dom": "18.3.1", + "@vitejs/plugin-react": "4.3.3", + "async": "3.2.6", + "glob": "10.4.5", "rollup-plugin-brotli": "3.1.0", "rollup-plugin-visualizer": "5.12.0", "strip-css-comments": "5.0.0", "transform-ast": "2.4.4", - "typescript": "5.4.5", - "vite": "5.3.3", - "vite-plugin-checker": "0.6.4", + "typescript": "5.6.3", + "vite": "5.4.9", + "vite-plugin-checker": "0.8.0", "workbox-build": "7.1.1", "workbox-core": "7.1.0", "workbox-precaching": "7.1.0" }, - "hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436" + "hash": "2dc40a4f634ae025081ca2239cba00b14a35fe94ab78ac0a4dd3023d882081d5" }, "overrides": { "@vaadin/bundles": "$@vaadin/bundles", diff --git a/src/main/java/com/primefactorsolutions/model/Actividad.java b/src/main/java/com/primefactorsolutions/model/Actividad.java deleted file mode 100644 index 6b0a4b1..0000000 --- a/src/main/java/com/primefactorsolutions/model/Actividad.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.primefactorsolutions.model; - -public final class Actividad { - private String nombre; - private double lunes; - private double martes; - private double miercoles; - private double jueves; - private double viernes; - private double sabado; - private double domingo; - private String tarea; - private double horas; - - public Actividad(final Builder builder) { - this.nombre = builder.nombre; - this.lunes = builder.lunes; - this.martes = builder.martes; - this.miercoles = builder.miercoles; - this.jueves = builder.jueves; - this.viernes = builder.viernes; - this.sabado = builder.sabado; - this.domingo = builder.domingo; - this.tarea = builder.tarea; - this.horas = builder.horas; - } - - public String getNombre() { - return nombre; - } - - public double getLunes() { - return lunes; - } - - public double getMartes() { - return martes; - } - - public double getMiercoles() { - return miercoles; - } - - public double getJueves() { - return jueves; - } - - public double getViernes() { - return viernes; - } - - public double getSabado() { - return sabado; - } - - public double getDomingo() { - return domingo; - } - - public String getTarea() { // Cambié aquí también - return tarea; - } - - public double getHoras() { - return horas; - } - - // Builder para crear instancias de Actividad - public static class Builder { - private String nombre; - private double lunes; - private double martes; - private double miercoles; - private double jueves; - private double viernes; - private double sabado; - private double domingo; - private String tarea; // Cambié 'tarea' por 'descripcion' - private double horas; - - public Builder tarea(final String tarea, final double horas) { - this.tarea = tarea; - this.horas = horas; - return this; - } - - public Builder tarea(final String tarea) { - this.tarea = tarea; - return this; - } - - public Builder nombre(final String nombre) { - this.nombre = nombre; - return this; - } - - public Builder lunes(final double horas) { - this.lunes = horas; - return this; - } - - public Builder martes(final double horas) { - this.martes = horas; - return this; - } - - public Builder miercoles(final double horas) { - this.miercoles = horas; - return this; - } - - public Builder jueves(final double horas) { - this.jueves = horas; - return this; - } - - public Builder viernes(final double horas) { - this.viernes = horas; - return this; - } - - public Builder sabado(final double horas) { - this.sabado = horas; - return this; - } - - public Builder domingo(final double horas) { - this.domingo = horas; - return this; - } - - public Actividad build() { - return new Actividad(this); - } - } -} diff --git a/src/main/java/com/primefactorsolutions/model/HoursWorked.java b/src/main/java/com/primefactorsolutions/model/HoursWorked.java index d02310f..e5ef3f0 100644 --- a/src/main/java/com/primefactorsolutions/model/HoursWorked.java +++ b/src/main/java/com/primefactorsolutions/model/HoursWorked.java @@ -1,41 +1,56 @@ package com.primefactorsolutions.model; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; -import java.util.UUID; +import java.time.LocalDate; +import java.time.temporal.WeekFields; +import java.util.List; +import java.util.Locale; +@Data @Entity +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) public class HoursWorked extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.UUID) - private UUID id; - @ManyToOne + @JoinColumn(name = "employee_id", nullable = true) private Employee employee; + @ManyToOne + @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 HoursWorked() { } - - public UUID getId() { - return id; + public static double calculateTotalHours(List activities) { + return activities.stream() + .mapToDouble(activity -> activity.hours + activity.horasTareasEspecificas) + .sum(); } - public void setId(final UUID id) { - this.id = id; + public static double calculatePendingHours(List activities) { + double totalHoursWorked = calculateTotalHours(activities); + return Math.max(0, 40 - totalHoursWorked); } public Employee getEmployee() { return employee; } - public void setEmployee(final Employee value) { - this.employee = value; + public void setEmployee(final Employee employee) { + this.employee = employee; } public int getWeekNumber() { @@ -45,12 +60,90 @@ public class HoursWorked extends BaseEntity { public void setWeekNumber(final int weekNumber) { this.weekNumber = weekNumber; } + public LocalDate getDate() { + return date; + } + + public void setDate(final LocalDate date) { + this.date = date; + if (date != null) { + WeekFields weekFields = WeekFields.of(Locale.getDefault()); + this.weekNumber = date.get(weekFields.weekOfWeekBasedYear()); + } + } + + public String getActividad() { + return actividad; + } + + public void setActividad(final String actividad) { + this.actividad = actividad; + } + + public double getHours() { + return hours; + } + + public void setHours(final double hours) { + this.hours = hours; + } public double getTotalHours() { - return totalHours; + double total = this.getHours(); + return totalHours + total; } public void setTotalHours(final double totalHours) { this.totalHours = totalHours; } + + public Team getTeam() { + return team; + } + + public void setTeam(final Team team) { + this.team = team; + } + + public String getTareasEspecificas() { + return tareasEspecificas; + } + + public void setTareasEspecificas(final String tareasEspecificas) { + this.tareasEspecificas = tareasEspecificas; + } + + public double getHorasTareasEspecificas() { + return horasTareasEspecificas; + } + + public void setHorasTareasEspecificas(final double horasTareasEspecificas) { + this.tareasEspecificas = tareasEspecificas; + } + public double getHoraspendientes() { + //double horasTrabajadas = this.getTotalHours() + this.getHorasTareasEspecificas(); + return 40; + } + + public void setHoraspendientes(final double horaspendientes) { + this.horaspendientes = horaspendientes; + } + + public double getHoursWorked() { + return hours; + } + + public void setHoursWorked(final double hoursWorked) { + this.hours = hoursWorked; + } + + public double getTotalHoursWorked() { + return totalHours; + } + + public void setTotalHoursWorked(final double totalHoursWorked) { + this.totalHours = totalHoursWorked; + } + + } diff --git a/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java b/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java index 6e0e69c..8662eb2 100644 --- a/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java +++ b/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java @@ -2,13 +2,15 @@ package com.primefactorsolutions.repositories; import com.primefactorsolutions.model.HoursWorked; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; +import java.time.LocalDate; import java.util.List; +import java.util.UUID; -@Repository -public interface HoursWorkedRepository extends JpaRepository { - // Puedes definir consultas personalizadas aquí si es necesario. + +public interface HoursWorkedRepository extends JpaRepository { List findByWeekNumber(int weekNumber); - + List findByDate(LocalDate date); + List findByEmployeeIdAndWeekNumber(UUID employeeId, int weekNumber); } + diff --git a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java index 2bfe882..80e4e88 100644 --- a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java +++ b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java @@ -2,10 +2,12 @@ package com.primefactorsolutions.service; import com.primefactorsolutions.model.HoursWorked; import com.primefactorsolutions.repositories.HoursWorkedRepository; +import org.apache.commons.beanutils.BeanComparator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.List; +import java.time.LocalDate; +import java.util.*; @Service public class HoursWorkedService { @@ -20,6 +22,20 @@ public class HoursWorkedService { return hoursWorkedRepository.findAll(); } + public double getTotalHoursWorkedByEmployeeForWeek(UUID employeeId, int weekNumber) { + List hoursWorkedList = hoursWorkedRepository.findByWeekNumber(weekNumber); + return hoursWorkedList.stream() + .filter(hw -> hw.getEmployee().getId().equals(employeeId)) + .mapToDouble(HoursWorked::getTotalHours) + .sum(); + } + + public HoursWorked findHoursWorked(final UUID id) { + Optional hoursWorked = hoursWorkedRepository.findById(id); + HoursWorked hw = hoursWorked.get(); + return hw; + } + public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) { return hoursWorkedRepository.save(hoursWorked); } @@ -28,13 +44,56 @@ public class HoursWorkedService { return hoursWorkedRepository.save(hoursWorked); } - public void deleteHoursWorked(final Long id) { - hoursWorkedRepository.deleteById(id); + public double getTotalHoursForEmployee(UUID employeeId, int weekNumber) { + List activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber); + return HoursWorked.calculateTotalHours(activities); + } + + public double getPendingHoursForEmployee(UUID employeeId, int weekNumber) { + List activities = hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber); + return HoursWorked.calculatePendingHours(activities); } public List findByWeekNumber(final int weekNumber) { return hoursWorkedRepository.findByWeekNumber(weekNumber); } -} + public List findByDate(final LocalDate date) { + return hoursWorkedRepository.findByDate(date); + } + public List findByDateAndWeekNumber(final LocalDate date, final int weekNumber) { + return hoursWorkedRepository.findByDate(date); + } + + public List findHoursWorkeds( + final int start, final int pageSize, final String sortProperty, final boolean asc) { + List hoursWorkeds = hoursWorkedRepository.findAll(); + + int end = Math.min(start + pageSize, hoursWorkeds.size()); + hoursWorkeds.sort(new BeanComparator<>(sortProperty)); + + if (!asc) { + Collections.reverse(hoursWorkeds); + } + + return hoursWorkeds.subList(start, end); + } + + public List findHoursWorkeds(final int start, final int pageSize) { + List hoursWorkeds = hoursWorkedRepository.findAll(); + + int end = Math.min(start + pageSize, hoursWorkeds.size()); + return hoursWorkeds.subList(start, end); + } + + public HoursWorked getHoursWorked(final UUID id) { + final Optional hoursWorked = hoursWorkedRepository.findById(id); + return hoursWorked.get(); + } + + public List findListHoursWorkedEmployee(final UUID employeeId, final int weekNumber) { + return hoursWorkedRepository.findByEmployeeIdAndWeekNumber(employeeId, weekNumber); + } + +} diff --git a/src/main/java/com/primefactorsolutions/service/ReportService.java b/src/main/java/com/primefactorsolutions/service/ReportService.java index 15f6405..a6de7e8 100644 --- a/src/main/java/com/primefactorsolutions/service/ReportService.java +++ b/src/main/java/com/primefactorsolutions/service/ReportService.java @@ -172,6 +172,5 @@ public class ReportService { cfg.setFallbackOnNullLoopVariable(false); cfg.setSQLDateAndTimeTimeZone(TimeZone.getDefault()); - return cfg; - } -} + return cfg;} +} \ 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 new file mode 100644 index 0000000..7de2d98 --- /dev/null +++ b/src/main/java/com/primefactorsolutions/views/HoursWorkedListView.java @@ -0,0 +1,248 @@ +package com.primefactorsolutions.views; + +import com.primefactorsolutions.model.Employee; +import com.primefactorsolutions.model.HoursWorked; +import com.primefactorsolutions.model.Team; +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.html.Main; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +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.joda.time.Hours; +import org.springframework.context.annotation.Scope; +import org.vaadin.firitin.components.grid.PagingGrid; + +import java.time.LocalDate; +import java.time.temporal.IsoFields; +import java.util.*; +import java.util.stream.Collectors; + +@SpringComponent +@PermitAll +@Scope("prototype") +@PageTitle("Registro de Horas Trabajadas") +@Route(value = "/hours-worked-list", layout = MainLayout.class) +public class HoursWorkedListView extends Main { + + private final HoursWorkedService hoursWorkedService; + private final EmployeeService employeeService; + private final TeamService teamService; + private final PagingGrid hoursWorkedGrid = new PagingGrid<>(); + private List employees = Collections.emptyList(); + private ComboBox employeeFilter; + private ComboBox teamFilter; + private UUID selectedEmployeeId; + + + public HoursWorkedListView(final HoursWorkedService hoursWorkedService, + final EmployeeService employeeService, + final TeamService teamService) { + this.hoursWorkedService = hoursWorkedService; + this.employeeService = employeeService; + this.teamService = teamService; + this.employees = employeeService.findAllEmployees(); + + initializeView(); + refreshGridListHoursWorked(null, null); + } + + private void refreshGridListHoursWorked(final Employee employee, + final Team team) { + hoursWorkedGrid.setPagingDataProvider((page, pageSize) -> { + int start = (int) (page * hoursWorkedGrid.getPageSize()); + List hoursWorkedList = fetchFilteredHoursWorked(start, pageSize, employee, team); + + double totalHours = hoursWorkedList.stream() + .mapToDouble(HoursWorked::getTotalHours) + .sum(); + + Notification.show("Total de horas trabajadas: " + totalHours, 3000, Notification.Position.BOTTOM_CENTER); + + return hoursWorkedList; + }); + hoursWorkedGrid.getDataProvider().refreshAll(); + } + + private List fetchFilteredHoursWorked(final int start, + final int pageSize, + final Employee employee, + final Team team) { + List filteredHoursWorked = hoursWorkedService.findAll(); + + if (employee != null && !"TODOS".equals(employee.getFirstName())) { + filteredHoursWorked = filteredHoursWorked.stream() + .filter(hw -> hw.getEmployee().getId().equals(employee.getId())) + .collect(Collectors.toList()); + } + + if (team != null && !"TODOS".equals(team.getName())) { + filteredHoursWorked = filteredHoursWorked.stream() + .filter(hw -> hw.getEmployee().getTeam() != null && hw.getEmployee().getTeam().getId().equals(team.getId())) + .collect(Collectors.toList()); + } + + for (HoursWorked hoursWorked : filteredHoursWorked) { + if (employee != null && hoursWorked.getEmployee().getId().equals(employee.getId())) { + LocalDate date = hoursWorked.getDate(); + int currentWeek = date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + + double totalWorkedInSameWeek = filteredHoursWorked.stream() + .filter(hw -> hw.getEmployee().getId().equals(employee.getId()) && + hw.getDate().get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) == currentWeek) + .mapToDouble(HoursWorked::getHours) + .sum(); + + double updatedPendingHours = totalWorkedInSameWeek - hoursWorked.getHours(); + hoursWorked.setHoraspendientes(updatedPendingHours); + } + } + + int end = Math.min(start + pageSize, filteredHoursWorked.size()); + return filteredHoursWorked.subList(start, end); + } + + private void initializeView() { + setupFilters(); + add(createAddHoursWorked()); + setupListHoursWorkedGrid(); + add(hoursWorkedGrid); + add(createActionButtons()); + } + + private void setupFilters() { + add(createEmployeeFilter()); + add(createTeamFilter()); + } + + private void setupListHoursWorkedGrid() { + hoursWorkedGrid.addColumn(hw -> hw.getDate() != null ? hw.getDate().toString() : "") + .setHeader("Fecha") + .setSortable(true); + hoursWorkedGrid.addColumn(hw -> hw.getWeekNumber()) + .setHeader("Semana") + .setSortable(true); + hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getFirstName() + " " + hw.getEmployee().getLastName()) + .setHeader("Empleado"); + hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getTeam() != null ? hw.getEmployee().getTeam().getName() : "Sin asignar") + .setHeader("Equipo"); + hoursWorkedGrid.addColumn(HoursWorked::getActividad).setHeader("Actividad"); + hoursWorkedGrid.addColumn(hw -> hw.getHours() ).setHeader("Total Horas").setSortable(true); + hoursWorkedGrid.addColumn(hw -> hw.getHoraspendientes() - calcularTotal(hw)).setHeader("Horas Pendientes").setSortable(true); + + hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); + hoursWorkedGrid.setPageSize(5); + hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> { + HoursWorked selectedHoursWorked = event.getValue(); + if (selectedHoursWorked != null) { + selectedEmployeeId = selectedHoursWorked.getEmployee().getId(); + } + }); + } + + private double calcularTotal(HoursWorked hoursWorked){ + List listHoursworkedemploye = hoursWorkedService.findListHoursWorkedEmployee( + hoursWorked.getEmployee().getId(), hoursWorked.getWeekNumber()); + return calculateTotalUtilized(listHoursworkedemploye); + } + + private double calculateTotalUtilized(final List employeeRequests) { + return employeeRequests.stream() + .filter(Objects::nonNull) + .mapToDouble(hoursworked -> hoursworked.getHours()) + .sum(); + } + + 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) { + Button button = new Button(label); + button.addClickListener(event -> onClickAction.run()); + return button; + } + + private Button createAddHoursWorked() { + return createButton("Agregar Actividad", this::navigateToHours); + } + + 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(team -> getTeamLabel(team)); + teamFilter.setValue(teams.getFirst()); + teamFilter.addValueChangeListener(event -> + refreshGridListHoursWorked( + employeeFilter.getValue(), + event.getValue() + ) + ); + return teamFilter; + } + + private String getTeamLabel(final Team team) { + return team != null && !"TODOS".equals(team.getName()) ? team.getName() : "TODOS"; + } + + private Employee createAllEmployeesOption() { + Employee allEmployeesOption = new Employee(); + allEmployeesOption.setFirstName("TODOS"); + return allEmployeesOption; + } + + private Team createAllTeamsOption() { + Team allTeamsOption = new Team(); + allTeamsOption.setName("TODOS"); + return allTeamsOption; + } +} diff --git a/src/main/java/com/primefactorsolutions/views/HoursWorkedMonthView.java b/src/main/java/com/primefactorsolutions/views/HoursWorkedMonthView.java deleted file mode 100644 index 88d86e6..0000000 --- a/src/main/java/com/primefactorsolutions/views/HoursWorkedMonthView.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.primefactorsolutions.views; - -import com.primefactorsolutions.model.Employee; -import com.primefactorsolutions.model.Actividad; -import com.primefactorsolutions.service.EmployeeService; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.datepicker.DatePicker; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.html.Label; -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.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.spring.annotation.SpringComponent; -import jakarta.annotation.security.PermitAll; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Scope; - -import java.time.LocalDate; -import java.util.List; - -@SpringComponent -@PermitAll -@Scope("prototype") -@PageTitle("Hours Worked Month") -@Route(value = "/hours-worked-month/me", layout = MainLayout.class) -public class HoursWorkedMonthView extends VerticalLayout { - private final EmployeeService employeeService; - private final ComboBox employeeComboBox = new ComboBox<>("Empleado"); - private final ComboBox equipoDropdown = new ComboBox<>("Equipo"); - private final Grid grid = new Grid<>(Actividad.class); - - private final Label totalCompletadoLabel = new Label(); - private final Label horasPendientesLabel = new Label(); - private final Label totalAcumuladasLabel = new Label(); - private final Label horasAdeudadasLabel = new Label(); - private final Button actualizarButton = new Button("Actualizar"); - private final Button guardarButton = new Button("Guardar"); - private final Button cerrarButton = new Button("Cerrar"); - - private LocalDate selectedMonth; - - @Autowired - public HoursWorkedMonthView(final EmployeeService employeeService) { - this.employeeService = employeeService; - configurarVista(); - } - - private void configurarVista() { - DatePicker monthPicker = new DatePicker("Selecciona un mes"); - monthPicker.setValue(LocalDate.now()); - monthPicker.addValueChangeListener(event -> { - selectedMonth = event.getValue().withDayOfMonth(1); - //cargarDatosMes(selectedMonth); - }); - - equipoDropdown.setItems("Equipo 1", "Equipo 2", "Equipo 3"); - equipoDropdown.setWidth("250px"); - - setEmployeeComboBoxProperties(); - - configurarGrid(); - - actualizarButton.addClickListener(event -> actualizarDatos()); - guardarButton.addClickListener(event -> guardarActividades()); - cerrarButton.addClickListener(event -> closeView()); - - HorizontalLayout headerLayout = new HorizontalLayout(monthPicker, equipoDropdown, employeeComboBox); - add( - headerLayout, grid, totalCompletadoLabel, - horasPendientesLabel, totalAcumuladasLabel, - horasAdeudadasLabel, actualizarButton, - guardarButton, cerrarButton); - } - - private void setEmployeeComboBoxProperties() { - employeeComboBox.setWidth("250px"); - employeeComboBox.setPlaceholder("Buscar empleado..."); - employeeComboBox.setItems(employeeService.findAllEmployees()); - employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName()); - } - - private void configurarGrid() { - grid.removeAllColumns(); - grid.addColumn(Actividad::getLunes).setHeader("Lunes"); - grid.addColumn(Actividad::getMartes).setHeader("Martes"); - grid.addColumn(Actividad::getMiercoles).setHeader("Miércoles"); - grid.addColumn(Actividad::getJueves).setHeader("Jueves"); - grid.addColumn(Actividad::getViernes).setHeader("Viernes"); - grid.addColumn(Actividad::getSabado).setHeader("Sábado"); - grid.addColumn(Actividad::getDomingo).setHeader("Domingo"); - grid.addColumn(this::calcularTotalPorDia).setHeader("Total Semanal").setKey("totalSemanal"); - } - -// private void cargarDatosMes(final LocalDate month) { -// List actividadesDelMes = obtenerActividadesDelMes(month); -// grid.setItems(actividadesDelMes); -// -// double totalCompletado = calcularTotalCompletado(actividadesDelMes); -// double horasPendientes = calcularHorasPendientes(totalCompletado); -// double totalAcumuladas = 166; -// double horasAdeudadas = 2; -// -// totalCompletadoLabel.setText("Prom. Hrs/Semana Completadas: " + totalCompletado); -// horasPendientesLabel.setText("Prom. Hrs/Semana Pendientes: " + horasPendientes); -// totalAcumuladasLabel.setText("Total Hrs./Mes Acumuladas: " + totalAcumuladas); -// horasAdeudadasLabel.setText("Total Hrs./Mes Adeudadas: " + horasAdeudadas); -// } - -// private List obtenerActividadesDelMes(final LocalDate month) { -// LocalDate startOfMonth = month.with(TemporalAdjusters.firstDayOfMonth()); -// LocalDate endOfMonth = month.with(TemporalAdjusters.lastDayOfMonth()); -// -// List actividadesDelMes = new ArrayList<>(); -// -// for (LocalDate date = startOfMonth; date.isBefore(endOfMonth.plusDays(1)); date = date.plusDays(1)) { -// Actividad actividad = new Actividad.Builder() -// .lunes(0) -// .martes(0) -// .miercoles(0) -// .jueves(0) -// .viernes(0) -// .sabado(0) -// .domingo(0) -// .build(); -// actividadesDelMes.add(actividad); -// } -// -// return actividadesDelMes; -// } - - private double calcularTotalCompletado(final List actividades) { - return actividades.stream() - .mapToDouble(this::calcularTotalPorDia) - .sum(); - } - - private double calcularTotalPorDia(final Actividad actividad) { - return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles() - + actividad.getJueves() + actividad.getViernes() + actividad.getSabado() - + actividad.getDomingo(); - } - - private double calcularHorasPendientes(final double totalCompletado) { - return 40 - totalCompletado; - } - - private void actualizarDatos() { - Notification.show("Datos actualizados."); - } - - private void guardarActividades() { - Notification.show("Actividades guardadas correctamente."); - } - - private void closeView() { - getUI().ifPresent(ui -> ui.navigate("")); - } -} diff --git a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java index 7c1f644..8387740 100644 --- a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java +++ b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java @@ -1,419 +1,255 @@ package com.primefactorsolutions.views; -import com.primefactorsolutions.model.Actividad; import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.HoursWorked; +import com.primefactorsolutions.model.Team; import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.HoursWorkedService; -import com.vaadin.flow.component.datepicker.DatePicker; -import com.vaadin.flow.component.grid.Grid; +import com.primefactorsolutions.service.TeamService; +import com.vaadin.flow.component.Component; +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.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.*; import com.vaadin.flow.spring.annotation.SpringComponent; import jakarta.annotation.security.PermitAll; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Scope; -import com.vaadin.flow.component.html.Label; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.vaadin.firitin.components.datepicker.VDatePicker; +import org.vaadin.firitin.form.BeanValidationForm; - -import java.time.DayOfWeek; import java.time.LocalDate; +import java.time.YearMonth; import java.time.temporal.IsoFields; -import java.time.temporal.WeekFields; import java.util.ArrayList; import java.util.List; -import java.util.Locale; +import java.util.UUID; @SpringComponent @PermitAll @Scope("prototype") -@PageTitle("Hours Worked") -@Route(value = "/hours-worked/me", layout = MainLayout.class) -public class HoursWorkedView extends VerticalLayout { - private final List actividades = new ArrayList<>(); - private final List actividadesEspecificas = new ArrayList<>(); // Nueva lista para tareas específicas - private final Grid grid = new Grid<>(Actividad.class); - private final Grid gridActividadesEspecificas = new Grid<>(Actividad.class); +@PageTitle("Horas Trabajadas") +@Route(value = "/hours-worked-list/:hours-workedId?/:action?", layout = MainLayout.class) +public class HoursWorkedView extends BeanValidationForm implements HasUrlParameter { + private final VDatePicker dateField = new VDatePicker("Fecha"); + private final ComboBox teamField = new ComboBox<>("Equipo"); + 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 ComboBox employeeComboBox = new ComboBox<>("Employee"); - private final ComboBox equipoDropdown = new ComboBox<>("Equipo"); - - private final ComboBox tareasEspecificasDropdown = new ComboBox<>("Tareas Específicas"); - private final TextField tareaEspecificaInput = new TextField("Especificar Tarea"); - private TextField horasTareaInput = crearCampoHora("Horas Tareas"); - - private LocalDate selectedStartOfWeek; - private int weekNumber; - - @Autowired - private final EmployeeService employeeService; - @Autowired - private final HoursWorkedService hoursWorkedService; - - private final Label fechasLabel = new Label("Selecciona una semana para ver las fechas."); + 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 Label horasPendientesLabel = new Label(); - private final Label numeroSemanaLabel = new Label("Número de la Semana: "); + private final HoursWorkedService hoursWorkedService; + private final EmployeeService employeeService; + private final TeamService teamService; + private HoursWorked hoursWorked; + private Employee employee; - private DatePicker fechaPicker = new DatePicker("Selecciona una fecha"); - @Autowired - private InternalResourceViewResolver defaultViewResolver; - @Qualifier("defaultServletHandlerMapping") - @Autowired - private HandlerMapping defaultServletHandlerMapping; + private Button saveButton; - - public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) { - this.employeeService = employeeService; + public HoursWorkedView(final HoursWorkedService hoursWorkedService, + final EmployeeService employeeService, + final TeamService teamService) { + super(HoursWorked.class); this.hoursWorkedService = hoursWorkedService; - configurarVista(); - configurarGrid(); - configurarGridActividadesEspecificas(); - cargarDatos(); - configurarTareasEspecificas(); + this.employeeService = employeeService; + this.teamService = teamService; + + initializeDateField(); + initializeTeamField(); + initializeEmployeeField(); + configureTareasEspecificas(); + } - private void configurarTareasEspecificas() { - tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", - "Aprendizajes", "Proyectos PFS", "Otros"); + @Override + public void setParameter(final BeforeEvent beforeEvent, final String action) { + final RouteParameters params = beforeEvent.getRouteParameters(); + final String s = params.get("hours-workedId").orElse(null); + + if ("new".equals(action)) { + setEntityWithEnabledSave(new HoursWorked()); + } else { + UUID hoursWorkedId = UUID.fromString(s); + var hoursWorked = hoursWorkedService.getHoursWorked(hoursWorkedId); + setEntityWithEnabledSave(hoursWorked); + + if ("edit".equals(action) && !s.isEmpty()) { + saveButton.setVisible(true); + } else if ("view".equals(action) && !s.isEmpty()) { + saveButton.setVisible(false); + } + } + } + + @Override + protected List getFormComponents() { + return List.of( + 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(); - // Activa el campo de texto si la opción seleccionada es "Otros" - tareaEspecificaInput.setVisible("Otros".equals(selected)); - if (!"Otros".equals(selected)) { + boolean isOtros = "Otros".equals(selected); + tareaEspecificaInput.setVisible(isOtros); + horasTareaEspecificaField.setVisible(true); + if (!isOtros) { tareaEspecificaInput.clear(); + horasTareaEspecificaField.clear(); } }); tareaEspecificaInput.setVisible(false); + horasTareaEspecificaField.setVisible(false); } - private void cargarDatos() { - if (selectedStartOfWeek != null && weekNumber > 0) { - actividades.clear(); - actividadesEspecificas.clear(); - List listaDeHorasTrabajadas = obtenerDatos(); - grid.setItems(actividades); - gridActividadesEspecificas.setItems(actividadesEspecificas); - calcularTotalHoras(listaDeHorasTrabajadas); - } + protected Button createSaveButton() { + saveButton = new Button("Guardar"); + saveButton.addClickListener(event -> saveHoursWorked()); + return saveButton; } - private void setEmployeeComboBoxProperties() { - employeeComboBox.setWidth("250px"); - employeeComboBox.setPlaceholder("Buscar empleado..."); - employeeComboBox.setItems(employeeService.findAllEmployees()); - employeeComboBox.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName()); - employeeComboBox.setAllowCustomValue(false); - employeeComboBox.addCustomValueSetListener(event -> - Notification.show("Selecciona un empleado válido de la lista.") + protected Button createCloseButton() { + Button closeButton = new Button("Cerrar"); + closeButton.addClickListener(event -> closeForm()); + return closeButton; + } + + private void initializeTeamField() { + List teams = new ArrayList<>(teamService.findAllTeams()); + teamField.setItems(teamService.findAllTeams()); + teamField.setItemLabelGenerator(Team::getName); + teamField.setValue(teams.getFirst()); + teamField.addValueChangeListener(event -> { + if (teams != null) { + employeeField.getValue(); + event.getValue(); + } + } ); - - employeeComboBox.addValueChangeListener(event -> { - Employee selectedEmployee = event.getValue(); - if (selectedEmployee != null) { - Notification.show("Empleado seleccionado: " - + selectedEmployee.getFirstName() + " " - + selectedEmployee.getLastName()); - } - }); } - private int getWeekOfYear(final LocalDate date) { - return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + 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 void configurarVista() { - fechaPicker.addValueChangeListener(event -> { + 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.addValueChangeListener(event -> { LocalDate selectedDate = event.getValue(); if (selectedDate != null) { - selectedStartOfWeek = getStartOfWeek(selectedDate); - LocalDate endOfWeek = selectedStartOfWeek.plusDays(6); - weekNumber = getWeekOfYear(selectedDate); - fechasLabel.setText("Semana del " + selectedStartOfWeek + " al " + endOfWeek); - numeroSemanaLabel.setText("Número de la Semana: " + weekNumber); - cargarDatos(); + int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + Notification.show("Número de la semana: " + weekNumber, 3000, Notification.Position.BOTTOM_CENTER); + if (hoursWorked != null) { + hoursWorked.setWeekNumber(weekNumber); + } } }); - - Button verMesButton = new Button("Ver Mes", event -> { - getUI().ifPresent(ui -> ui.navigate(HoursWorkedMonthView.class)); - }); - - equipoDropdown.setItems("ABC", "DEF", "XYZ"); - equipoDropdown.setWidth("250px"); - - equipoDropdown.addValueChangeListener(event -> { - String selectedEquipo = event.getValue(); - if (selectedEquipo != null) { - // Filtra la lista de empleados según el equipo seleccionado - List filteredEmployees = employeeService.findEmployeesByTeam(selectedEquipo); - employeeComboBox.setItems(filteredEmployees); - } - }); - - setEmployeeComboBoxProperties(); - - HorizontalLayout filtersLayout = new HorizontalLayout(equipoDropdown, employeeComboBox); - filtersLayout.setSpacing(true); - - HorizontalLayout actividadFormLayout = configurarFormularioActividades(); - HorizontalLayout tareasEspecificasLayout = configurarTareasEspecificasLayout(); - - - Button actualizarButton = new Button("Actualizar Totales", event -> actualizarTotales()); - Button guardarButton = new Button("Guardar", event -> guardarActividades()); - Button cerrarButton = new Button("Cerrar", event -> this.closeView()); - - HorizontalLayout buttonsLayout = new HorizontalLayout(actualizarButton, guardarButton, - cerrarButton, verMesButton); - - VerticalLayout totalesLayout = new VerticalLayout(totalCompletadoLabel, horasPendientesLabel); - totalesLayout.setSpacing(true); - totalesLayout.setPadding(true); - - add(fechaPicker, fechasLabel, numeroSemanaLabel, filtersLayout, actividadFormLayout, - tareasEspecificasLayout, grid, gridActividadesEspecificas, buttonsLayout, totalesLayout); } - private void configurarGrid() { - grid.removeAllColumns(); - grid.setItems(actividades); - grid.addColumn(Actividad::getNombre).setHeader("Actividad"); - grid.addColumn(Actividad::getLunes).setHeader("Lunes"); - grid.addColumn(Actividad::getMartes).setHeader("Martes"); - grid.addColumn(Actividad::getMiercoles).setHeader("Miércoles"); - grid.addColumn(Actividad::getJueves).setHeader("Jueves"); - grid.addColumn(Actividad::getViernes).setHeader("Viernes"); - grid.addColumn(Actividad::getSabado).setHeader("Sábado"); - grid.addColumn(Actividad::getDomingo).setHeader("Domingo"); - grid.addColumn(this::calcularTotalPorDia).setHeader("Total Día").setKey("totalDia"); - } - - private void configurarGridActividadesEspecificas() { - gridActividadesEspecificas.removeAllColumns(); - gridActividadesEspecificas.setItems(actividadesEspecificas); - gridActividadesEspecificas.addColumn(Actividad::getTarea).setHeader("Actividad"); - gridActividadesEspecificas.addColumn(Actividad::getLunes).setHeader("Lunes"); - gridActividadesEspecificas.addColumn(Actividad::getMartes).setHeader("Martes"); - gridActividadesEspecificas.addColumn(Actividad::getMiercoles).setHeader("Miércoles"); - gridActividadesEspecificas.addColumn(Actividad::getJueves).setHeader("Jueves"); - gridActividadesEspecificas.addColumn(Actividad::getViernes).setHeader("Viernes"); - gridActividadesEspecificas.addColumn(Actividad::getSabado).setHeader("Sábado"); - gridActividadesEspecificas.addColumn(Actividad::getDomingo).setHeader("Domingo"); - gridActividadesEspecificas.addColumn(this::calcularTotalPorDia).setHeader("Total Día Específico") - .setKey("totalDiaEspecifico"); - } - - private HorizontalLayout configurarFormularioActividades() { - TextField actividadNombre = new TextField("Actividad"); - actividadNombre.setWidth("200px"); - - TextField horasInput = crearCampoHora("Horas"); - - Button agregarActividadButton = new Button("Agregar Actividad", e -> { - try { - LocalDate selectedDate = fechaPicker.getValue(); - if (selectedDate == null) { - Notification.show("Por favor, selecciona una fecha."); - return; - } - DayOfWeek selectedDay = selectedDate.getDayOfWeek(); - - Actividad.Builder actividadBuilder = new Actividad.Builder() - .nombre(actividadNombre.getValue()); - - double horas = parseHoras(horasInput.getValue()); - switch (selectedDay) { - case MONDAY -> actividadBuilder.lunes(horas); - case TUESDAY -> actividadBuilder.martes(horas); - case WEDNESDAY -> actividadBuilder.miercoles(horas); - case THURSDAY -> actividadBuilder.jueves(horas); - case FRIDAY -> actividadBuilder.viernes(horas); - case SATURDAY -> actividadBuilder.sabado(horas); - case SUNDAY -> actividadBuilder.domingo(horas); - default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay); - } - - String tareaSeleccionada = tareasEspecificasDropdown.getValue(); - double horasTarea = parseHoras(horasTareaInput.getValue()); - - if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty()) { - actividadBuilder.tarea(tareaSeleccionada).lunes(horasTarea); - Actividad nuevaActividadEspecifica = actividadBuilder.build(); - actividadesEspecificas.add(nuevaActividadEspecifica); - gridActividadesEspecificas.setItems(actividadesEspecificas); - } else { - Actividad nuevaActividad = actividadBuilder.build(); - actividades.add(nuevaActividad); - grid.setItems(actividades); - } - actualizarTotales(); - Notification.show("Actividad agregada correctamente"); - actividadNombre.clear(); - horasInput.clear(); - tareasEspecificasDropdown.clear(); - horasTareaInput.clear(); - } catch (NumberFormatException ex) { - Notification.show("Error: Por favor ingresa números válidos para las horas."); - } - }); - - return new HorizontalLayout(actividadNombre, horasInput, agregarActividadButton); - } - - private HorizontalLayout configurarTareasEspecificasLayout() { - - Button agregarTareaButton = new Button("Agregar Tarea PFS", e -> { - try { - String tareaSeleccionada = tareasEspecificasDropdown.getValue(); - String tareaNombre = "Otros" - .equals(tareaSeleccionada) ? tareaEspecificaInput - .getValue() : tareaSeleccionada; - - if (tareaNombre == null || tareaNombre.isEmpty()) { - Notification.show("Por favor, especifica la tarea."); - return; - } - - double horasTarea = parseHoras(horasTareaInput.getValue()); - if (horasTarea <= 0) { - Notification.show("Por favor, ingresa un número válido para las horas."); - return; - } - - if (tareaSeleccionada != null && !tareaSeleccionada.isEmpty() && horasTarea > 0) { - LocalDate selectedDate = fechaPicker.getValue(); - if (selectedDate == null) { - Notification.show("Selecciona una fecha para asignar la tarea."); - return; - } - - DayOfWeek selectedDay = selectedDate.getDayOfWeek(); - Actividad.Builder actividadBuilder = new Actividad.Builder().tarea(tareaNombre); - - switch (selectedDay) { - case MONDAY -> actividadBuilder.lunes(horasTarea); - case TUESDAY -> actividadBuilder.martes(horasTarea); - case WEDNESDAY -> actividadBuilder.miercoles(horasTarea); - case THURSDAY -> actividadBuilder.jueves(horasTarea); - case FRIDAY -> actividadBuilder.viernes(horasTarea); - case SATURDAY -> actividadBuilder.sabado(horasTarea); - case SUNDAY -> actividadBuilder.domingo(horasTarea); - default -> throw new IllegalArgumentException("Día seleccionado no válido: " + selectedDay); - } - - Actividad nuevaActividadEspecifica = actividadBuilder.build(); - actividadesEspecificas.add(nuevaActividadEspecifica); - - gridActividadesEspecificas.setItems(actividadesEspecificas); - actualizarTotales(); - - tareasEspecificasDropdown.clear(); - tareaEspecificaInput.clear(); - horasTareaInput.clear(); - - Notification.show("Tarea específica agregada correctamente"); - } else { - Notification.show("Selecciona una tarea y asegúrate de ingresar horas válidas."); - } - } catch (NumberFormatException ex) { - Notification.show("Error: Por favor ingresa un número válido para las horas."); - } - }); - - HorizontalLayout layout = new HorizontalLayout(tareasEspecificasDropdown, - tareaEspecificaInput, horasTareaInput, agregarTareaButton); - layout.setSpacing(true); - return layout; - } - - private TextField crearCampoHora(final String placeholder) { - TextField field = new TextField(placeholder); - field.setWidth("80px"); - field.setPlaceholder("0.0"); - return field; - } - - private double parseHoras(final String value) { - if (value == null || value.trim().isEmpty()) { - return 0.0; + private void saveHoursWorked() { + if (isFormValid()) { + HoursWorked hoursWorked = getEntity(); + setFieldValues(hoursWorked); + hoursWorkedService.save(hoursWorked); + Notification.show("Horas trabajadas guardadas correctamente."); + closeForm(); } - return Double.parseDouble(value); } - private LocalDate getStartOfWeek(final LocalDate date) { - WeekFields weekFields = WeekFields.of(Locale.getDefault()); - return date.with(weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue()); - } - - private double calcularTotalPorDia(final Actividad actividad) { - return actividad.getLunes() + actividad.getMartes() + actividad.getMiercoles() - + actividad.getJueves() + actividad.getViernes() + actividad.getSabado() + actividad.getDomingo(); - } - - private void actualizarTotales() { - double totalActividadesGenerales = actividades.stream() - .mapToDouble(this::calcularTotalPorDia) - .sum(); - double totalActividadesEspecificas = actividadesEspecificas.stream() - .mapToDouble(this::calcularTotalPorDia) - .sum(); - double totalFinal = totalActividadesGenerales + totalActividadesEspecificas; - double horasPendientes = 40 - totalFinal; - - totalCompletadoLabel.setText("Total Hrs/Semana Completadas: " + totalFinal); - horasPendientesLabel.setText("Horas Pendientes: " + horasPendientes); - } - - private void guardarActividades() { - Employee selectedEmployee = employeeComboBox.getValue(); - String selectedEquipo = equipoDropdown.getValue(); - - if (selectedEmployee == null || selectedEquipo == null) { - Notification.show("Por favor selecciona un equipo y un empleado."); - return; - } - - double totalHorasSemana = actividades.stream() - .mapToDouble(this::calcularTotalPorDia) - .sum(); - - HoursWorked hoursWorked = new HoursWorked(); - hoursWorked.setEmployee(selectedEmployee); - hoursWorked.setWeekNumber(weekNumber); - hoursWorked.setTotalHours(totalHorasSemana); - + private void setFieldValues(final HoursWorked hoursWorked) { + hoursWorked.setDate(dateField.getValue()); + hoursWorked.setTeam(teamField.getValue()); + hoursWorked.setEmployee(employeeField.getValue()); + hoursWorked.setActividad(activityField.getValue()); try { - hoursWorkedService.saveHoursWorked(hoursWorked); // Usa saveHoursWorked directamente - Notification.show("Actividades guardadas correctamente."); - System.out.println(hoursWorked); - } catch (Exception e) { - Notification.show("Error al guardar actividades: " + e.getMessage()); + 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())) { + hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue()); + try { + double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue()); + hoursWorked.setHorasTareasEspecificas(horasEspecifica); + double totalHoras = hoursWorked.getHours() + horasEspecifica; + hoursWorked.setTotalHours(totalHoras); + } catch (NumberFormatException e) { + Notification.show("Por favor, ingrese un número válido para las horas de la tarea específica."); + } } } - private double calcularTotalHoras(final List listaDeHorasTrabajadas) { - return listaDeHorasTrabajadas.stream() - .mapToDouble(HoursWorked::getTotalHours) - .sum(); + private void closeForm() { + getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString())); } - private List obtenerDatos() { - return new ArrayList<>(); + private boolean isFormValid() { + return dateField.getValue() != null + && + teamField.getValue() != null + && + employeeField.getValue() != null + && + !activityField.isEmpty(); + } - private void closeView() { - getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class)); + private void configureViewOrEditAction(final String action) { + if ("edit".equals(action) && hoursWorked != null) { + setFieldsReadOnly(false); + } else if ("view".equals(action) && hoursWorked != null) { + setFieldsReadOnly(true); + saveButton.setEnabled(false); + } } -} \ No newline at end of file + + private void setFieldsReadOnly(final boolean readOnly) { + dateField.setReadOnly(readOnly); + teamField.setReadOnly(readOnly); + employeeField.setReadOnly(readOnly); + activityField.setReadOnly(readOnly); + } +} diff --git a/src/main/java/com/primefactorsolutions/views/MainLayout.java b/src/main/java/com/primefactorsolutions/views/MainLayout.java index a6dfaa0..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("Horas Trabajadas", HoursWorkedView.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/java/com/primefactorsolutions/views/ReporteView.java b/src/main/java/com/primefactorsolutions/views/ReporteView.java index 1594cc5..653724f 100644 --- a/src/main/java/com/primefactorsolutions/views/ReporteView.java +++ b/src/main/java/com/primefactorsolutions/views/ReporteView.java @@ -50,7 +50,6 @@ public class ReporteView extends VerticalLayout { private final Span semanaInfoSpan = new Span(); - // Obtener el año actual private int currentYear = LocalDate.now().getYear(); @@ -73,25 +72,22 @@ public class ReporteView extends VerticalLayout { // Configurar el ComboBox de semanas initializeSemanaComboBox(); - // Listener para actualizar `semanaInfoSpan` con la selección del usuario en `semanaComboBox` + // Listener para actualizar semanaInfoSpan con la selección del usuario en semanaComboBox semanaComboBox.addValueChangeListener(event -> { String selectedWeek = event.getValue(); - semanaInfoSpan.setText(selectedWeek != null - ? selectedWeek : "Selecciona una semana"); + semanaInfoSpan.setText(selectedWeek != null ? selectedWeek : "Selecciona una semana"); }); Button reportButton = new Button("Generar Reporte de Horas Trabajadas", event -> generateHoursWorkedReport()); - HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, - semanaComboBox, reportButton); + HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaComboBox, reportButton); add(filtersLayout); - // Añadir `headerLayout` al diseño principal para el encabezado dinámico + // Añadir headerLayout al diseño principal para el encabezado dinámico add(headerLayout); updateHeaderLayout(null, null); - grid.addColumn(map -> map.get("ID")).setHeader("ID") - .getElement().getStyle().set("font-weight", "bold"); + grid.addColumn(map -> map.get("ID")).setHeader("ID").getElement().getStyle().set("font-weight", "bold"); grid.addColumn(map -> map.get("Employee ID")).setHeader("Employee ID"); grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado"); grid.addColumn(map -> map.get("Horas Trabajadas")).setHeader("Horas Trabajadas"); @@ -105,8 +101,7 @@ public class ReporteView extends VerticalLayout { int year = LocalDate.now().getYear(); LocalDate startOfYear = LocalDate.of(year, 1, 5); // Suponemos que la semana comienza el 5 de enero. - List semanas = startOfYear.datesUntil(LocalDate - .of(year + 1, 1, 1), + List semanas = startOfYear.datesUntil(LocalDate.of(year + 1, 1, 1), java.time.Period.ofWeeks(1)) .map(date -> { int weekNumber = date.get(WeekFields.of(DayOfWeek.MONDAY, 1) @@ -133,27 +128,25 @@ public class ReporteView extends VerticalLayout { String selectedWeek = semanaComboBox.getValue(); if (selectedEquipo == null || selectedWeek == null) { Notification.show("Por favor, selecciona un equipo y una semana para generar el reporte.", - 3000, - Notification.Position.MIDDLE); + 3000, Notification.Position.MIDDLE); return; } - int weekNumber = Integer.parseInt(selectedWeek.split(" ")[1] - .replace(":", "")); - LocalDate selectedDate = LocalDate.now() - .with(WeekFields.of(DayOfWeek.FRIDAY, 1) - .weekOfWeekBasedYear(), weekNumber); + int weekNumber = Integer.parseInt(selectedWeek.split(" ")[1].replace(":", "")); + LocalDate selectedDate = LocalDate.now().with(WeekFields.of(DayOfWeek.FRIDAY, 1) + .weekOfWeekBasedYear(), weekNumber); updateHeaderLayout(selectedEquipo, selectedDate); List hoursWorkedList = hoursWorkedService.findAll().stream() - .filter(hw -> hw.getEmployee().getTeam().getId() - .equals(selectedEquipo.getId()) && hw.getWeekNumber() == weekNumber) + .filter(hw -> hw.getEmployee().getTeam().getId().equals(selectedEquipo + .getId()) && hw.getWeekNumber() == weekNumber) .collect(Collectors.toList()); + System.out.println(hoursWorkedList); if (hoursWorkedList.isEmpty()) { + Notification.show("No hay horas trabajadas disponibles para generar el reporte.", - 3000, - Notification.Position.MIDDLE); + 3000, Notification.Position.MIDDLE); return; } @@ -164,8 +157,8 @@ public class ReporteView extends VerticalLayout { map.put("Employee ID", hoursWorked.getEmployee().getId().toString()); map.put("Empleado", hoursWorked.getEmployee().getFirstName() + " " + hoursWorked.getEmployee().getLastName()); - map.put("Horas Trabajadas", hoursWorked.getTotalHours()); - map.put("Horas Pendientes", 40 - hoursWorked.getTotalHours()); + map.put("Horas Trabajadas", hoursWorked.getHours()); + map.put("Horas Pendientes", 40 - hoursWorked.getHours()); map.put("Observaciones", ""); return map; }) @@ -188,8 +181,8 @@ public class ReporteView extends VerticalLayout { String formattedEndDate = endOfWeek.getDayOfMonth() + " de " + endOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault()); - headerLayout.add(new Span("Informe " + String.format("%03d", weekNumber) - + "/" + currentYear) {{ + headerLayout.add(new Span("Informe " + + String.format("%03d", weekNumber) + "/" + currentYear) {{ getStyle().set("font-size", "24px"); getStyle().set("font-weight", "bold"); }}); @@ -213,14 +206,13 @@ public class ReporteView extends VerticalLayout { } } - private void generateExcelDownloadLink(final List> data, - final int weekNumber) { + private void generateExcelDownloadLink(final List> data, final int weekNumber) { try { List headers = List.of("ID", "Employee ID", "Empleado", "Horas Trabajadas", "Horas Pendientes", "Observaciones"); String selectedTeam = equipoComboBox.getValue().getName(); - byte[] excelBytes = reportService.writeAsExcel("hours_worked_report", - headers, data, selectedTeam, weekNumber, currentYear); + byte[] excelBytes = reportService.writeAsExcel( + "hours_worked_report", headers, data, selectedTeam, weekNumber, currentYear); StreamResource excelResource = new StreamResource("hours_worked_report.xlsx", () -> new ByteArrayInputStream(excelBytes)); @@ -239,15 +231,6 @@ public class ReporteView extends VerticalLayout { private int getWeekOfYear(final LocalDate date) { return date.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); - } - - public int getCurrentYear() { - return currentYear; - } - - // Opcional: Si deseas permitir cambiar el año actual manualmente, agrega un setter - public void setCurrentYear(final int currentYear) { - this.currentYear = currentYear; - } - } + +} \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 5cb7a98..8fc10fc 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, 8, 46, '5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 'b0e8f394-78c1-4d8a-9c57-dc6e8b36a5fa');