From 923dc0b826219f0abea949c332215446a114fdb1 Mon Sep 17 00:00:00 2001 From: Melina Gutierrez Date: Thu, 14 Nov 2024 13:24:38 -0400 Subject: [PATCH] #45-Registro Semanal de Horas Trabajadas guardar solo una actividad --- .../model/ActividadesHours.java | 81 +++ .../primefactorsolutions/model/Employee.java | 18 +- .../model/HoursWorked.java | 108 ++-- .../ActividadesHoursRepository.java | 7 + .../repositories/HoursWorkedRepository.java | 8 +- .../service/HoursWorkedService.java | 21 +- .../views/EmployeeView.java | 72 ++- .../views/HoursWorkedView.java | 539 ++++-------------- .../views/TimesheetView.java | 2 +- 9 files changed, 321 insertions(+), 535 deletions(-) create mode 100644 src/main/java/com/primefactorsolutions/model/ActividadesHours.java create mode 100644 src/main/java/com/primefactorsolutions/repositories/ActividadesHoursRepository.java diff --git a/src/main/java/com/primefactorsolutions/model/ActividadesHours.java b/src/main/java/com/primefactorsolutions/model/ActividadesHours.java new file mode 100644 index 0000000..5a93ec4 --- /dev/null +++ b/src/main/java/com/primefactorsolutions/model/ActividadesHours.java @@ -0,0 +1,81 @@ +package com.primefactorsolutions.model; + +import jakarta.persistence.*; +import java.time.LocalDate; +import java.util.UUID; + +@Entity +@Table(name = "Actividades_hours") +public class ActividadesHours { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + @ManyToOne + @JoinColumn(name = "employee_id", nullable = false) + private Employee employee; + + @ManyToOne + @JoinColumn(name = "actividad_id", nullable = false) + private Actividad actividad; + + @Column(nullable = false) + private double totalHours; + + @Column(nullable = false) + private int weekNumber; + + @Column(nullable = false) + private LocalDate fecha; + + // Getters y Setters + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + public Actividad getActividad() { + return actividad; + } + + public void setActividad(Actividad actividad) { + this.actividad = actividad; + } + + public double getTotalHours() { + return totalHours; + } + + public void setTotalHours(double totalHours) { + this.totalHours = totalHours; + } + + public int getWeekNumber() { + return weekNumber; + } + + public void setWeekNumber(int weekNumber) { + this.weekNumber = weekNumber; + } + + public LocalDate getFecha() { + return fecha; + } + + public void setFecha(LocalDate fecha) { + this.fecha = fecha; + } +} diff --git a/src/main/java/com/primefactorsolutions/model/Employee.java b/src/main/java/com/primefactorsolutions/model/Employee.java index 1d3df41..4d06336 100644 --- a/src/main/java/com/primefactorsolutions/model/Employee.java +++ b/src/main/java/com/primefactorsolutions/model/Employee.java @@ -28,36 +28,40 @@ public class Employee extends BaseEntity implements UserDetails { @Pattern(regexp = "^[a-zA-Z ]+$", message = "El apellido solo debe contener letras") private String lastName; private LocalDate birthday; - @Pattern(regexp = "^[a-zA-Z ]+$", message = "La ciudad de nacimiento solo debe contener letras") + @Pattern(regexp = "^[a-zA-Z ,]+$", message = "La ciudad de nacimiento solo debe contener letras, espacios o comas") private String birthCity; private String age; - @Size(max = 100, message = "La dirección de residencia no debe exceder 100 caracteres") + @Size(max = 50, message = "La dirección de residencia no debe exceder 50 caracteres") private String residenceAddress; - @Size(max = 100, message = "La dirección local no debe exceder 100 caracteres") + @Size(max = 30, message = "La dirección local no debe exceder 100 caracteres") + @Pattern(regexp = "^[a-zA-Z -]+$", message = "La dirección local solo debe contener letras y guion") private String localAddress; @Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números") private String phoneNumber; @Email(message = "El correo personal no tiene un formato válido") private String personalEmail; + @Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras") private String position; @ManyToOne @JoinColumn(name = "team_id", nullable = false) private Team team; - @Size(max = 100, message = "El nombre de contacto de emergencia no debe exceder 100 caracteres") + + @Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre y apellido de contacto de emergencia solo debe contener letras") private String emergencyCName; - @Size(max = 100, message = "La dirección de contacto de emergencia no debe exceder 100 caracteres") private String emergencyCAddress; @Pattern(regexp = "^[0-9]+$", message = "El teléfono de contacto de emergencia debe contener solo números") private String emergencyCPhone; @Email(message = "El correo de contacto de emergencia no tiene un formato válido") private String emergencyCEmail; + + @Max(value = 10, message = "El número de hijos no puede exceder a 10") @Pattern(regexp = "^[0-9]+$", message = "La cantidad de hijos debe contener solo números") private String numberOfChildren; + @Pattern(regexp = "^[0-9]+$", message = "El CI debe contener solo números") private String ci; private String issuedIn; - @Size(max = 100, message = "El título no debe exceder 100 caracteres") private String pTitle1; private String pTitle2; private String pTitle3; @@ -70,9 +74,7 @@ public class Employee extends BaseEntity implements UserDetails { private String certification2; private String certification3; private String certification4; - @Size(max = 255, message = "El reconocimiento no debe exceder 255 caracteres") private String recognition; - @Size(max = 500, message = "Los logros no deben exceder 500 caracteres") private String achievements; private String language; diff --git a/src/main/java/com/primefactorsolutions/model/HoursWorked.java b/src/main/java/com/primefactorsolutions/model/HoursWorked.java index 585ee9b..3f203b4 100644 --- a/src/main/java/com/primefactorsolutions/model/HoursWorked.java +++ b/src/main/java/com/primefactorsolutions/model/HoursWorked.java @@ -1,68 +1,43 @@ 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.CascadeType; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import java.time.LocalDate; -import java.time.temporal.WeekFields; -import java.util.Locale; import java.util.UUID; @Data @Entity @AllArgsConstructor +@NoArgsConstructor @EqualsAndHashCode(callSuper = true) public class HoursWorked extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.UUID) + @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne + @JoinColumn(name = "employee_id", nullable = false) private Employee employee; - @ManyToOne(cascade = CascadeType.PERSIST) // Añadimos cascade para que persista la actividad automáticamente - private Actividad actividad; + @ManyToOne + @JoinColumn(name = "team_id", nullable = false) + private Team team; private int weekNumber; + private LocalDate date; + private String actividad; + private double hours; private double totalHours; - private LocalDate fecha; - - public HoursWorked() {} - public HoursWorked(Employee employee) { - this.employee = employee; - this.weekNumber = 0; - this.totalHours = 0; - this.fecha = LocalDate.now(); - } - - public Actividad getActividad() { - return this.actividad; - } - - public void setActividad(Actividad actividad){ - this.actividad = actividad; - } - - public UUID getId() { - return id; - } - - public void setId(final UUID id) { - this.id = id; - } public Employee getEmployee() { return employee; } - public void setEmployee(final Employee employee) { + public void setEmployee(Employee employee) { this.employee = employee; } @@ -70,45 +45,48 @@ public class HoursWorked extends BaseEntity { return weekNumber; } - public void setWeekNumber(final int weekNumber) { + public void setWeekNumber(int weekNumber) { this.weekNumber = weekNumber; } + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getActividad() { + return actividad; + } + + public void setActividad(String actividad) { + this.actividad = actividad; + } + + public double getHours() { + return hours; + } + + public void setHours(double hours) { + this.hours = hours; + } + public double getTotalHours() { return totalHours; } - public void setTotalHours(final double totalHours) { + public void setTotalHours(double totalHours) { this.totalHours = totalHours; } - public LocalDate getFecha() { - return fecha; + public Team getTeam() { + return team; } - public void setFecha(final LocalDate fecha) { - this.fecha = fecha; - // Actualiza el número de semana automáticamente al establecer la fecha - if (fecha != null) { - this.weekNumber = calculateWeekNumber(fecha); - } - } - - // Método adicional para calcular el número de semana basado en la fecha - private int calculateWeekNumber(LocalDate date) { - WeekFields weekFields = WeekFields.of(Locale.getDefault()); - return date.get(weekFields.weekOfWeekBasedYear()); - } - - @Override - public String toString() { - return "HoursWorked{" + - "id=" + id + - ", employee=" + (employee != null ? employee.getFirstName() : "N/A") + - ", actividad=" + (actividad != null ? actividad.getNombre() : "N/A") + - ", weekNumber=" + weekNumber + - ", totalHours=" + totalHours + - ", fecha=" + fecha + - '}'; + public void setTeam(Team team) { + this.team = team; } } + diff --git a/src/main/java/com/primefactorsolutions/repositories/ActividadesHoursRepository.java b/src/main/java/com/primefactorsolutions/repositories/ActividadesHoursRepository.java new file mode 100644 index 0000000..72a3013 --- /dev/null +++ b/src/main/java/com/primefactorsolutions/repositories/ActividadesHoursRepository.java @@ -0,0 +1,7 @@ +package com.primefactorsolutions.repositories; +import com.primefactorsolutions.model.ActividadesHours; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.UUID; + +public interface ActividadesHoursRepository extends JpaRepository { +} diff --git a/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java b/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java index b3d0bdb..87162d9 100644 --- a/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java +++ b/src/main/java/com/primefactorsolutions/repositories/HoursWorkedRepository.java @@ -2,11 +2,13 @@ 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 { + +public interface HoursWorkedRepository extends JpaRepository { List findByWeekNumber(int weekNumber); + List findByDate(LocalDate date); } diff --git a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java index 2bfe882..605f6f6 100644 --- a/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java +++ b/src/main/java/com/primefactorsolutions/service/HoursWorkedService.java @@ -5,7 +5,9 @@ import com.primefactorsolutions.repositories.HoursWorkedRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.List; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; @Service public class HoursWorkedService { @@ -20,6 +22,11 @@ public class HoursWorkedService { return hoursWorkedRepository.findAll(); } + public HoursWorked findHoursWorked(final UUID id) { + Optional hoursWorked = hoursWorkedRepository.findById(id); + return hoursWorked.orElse(null); + } + public HoursWorked saveHoursWorked(final HoursWorked hoursWorked) { return hoursWorkedRepository.save(hoursWorked); } @@ -28,13 +35,17 @@ public class HoursWorkedService { return hoursWorkedRepository.save(hoursWorked); } - public void deleteHoursWorked(final Long id) { - hoursWorkedRepository.deleteById(id); - } - 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); + } + } diff --git a/src/main/java/com/primefactorsolutions/views/EmployeeView.java b/src/main/java/com/primefactorsolutions/views/EmployeeView.java index 4cdbc08..a80ae9e 100644 --- a/src/main/java/com/primefactorsolutions/views/EmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/EmployeeView.java @@ -56,7 +56,6 @@ public class EmployeeView extends BeanValidationForm implements HasUrl private final TimeOffRequestService requestService; private final TeamService teamService; - // TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente. private final TextField username = createTextField("Username: ", 30, true); private final TextField firstName = createTextField("Nombres: ", 30, true); @@ -65,24 +64,19 @@ public class EmployeeView extends BeanValidationForm implements HasUrl private final ComboBox gender = createGenderComboBox(); private final VDatePicker birthday = new VDatePicker("Fecha de Nacimiento"); private final TextField age = createTextField("Edad", 3, false); - private final TextField birthCity = createTextField("Ciudad y País de Nacimiento", 20, false); + private final TextField birthCity = createTextField("Ciudad y País de Nacimiento ejemplo: (Ciudad, País) ", 30, false); private final TextField residenceAddress = createTextField("Dirección de Residencia", 50, false); - private final TextField localAddress = createTextField("Dep/Provincia de Residencia", 10, false); + private final TextField localAddress = createTextField("Departamento y Provincia de Residencia ejemplo: (Departamento-Provincia)", 30, false); private final ComboBox maritalStatus = createMaritalStatusComboBox(); - private final TextField numberOfChildren = createTextField("Numero de Hijos", 3, false); - private final TextField ci = createTextField("CI", 30, false); - private final TextField issuedIn = createTextField("Expedido en ", 30, false); + private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false); + private final TextField ci = createTextField("CI", 10, false); + private final TextField issuedIn = createTextField("Expedido en ", 10, false); private final TextField phoneNumber = createTextField("Teléfono", 8, false); - private final EmailField personalEmail = createEmailField("E-mail"); - private final TextField cod = createTextField("Codigo de Empleado", 30, false); - private final TextField position = createTextField("Cargo", 30, false); - private final ComboBox team = new ComboBox<>("Equipo"); - private final TextField leadManager = createTextField("Lead/Manager", 30, false); - private final TextField project = createTextField("Proyecto", 30, false); + private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)"); private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false); private final TextField emergencyCAddress = createTextField("Dirección de Contacto", 50, false); private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false); - private final EmailField emergencyCEmail = createEmailField("Email de Contacto"); + private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)"); private final MemoryBuffer buffer = new MemoryBuffer(); private final Upload upload = new Upload(buffer); @@ -101,10 +95,15 @@ public class EmployeeView extends BeanValidationForm implements HasUrl private final TextField certification4 = createTextField("Certificación 4", 30, false); private final TextField recognition = createTextField("Reconocimientos", 30, false); private final TextField achievements = createTextField("Logros Profesionales", 30, false); - private final TextField language = createTextField("Idioma", 30, false); + private final TextField language = createTextField("Idioma", 50, false); private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false); - //INFORMACION DE CONTRATACION + //INFORMACION ADMINISTRATIVA + private final TextField cod = createTextField("Codigo de Empleado", 20, false); + private final TextField position = createTextField("Cargo", 30, false); + private final ComboBox team = new ComboBox<>("Equipo"); + private final TextField leadManager = createTextField("Lead/Manager", 30, false); + private final TextField project = createTextField("Proyecto", 30, false); private final VDatePicker dateOfEntry = new VDatePicker("Fecha de Ingreso"); private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro"); private final TextField contractType = createTextField("Tipo de Contratación", 30, false); @@ -141,7 +140,7 @@ public class EmployeeView extends BeanValidationForm implements HasUrl //TITULOS PARA INFORMACIÓN ADMINISTRATIVA private final H2 infoAdm = new H2("Información Administrativa"); private final H3 infoCont = new H3("Información de Contratación"); - private final H3 datBanc = new H3("Datos Bancados"); + private final H3 datBanc = new H3("Datos Bancarios"); private final H3 datGest = new H3("Datos Gestora Pública y Seguro Social"); public EmployeeView(final EmployeeService employeeService, @@ -177,6 +176,8 @@ public class EmployeeView extends BeanValidationForm implements HasUrl reportButton.setVisible(true); birthday.addValueChangeListener(event -> calculateAge()); birthday.setMax(java.time.LocalDate.now()); + dateOfEntry.addValueChangeListener(event -> calculateSeniority()); + dateOfEntry.addValueChangeListener(event -> calculateSeniority()); reportButton.addClickListener((ComponentEventListener>) buttonClickEvent -> { var employee = getEntity(); @@ -204,10 +205,30 @@ public class EmployeeView extends BeanValidationForm implements HasUrl int birthYear = birthday.getValue().getYear(); int ages = currentYear - birthYear; age.setValue(String.valueOf(ages)); + if (ages < 18) { + birthday.setInvalid(true); + birthday.setErrorMessage("La edad no puede ser menor a 18 años."); + Notification.show("La edad ingresada no es válida, debe ser mayor o igual a 18 años."); + } else { + birthday.setInvalid(false); + } System.out.println(age); } } + private void calculateSeniority() { + LocalDate entryDate = dateOfEntry.getValue(); + LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now(); + + if (entryDate != null) { + long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate); + String seniorityValue = yearsOfService + " años "; + seniority.setValue(seniorityValue); + } else { + seniority.setValue("No disponible"); + } + } + private void configureUpload() { upload.setAcceptedFileTypes("image/jpeg", "image/png"); upload.setMaxFileSize(1024 * 1024); @@ -219,11 +240,15 @@ public class EmployeeView extends BeanValidationForm implements HasUrl getEntity().setProfileImage(base64Image); - profileImagePreview.setSrc("data:image/jpeg;base64," + base64Image); + profileImagePreview.setSrc("data:image/png;base64," + base64Image); profileImagePreview.setMaxWidth("150px"); profileImagePreview.setMaxHeight("150px"); } catch (IOException e) { - Notification.show("Error al subir la imagen."); + Notification.show("Error al subir la imagen: " + e.getMessage()); + e.printStackTrace(); + } catch (Exception e) { + Notification.show("Error en el servidor al procesar la imagen."); + e.printStackTrace(); } }); } @@ -394,12 +419,12 @@ public class EmployeeView extends BeanValidationForm implements HasUrl if (employee.getProfileImage() != null && !employee.getProfileImage().isEmpty()) { profileImagePreview.setSrc("data:image/jpeg;base64," + employee.getProfileImage()); profileImagePreview.setVisible(true); - profileImagePreview.setMaxWidth("150px"); - profileImagePreview.setMaxHeight("150px"); + profileImagePreview.setMaxWidth("250px"); + profileImagePreview.setMaxHeight("250px"); - upload.setVisible(false); + upload.setVisible(true); } else { - profileImagePreview.setVisible(false); + profileImagePreview.setVisible(true); upload.setVisible(true); } } @@ -479,6 +504,7 @@ public class EmployeeView extends BeanValidationForm implements HasUrl emergencyCPhone.setReadOnly(false); emergencyCEmail.setReadOnly(false); upload.setVisible(false); + profileImagePreview.setVisible(true); age.setReadOnly(false); gender.setReadOnly(false); status.setReadOnly(false); @@ -526,7 +552,6 @@ public class EmployeeView extends BeanValidationForm implements HasUrl birthCity, residenceAddress, localAddress, maritalStatus, ci, issuedIn, numberOfChildren, phoneNumber, personalEmail, - cod, position, team, leadManager, project, contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail, infProf, titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3, @@ -534,6 +559,7 @@ public class EmployeeView extends BeanValidationForm implements HasUrl logros, recognition, achievements, idioma, language, languageLevel, infoAdm, + cod, position, team, leadManager, project, infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary, datBanc, bankName, accountNumber, datGest, gpss, sss, beneficiaries, diff --git a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java index 6614aa2..d0f9df8 100644 --- a/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java +++ b/src/main/java/com/primefactorsolutions/views/HoursWorkedView.java @@ -1,477 +1,156 @@ 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.grid.Grid; -import com.vaadin.flow.component.html.H2; +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.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.context.annotation.Scope; -import com.vaadin.flow.component.html.Label; -import org.vaadin.firitin.components.datepicker.VDatePicker; +import org.vaadin.firitin.form.BeanValidationForm; - -import java.time.DayOfWeek; -import java.time.LocalDate; -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("Horas Trabajadas") -@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); +@Route(value = "/timesheets/:hours-worked?/:action?", layout = MainLayout.class) +public class HoursWorkedView extends BeanValidationForm implements HasUrlParameter { + private final DatePicker dateField = new DatePicker("Fecha"); + private final ComboBox teamField = new ComboBox<>("Equipo"); + private final ComboBox employeeField = new ComboBox<>("Empleado"); + private final TextField activityField = new TextField("Actividad"); - private final ComboBox employeeComboBox = new ComboBox<>("Empleado"); - private final ComboBox equipoDropdown = new ComboBox<>("Equipo"); - private VDatePicker fechaPicker = new VDatePicker("Selecciona una fecha"); - - - 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 EmployeeService employeeService; + private final TeamService teamService; + private HoursWorked request; + private Employee employee; - 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 Button saveButton; - public HoursWorkedView(final EmployeeService employeeService, final HoursWorkedService hoursWorkedService) { - this.employeeService = employeeService; + public HoursWorkedView(HoursWorkedService hoursWorkedService, + EmployeeService employeeService, + TeamService teamService, TimeOffRequestService timeOffRequestService) { + super(HoursWorked.class); this.hoursWorkedService = hoursWorkedService; - configurarVista(); - configurarGrid(); - configurarGridActividadesEspecificas(); - cargarDatos(); - configurarTareasEspecificas(); + this.employeeService = employeeService; + this.teamService = teamService; + + // Initialize combo boxes + initializeTeamField(); + initializeEmployeeField(); } - private void configurarTareasEspecificas() { - tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", "Colaboraciones", - "Aprendizajes", "Proyectos PFS", "Otros"); - tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); + @Override + public void setParameter(BeforeEvent beforeEvent, String action) { + final RouteParameters params = beforeEvent.getRouteParameters(); + final String hoursWorkedId = params.get("hoursworkedId").orElse(null); - 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)) { - tareaEspecificaInput.clear(); - } - }); - tareaEspecificaInput.setVisible(false); - } - - private void cargarDatos() { - if (selectedStartOfWeek != null && weekNumber > 0) { - List listaDeHorasTrabajadas = obtenerDatos(); - - for (HoursWorked hours : listaDeHorasTrabajadas) { - LocalDate activityDate = hours.getFecha(); - int activityWeek = getWeekOfYear(activityDate); - if (activityWeek == weekNumber) { - Actividad actividad = hours.getActividad(); - if (actividad != null) { - DayOfWeek dayOfWeek = activityDate.getDayOfWeek(); - agregarOActualizarActividad(actividad, dayOfWeek, hours.getTotalHours()); - } - } - } - - grid.setItems(actividades); - gridActividadesEspecificas.setItems(actividadesEspecificas); - calcularTotalHoras(listaDeHorasTrabajadas); + if ("new".equals(action)) { + setEntityWithEnabledSave(new HoursWorked()); + } else if (hoursWorkedId != null) { + UUID id = UUID.fromString(hoursWorkedId); + request = hoursWorkedService.findHoursWorked(id); + setEntity(request); + configureViewOrEditAction(action); } } - 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.") + @Override + protected List getFormComponents() { + return List.of( + createEmployeeHeader(), + dateField, + teamField, + employeeField, + activityField, + createCloseButton() ); + } - employeeComboBox.addValueChangeListener(event -> { - Employee selectedEmployee = event.getValue(); - if (selectedEmployee != null) { - Notification.show("Empleado seleccionado: " - + selectedEmployee.getFirstName() + " " - + selectedEmployee.getLastName()); - } + protected Button createSaveButton() { + saveButton = new Button("Guardar"); + saveButton.addClickListener(event -> saveHoursWorked()); + return saveButton; + } + + protected Button createCloseButton() { + Button closeButton = new Button("Cerrar"); + closeButton.addClickListener(event -> closeForm()); + return closeButton; + } + + private void initializeTeamField() { + teamField.setItems(teamService.findAllTeams()); + teamField.setItemLabelGenerator(Team::getName); + teamField.addValueChangeListener(event -> { + teamField.getValue(); }); } - private int getWeekOfYear(final LocalDate date) { - return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + private void initializeEmployeeField() { + employeeField.setItems(employeeService.findAllEmployees()); + employeeField.setItemLabelGenerator(employee -> employee.getFirstName() + " " + employee.getLastName()); } - private void configurarVista() { - fechaPicker.setMax(LocalDate.now()); - fechaPicker.addValueChangeListener(event -> { - LocalDate selectedDate = event.getValue(); - if (selectedDate != null) { - selectedStartOfWeek = getStartOfWeek(selectedDate); - weekNumber = getWeekOfYear(selectedDate); - cargarDatos(); - } - }); - - 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, filtersLayout, actividadFormLayout, - equipoLabel, grid, empresaLabel, tareasEspecificasLayout, - 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"); - grid.setItems(actividades); - } - - 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; - } - - int selectedWeekNumber = getWeekOfYear(selectedDate); - if (selectedWeekNumber != weekNumber) { - Notification.show("Solo puedes agregar actividades dentro de la semana actual."); - 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); - } - - Actividad nuevaActividad = actividadBuilder.build(); - agregarOActualizarActividad(nuevaActividad, selectedDay, horas); - - 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 void agregarOActualizarActividad(final Actividad nuevaActividad, final DayOfWeek dia, final double horas) { - Actividad actividadExistente = actividades.stream() - .filter(a -> a.getNombre().equals(nuevaActividad.getNombre())) - .findFirst() - .orElse(null); - - if (actividadExistente != null) { - // Si la actividad ya existe, actualiza las horas para el día correspondiente - switch (dia) { - case MONDAY -> actividadExistente.setLunes(horas); - case TUESDAY -> actividadExistente.setMartes(horas); - case WEDNESDAY -> actividadExistente.setMiercoles(horas); - case THURSDAY -> actividadExistente.setJueves(horas); - case FRIDAY -> actividadExistente.setViernes(horas); - case SATURDAY -> actividadExistente.setSabado(horas); - case SUNDAY -> actividadExistente.setDomingo(horas); - default -> throw new IllegalArgumentException("Día seleccionado no válido: " + dia); - } - } else { - switch (dia) { - case MONDAY -> nuevaActividad.setLunes(horas); - case TUESDAY -> nuevaActividad.setMartes(horas); - case WEDNESDAY -> nuevaActividad.setMiercoles(horas); - case THURSDAY -> nuevaActividad.setJueves(horas); - case FRIDAY -> nuevaActividad.setViernes(horas); - case SATURDAY -> nuevaActividad.setSabado(horas); - case SUNDAY -> nuevaActividad.setDomingo(horas); - default -> throw new IllegalArgumentException("Día seleccionado no válido: " + dia); - } - actividades.add(nuevaActividad); - } - grid.setItems(actividades); - guardarActividades(); - } - - - 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; - } - return Double.parseDouble(value); - } - - private LocalDate getStartOfWeek(final LocalDate date) { - WeekFields weekFields = WeekFields.of(Locale.getDefault()); - return date.with(weekFields.dayOfWeek(), DayOfWeek.FRIDAY.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; - } - - if (actividades.isEmpty()) { - Notification.show("No hay actividades para guardar."); - return; - } - - double totalHorasSemana = actividades.stream() - .mapToDouble(this::calcularTotalPorDia) - .sum(); - - List savedHoursWorked = new ArrayList<>(); // Para almacenar los objetos guardados - - actividades.forEach(actividad -> { - HoursWorked hoursWorked = new HoursWorked(); - hoursWorked.setActividad(actividad); - hoursWorked.setFecha(fechaPicker.getValue()); - hoursWorked.setEmployee(selectedEmployee); - hoursWorked.setWeekNumber(weekNumber); - hoursWorked.setTotalHours(totalHorasSemana); - + private void saveHoursWorked() { + if (isFormValid()) { + HoursWorked hoursWorked = getEntity(); + setFieldValues(hoursWorked); hoursWorkedService.saveHoursWorked(hoursWorked); - - }); - Notification.show("Actividades guardadas correctamente."); + Notification.show("Horas trabajadas guardadas correctamente."); + closeForm(); + } } - private double calcularTotalHoras(final List listaDeHorasTrabajadas) { - return listaDeHorasTrabajadas.stream() - .mapToDouble(HoursWorked::getTotalHours) - .sum(); + private void setFieldValues(HoursWorked hoursWorked) { + hoursWorked.setDate(dateField.getValue()); + hoursWorked.setTeam(teamField.getValue()); + hoursWorked.setEmployee(employeeField.getValue()); + hoursWorked.setActividad(activityField.getValue()); } - private List obtenerDatos() { - return new ArrayList<>(); + private void closeForm() { + getUI().ifPresent(ui -> ui.navigate("hoursworked")); } - private void closeView() { - getUI().ifPresent(ui -> ui.navigate(HoursWorkedView.class)); + private boolean isFormValid() { + return dateField.getValue() != null && + teamField.getValue() != null && + employeeField.getValue() != null && + !activityField.isEmpty(); } + private void configureViewOrEditAction(String action) { + if ("edit".equals(action) && request != null) { + setFieldsReadOnly(false); + } else if ("view".equals(action) && request != null) { + setFieldsReadOnly(true); + saveButton.setEnabled(false); + } + } + + private void setFieldsReadOnly(boolean readOnly) { + dateField.setReadOnly(readOnly); + teamField.setReadOnly(readOnly); + employeeField.setReadOnly(readOnly); + 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/TimesheetView.java b/src/main/java/com/primefactorsolutions/views/TimesheetView.java index 9a8b351..eb23e4d 100644 --- a/src/main/java/com/primefactorsolutions/views/TimesheetView.java +++ b/src/main/java/com/primefactorsolutions/views/TimesheetView.java @@ -11,7 +11,7 @@ import org.springframework.context.annotation.Scope; @PermitAll @Scope("prototype") @PageTitle("Timesheet") -@Route(value = "/timesheets/me", layout = MainLayout.class) +@Route(value = "/timesheets", layout = MainLayout.class) public class TimesheetView extends Main { }