Compare commits

..

29 Commits

Author SHA1 Message Date
450e121374 Merge pull request 'Observaciones-vacaciones' (#83) from Observaciones-vacaciones into main
All checks were successful
Builder / Build-Project (push) Successful in 2m48s
Reviewed-on: #83
2024-11-25 02:17:50 +00:00
9aea51c8c1 fixed rebase conflicts
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m49s
2024-11-24 21:10:50 -05:00
e6c9d5c3e6 Descargar reporte de vacaciones personal en pdf. 2024-11-24 21:07:11 -05:00
caab437d17 Descargar reporte general de vacaciones en excel. 2024-11-24 21:04:18 -05:00
5fea733a7f Descontar a cero las vacaciones cuando llegue la fecha de salida 2024-11-24 20:59:59 -05:00
97a9c0ba34 Traducir modulo documentos 2024-11-24 20:56:04 -05:00
d27d71f69f No permitir solicitudes de vacaciones con anticimacion de 3 meses maximo a 15 dias minimo. 2024-11-24 20:53:53 -05:00
8177371159 No descontar dias si la solicitud de vacacion no fue aprobada. 2024-11-24 20:53:53 -05:00
9827c079dd Limitar a 10 dias maximo para tomar vacaciones por gestion 2024-11-24 20:53:53 -05:00
aebae5d550 Agregar condicion para tomar dia libre por cumpleaños 2024-11-24 20:53:53 -05:00
290c5532df Excluir sabado y domingo cuando se solicita una vacacion 2024-11-24 20:53:53 -05:00
5bcab5fd7e Remover estados de solicitud (COMPLETADO, CANCELADO, EN_REVISION) 2024-11-24 20:53:53 -05:00
b26ab28297 Merge pull request 'mejorar-detalles' (#77) from mejorar-detalles into main
All checks were successful
Builder / Build-Project (push) Successful in 2m44s
Reviewed-on: #77
2024-11-25 01:52:43 +00:00
9e53bc33d7 corregir metodo de getTeamLeadName
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m46s
2024-11-24 20:48:29 -05:00
0e142934fa fix conflicts
All checks were successful
PR Builder / Build-PR (pull_request) Successful in 2m45s
2024-11-24 20:36:18 -05:00
Melina Gutierrez
7a2381c163 corregir clase de reportes 2024-11-24 20:33:07 -05:00
Melina Gutierrez
726e34ed10 corregir clase de reportes 2024-11-24 20:33:07 -05:00
Melina Gutierrez
1c54f1c595 añadir nuevos requisitos en Empleado 2024-11-24 20:33:07 -05:00
Melina Gutierrez
fc51e227a5 añadir nuevos requisitos en Empleado 2024-11-24 20:33:07 -05:00
Melina Gutierrez
09f87c2204 añadir nuevos requisitos en Empleado 2024-11-24 20:33:04 -05:00
Melina Gutierrez
da02e7dfde Empleado con las revisiones 2024-11-24 20:31:02 -05:00
Melina Gutierrez
2d6c2e4327 Empleado con las revisiones 2024-11-24 20:31:02 -05:00
Melina Gutierrez
fe84668fb3 Mejorar la visibilidad de la foto de perfil 2024-11-24 20:31:02 -05:00
1d135c9869 cambios de diseno tablas
All checks were successful
Builder / Build-Project (push) Successful in 2m40s
2024-11-24 16:51:58 -05:00
54ff167124 improve tables layout
All checks were successful
Builder / Build-Project (push) Successful in 2m44s
2024-11-17 17:15:58 -05:00
2a561a925c change list page size
All checks were successful
Builder / Build-Project (push) Successful in 2m45s
2024-11-17 16:37:57 -05:00
0695223c31 fix file upload
All checks were successful
Builder / Build-Project (push) Successful in 2m43s
2024-11-17 16:15:08 -05:00
4de84d7320 fix packages.json
All checks were successful
Builder / Build-Project (push) Successful in 2m47s
2024-11-15 11:42:46 -05:00
1857dcb5a9 Merge pull request 'registro-horas' (#76) from registro-horas into main
Some checks failed
Builder / Build-Project (push) Has been cancelled
Reviewed-on: #76
Reviewed-by: alex <alex@primefactorsolutions.com>
2024-11-15 16:40:49 +00:00
30 changed files with 1000 additions and 487 deletions

BIN
Reporte_Vacaciones.xlsx Normal file

Binary file not shown.

View File

@ -4,7 +4,7 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@f0rce/ace-widget": "1.0.2", "@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.2", "@polymer/polymer": "3.5.1",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1", "@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1", "@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19", "@vaadin/common-frontend": "0.0.19",
@ -19,30 +19,29 @@
"@vaadin/vaadin-usage-statistics": "2.1.3", "@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0", "construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3", "date-fns": "2.29.3",
"lit": "3.2.1", "lit": "3.1.4",
"print-js": "1.6.0", "print-js": "1.6.0",
"proj4": "2.12.1", "proj4": "2.12.1",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-router-dom": "6.26.2" "react-router-dom": "6.23.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-react": "7.25.7", "@babel/preset-react": "7.24.7",
"@preact/signals-react-transform": "0.4.0", "@rollup/plugin-replace": "5.0.7",
"@rollup/plugin-replace": "6.0.1", "@rollup/pluginutils": "5.1.0",
"@rollup/pluginutils": "5.1.2", "@types/react": "18.3.3",
"@types/react": "18.3.11", "@types/react-dom": "18.3.0",
"@types/react-dom": "18.3.1", "@vitejs/plugin-react": "4.3.1",
"@vitejs/plugin-react": "4.3.3", "async": "3.2.5",
"async": "3.2.6", "glob": "10.4.1",
"glob": "10.4.5",
"rollup-plugin-brotli": "3.1.0", "rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0", "rollup-plugin-visualizer": "5.12.0",
"strip-css-comments": "5.0.0", "strip-css-comments": "5.0.0",
"transform-ast": "2.4.4", "transform-ast": "2.4.4",
"typescript": "5.6.3", "typescript": "5.4.5",
"vite": "5.4.9", "vite": "5.3.3",
"vite-plugin-checker": "0.8.0", "vite-plugin-checker": "0.6.4",
"workbox-build": "7.1.1", "workbox-build": "7.1.1",
"workbox-core": "7.1.0", "workbox-core": "7.1.0",
"workbox-precaching": "7.1.0" "workbox-precaching": "7.1.0"
@ -50,7 +49,7 @@
"vaadin": { "vaadin": {
"dependencies": { "dependencies": {
"@f0rce/ace-widget": "1.0.2", "@f0rce/ace-widget": "1.0.2",
"@polymer/polymer": "3.5.2", "@polymer/polymer": "3.5.1",
"@vaadin-component-factory/vcf-pdf-viewer": "2.0.1", "@vaadin-component-factory/vcf-pdf-viewer": "2.0.1",
"@vaadin/bundles": "24.5.1", "@vaadin/bundles": "24.5.1",
"@vaadin/common-frontend": "0.0.19", "@vaadin/common-frontend": "0.0.19",
@ -65,35 +64,34 @@
"@vaadin/vaadin-usage-statistics": "2.1.3", "@vaadin/vaadin-usage-statistics": "2.1.3",
"construct-style-sheets-polyfill": "3.1.0", "construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3", "date-fns": "2.29.3",
"lit": "3.2.1", "lit": "3.1.4",
"print-js": "1.6.0", "print-js": "1.6.0",
"proj4": "2.12.1", "proj4": "2.12.1",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-router-dom": "6.26.2" "react-router-dom": "6.23.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-react": "7.25.7", "@babel/preset-react": "7.24.7",
"@preact/signals-react-transform": "0.4.0", "@rollup/plugin-replace": "5.0.7",
"@rollup/plugin-replace": "6.0.1", "@rollup/pluginutils": "5.1.0",
"@rollup/pluginutils": "5.1.2", "@types/react": "18.3.3",
"@types/react": "18.3.11", "@types/react-dom": "18.3.0",
"@types/react-dom": "18.3.1", "@vitejs/plugin-react": "4.3.1",
"@vitejs/plugin-react": "4.3.3", "async": "3.2.5",
"async": "3.2.6", "glob": "10.4.1",
"glob": "10.4.5",
"rollup-plugin-brotli": "3.1.0", "rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.12.0", "rollup-plugin-visualizer": "5.12.0",
"strip-css-comments": "5.0.0", "strip-css-comments": "5.0.0",
"transform-ast": "2.4.4", "transform-ast": "2.4.4",
"typescript": "5.6.3", "typescript": "5.4.5",
"vite": "5.4.9", "vite": "5.3.3",
"vite-plugin-checker": "0.8.0", "vite-plugin-checker": "0.6.4",
"workbox-build": "7.1.1", "workbox-build": "7.1.1",
"workbox-core": "7.1.0", "workbox-core": "7.1.0",
"workbox-precaching": "7.1.0" "workbox-precaching": "7.1.0"
}, },
"hash": "2dc40a4f634ae025081ca2239cba00b14a35fe94ab78ac0a4dd3023d882081d5" "hash": "1a0f17d48b329307b5862bc57499307d1b89f7d89260121c2b7189f76957c436"
}, },
"overrides": { "overrides": {
"@vaadin/bundles": "$@vaadin/bundles", "@vaadin/bundles": "$@vaadin/bundles",

View File

@ -120,6 +120,11 @@
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
<version>1.9.4</version> <version>1.9.4</version>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId> <artifactId>jackson-core</artifactId>

Binary file not shown.

View File

@ -1,36 +1,35 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
public enum DocumentType { public enum DocumentType {
All, TODOS,
ID_CARD, CARNET_DE_IDENTIDAD,
PAY_STUB, RECIBOS_DE_PAGO,
PAY_SLIPS, CONTRATO_DE_TRABAJO,
EMPLOYMENT_CONTRACT, CERTIFICADO_DE_TRABAJO,
WORK_CERTIFICATES,
NDA, NDA,
MEMORANDUMS, MEMORÁNDUMS,
CONTRACT_APPROVAL_MTEPS, APROBACIÓN_DE_CONTRATO_MTEPS,
BACKGROUND_CHECK_CERTIFICATE, CERTIFICADO_DE_ANTECEDENTES,
PRE_EMPLOYMENT_EVALUATION, EVALUACIÓN_PRE_EMPLEO,
INSURANCE_REGISTRATION_FORM, FORMULARIO_DE_INSCRIPCIÓN_AL_SEGURO,
INSURANCE_CANCELLATION_FORM, FORMULARIO_DE_CANCELACIÓN_DE_SEGURO,
PROFESSIONAL_DEGREE_1, TÍTULO_PROFESIONAL_1,
PROFESSIONAL_CERTIFICATE_1, CERTIFICACIÓN_PROFESIONAL_1,
PROFESSIONAL_DEGREE_2, TÍTULO_PROFESIONAL_2,
PROFESSIONAL_CERTIFICATE_2, CERTIFICACIÓN_PROFECIONAL_2,
PROFESSIONAL_DEGREE_3, TÍTULO_PROFESIONAL_3,
PROFESSIONAL_CERTIFICATE_3, CERTIFICACIÓN_PROFECIONAL_3,
GENERAL_LABOR_REGULATIONS, NORMATIVA_LABORAL_GENERAL,
REMOTE_WORK_GUIDELINES, NORMAS_DE_TRABAJO_REMOTO,
SAFETY_REGULATIONS, NORMAS_DE_SEGURIDAD,
HUMAN_RESOURCES_GUIDELINES, INSTRUCTIVOS_DE_RECURSOS_HUMANOS,
ADMINISTRATION_FUNCTIONS_MANUAL, MANUAL_DE_FUNCIONES_DE_ADMINISTRACIÓN,
ENGINEERING_FUNCTIONS_MANUAL, MANUAL_DE_FUNCIONES_DE_INGENIERÍA,
GENERAL_LABOR_LAW, LEY_GENERAL_DEL_TRABAJO,
SUPREME_DECREE, DECRETOS_SUPREMOS,
REGULATORY_RESOLUTION, RESOLUCIONES_O_DISPOSICIONES_REGLAMENTARIAS,
COMPLEMENTARY_REGULATION, NORMATIVA_COMPLEMENTARIA,
HEALTH_SAFETY_LAW, LEY_GRAL_DE_HIGIENE_SALUD_SEGURIDAD_OCUPACIONAL_Y_BIENESTAR,
INTERNSHIP_RULES, NORMATIVA_REGLAMENTARIA_PARA_DESARROLLO_DE_PASANTÍAS,
OTHER OTROS
} }

View File

@ -40,6 +40,10 @@ public class Employee extends BaseEntity implements UserDetails {
private String phoneNumber; private String phoneNumber;
@Email(message = "El correo personal no tiene un formato válido") @Email(message = "El correo personal no tiene un formato válido")
private String personalEmail; private String personalEmail;
@Pattern(regexp = "^[0-9]+$", message = "El número de teléfono debe contener solo números")
private String phoneNumberProfesional;
@Email(message = "El correo profesional no tiene un formato válido")
private String profesionalEmail;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "El cargo solo debe contener letras")
private String position; private String position;
@ -56,12 +60,8 @@ public class Employee extends BaseEntity implements UserDetails {
private String emergencyCPhone; private String emergencyCPhone;
@Email(message = "El correo de contacto de emergencia no tiene un formato válido") @Email(message = "El correo de contacto de emergencia no tiene un formato válido")
private String emergencyCEmail; 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; private String numberOfChildren;
@Pattern(regexp = "^[a-zA-Z0-9]+$", message = "El CI debe contener solo letras y números")
@Pattern(regexp = "^[0-9]+$", message = "El CI debe contener solo números")
private String ci; private String ci;
private String issuedIn; private String issuedIn;
private String pTitle1; private String pTitle1;
@ -79,23 +79,24 @@ public class Employee extends BaseEntity implements UserDetails {
private String recognition; private String recognition;
private String achievements; private String achievements;
private String language; private String language1;
private String languageLevel; private String language1Level;
private String language2;
private String language2Level;
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "El código debe contener solo letras y números") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "El código debe contener solo letras y números")
private String cod; private String cod;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El lead manager solo debe contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "El lead manager solo debe contener letras")
private String leadManager; private String leadManager;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El proyecto solo debe contener letras")
private String project;
private LocalDate dateOfEntry; private LocalDate dateOfEntry;
private LocalDate dateOfExit; private LocalDate dateOfExit;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El tipo de contrato solo debe contener letras")
private String contractType;
@Pattern(regexp = "^[0-9]+$", message = "La antigüedad debe contener solo números")
private String seniority; private String seniority;
@Pattern(regexp = "^[0-9]+(\\.[0-9]{1,2})?$", message = "El salario debe ser un número con hasta dos decimales") @Pattern(regexp = "^[0-9]+(\\.[0-9]{1,2})?$", message = "El salario debe ser un número con hasta dos decimales")
private String salary; private String salarytotal;
private String salaryBasic;
private String bonoProfesional;
private String antiguedad;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre del banco solo debe contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "El nombre del banco solo debe contener letras")
private String bankName; private String bankName;
@Pattern(regexp = "^[0-9]+$", message = "El número de cuenta debe contener solo números") @Pattern(regexp = "^[0-9]+$", message = "El número de cuenta debe contener solo números")
@ -104,8 +105,9 @@ public class Employee extends BaseEntity implements UserDetails {
private String gpss; private String gpss;
private String sss; private String sss;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras") @Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras")
private String beneficiaries; private String beneficiarie1;
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Los derechohabientes solo deben contener letras")
private String beneficiarie2;
@Column(columnDefinition = "TEXT") @Column(columnDefinition = "TEXT")
private String profileImage; private String profileImage;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@ -168,4 +170,20 @@ public class Employee extends BaseEntity implements UserDetails {
MALE, MALE,
FEMALE FEMALE
} }
@Enumerated(EnumType.STRING)
private ContractType contractType;
public enum ContractType {
CONTRATO_LABORAL,
CONTRATO_CIVIL_O_SERVICIOS,
CONTRATO_PLAZO_FIJO,
CONSULTORIA_INTERNA,
CONSULTORIA_EXTERNA,
MIXTO,
OTROS
}
@Size(max = 255, message = "El detalle del contrato no debe exceder 255 caracteres")
private String otherContractDetail;
} }

View File

@ -28,15 +28,24 @@ public class HoursWorked extends BaseEntity {
private int weekNumber; private int weekNumber;
private LocalDate date; private LocalDate date;
private String actividad; private String actividad;
private String tareasEspecificas;
private double hours; private double hours;
private double horasTareasEspecificas;
private double horaspendientes; private double horaspendientes;
private double totalHours; private double totalHours;
private String tareaEspecifica;
public String getTareaEspecifica() {
return tareaEspecifica;
}
public void setTareaEspecifica(final String tareaEspecifica) {
this.tareaEspecifica = tareaEspecifica;
}
public static double calculateTotalHours(final List<HoursWorked> activities) { public static double calculateTotalHours(final List<HoursWorked> activities) {
return activities.stream() return activities.stream()
.mapToDouble(activity -> activity.hours + activity.horasTareasEspecificas) .mapToDouble(activity -> activity.hours)
.sum(); .sum();
} }
@ -105,21 +114,6 @@ public class HoursWorked extends BaseEntity {
this.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() { public double getHoraspendientes() {
//double horasTrabajadas = this.getTotalHours() + this.getHorasTareasEspecificas(); //double horasTrabajadas = this.getTotalHours() + this.getHorasTareasEspecificas();
return 40; return 40;
@ -129,21 +123,4 @@ public class HoursWorked extends BaseEntity {
this.horaspendientes = 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;
}
} }

View File

@ -10,7 +10,4 @@ public enum TimeOffRequestStatus {
VENCIDO, VENCIDO,
SOLICITADO, SOLICITADO,
EN_REVISION,
COMPLETADO,
CANCELADO,
} }

View File

@ -14,7 +14,6 @@ public enum TimeOffRequestType {
AÑO_NUEVO_ANDINO, AÑO_NUEVO_ANDINO,
ANIVERSARIO_DEPARTAMENTAL, ANIVERSARIO_DEPARTAMENTAL,
DIA_DE_TODOS_LOS_DIFUNTOS, DIA_DE_TODOS_LOS_DIFUNTOS,
CUMPLEAÑOS, CUMPLEAÑOS,
MATERNIDAD, MATERNIDAD,
PATERNIDAD, PATERNIDAD,

View File

@ -9,9 +9,7 @@ import java.util.UUID;
public interface EmployeeRepository extends JpaRepository<Employee, UUID> { public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByUsername(String username); Optional<Employee> findByUsername(String username);
Optional<Employee> findByPersonalEmail(String personalEmail); Optional<Employee> findByPersonalEmail(String personalEmail);
Optional<Employee> findByTeamId(UUID teamId); List<Employee> findAllByTeamId(UUID teamId);
List<Employee> findByTeamName(String teamName); List<Employee> findByTeamName(String teamName);
} }

View File

@ -1,4 +1,5 @@
package com.primefactorsolutions.service; package com.primefactorsolutions.service;
import com.google.common.base.Strings;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -50,11 +51,15 @@ public class EmployeeService {
public String getTeamLeadName(final UUID teamId) { public String getTeamLeadName(final UUID teamId) {
// Encuentra al empleado con el rol de lead_manager en el equipo especificado // Encuentra al empleado con el rol de lead_manager en el equipo especificado
Optional<Employee> leadManager = employeeRepository.findByTeamId(teamId); List<Employee> teamMembers = employeeRepository.findAllByTeamId(teamId);
Optional<Employee> leadManager = teamMembers.stream()
.filter(e -> Strings.isNullOrEmpty(e.getLeadManager()))
.findFirst();
return leadManager.map(employee -> employee.getFirstName() + " " + employee.getLastName()) return leadManager.map(employee -> employee.getFirstName() + " " + employee.getLastName())
.orElse("No asignado"); .orElse("No asignado");
} }
public List<Employee> findEmployees( public List<Employee> findEmployees(
final int start, final int pageSize, final String sortProperty, final boolean asc) { final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Employee> employees = employeeRepository.findAll(); List<Employee> employees = employeeRepository.findAll();

View File

@ -8,7 +8,6 @@ import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog; import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider; import com.vaadin.flow.data.provider.DataProvider;
@ -31,13 +30,13 @@ import java.util.stream.Stream;
@PageTitle("Assessments") @PageTitle("Assessments")
@Route(value = "/assessments", layout = MainLayout.class) @Route(value = "/assessments", layout = MainLayout.class)
@PermitAll @PermitAll
public class AssessmentsListView extends Main { public class AssessmentsListView extends BaseView {
public AssessmentsListView(final AssessmentService assessmentService) { public AssessmentsListView(final AssessmentService assessmentService) {
final HorizontalLayout hl = new HorizontalLayout(); final HorizontalLayout hl = new HorizontalLayout();
final Button addAssessment = new Button("Add Assessment"); final Button addAssessment = new Button("Add Assessment");
addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { addAssessment.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(AssessmentView.class, "new"); this.getUI().flatMap(ui -> ui.navigate(AssessmentView.class, "new"));
}); });
hl.add(addAssessment); hl.add(addAssessment);
@ -51,7 +50,7 @@ public class AssessmentsListView extends Main {
grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> { grid.addComponentColumn((ValueProvider<Assessment, Component>) assessment -> {
var result = new Button("Result", event -> var result = new Button("Result", event ->
this.getUI().get().navigate(SubmissionView.class, assessment.getId().toString()) this.getUI().flatMap(ui -> ui.navigate(SubmissionView.class, assessment.getId().toString()))
); );
result.setEnabled(assessment.isCompleted()); result.setEnabled(assessment.isCompleted());
@ -95,6 +94,7 @@ public class AssessmentsListView extends Main {
return assessmentService.getAssessments().size(); return assessmentService.getAssessments().size();
} }
@SuppressWarnings("unused")
@Override @Override
public Stream<Assessment> fetch(final Query<Assessment, Object> query) { public Stream<Assessment> fetch(final Query<Assessment, Object> query) {
int limit = query.getLimit(); int limit = query.getLimit();
@ -121,7 +121,7 @@ public class AssessmentsListView extends Main {
}); });
grid.setAllRowsVisible(true); grid.setAllRowsVisible(true);
add(hl, grid); getCurrentPageLayout().add(hl, grid);
} }
} }

View File

@ -0,0 +1,16 @@
package com.primefactorsolutions.views;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import lombok.Getter;
@Getter
public class BaseView extends Main {
private final VerticalLayout currentPageLayout;
public BaseView() {
currentPageLayout = new VerticalLayout();
add(currentPageLayout);
}
}

View File

@ -6,7 +6,6 @@ import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider; import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener; import com.vaadin.flow.data.provider.DataProviderListener;
@ -27,16 +26,14 @@ import java.util.stream.Stream;
@PageTitle("Candidates") @PageTitle("Candidates")
@Route(value = "/candidates", layout = MainLayout.class) @Route(value = "/candidates", layout = MainLayout.class)
@PermitAll @PermitAll
public class CandidatesListView extends Main { public class CandidatesListView extends BaseView {
private final CandidateService candidateService;
public CandidatesListView(final CandidateService candidateService) { public CandidatesListView(final CandidateService candidateService) {
this.candidateService = candidateService;
final HorizontalLayout hl = new HorizontalLayout(); final HorizontalLayout hl = new HorizontalLayout();
final Button addCandidate = new Button("Add Candidate"); final Button addCandidate = new Button("Add Candidate");
addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { addCandidate.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(CandidateView.class, "new"); this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new"));
}); });
hl.add(addCandidate); hl.add(addCandidate);
@ -46,7 +43,7 @@ public class CandidatesListView extends Main {
grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> { grid.addComponentColumn((ValueProvider<Candidate, Component>) candidate -> {
final Button edit = new Button("Edit"); final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(CandidateView.class, candidate.getId().toString())); this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString())));
return edit; return edit;
}); });
@ -61,6 +58,7 @@ public class CandidatesListView extends Main {
return candidateService.getCandidates().size(); return candidateService.getCandidates().size();
} }
@SuppressWarnings("unused")
@Override @Override
public Stream<Candidate> fetch(final Query<Candidate, Object> query) { public Stream<Candidate> fetch(final Query<Candidate, Object> query) {
int limit = query.getLimit(); int limit = query.getLimit();
@ -85,6 +83,6 @@ public class CandidatesListView extends Main {
} }
}); });
add(hl, grid); getCurrentPageLayout().add(hl, grid);
} }
} }

View File

@ -0,0 +1,5 @@
package com.primefactorsolutions.views;
public class Constants {
public static final int PAGE_SIZE = 10;
}

View File

@ -39,9 +39,9 @@ import java.io.InputStream;
@PageTitle("Document") @PageTitle("Document")
@Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class) @Route(value = "/documents/:documentId?/:action?", layout = MainLayout.class)
public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> { public class DocumentView extends BeanValidationForm<Document> implements HasUrlParameter<String> {
private final TextField fileName = new TextField("Document Name"); private final TextField fileName = new TextField("Nombre del documento");
private final ComboBox<DocumentType> documentType = new ComboBox<>("Document Type"); private final ComboBox<DocumentType> documentType = new ComboBox<>("Tipo de documento");
private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Employee"); private final ComboBox<Employee> employeeComboBox = new ComboBox<>("Empleado");
private final MemoryBuffer buffer = new MemoryBuffer(); private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload uploadButton = new Upload(buffer); private final Upload uploadButton = new Upload(buffer);
private final DocumentService documentService; private final DocumentService documentService;
@ -68,19 +68,19 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
} }
protected Button createSaveButton() { protected Button createSaveButton() {
saveButton = new Button("Save"); saveButton = new Button("Guardar");
saveButton.addClickListener(event -> saveDocument()); saveButton.addClickListener(event -> saveDocument());
return saveButton; return saveButton;
} }
protected Button createCloseButton() { protected Button createCloseButton() {
Button closeButton = new Button("Close"); Button closeButton = new Button("Salir");
closeButton.addClickListener(event -> closeForm()); closeButton.addClickListener(event -> closeForm());
return closeButton; return closeButton;
} }
protected Button createViewDocumentButton() { protected Button createViewDocumentButton() {
viewDocumentButton = new Button("View Document"); viewDocumentButton = new Button("Ver documento");
viewDocumentButton.setEnabled(false); viewDocumentButton.setEnabled(false);
viewDocumentButton.addClickListener(event -> viewDocument()); viewDocumentButton.addClickListener(event -> viewDocument());
return viewDocumentButton; return viewDocumentButton;
@ -130,7 +130,7 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
ui.getPage().open(registration.getResourceUri().toString()); ui.getPage().open(registration.getResourceUri().toString());
}); });
} catch (IOException e) { } catch (IOException e) {
Notification.show("Error reading file."); Notification.show("Error al leer el archivo.");
} }
} }
@ -148,10 +148,10 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
setDocumentCreator(document); setDocumentCreator(document);
documentService.saveDocument(document); documentService.saveDocument(document);
Notification.show("File saved successfully."); Notification.show("Archivo guardado correctamente.");
clearForm(); clearForm();
} else { } else {
Notification.show("Save failed: Please complete all fields and upload a file."); Notification.show("Error al guardar: Por favor, complete todos los campos y cargue un archivo.");
} }
} }
@ -179,7 +179,7 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
try { try {
return buffer.getInputStream().readAllBytes(); return buffer.getInputStream().readAllBytes();
} catch (IOException e) { } catch (IOException e) {
Notification.show("Error reading file data."); Notification.show("Error al leer los datos del archivo.");
return new byte[0]; return new byte[0];
} }
} }
@ -220,13 +220,13 @@ public class DocumentView extends BeanValidationForm<Document> implements HasUrl
uploadButton.setAcceptedFileTypes(".pdf"); uploadButton.setAcceptedFileTypes(".pdf");
uploadButton.addSucceededListener(event -> { uploadButton.addSucceededListener(event -> {
fileUploaded = true; fileUploaded = true;
Notification.show("File uploaded successfully."); Notification.show("Archivo cargado correctamente.");
viewDocumentButton.setEnabled(true); viewDocumentButton.setEnabled(true);
updateSaveButtonState(); updateSaveButtonState();
}); });
uploadButton.getElement().addEventListener("file-remove", event -> { uploadButton.getElement().addEventListener("file-remove", event -> {
fileUploaded = false; fileUploaded = false;
Notification.show("File removed."); Notification.show("Archivo eliminado.");
viewDocumentButton.setEnabled(false); viewDocumentButton.setEnabled(false);
updateSaveButtonState(); updateSaveButtonState();
}); });

View File

@ -5,13 +5,22 @@ import com.primefactorsolutions.model.DocumentType;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.DocumentService; import com.primefactorsolutions.service.DocumentService;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.UI; import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.grid.GridSortOrder; import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.SortDirection; import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration; import com.vaadin.flow.server.StreamRegistration;
@ -24,13 +33,14 @@ import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.List; import java.util.List;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Documents") @PageTitle("Documents")
@Route(value = "/documents", layout = MainLayout.class) @Route(value = "/documents", layout = MainLayout.class)
@PermitAll @PermitAll
public class DocumentsListView extends Main { public class DocumentsListView extends BaseView {
private final DocumentService documentService; private final DocumentService documentService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
@ -46,16 +56,24 @@ public class DocumentsListView extends Main {
} }
private void initializeView() { private void initializeView() {
getCurrentPageLayout().add(createActionButton("Añadir documento", this::navigateToAddDocumentView));
final HorizontalLayout hl = new HorizontalLayout();
hl.add(createDocumentTypeFilter());
hl.add(createEmployeeFilter());
getCurrentPageLayout().add(hl);
configureDocumentGrid(); configureDocumentGrid();
add(createActionButton("Add Document", this::navigateToAddDocumentView)); getCurrentPageLayout().add(documentGrid);
add(createDocumentTypeFilter());
add(createEmployeeFilter());
add(documentGrid);
} }
private void configureDocumentGrid() { private void configureDocumentGrid() {
documentGrid.setColumns("fileName", "documentType", "creator"); documentGrid.setColumns("fileName", "documentType", "creator");
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Employee"); documentGrid.getColumnByKey("fileName").setHeader("Nombre archivo");
documentGrid.getColumnByKey("documentType").setHeader("Tipo");
documentGrid.getColumnByKey("creator").setHeader("Creador");
documentGrid.addComponentColumn(this::createEmployeeSpan).setHeader("Empleado");
addActionColumns(); addActionColumns();
configurePagination(); configurePagination();
} }
@ -67,13 +85,20 @@ public class DocumentsListView extends Main {
} }
private void addActionColumns() { private void addActionColumns() {
addDocumentActionColumn("View", this::navigateToDocumentView); documentGrid.addComponentColumn((ValueProvider<Document, Component>) document -> {
addDocumentActionColumn("Edit", this::navigateToEditDocumentView); final MenuBar menuBar = new MenuBar();
addDocumentActionColumn("Download", this::downloadDocument); menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
} final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
private void addDocumentActionColumn(final String label, final DocumentActionHandler handler) { navigateToDocumentView(document));
documentGrid.addComponentColumn(document -> createActionButton(label, () -> handler.handle(document))); final MenuItem editItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.PENCIL, "Editar");
editItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
navigateToEditDocumentView(document));
final MenuItem downloadItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.DOWNLOAD, "Descargar");
downloadItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
downloadDocument(document));
return menuBar;
});
} }
private Button createActionButton(final String label, final Runnable onClickAction) { private Button createActionButton(final String label, final Runnable onClickAction) {
@ -83,7 +108,7 @@ public class DocumentsListView extends Main {
} }
private ComboBox<DocumentType> createDocumentTypeFilter() { private ComboBox<DocumentType> createDocumentTypeFilter() {
documentTypeFilter = new ComboBox<>("Document Type"); documentTypeFilter = new ComboBox<>("Tipo de documento");
documentTypeFilter.setItems(DocumentType.values()); documentTypeFilter.setItems(DocumentType.values());
documentTypeFilter.setValue(DocumentType.values()[0]); documentTypeFilter.setValue(DocumentType.values()[0]);
documentTypeFilter.addValueChangeListener(event -> { documentTypeFilter.addValueChangeListener(event -> {
@ -93,7 +118,7 @@ public class DocumentsListView extends Main {
} }
private ComboBox<Employee> createEmployeeFilter() { private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Employee"); employeeFilter = new ComboBox<>("Empleado");
List<Employee> employees = employeeService.findAllEmployees(); List<Employee> employees = employeeService.findAllEmployees();
employees.addFirst(createAllEmployeesOption()); employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees); employeeFilter.setItems(employees);
@ -107,12 +132,13 @@ public class DocumentsListView extends Main {
private Employee createAllEmployeesOption() { private Employee createAllEmployeesOption() {
Employee allEmployeesOption = new Employee(); Employee allEmployeesOption = new Employee();
allEmployeesOption.setFirstName("All"); allEmployeesOption.setFirstName("TODOS");
return allEmployeesOption; return allEmployeesOption;
} }
private String getEmployeeLabel(final Employee employee) { private String getEmployeeLabel(final Employee employee) {
return employee.getFirstName().equals("All") ? "All" : employee.getFirstName() + " " + employee.getLastName(); return employee.getFirstName().equals("TODOS")
? "TODOS" : employee.getFirstName() + " " + employee.getLastName();
} }
private void navigateToEditDocumentView(final Document document) { private void navigateToEditDocumentView(final Document document) {
@ -133,7 +159,7 @@ public class DocumentsListView extends Main {
private void configurePagination() { private void configurePagination() {
documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); documentGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
documentGrid.setPageSize(5); documentGrid.setPageSize(PAGE_SIZE);
} }
private void updateDocumentGrid(final DocumentType documentType, final Employee employee) { private void updateDocumentGrid(final DocumentType documentType, final Employee employee) {
@ -197,9 +223,4 @@ public class DocumentsListView extends Main {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource); StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString()); ui.getPage().open(registration.getResourceUri().toString());
} }
@FunctionalInterface
private interface DocumentActionHandler {
void handle(Document document);
}
} }

View File

@ -50,12 +50,10 @@ import java.util.UUID;
@PageTitle("Employee") @PageTitle("Employee")
@Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class) @Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class)
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> { public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final ReportService reportService; private final ReportService reportService;
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final TeamService teamService; private final TeamService teamService;
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente. // 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 username = createTextField("Username: ", 30, true);
private final TextField firstName = createTextField("Nombres: ", 30, true); private final TextField firstName = createTextField("Nombres: ", 30, true);
@ -70,21 +68,21 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final TextField localAddress = createTextField("Departamento y Provincia de Residencia " private final TextField localAddress = createTextField("Departamento y Provincia de Residencia "
+ " ejemplo: (Departamento-Provincia)", 30, false); + " ejemplo: (Departamento-Provincia)", 30, false);
private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox(); private final ComboBox<Employee.MaritalStatus> maritalStatus = createMaritalStatusComboBox();
private final TextField numberOfChildren = createTextField("Numero de Hijos", 2, false); private final TextField numberOfChildren = createTextField("Numero de Hijos", 1, false);
private final TextField ci = createTextField("CI", 10, false); private final TextField ci = createTextField("CI", 10, false);
private final TextField issuedIn = createTextField("Expedido en ", 10, false); private final TextField issuedIn = createTextField("Expedido en ", 10, false);
private final TextField phoneNumber = createTextField("Teléfono", 8, false); private final TextField phoneNumber = createTextField("Teléfono", 8, false);
private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)"); private final EmailField personalEmail = createEmailField("E-mail ejemplo: (ejemplo@gmail.com)");
private final TextField phoneNumberProfesional = createTextField("Teléfono Laboral", 8, false);
private final EmailField profesionalEmail = createEmailField("E-mail Laboral ejemplo: "
+ "(ejemplo@primerfactorsolutions.com)");
private final TextField emergencyCName = createTextField("Nombres y Apellidos de Contacto", 50, false); 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 emergencyCAddress = createTextField("Dirección de Contacto", 50, false);
private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false); private final TextField emergencyCPhone = createTextField("Teléfono de Contacto", 8, false);
private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)"); private final EmailField emergencyCEmail = createEmailField("Email de Contacto ejemplo: (ejemplo@gmail.com)");
private final MemoryBuffer buffer = new MemoryBuffer(); private final MemoryBuffer buffer = new MemoryBuffer();
private final Upload upload = new Upload(buffer); private final Upload upload = new Upload(buffer);
private final Image profileImagePreview = new Image(); private final Image profileImagePreview = new Image();
//INFORMACION PROFESIONAL
private final TextField pTitle1 = createTextField("Título 1", 30, false); private final TextField pTitle1 = createTextField("Título 1", 30, false);
private final TextField pTitle2 = createTextField("Título 2", 30, false); private final TextField pTitle2 = createTextField("Título 2", 30, false);
private final TextField pTitle3 = createTextField("Título 3", 30, false); private final TextField pTitle3 = createTextField("Título 3", 30, false);
@ -97,49 +95,46 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final TextField certification4 = createTextField("Certificación 4", 30, false); private final TextField certification4 = createTextField("Certificación 4", 30, false);
private final TextField recognition = createTextField("Reconocimientos", 30, false); private final TextField recognition = createTextField("Reconocimientos", 30, false);
private final TextField achievements = createTextField("Logros Profesionales", 30, false); private final TextField achievements = createTextField("Logros Profesionales", 30, false);
private final TextField language = createTextField("Idioma", 50, false); private final TextField language1 = createTextField("Idioma 1", 30, false);
private final TextField languageLevel = createTextField("Nivel de Idioma", 30, false); private final TextField language1Level = createTextField("Nivel de Idioma", 30, false);
private final TextField language2 = createTextField("Idioma 2", 30, false);
//INFORMACION ADMINISTRATIVA private final TextField language2Level = createTextField("Nivel de Idioma", 30, false);
private final TextField cod = createTextField("Codigo de Empleado", 20, false); private final TextField cod = createTextField("Codigo de Empleado", 20, false);
private final TextField position = createTextField("Cargo", 30, false); private final TextField position = createTextField("Cargo", 30, false);
private final ComboBox<Team> team = new ComboBox<>("Equipo"); private final ComboBox<Team> team = new ComboBox<>("Equipo");
private final TextField leadManager = createTextField("Lead/Manager", 30, false); 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 dateOfEntry = new VDatePicker("Fecha de Ingreso");
private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro"); private final VDatePicker dateOfExit = new VDatePicker("Fecha de Retiro");
private final TextField contractType = createTextField("Tipo de Contratación", 30, false); private final ComboBox<Employee.ContractType> contractType = createContractTypeComboBox();
private final TextField seniority = createTextField("Antiguedad", 30, false); private final TextField seniority = createTextField("Antiguedad", 30, false);
private final TextField salary = createTextField("Salario", 30, false); private final TextField salaryTotal = createTextField("Salario Total", 10, false);
private final TextField salaryBasic = createTextField("Salario Basico", 10, false);
private final TextField antiguedad = createTextField("Descuento por Antiguedad", 10, false);
private final TextField bonoProfesional = createTextField("Bono Profesional", 30, false);
private final TextField bankName = createTextField("Banco", 30, false); private final TextField bankName = createTextField("Banco", 30, false);
private final TextField accountNumber = createTextField("Nro. de Cuenta", 30, false); private final TextField accountNumber = createTextField("Nro. de Cuenta", 30, false);
private final TextField gpss = createTextField("Código Único de Asegurado (GPSS)", 30, false); private final TextField gpss = createTextField("Código Único de Asegurado (GPSS)", 30, false);
private final TextField sss = createTextField("Matricula de Asegurado (SSS)", 30, false); private final TextField sss = createTextField("Matricula de Asegurado (SSS)", 30, false);
private final TextField beneficiaries = createTextField("Derechohabientes", 30, false); private final TextField beneficiarie1 = createTextField("Derechohabiente 1", 30, false);
private final TextField beneficiarie2 = createTextField("Derechohabiente 2", 30, false);
private static final String SAVE_BUTTON_TEXT = "Save"; private static final String SAVE_BUTTON_TEXT = "Save";
private static final String EDIT_BUTTON_TEXT = "Edit"; private static final String EDIT_BUTTON_TEXT = "Edit";
private static final String NOTIFICATION_SAVE_SUCCESS = "Employee saved successfully."; private static final String NOTIFICATION_SAVE_SUCCESS = "Employee saved successfully.";
private static final String NOTIFICATION_VALIDATE_ERROR = "Please complete the required fields correctly."; private static final String NOTIFICATION_VALIDATE_ERROR = "Please complete the required fields correctly.";
private static final String PHONE_NUMBER_ERROR_MESSAGE = "El teléfono debe contener solo números."; private static final String PHONE_NUMBER_ERROR_MESSAGE = "El teléfono debe contener solo números.";
private final Button saveButton = new Button(SAVE_BUTTON_TEXT, e -> saveEmployee()); private final Button saveButton = new Button(SAVE_BUTTON_TEXT, e -> saveEmployee());
private final Button editButton = new Button(EDIT_BUTTON_TEXT, e -> enableEditMode()); private final Button editButton = new Button(EDIT_BUTTON_TEXT, e -> enableEditMode());
private final Button reportButton = new Button("Generar Ficha"); private final Button reportButton = new Button("Generar Ficha");
private final Dialog dialog = new Dialog(); private final Dialog dialog = new Dialog();
private final PdfViewer pdfViewer = new PdfViewer(); private final PdfViewer pdfViewer = new PdfViewer();
//TITULOS PARA INFORMACION PERSONAL
private final H2 infoPer = new H2("Información Personal"); private final H2 infoPer = new H2("Información Personal");
private final H3 infoGenr = new H3("Información General"); private final H3 infoGenr = new H3("Información General");
private final H3 contEmerg = new H3("Contacto de Emergencia"); private final H3 contEmerg = new H3("Contacto de Emergencia");
//TITULOS PARA INFORMACIÓN PROFESIONAL
private final H2 infProf = new H2("Información Profesional"); private final H2 infProf = new H2("Información Profesional");
private final H3 titulos = new H3("Titulos Profesionales y Estudios Realizados"); private final H3 titulos = new H3("Titulos Profesionales y Estudios Realizados");
private final H3 certif = new H3("Certificaciones Profesionales"); private final H3 certif = new H3("Certificaciones Profesionales");
private final H3 logros = new H3("Otros Logros y Reconocimientos"); private final H3 logros = new H3("Otros Logros y Reconocimientos");
private final H3 idioma = new H3("Dominio de Idiomas"); private final H3 idioma = new H3("Dominio de Idiomas");
//TITULOS PARA INFORMACIÓN ADMINISTRATIVA
private final H2 infoAdm = new H2("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 infoCont = new H3("Información de Contratación");
private final H3 datBanc = new H3("Datos Bancarios"); private final H3 datBanc = new H3("Datos Bancarios");
@ -155,32 +150,44 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
this.requestService = requestService; this.requestService = requestService;
this.teamService = teamService; this.teamService = teamService;
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
configureComponents(); configureComponents();
addClassName("main-layout"); addClassName("main-layout");
} }
private void makeUpperCase(final TextField textField) {
textField.addValueChangeListener(event -> {
String value = event.getValue();
if (value != null) {
textField.setValue(value.toUpperCase());
}
});
}
private void configureComponents() { private void configureComponents() {
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER); phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue())); phoneNumber.addValueChangeListener(e -> validatePhoneNumber(phoneNumber, e.getValue()));
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER); emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue())); emergencyCPhone.addValueChangeListener(e -> validatePhoneNumber(emergencyCPhone, e.getValue()));
firstName.setValueChangeMode(ValueChangeMode.EAGER); firstName.setValueChangeMode(ValueChangeMode.EAGER);
firstName.addValueChangeListener(e -> validateNameField(firstName, e.getValue())); firstName.addValueChangeListener(e -> validateNameField(firstName, e.getValue()));
lastName.setValueChangeMode(ValueChangeMode.EAGER); lastName.setValueChangeMode(ValueChangeMode.EAGER);
lastName.addValueChangeListener(e -> validateNameField(lastName, e.getValue())); lastName.addValueChangeListener(e -> validateNameField(lastName, e.getValue()));
createTeamComboBox(); createTeamComboBox();
configureUpload(); configureUpload();
saveButton.setVisible(true); saveButton.setVisible(true);
editButton.setVisible(true); editButton.setVisible(true);
reportButton.setVisible(true); reportButton.setVisible(true);
birthday.addValueChangeListener(event -> calculateAge()); birthday.addValueChangeListener(event -> calculateAge());
birthday.setMax(java.time.LocalDate.now()); birthday.setMax(java.time.LocalDate.now().minusYears(18));
salaryTotal.addValueChangeListener(event -> calculateSalaryTotal());
dateOfEntry.addValueChangeListener(event -> calculateSeniority()); dateOfEntry.addValueChangeListener(event -> calculateSeniority());
dateOfEntry.addValueChangeListener(event -> calculateSeniority()); dateOfExit.addValueChangeListener(event -> {
if (event.getValue() != null) {
status.setValue(Employee.Status.INACTIVE);
} else {
status.setValue(Employee.Status.ACTIVE);
}
});
reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { reportButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
var employee = getEntity(); var employee = getEntity();
byte[] pdfContent = reportService.writeAsPdf("ficha", employee); byte[] pdfContent = reportService.writeAsPdf("ficha", employee);
@ -189,6 +196,41 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
dialog.open(); dialog.open();
}); });
makeUpperCase(firstName);
makeUpperCase(lastName);
makeUpperCase(birthCity);
makeUpperCase(residenceAddress);
makeUpperCase(localAddress);
makeUpperCase(position);
makeUpperCase(emergencyCName);
makeUpperCase(emergencyCAddress);
makeUpperCase(ci);
makeUpperCase(issuedIn);
makeUpperCase(pTitle1);
makeUpperCase(pTitle2);
makeUpperCase(pTitle3);
makeUpperCase(pStudy1);
makeUpperCase(pStudy2);
makeUpperCase(pStudy3);
makeUpperCase(certification1);
makeUpperCase(certification2);
makeUpperCase(certification3);
makeUpperCase(certification4);
makeUpperCase(recognition);
makeUpperCase(achievements);
makeUpperCase(language1);
makeUpperCase(language1Level);
makeUpperCase(language2);
makeUpperCase(language2Level);
makeUpperCase(cod);
makeUpperCase(leadManager);
makeUpperCase(seniority);
makeUpperCase(bankName);
makeUpperCase(accountNumber);
makeUpperCase(gpss);
makeUpperCase(sss);
makeUpperCase(beneficiarie1);
makeUpperCase(beneficiarie2);
initDialog(); initDialog();
} }
@ -207,13 +249,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
int birthYear = birthday.getValue().getYear(); int birthYear = birthday.getValue().getYear();
int ages = currentYear - birthYear; int ages = currentYear - birthYear;
age.setValue(String.valueOf(ages)); age.setValue(String.valueOf(ages));
if (ages < 18) { birthday.setInvalid(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); System.out.println(age);
} }
} }
@ -221,7 +257,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private void calculateSeniority() { private void calculateSeniority() {
LocalDate entryDate = dateOfEntry.getValue(); LocalDate entryDate = dateOfEntry.getValue();
LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now(); LocalDate exitDate = dateOfExit.getValue() != null ? dateOfExit.getValue() : LocalDate.now();
if (entryDate != null) { if (entryDate != null) {
long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate); long yearsOfService = ChronoUnit.YEARS.between(entryDate, exitDate);
String seniorityValue = yearsOfService + " años "; String seniorityValue = yearsOfService + " años ";
@ -231,6 +266,44 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
} }
} }
private void calculateSalaryTotal() {
if (contractType.getValue() == Employee.ContractType.CONTRATO_LABORAL) {
salaryBasic.setVisible(true);
bonoProfesional.setVisible(true);
antiguedad.setVisible(true);
salaryTotal.setVisible(true);
salaryBasic.addValueChangeListener(event -> updateTotalSalary());
bonoProfesional.addValueChangeListener(event -> updateTotalSalary());
antiguedad.addValueChangeListener(event -> updateTotalSalary());
} else {
salaryBasic.setVisible(false);
bonoProfesional.setVisible(false);
antiguedad.setVisible(false);
salaryTotal.setVisible(true);
}
salaryTotal.getValue();
}
private void updateTotalSalary() {
try {
double basic = parseDoubleValue(salaryBasic.getValue());
double bonus = parseDoubleValue(bonoProfesional.getValue());
double seniorityBonus = parseDoubleValue(antiguedad.getValue());
double totalSalary = basic + bonus + seniorityBonus;
salaryTotal.setValue(String.valueOf(totalSalary));
} catch (Exception e) {
salaryTotal.setValue("0.0");
}
}
private double parseDoubleValue(final String value) {
try {
return value != null && !value.isEmpty() ? Double.parseDouble(value) : 0.0;
} catch (NumberFormatException e) {
return 0.0;
}
}
private void configureUpload() { private void configureUpload() {
upload.setAcceptedFileTypes("image/jpeg", "image/png"); upload.setAcceptedFileTypes("image/jpeg", "image/png");
upload.setMaxFileSize(1024 * 1024); upload.setMaxFileSize(1024 * 1024);
@ -239,9 +312,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
buffer.getInputStream().transferTo(outputStream); buffer.getInputStream().transferTo(outputStream);
byte[] imageBytes = outputStream.toByteArray(); byte[] imageBytes = outputStream.toByteArray();
String base64Image = Base64.getEncoder().encodeToString(imageBytes); String base64Image = Base64.getEncoder().encodeToString(imageBytes);
getEntity().setProfileImage(base64Image); getEntity().setProfileImage(base64Image);
profileImagePreview.setSrc("data:image/png;base64," + base64Image); profileImagePreview.setSrc("data:image/png;base64," + base64Image);
profileImagePreview.setMaxWidth("150px"); profileImagePreview.setMaxWidth("150px");
profileImagePreview.setMaxHeight("150px"); profileImagePreview.setMaxHeight("150px");
@ -266,11 +337,9 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
H2 headline = new H2("Ficha Empleado"); H2 headline = new H2("Ficha Empleado");
headline.getStyle().set("margin", "var(--lumo-space-m) 0 0 0") headline.getStyle().set("margin", "var(--lumo-space-m) 0 0 0")
.set("font-size", "1.5em").set("font-weight", "bold"); .set("font-size", "1.5em").set("font-weight", "bold");
final Button cancelDialogButton = new Button("Close", e -> dialog.close()); final Button cancelDialogButton = new Button("Close", e -> dialog.close());
final HorizontalLayout buttonLayout = new HorizontalLayout(cancelDialogButton); final HorizontalLayout buttonLayout = new HorizontalLayout(cancelDialogButton);
buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END); buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
final VerticalLayout dialogLayout = new VerticalLayout(headline, pdfViewer, buttonLayout); final VerticalLayout dialogLayout = new VerticalLayout(headline, pdfViewer, buttonLayout);
dialogLayout.getStyle().set("height", "100%"); dialogLayout.getStyle().set("height", "100%");
dialogLayout.getStyle().set("overflow", "hidden"); dialogLayout.getStyle().set("overflow", "hidden");
@ -280,7 +349,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH); dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
dialogLayout.getStyle().set("width", "700px").set("max-width", "100%"); dialogLayout.getStyle().set("width", "700px").set("max-width", "100%");
dialogLayout.getStyle().set("height", "800px").set("max-height", "100%"); dialogLayout.getStyle().set("height", "800px").set("max-height", "100%");
dialog.add(dialogLayout); dialog.add(dialogLayout);
} }
@ -299,6 +367,16 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
return comboBox; return comboBox;
} }
private ComboBox<Employee.ContractType> createContractTypeComboBox() {
ComboBox<Employee.ContractType> comboBox = new ComboBox<>("Tipo de Contrato");
comboBox.setItems(Employee.ContractType.values());
comboBox.setItemLabelGenerator(Employee.ContractType::name);
comboBox.setRequiredIndicatorVisible(true);
comboBox.setWidth("300px");
comboBox.setMinWidth("200px");
return comboBox;
}
private VerticalLayout createContentLayout() { private VerticalLayout createContentLayout() {
VerticalLayout contentLayout = new VerticalLayout(); VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setWidth("100%"); contentLayout.setWidth("100%");
@ -316,7 +394,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private EmailField createEmailField(final String label) { private EmailField createEmailField(final String label) {
EmailField emailField = new EmailField(label); EmailField emailField = new EmailField(label);
emailField.setWidthFull(); emailField.setWidthFull();
emailField.setMaxLength(30); emailField.setMaxLength(50);
return emailField; return emailField;
} }
@ -372,7 +450,10 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
Employee employee = getEntity(); Employee employee = getEntity();
employee.setStatus(status.getValue()); employee.setStatus(status.getValue());
employee.setAge(age.getValue()); employee.setAge(age.getValue());
employee.setSalaryBasic(salaryBasic.getValue());
employee.setBonoProfesional(bonoProfesional.getValue());
employee.setAntiguedad(antiguedad.getValue());
employee.setSalarytotal((salaryTotal.getValue()));
employeeService.createOrUpdate(employee); employeeService.createOrUpdate(employee);
Notification.show(NOTIFICATION_SAVE_SUCCESS); Notification.show(NOTIFICATION_SAVE_SUCCESS);
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class)); getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
@ -391,28 +472,33 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
public void setParameter(final BeforeEvent beforeEvent, final String action) { public void setParameter(final BeforeEvent beforeEvent, final String action) {
final RouteParameters params = beforeEvent.getRouteParameters(); final RouteParameters params = beforeEvent.getRouteParameters();
final String s = params.get("employeeId").orElse(null); final String s = params.get("employeeId").orElse(null);
if ("new".equals(action)) { if ("new".equals(action)) {
setEntityWithEnabledSave(new Employee()); setEntityWithEnabledSave(new Employee());
saveButton.setVisible(true); saveButton.setVisible(true);
editButton.setVisible(false); editButton.setVisible(false);
setFieldsEditable(); setFieldsEditable();
upload.setVisible(true);
salaryTotal.setValue(String.valueOf(true));
} else { } else {
UUID employeeId = UUID.fromString(s); UUID employeeId = UUID.fromString(s);
var employee = employeeService.getEmployee(employeeId); var employee = employeeService.getEmployee(employeeId);
setEntityWithEnabledSave(employee); setEntityWithEnabledSave(employee);
if ("edit".equals(action) && !s.isEmpty()) { if ("edit".equals(action) && !s.isEmpty()) {
saveButton.setVisible(true); saveButton.setVisible(true);
editButton.setVisible(false); editButton.setVisible(false);
status.setValue(employee.getStatus()); status.setValue(employee.getStatus());
setFieldsEditable(); setFieldsEditable();
upload.setVisible(true);
displayProfileImage(employee);
salaryTotal.setValue(employee.getSalarytotal());
} else if ("view".equals(action) && !s.isEmpty()) { } else if ("view".equals(action) && !s.isEmpty()) {
setFieldsReadOnly(); setFieldsReadOnly();
saveButton.setVisible(false); saveButton.setVisible(false);
editButton.setVisible(true); editButton.setVisible(true);
setFieldsReadOnly(); setFieldsReadOnly();
displayProfileImage(employee); displayProfileImage(employee);
upload.setVisible(true);
salaryTotal.setValue(employee.getSalarytotal());
} }
} }
} }
@ -423,7 +509,6 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
profileImagePreview.setVisible(true); profileImagePreview.setVisible(true);
profileImagePreview.setMaxWidth("250px"); profileImagePreview.setMaxWidth("250px");
profileImagePreview.setMaxHeight("250px"); profileImagePreview.setMaxHeight("250px");
upload.setVisible(true); upload.setVisible(true);
} else { } else {
profileImagePreview.setVisible(true); profileImagePreview.setVisible(true);
@ -432,7 +517,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
} }
private void setFieldsReadOnly() { private void setFieldsReadOnly() {
username.setReadOnly(false); username.setReadOnly(true);
firstName.setReadOnly(true); firstName.setReadOnly(true);
lastName.setReadOnly(true); lastName.setReadOnly(true);
status.setReadOnly(true); status.setReadOnly(true);
@ -444,6 +529,8 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
numberOfChildren.setReadOnly(true); numberOfChildren.setReadOnly(true);
phoneNumber.setReadOnly(true); phoneNumber.setReadOnly(true);
personalEmail.setReadOnly(true); personalEmail.setReadOnly(true);
phoneNumberProfesional.setReadOnly(true);
profesionalEmail.setReadOnly(true);
position.setReadOnly(true); position.setReadOnly(true);
team.setReadOnly(true); team.setReadOnly(true);
emergencyCName.setReadOnly(true); emergencyCName.setReadOnly(true);
@ -469,21 +556,26 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
certification4.setReadOnly(true); certification4.setReadOnly(true);
recognition.setReadOnly(true); recognition.setReadOnly(true);
achievements.setReadOnly(true); achievements.setReadOnly(true);
language.setReadOnly(true); language1.setReadOnly(true);
languageLevel.setReadOnly(true); language1Level.setReadOnly(true);
language2.setReadOnly(true);
language2Level.setReadOnly(true);
cod.setReadOnly(true); cod.setReadOnly(true);
leadManager.setReadOnly(true); leadManager.setReadOnly(true);
project.setReadOnly(true);
dateOfEntry.setReadOnly(true); dateOfEntry.setReadOnly(true);
dateOfExit.setReadOnly(true); dateOfExit.setReadOnly(true);
contractType.setReadOnly(true); contractType.setReadOnly(true);
seniority.setReadOnly(true); seniority.setReadOnly(true);
salary.setReadOnly(true); salaryTotal.setReadOnly(true);
salaryBasic.setReadOnly(true);
bonoProfesional.setReadOnly(true);
antiguedad.setReadOnly(true);
bankName.setReadOnly(true); bankName.setReadOnly(true);
accountNumber.setReadOnly(true); accountNumber.setReadOnly(true);
gpss.setReadOnly(true); gpss.setReadOnly(true);
sss.setReadOnly(true); sss.setReadOnly(true);
beneficiaries.setReadOnly(true); beneficiarie1.setReadOnly(true);
beneficiarie2.setReadOnly(true);
} }
private void setFieldsEditable() { private void setFieldsEditable() {
@ -499,6 +591,8 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
numberOfChildren.setReadOnly(false); numberOfChildren.setReadOnly(false);
phoneNumber.setReadOnly(false); phoneNumber.setReadOnly(false);
personalEmail.setReadOnly(false); personalEmail.setReadOnly(false);
phoneNumberProfesional.setReadOnly(false);
profesionalEmail.setReadOnly(false);
position.setReadOnly(false); position.setReadOnly(false);
team.setReadOnly(false); team.setReadOnly(false);
emergencyCName.setReadOnly(false); emergencyCName.setReadOnly(false);
@ -524,21 +618,26 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
certification4.setReadOnly(false); certification4.setReadOnly(false);
recognition.setReadOnly(false); recognition.setReadOnly(false);
achievements.setReadOnly(false); achievements.setReadOnly(false);
language.setReadOnly(false); language1.setReadOnly(false);
languageLevel.setReadOnly(false); language1Level.setReadOnly(false);
language2.setReadOnly(false);
language2Level.setReadOnly(false);
cod.setReadOnly(false); cod.setReadOnly(false);
leadManager.setReadOnly(false); leadManager.setReadOnly(false);
project.setReadOnly(false);
dateOfEntry.setReadOnly(false); dateOfEntry.setReadOnly(false);
dateOfExit.setReadOnly(false); dateOfExit.setReadOnly(false);
contractType.setReadOnly(false); contractType.setReadOnly(false);
seniority.setReadOnly(false); seniority.setReadOnly(false);
salary.setReadOnly(false); salaryTotal.setReadOnly(false);
salaryBasic.setReadOnly(false);
bonoProfesional.setReadOnly(false);
antiguedad.setReadOnly(false);
bankName.setReadOnly(false); bankName.setReadOnly(false);
accountNumber.setReadOnly(false); accountNumber.setReadOnly(false);
gpss.setReadOnly(false); gpss.setReadOnly(false);
sss.setReadOnly(false); sss.setReadOnly(false);
beneficiaries.setReadOnly(false); beneficiarie1.setReadOnly(false);
beneficiarie2.setReadOnly(false);
} }
@Override @Override
@ -553,18 +652,19 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
birthday, age, birthday, age,
birthCity, residenceAddress, localAddress, birthCity, residenceAddress, localAddress,
maritalStatus, ci, issuedIn, numberOfChildren, maritalStatus, ci, issuedIn, numberOfChildren,
phoneNumber, personalEmail, phoneNumber, personalEmail, phoneNumberProfesional, profesionalEmail,
contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail, contEmerg, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail,
infProf, infProf,
titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3, titulos, pTitle1, pTitle2, pTitle3, pStudy1, pStudy2, pStudy3,
certif, certification1, certification2, certification3, certification4, certif, certification1, certification2, certification3, certification4,
logros, recognition, achievements, logros, recognition, achievements,
idioma, language, languageLevel, idioma, language1, language1Level, language2, language2Level,
infoAdm, infoAdm,
cod, position, team, leadManager, project, cod, position, team, leadManager,
infoCont, dateOfEntry, dateOfExit, contractType, seniority, salary, infoCont, dateOfEntry, dateOfExit, contractType, seniority,
salaryBasic, bonoProfesional, antiguedad, salaryTotal,
datBanc, bankName, accountNumber, datBanc, bankName, accountNumber,
datGest, gpss, sss, beneficiaries, datGest, gpss, sss, beneficiarie1, beneficiarie2,
saveButton, editButton, reportButton, dialog saveButton, editButton, reportButton, dialog
); );
} }

View File

@ -3,8 +3,6 @@ package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
@ -16,13 +14,12 @@ import org.springframework.context.annotation.Scope;
import java.util.List; import java.util.List;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Employees") @PageTitle("Employees")
@Route(value = "/employees", layout = MainLayout.class) @Route(value = "/employees", layout = MainLayout.class)
@PermitAll @PermitAll
public class EmployeesListView extends Main { public class EmployeesListView extends BaseView {
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class); private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
@ -34,10 +31,9 @@ public class EmployeesListView extends Main {
} }
private void setupView() { private void setupView() {
add(new H2("Employee List"));
configureTable(); configureTable();
add(createAddEmployeeButton()); getCurrentPageLayout().add(createAddEmployeeButton());
add(table); getCurrentPageLayout().add(table);
} }
private void configureTable() { private void configureTable() {
@ -75,7 +71,7 @@ public class EmployeesListView extends Main {
private void setupPagingGrid() { private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(5); table.setPageSize(Constants.PAGE_SIZE);
} }
private void refreshGrid() { private void refreshGrid() {

View File

@ -6,11 +6,19 @@ import com.primefactorsolutions.model.Team;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.HoursWorkedService; import com.primefactorsolutions.service.HoursWorkedService;
import com.primefactorsolutions.service.TeamService; import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Main; import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
@ -23,22 +31,21 @@ import java.time.temporal.IsoFields;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@PageTitle("Registro de Horas Trabajadas") @PageTitle("Registro de Horas Trabajadas")
@Route(value = "/hours-worked-list", layout = MainLayout.class) @Route(value = "/hours-worked-list", layout = MainLayout.class)
public class HoursWorkedListView extends Main { public class HoursWorkedListView extends BaseView {
private final HoursWorkedService hoursWorkedService; private final HoursWorkedService hoursWorkedService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final TeamService teamService; private final TeamService teamService;
private final PagingGrid<HoursWorked> hoursWorkedGrid = new PagingGrid<>(); private final PagingGrid<HoursWorked> hoursWorkedGrid = new PagingGrid<>();
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter; private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter; private ComboBox<Team> teamFilter;
private UUID selectedEmployeeId;
public HoursWorkedListView(final HoursWorkedService hoursWorkedService, public HoursWorkedListView(final HoursWorkedService hoursWorkedService,
final EmployeeService employeeService, final EmployeeService employeeService,
@ -46,7 +53,6 @@ public class HoursWorkedListView extends Main {
this.hoursWorkedService = hoursWorkedService; this.hoursWorkedService = hoursWorkedService;
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.teamService = teamService;
this.employees = employeeService.findAllEmployees();
initializeView(); initializeView();
refreshGridListHoursWorked(null, null); refreshGridListHoursWorked(null, null);
@ -111,23 +117,25 @@ public class HoursWorkedListView extends Main {
} }
private void initializeView() { private void initializeView() {
getCurrentPageLayout().add(createAddHoursWorked());
setupFilters(); setupFilters();
add(createAddHoursWorked());
setupListHoursWorkedGrid(); setupListHoursWorkedGrid();
add(hoursWorkedGrid); getCurrentPageLayout().add(hoursWorkedGrid);
add(createActionButtons());
} }
private void setupFilters() { private void setupFilters() {
add(createEmployeeFilter()); final HorizontalLayout hl = new HorizontalLayout();
add(createTeamFilter()); hl.add(createEmployeeFilter());
hl.add(createTeamFilter());
getCurrentPageLayout().add(hl);
} }
private void setupListHoursWorkedGrid() { private void setupListHoursWorkedGrid() {
hoursWorkedGrid.addColumn(hw -> hw.getDate() != null ? hw.getDate().toString() : "") hoursWorkedGrid.addColumn(hw -> hw.getDate() != null ? hw.getDate().toString() : "")
.setHeader("Fecha") .setHeader("Fecha")
.setSortable(true); .setSortable(true);
hoursWorkedGrid.addColumn(hw -> hw.getWeekNumber()) hoursWorkedGrid.addColumn(HoursWorked::getWeekNumber)
.setHeader("Semana") .setHeader("Semana")
.setSortable(true); .setSortable(true);
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getFirstName() + " " + hw.getEmployee().getLastName()) hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getFirstName() + " " + hw.getEmployee().getLastName())
@ -135,19 +143,56 @@ public class HoursWorkedListView extends Main {
hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getTeam() != null ? hw.getEmployee().getTeam() hoursWorkedGrid.addColumn(hw -> hw.getEmployee().getTeam() != null ? hw.getEmployee().getTeam()
.getName() : "Sin asignar") .getName() : "Sin asignar")
.setHeader("Equipo"); .setHeader("Equipo");
hoursWorkedGrid.addColumn(HoursWorked::getActividad).setHeader("Actividad"); hoursWorkedGrid.addColumn(hw -> {
hoursWorkedGrid.addColumn(hw -> hw.getHours()).setHeader("Total Horas").setSortable(true); String actividad = hw.getActividad() != null ? hw.getActividad() : "Sin Actividad";
String tareaEspecifica = hw.getTareaEspecifica() != null ? hw.getTareaEspecifica() : "";
return !tareaEspecifica.isEmpty() ? tareaEspecifica : actividad;
}).setHeader("Actividad");
hoursWorkedGrid.addColumn(hw -> {
if (hw.getTareaEspecifica() != null && !hw.getTareaEspecifica().isEmpty()) {
return calcularHorasPorTareaEspecifica(hw);
} else {
return calcularHorasPorActividadGeneral(hw);
}
}).setHeader("Total Horas").setSortable(true);
hoursWorkedGrid.addColumn(hw -> hw.getHoraspendientes() - calcularTotal(hw)).setHeader("Horas Pendientes") hoursWorkedGrid.addColumn(hw -> hw.getHoraspendientes() - calcularTotal(hw)).setHeader("Horas Pendientes")
.setSortable(true); .setSortable(true);
hoursWorkedGrid.addComponentColumn((ValueProvider<HoursWorked, Component>) hoursWorked -> {
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.EYE, "Ver");
viewItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
navigateToHoursWorkedView(hoursWorked.getEmployee().getId());
});
return menuBar;
});
hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); hoursWorkedGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
hoursWorkedGrid.setPageSize(5); hoursWorkedGrid.setPageSize(PAGE_SIZE);
hoursWorkedGrid.asSingleSelect().addValueChangeListener(event -> { }
HoursWorked selectedHoursWorked = event.getValue();
if (selectedHoursWorked != null) { private double calcularHorasPorTareaEspecifica(final HoursWorked hoursWorked) {
selectedEmployeeId = selectedHoursWorked.getEmployee().getId(); List<HoursWorked> tareas = hoursWorkedService.findListHoursWorkedEmployee(
} hoursWorked.getEmployee().getId(), hoursWorked.getWeekNumber());
}); return tareas.stream()
.filter(hw -> Objects.equals(hw.getTareaEspecifica(), hoursWorked.getTareaEspecifica()))
.mapToDouble(HoursWorked::getHours)
.sum();
}
private double calcularHorasPorActividadGeneral(final HoursWorked hoursWorked) {
List<HoursWorked> actividades = hoursWorkedService.findListHoursWorkedEmployee(
hoursWorked.getEmployee().getId(), hoursWorked.getWeekNumber());
return actividades.stream()
.filter(hw -> Objects.equals(hw.getActividad(), hoursWorked.getActividad())
&& (hw.getTareaEspecifica() == null || hw.getTareaEspecifica().isEmpty()))
.mapToDouble(HoursWorked::getHours)
.sum();
}
private void navigateToHoursWorkedView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + idEmployee.toString()));
} }
private double calcularTotal(final HoursWorked hoursWorked) { private double calcularTotal(final HoursWorked hoursWorked) {
@ -159,33 +204,14 @@ public class HoursWorkedListView extends Main {
private double calculateTotalUtilized(final List<HoursWorked> employeeRequests) { private double calculateTotalUtilized(final List<HoursWorked> employeeRequests) {
return employeeRequests.stream() return employeeRequests.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.mapToDouble(hoursworked -> hoursworked.getHours()) .mapToDouble(HoursWorked::getHours)
.sum(); .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) { private Button createButton(final String label, final Runnable onClickAction) {
Button button = new Button(label); final Button button = new Button(label);
button.addClickListener(event -> onClickAction.run()); button.addClickListener(event -> onClickAction.run());
return button; return button;
} }
@ -199,7 +225,7 @@ public class HoursWorkedListView extends Main {
private ComboBox<Employee> createEmployeeFilter() { private ComboBox<Employee> createEmployeeFilter() {
employeeFilter = new ComboBox<>("Empleado"); employeeFilter = new ComboBox<>("Empleado");
List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees()); final List<Employee> employees = new ArrayList<>(employeeService.findAllEmployees());
employees.addFirst(createAllEmployeesOption()); employees.addFirst(createAllEmployeesOption());
employeeFilter.setItems(employees); employeeFilter.setItems(employees);
employeeFilter.setItemLabelGenerator(this::getEmployeeFullName); employeeFilter.setItemLabelGenerator(this::getEmployeeFullName);
@ -210,6 +236,7 @@ public class HoursWorkedListView extends Main {
teamFilter.getValue() teamFilter.getValue()
) )
); );
return employeeFilter; return employeeFilter;
} }
@ -223,7 +250,7 @@ public class HoursWorkedListView extends Main {
List<Team> teams = new ArrayList<>(teamService.findAllTeams()); List<Team> teams = new ArrayList<>(teamService.findAllTeams());
teams.addFirst(createAllTeamsOption()); teams.addFirst(createAllTeamsOption());
teamFilter.setItems(teams); teamFilter.setItems(teams);
teamFilter.setItemLabelGenerator(team -> getTeamLabel(team)); teamFilter.setItemLabelGenerator(this::getTeamLabel);
teamFilter.setValue(teams.getFirst()); teamFilter.setValue(teams.getFirst());
teamFilter.addValueChangeListener(event -> teamFilter.addValueChangeListener(event ->
refreshGridListHoursWorked( refreshGridListHoursWorked(

View File

@ -1,4 +1,5 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.model.HoursWorked; import com.primefactorsolutions.model.HoursWorked;
import com.primefactorsolutions.model.Team; import com.primefactorsolutions.model.Team;
@ -107,7 +108,8 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
private void configureTareasEspecificas() { private void configureTareasEspecificas() {
tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones", tareasEspecificasDropdown.setItems("Entrevistas", "Reuniones",
"Colaboraciones", "Aprendizajes", "Proyectos PFS", "Otros"); "Colaboraciones", "Aprendizajes", "Proyectos PFS",
"Consulta Medica", "Afiliación al Seguro", "Fallas Tecnicas", "Otros");
tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea..."); tareasEspecificasDropdown.setPlaceholder("Selecciona una tarea...");
tareasEspecificasDropdown.addValueChangeListener(event -> { tareasEspecificasDropdown.addValueChangeListener(event -> {
@ -180,7 +182,8 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
LocalDate selectedDate = event.getValue(); LocalDate selectedDate = event.getValue();
if (selectedDate != null) { if (selectedDate != null) {
int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); int weekNumber = selectedDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
Notification.show("Número de la semana: " + weekNumber, 3000, Notification.Position.BOTTOM_CENTER); Notification.show("Número de la semana: " + weekNumber,
3000, Notification.Position.BOTTOM_CENTER);
if (hoursWorked != null) { if (hoursWorked != null) {
hoursWorked.setWeekNumber(weekNumber); hoursWorked.setWeekNumber(weekNumber);
} }
@ -191,13 +194,43 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
private void saveHoursWorked() { private void saveHoursWorked() {
if (isFormValid()) { if (isFormValid()) {
HoursWorked hoursWorked = getEntity(); HoursWorked hoursWorked = getEntity();
String actividad = activityField.getValue();
String tareaEspecifica = tareasEspecificasDropdown.getValue();
if (actividad != null && !actividad.isEmpty() && tareaEspecifica != null) {
Notification.show("Solo puedes elegir una: actividad del proyecto o tarea de la empresa.",
3000, Notification.Position.BOTTOM_CENTER);
return;
}
if (actividad != null && !actividad.isEmpty()) {
hoursWorked.setActividad(actividad);
} else if (tareaEspecifica != null) {
if ("Otros".equals(tareaEspecifica)) {
// Validar que se ingresó una tarea específica en el campo de texto
String tareaEspecificaInputValue = tareaEspecificaInput.getValue();
if (tareaEspecificaInputValue == null || tareaEspecificaInputValue.isEmpty()) {
Notification.show("Por favor, ingresa una tarea específica.",
3000, Notification.Position.BOTTOM_CENTER);
return;
}
hoursWorked.setTareaEspecifica(tareaEspecificaInputValue);
} else {
hoursWorked.setTareaEspecifica(tareaEspecifica);
}
} else {
Notification.show("Por favor, selecciona una actividad o tarea para guardar.",
3000, Notification.Position.BOTTOM_CENTER);
return;
}
setFieldValues(hoursWorked); setFieldValues(hoursWorked);
hoursWorkedService.save(hoursWorked); hoursWorkedService.save(hoursWorked);
Notification.show("Horas trabajadas guardadas correctamente."); Notification.show("Horas trabajadas guardadas correctamente.",
3000, Notification.Position.BOTTOM_CENTER);
closeForm(); closeForm();
} }
} }
private void setFieldValues(final HoursWorked hoursWorked) { private void setFieldValues(final HoursWorked hoursWorked) {
hoursWorked.setDate(dateField.getValue()); hoursWorked.setDate(dateField.getValue());
hoursWorked.setTeam(teamField.getValue()); hoursWorked.setTeam(teamField.getValue());
@ -210,10 +243,10 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
Notification.show("Por favor, ingrese un número válido para las horas."); Notification.show("Por favor, ingrese un número válido para las horas.");
} }
if ("Otros".equals(tareasEspecificasDropdown.getValue())) { if ("Otros".equals(tareasEspecificasDropdown.getValue())) {
hoursWorked.setTareasEspecificas(tareaEspecificaInput.getValue()); hoursWorked.setActividad(tareaEspecificaInput.getValue());
try { try {
double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue()); double horasEspecifica = Double.parseDouble(horasTareaEspecificaField.getValue());
hoursWorked.setHorasTareasEspecificas(horasEspecifica); hoursWorked.setHours(horasEspecifica);
double totalHoras = hoursWorked.getHours() + horasEspecifica; double totalHoras = hoursWorked.getHours() + horasEspecifica;
hoursWorked.setTotalHours(totalHoras); hoursWorked.setTotalHours(totalHoras);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -223,20 +256,27 @@ public class HoursWorkedView extends BeanValidationForm<HoursWorked> implements
} }
private void closeForm() { private void closeForm() {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString())); if (hoursWorked != null) {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list/" + hoursWorked.getId().toString()));
} else {
getUI().ifPresent(ui -> ui.navigate("hours-worked-list"));
}
} }
private boolean isFormValid() { private boolean isFormValid() {
boolean isTareaEspecificaValida = "Otros".equals(tareasEspecificasDropdown.getValue())
? !tareaEspecificaInput.isEmpty()
: tareasEspecificasDropdown.getValue() != null;
boolean isActividadValida = !activityField.isEmpty();
boolean isSoloUnaOpcionElegida = (isActividadValida && tareasEspecificasDropdown.isEmpty())
|| (!isActividadValida && isTareaEspecificaValida);
return dateField.getValue() != null return dateField.getValue() != null
&& && teamField.getValue() != null
teamField.getValue() != null && employeeField.getValue() != null
&& && isSoloUnaOpcionElegida;
employeeField.getValue() != null
&&
!activityField.isEmpty();
} }
private void configureViewOrEditAction(final String action) { private void configureViewOrEditAction(final String action) {
if ("edit".equals(action) && hoursWorked != null) { if ("edit".equals(action) && hoursWorked != null) {
setFieldsReadOnly(false); setFieldsReadOnly(false);

View File

@ -13,6 +13,7 @@ import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.flow.server.auth.AnonymousAllowed;
@SuppressWarnings("unused")
@Route("init-account") @Route("init-account")
@PageTitle("PFS Intra") @PageTitle("PFS Intra")
@AnonymousAllowed @AnonymousAllowed

View File

@ -4,11 +4,17 @@ import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService; import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
import com.vaadin.flow.component.button.Button; import com.primefactorsolutions.views.util.MenuBarUtils;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Main; import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
@ -19,24 +25,23 @@ import org.vaadin.firitin.components.grid.PagingGrid;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("PendingRequests") @PageTitle("Pending Requests")
@Route(value = "/pending-requests", layout = MainLayout.class) @Route(value = "/pending-requests", layout = MainLayout.class)
@PermitAll @PermitAll
public class PendingRequestsListView extends Main { public class PendingRequestsListView extends BaseView {
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final TeamService teamService; private final TeamService teamService;
private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>(); private final PagingGrid<TimeOffRequest> pendingRequestsGrid = new PagingGrid<>();
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter; private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter; private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter; private ComboBox<TimeOffRequestType> categoryFilter;
private UUID selectedRequestId;
public PendingRequestsListView(final TimeOffRequestService requestService, public PendingRequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService, final EmployeeService employeeService,
@ -44,7 +49,6 @@ public class PendingRequestsListView extends Main {
this.requestService = requestService; this.requestService = requestService;
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.teamService = teamService;
this.employees = employeeService.findAllEmployees();
initializeView(); initializeView();
refreshGeneralPendingRequestsGrid(null, null, null); refreshGeneralPendingRequestsGrid(null, null, null);
} }
@ -52,49 +56,46 @@ public class PendingRequestsListView extends Main {
private void initializeView() { private void initializeView() {
setupFilters(); setupFilters();
setupPendingRequestsGrid(); setupPendingRequestsGrid();
add(pendingRequestsGrid);
add(createActionButtons());
} }
private void setupFilters() { private void setupFilters() {
add(createEmployeeFilter()); final HorizontalLayout hl = new HorizontalLayout();
add(createTeamFilter()); hl.add(createEmployeeFilter());
add(createCategoryFilter()); hl.add(createTeamFilter());
hl.add(createCategoryFilter());
getCurrentPageLayout().add(hl);
} }
private void setupPendingRequestsGrid() { private void setupPendingRequestsGrid() {
pendingRequestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado"); pendingRequestsGrid.addColumn(this::getEmployeeFullName).setHeader("Empleado");
pendingRequestsGrid.addColumn(this::getTeamName).setHeader("Equipo"); pendingRequestsGrid.addColumn(this::getTeamName).setHeader("Equipo");
pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría"); pendingRequestsGrid.addColumn(this::getCategory).setHeader("Categoría");
pendingRequestsGrid.addComponentColumn((ValueProvider<TimeOffRequest, Component>) timeOffRequest -> {
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem approveItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.CHECK, "Aprobar");
approveItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
actionForRequest(timeOffRequest.getId(), TimeOffRequestStatus.APROBADO);
});
final MenuItem rejectItem = MenuBarUtils.createIconItem(menuBar, VaadinIcon.BAN, "Rechazar");
rejectItem.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
actionForRequest(timeOffRequest.getId(), TimeOffRequestStatus.RECHAZADO);
});
return menuBar;
});
pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); pendingRequestsGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
pendingRequestsGrid.setPageSize(5); pendingRequestsGrid.setPageSize(PAGE_SIZE);
pendingRequestsGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue(); getCurrentPageLayout().add(pendingRequestsGrid);
if (selectedRequest != null) {
selectedRequestId = selectedRequest.getId();
}
});
} }
private HorizontalLayout createActionButtons() { private void actionForRequest(final UUID selectedRequestId, final TimeOffRequestStatus status) {
Button approveButton = createActionButton("Aprobar", TimeOffRequestStatus.APROBADO); TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId);
Button rejectButton = createActionButton("Rechazar", TimeOffRequestStatus.RECHAZADO); request.setState(status);
Button closeButton = new Button("Salir", event -> navigateToMainView()); requestService.saveTimeOffRequest(request);
return new HorizontalLayout(approveButton, rejectButton, closeButton); refreshGeneralPendingRequestsGrid(null, null, null);
}
private Button createActionButton(final String caption, final TimeOffRequestStatus status) {
return new Button(caption, event -> {
if (selectedRequestId != null) {
TimeOffRequest request = requestService.findTimeOffRequest(selectedRequestId);
request.setState(status);
requestService.saveTimeOffRequest(request);
refreshGeneralPendingRequestsGrid(null, null, null);
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
} }
private void refreshGeneralPendingRequestsGrid(final Employee employee, private void refreshGeneralPendingRequestsGrid(final Employee employee,
@ -220,8 +221,4 @@ public class PendingRequestsListView extends Main {
allTeamsOption.setName("TODOS"); allTeamsOption.setName("TODOS");
return allTeamsOption; return allTeamsOption;
} }
private void navigateToMainView() {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
} }

View File

@ -6,7 +6,6 @@ import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider; import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener; import com.vaadin.flow.data.provider.DataProviderListener;
@ -27,17 +26,14 @@ import java.util.stream.Stream;
@PageTitle("Questions") @PageTitle("Questions")
@Route(value = "/questions", layout = MainLayout.class) @Route(value = "/questions", layout = MainLayout.class)
@PermitAll @PermitAll
public class QuestionsListView extends Main { public class QuestionsListView extends BaseView {
private final QuestionService questionService;
public QuestionsListView(final QuestionService questionService) { public QuestionsListView(final QuestionService questionService) {
this.questionService = questionService;
final HorizontalLayout hl = new HorizontalLayout(); final HorizontalLayout hl = new HorizontalLayout();
final Button addQuestion = new Button("Add Question"); final Button addQuestion = new Button("Add Question");
addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> { addQuestion.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(QuestionView.class, "new"); this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new")));
});
hl.add(addQuestion); hl.add(addQuestion);
final VGrid<Question> grid = new VGrid<>(Question.class); final VGrid<Question> grid = new VGrid<>(Question.class);
@ -45,7 +41,7 @@ public class QuestionsListView extends Main {
grid.addComponentColumn((ValueProvider<Question, Component>) question -> { grid.addComponentColumn((ValueProvider<Question, Component>) question -> {
final Button edit = new Button("Edit"); final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent ->
this.getUI().get().navigate(QuestionView.class, question.getId().toString())); this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString())));
return edit; return edit;
}); });
grid.setDataProvider(new DataProvider<>() { grid.setDataProvider(new DataProvider<>() {
@ -59,6 +55,7 @@ public class QuestionsListView extends Main {
return questionService.getQuestions().size(); return questionService.getQuestions().size();
} }
@SuppressWarnings("unused")
@Override @Override
public Stream<Question> fetch(final Query<Question, Object> query) { public Stream<Question> fetch(final Query<Question, Object> query) {
int limit = query.getLimit(); int limit = query.getLimit();
@ -84,6 +81,6 @@ public class QuestionsListView extends Main {
}); });
grid.setAllRowsVisible(true); grid.setAllRowsVisible(true);
add(hl, grid); getCurrentPageLayout().add(hl, grid);
} }
} }

View File

@ -35,7 +35,7 @@ import java.util.stream.Collectors;
@PermitAll @PermitAll
@Route(value = "/reportes", layout = MainLayout.class) @Route(value = "/reportes", layout = MainLayout.class)
@PageTitle("Reporte de Horas Trabajadas") @PageTitle("Reporte de Horas Trabajadas")
public class ReporteView extends VerticalLayout { public class ReporteView extends BaseView {
private final EmployeeService employeeService; private final EmployeeService employeeService;
private final HoursWorkedService hoursWorkedService; private final HoursWorkedService hoursWorkedService;
@ -51,7 +51,7 @@ public class ReporteView extends VerticalLayout {
private final Span semanaInfoSpan = new Span(); private final Span semanaInfoSpan = new Span();
// Obtener el año actual // Obtener el año actual
private int currentYear = LocalDate.now().getYear(); private final int currentYear = LocalDate.now().getYear();
@Autowired @Autowired
public ReporteView(final HoursWorkedService hoursWorkedService, public ReporteView(final HoursWorkedService hoursWorkedService,
@ -63,7 +63,7 @@ public class ReporteView extends VerticalLayout {
this.employeeService = employeeService; this.employeeService = employeeService;
H2 title = new H2("Reporte de Horas Trabajadas"); H2 title = new H2("Reporte de Horas Trabajadas");
add(title); getCurrentPageLayout().add(title);
List<Team> teams = teamService.findAllTeams(); List<Team> teams = teamService.findAllTeams();
equipoComboBox.setItems(teams); equipoComboBox.setItems(teams);
@ -80,11 +80,12 @@ public class ReporteView extends VerticalLayout {
Button reportButton = new Button("Generar Reporte de Horas Trabajadas", Button reportButton = new Button("Generar Reporte de Horas Trabajadas",
event -> generateHoursWorkedReport()); event -> generateHoursWorkedReport());
HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaComboBox, reportButton); getCurrentPageLayout().add(reportButton);
add(filtersLayout);
// Añadir `headerLayout` al diseño principal para el encabezado dinámico HorizontalLayout filtersLayout = new HorizontalLayout(equipoComboBox, semanaComboBox);
add(headerLayout); getCurrentPageLayout().add(filtersLayout);
getCurrentPageLayout().add(headerLayout);
updateHeaderLayout(null, null); updateHeaderLayout(null, null);
grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado"); grid.addColumn(map -> map.get("Empleado")).setHeader("Empleado");
@ -92,7 +93,7 @@ public class ReporteView extends VerticalLayout {
grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes"); grid.addColumn(map -> map.get("Horas Pendientes")).setHeader("Horas Pendientes");
grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones"); grid.addColumn(map -> map.get("Observaciones")).setHeader("Observaciones");
add(grid); getCurrentPageLayout().add(grid);
} }
private void initializeSemanaComboBox() { private void initializeSemanaComboBox() {
@ -104,12 +105,11 @@ public class ReporteView extends VerticalLayout {
.map(date -> { .map(date -> {
int weekNumber = date.get(WeekFields.of(DayOfWeek.MONDAY, 1) int weekNumber = date.get(WeekFields.of(DayOfWeek.MONDAY, 1)
.weekOfWeekBasedYear()); .weekOfWeekBasedYear());
LocalDate startOfWeek = date; LocalDate endOfWeek = date.plusDays(6);
LocalDate endOfWeek = startOfWeek.plusDays(6);
return String.format("Semana %d: %s - %s", return String.format("Semana %d: %s - %s",
weekNumber, weekNumber,
startOfWeek.getDayOfMonth() + " de " + startOfWeek.getMonth() date.getDayOfMonth() + " de " + date.getMonth()
.getDisplayName(TextStyle.FULL, Locale.getDefault()), .getDisplayName(TextStyle.FULL, Locale.getDefault()),
endOfWeek.getDayOfMonth() + " de " + endOfWeek.getMonth() endOfWeek.getDayOfMonth() + " de " + endOfWeek.getMonth()
.getDisplayName(TextStyle.FULL, Locale.getDefault()) .getDisplayName(TextStyle.FULL, Locale.getDefault())
@ -170,15 +170,8 @@ public class ReporteView extends VerticalLayout {
headerLayout.removeAll(); headerLayout.removeAll();
if (team != null && dateInWeek != null) { if (team != null && dateInWeek != null) {
LocalDate startOfWeek = dateInWeek.with(DayOfWeek.FRIDAY);
LocalDate endOfWeek = dateInWeek.with(DayOfWeek.THURSDAY);
int weekNumber = getWeekOfYear(dateInWeek); int weekNumber = getWeekOfYear(dateInWeek);
String formattedStartDate = startOfWeek.getDayOfMonth() + " de "
+ startOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
String formattedEndDate = endOfWeek.getDayOfMonth() + " de "
+ endOfWeek.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault());
headerLayout.add(new Span("Informe " headerLayout.add(new Span("Informe "
+ String.format("%03d", weekNumber) + "/" + currentYear) {{ + String.format("%03d", weekNumber) + "/" + currentYear) {{
getStyle().set("font-size", "24px"); getStyle().set("font-size", "24px");
@ -217,7 +210,7 @@ public class ReporteView extends VerticalLayout {
if (downloadLink == null) { if (downloadLink == null) {
downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel"); downloadLink = new Anchor(excelResource, "Descargar Reporte en Excel");
downloadLink.getElement().setAttribute("download", true); downloadLink.getElement().setAttribute("download", true);
add(downloadLink); getCurrentPageLayout().add(downloadLink);
} else { } else {
downloadLink.setHref(excelResource); downloadLink.setHref(excelResource);
} }

View File

@ -4,34 +4,54 @@ import com.primefactorsolutions.model.*;
import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService; import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.html.H3; import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.BeforeEvent; import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter; import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid; import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
import static com.primefactorsolutions.views.util.MenuBarUtils.createIconItem;
@SpringComponent @SpringComponent
@PermitAll @PermitAll
@Scope("prototype") @Scope("prototype")
@PageTitle("RequestEmployee") @PageTitle("Employee Request")
@Route(value = "/requests", layout = MainLayout.class) @Route(value = "/requests", layout = MainLayout.class)
public class RequestEmployeeView extends Div implements HasUrlParameter<String> { public class RequestEmployeeView extends BaseView implements HasUrlParameter<String> {
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
@ -41,7 +61,6 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private ComboBox<TimeOffRequestType> categoryFilter; private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<TimeOffRequestStatus> stateFilter; private ComboBox<TimeOffRequestStatus> stateFilter;
private UUID employeeId; private UUID employeeId;
private TimeOffRequest request;
public RequestEmployeeView(final TimeOffRequestService requestService, public RequestEmployeeView(final TimeOffRequestService requestService,
final EmployeeService employeeService, final EmployeeService employeeService,
@ -53,16 +72,19 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private void initializeView() { private void initializeView() {
requestService.updateRequestStatuses(); requestService.updateRequestStatuses();
Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
getCurrentPageLayout().add(downloadButton);
setupFilters(); setupFilters();
setupGrid(); setupGrid();
add(requestGrid, createActionButtons(), createSummaryLayout()); getCurrentPageLayout().add(requestGrid, new H3("Balance"), createSummaryLayout());
refreshRequestGrid(null, null); refreshRequestGrid(null, null);
} }
private void setupFilters() { private void setupFilters() {
categoryFilter = createCategoryFilter(); categoryFilter = createCategoryFilter();
stateFilter = createStateFilter(); stateFilter = createStateFilter();
add(categoryFilter, stateFilter); HorizontalLayout hl = new HorizontalLayout(categoryFilter, stateFilter);
getCurrentPageLayout().add(hl);
} }
private ComboBox<TimeOffRequestType> createCategoryFilter() { private ComboBox<TimeOffRequestType> createCategoryFilter() {
@ -94,15 +116,20 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
requestGrid.getColumnByKey("startDate").setHeader("Fecha de Inicio"); requestGrid.getColumnByKey("startDate").setHeader("Fecha de Inicio");
requestGrid.getColumnByKey("endDate").setHeader("Fecha de Fin"); requestGrid.getColumnByKey("endDate").setHeader("Fecha de Fin");
requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar"); requestGrid.getColumnByKey("daysToBeTake").setHeader("Días a Tomar");
requestGrid.addComponentColumn((ValueProvider<TimeOffRequest, Component>) timeOffRequest -> {
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View");
view.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
navigateToViewRequest(timeOffRequest));
final MenuItem edit = createIconItem(menuBar, VaadinIcon.PENCIL, "Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
navigateToEditRequest(timeOffRequest));
return menuBar;
});
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(5); requestGrid.setPageSize(PAGE_SIZE);
requestGrid.asSingleSelect().addValueChangeListener(event -> {
TimeOffRequest selectedRequest = event.getValue();
if (selectedRequest != null) {
request = selectedRequest;
}
});
} }
private Set<TimeOffRequestType> getStandardExclusions() { private Set<TimeOffRequestType> getStandardExclusions() {
@ -128,43 +155,47 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
Employee employee = employeeService.getEmployee(employeeId); Employee employee = employeeService.getEmployee(employeeId);
boolean isMale = employee.getGender() == Employee.Gender.MALE; boolean isMale = employee.getGender() == Employee.Gender.MALE;
int currentYear = LocalDate.now().getYear(); int currentYear = LocalDate.now().getYear();
LocalDate currentDate = LocalDate.now();
List<Vacation> vacations = vacationService.findVacations(); List<Vacation> vacations = vacationService.findVacations();
double healthLicence = 2; double healthLicence = getHealthLicence(employeeId);
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
if (healthRequests != null && !healthRequests.isEmpty()) {
healthLicence = healthRequests.getLast().getDaysBalance();
}
double totalFixedAndMovableHolidays = calculateHolidayDays(vacations); double totalFixedAndMovableHolidays = calculateHolidayDays(vacations);
double totalPersonalDays = calculatePersonalDays(vacations, isMale); double totalPersonalDays = calculatePersonalDays(vacations, isMale);
List<Double> vacationDays = calculateVacationDays(employee); List<Double> vacationDays = calculateVacationDays(employee);
double utilizedVacationCurrentDays = vacationDays.get(1); double totalVacationCurrentDays = calculateUtilizedVacationDays(
List<TimeOffRequest> vacationCurrentRequests = requestService vacationDays.get(1),
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ACTUAL); TimeOffRequestType.VACACION_GESTION_ACTUAL
if (vacationCurrentRequests != null && !vacationCurrentRequests.isEmpty()) { );
utilizedVacationCurrentDays = vacationCurrentRequests.getLast().getDaysBalance(); double totalVacationPreviousDays = calculateUtilizedVacationDays(
} vacationDays.get(0),
double totalVacationCurrentDays = vacationDays.get(1) - (vacationDays.get(1) - utilizedVacationCurrentDays); TimeOffRequestType.VACACION_GESTION_ANTERIOR
);
double utilizedVacationPreviousDays = vacationDays.get(0);
List<TimeOffRequest> vacationPreviousRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.VACACION_GESTION_ANTERIOR);
if (vacationPreviousRequests != null && !vacationPreviousRequests.isEmpty()) {
utilizedVacationPreviousDays = vacationPreviousRequests.getLast().getDaysBalance();
}
double totalVacationPreviousDays = vacationDays.getFirst()
- (vacationDays.getFirst() - utilizedVacationPreviousDays);
double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear); double utilizedFixedAndMovableHolidays = calculateHolidayUtilizedDays(currentYear);
double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear); double utilizedPersonalDays = calculatePersonalDaysUtilized(isMale, currentYear);
double remainingHolidayDays = totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays; double remainingHolidayDays = calculateRemainingHolidayDays(
double remainingPersonalDays = (totalPersonalDays - utilizedPersonalDays) + healthLicence; totalFixedAndMovableHolidays,
double remainingVacationDays = totalVacationCurrentDays + totalVacationPreviousDays; utilizedFixedAndMovableHolidays,
employee.getDateOfExit(),
currentDate
);
double remainingPersonalDays = calculateRemainingPersonalDays(
totalPersonalDays,
utilizedPersonalDays,
healthLicence,
employee.getDateOfExit(),
currentDate
);
double remainingVacationDays = calculateRemainingVacationDays(
totalVacationCurrentDays,
totalVacationPreviousDays,
employee.getDateOfExit(),
currentDate
);
double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays; double totalAvailableDays = remainingHolidayDays + remainingPersonalDays + remainingVacationDays;
@ -176,6 +207,51 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
); );
} }
private double getHealthLicence(final UUID employeeId) {
List<TimeOffRequest> healthRequests = requestService
.findByEmployeeAndCategory(employeeId, TimeOffRequestType.PERMISOS_DE_SALUD);
return healthRequests != null && !healthRequests.isEmpty() ? healthRequests.getLast().getDaysBalance() : 2;
}
private double calculateUtilizedVacationDays(final double vacationDays, final TimeOffRequestType requestType) {
List<TimeOffRequest> vacationRequests = requestService.findByEmployeeAndCategory(employeeId, requestType);
if (vacationRequests != null && !vacationRequests.isEmpty()) {
return vacationRequests.getLast().getDaysBalance();
}
return vacationDays;
}
private double calculateRemainingVacationDays(final double totalVacationCurrentDays,
final double totalVacationPreviousDays,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return totalVacationCurrentDays + totalVacationPreviousDays;
}
return 0;
}
private double calculateRemainingHolidayDays(final double totalFixedAndMovableHolidays,
final double utilizedFixedAndMovableHolidays,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return totalFixedAndMovableHolidays - utilizedFixedAndMovableHolidays;
}
return 0;
}
private double calculateRemainingPersonalDays(final double totalPersonalDays,
final double utilizedPersonalDays,
final double healthLicence,
final LocalDate exitDate,
final LocalDate currentDate) {
if (exitDate == null || exitDate.isAfter(currentDate)) {
return (totalPersonalDays - utilizedPersonalDays) + healthLicence;
}
return 0;
}
private double calculateHolidayDays(final List<Vacation> vacations) { private double calculateHolidayDays(final List<Vacation> vacations) {
return vacations.stream() return vacations.stream()
.filter(req -> req.getType() != Vacation.Type.OTHER) .filter(req -> req.getType() != Vacation.Type.OTHER)
@ -244,10 +320,8 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private double calculateHolidayUtilizedDays(final int year) { private double calculateHolidayUtilizedDays(final int year) {
return requests.stream() return requests.stream()
.filter(this::verificationIsHoliday) .filter(this::verificationIsHoliday)
.filter(this::verificationIsHoliday) .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
.filter(req -> req.getState() == TimeOffRequestStatus.APROBADO) || req.getState() == TimeOffRequestStatus.APROBADO)
.filter(req -> req.getState() == TimeOffRequestStatus.EN_USO)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> getStartDateYear(req) == year) .filter(req -> getStartDateYear(req) == year)
.mapToDouble(TimeOffRequest::getDaysToBeTake) .mapToDouble(TimeOffRequest::getDaysToBeTake)
.sum(); .sum();
@ -256,9 +330,8 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
private double calculatePersonalDaysUtilized(final boolean isMale, final int year) { private double calculatePersonalDaysUtilized(final boolean isMale, final int year) {
return requests.stream() return requests.stream()
.filter(req -> !verificationIsHoliday(req)) .filter(req -> !verificationIsHoliday(req))
.filter(req -> req.getState() == TimeOffRequestStatus.APROBADO) .filter(req -> req.getState() == TimeOffRequestStatus.TOMADO
.filter(req -> req.getState() == TimeOffRequestStatus.EN_USO) || req.getState() == TimeOffRequestStatus.APROBADO)
.filter(req -> req.getState() == TimeOffRequestStatus.TOMADO)
.filter(req -> !getStandardExclusions().contains(req.getCategory())) .filter(req -> !getStandardExclusions().contains(req.getCategory()))
.filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory()))) .filter(req -> !(isMale && getMaleSpecificExclusions().contains(req.getCategory())))
.filter(req -> !req.getCategory().name().startsWith("VACACION")) .filter(req -> !req.getCategory().name().startsWith("VACACION"))
@ -294,28 +367,6 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
return vacation.getType() != Vacation.Type.OTHER; return vacation.getType() != Vacation.Type.OTHER;
} }
private HorizontalLayout createActionButtons() {
Button viewButton = createButton("Ver", () -> navigateToViewRequest(request));
Button editButton = createButton("Editar", () -> navigateToEditRequest(request));
Button closeButton = new Button("Salir", event -> navigateToRequestsListView());
return new HorizontalLayout(viewButton, editButton, closeButton);
}
private Button createButton(final String caption, final Runnable action) {
return new Button(caption, event -> {
if (request != null) {
action.run();
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
}
private void navigateToRequestsListView() {
getUI().ifPresent(ui -> ui.navigate(RequestsListView.class));
}
private void navigateToEditRequest(final TimeOffRequest request) { private void navigateToEditRequest(final TimeOffRequest request) {
navigateToRequestView(request, "edit"); navigateToRequestView(request, "edit");
} }
@ -409,18 +460,164 @@ public class RequestEmployeeView extends Div implements HasUrlParameter<String>
return existingRequest.isEmpty(); return existingRequest.isEmpty();
} }
private String getDateString(final LocalDate date) {
return (date != null) ? date.toString() : "";
}
private ByteArrayInputStream generatePdfReport() {
try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
PDPage page = new PDPage();
document.addPage(page);
Employee employee = employeeService.getEmployee(employeeId);
PDPageContentStream contentStream = null;
try {
contentStream = new PDPageContentStream(document, page);
contentStream.setFont(PDType1Font.TIMES_BOLD, 18);
contentStream.beginText();
contentStream.newLineAtOffset(200, 750);
contentStream.showText("Reporte de Vacaciones");
contentStream.endText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 14);
contentStream.beginText();
contentStream.newLineAtOffset(50, 700);
contentStream.showText("Empleado: " + employee.getFirstName() + " " + employee.getLastName());
contentStream.newLineAtOffset(0, -15);
contentStream.showText("Equipo: " + employee.getTeam().getName());
contentStream.endText();
float tableTopY = 650;
float margin = 50;
float cellHeight = 20;
String[] headers = {"Categoría", "Estado", "Fecha de Inicio", "Fecha de Fin", "Días a Tomar"};
int columns = headers.length;
float[] columnWidths = new float[columns];
for (int i = 0; i < columns; i++) {
columnWidths[i] = getMaxColumnWidth(headers[i], requests, i);
}
contentStream.setFont(PDType1Font.TIMES_BOLD, 10);
float currentX = margin;
for (int i = 0; i < columns; i++) {
contentStream.addRect(currentX, tableTopY, columnWidths[i], -cellHeight);
contentStream.beginText();
contentStream.newLineAtOffset(currentX + 5, tableTopY - 15);
contentStream.showText(headers[i]);
contentStream.endText();
currentX += columnWidths[i];
}
contentStream.stroke();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 10);
float currentY = tableTopY - cellHeight;
for (TimeOffRequest request : requests) {
String startDate = getDateString(request.getStartDate());
String endDate = getDateString(request.getEndDate());
String[] rowData = {
request.getCategory().name() != null ? request.getCategory().name() : "",
request.getState().name() != null ? request.getState().name() : "",
startDate,
endDate,
String.valueOf(request.getDaysToBeTake() != null ? request.getDaysToBeTake() : 0)
};
currentX = margin;
for (int i = 0; i < columns; i++) {
contentStream.addRect(currentX, currentY, columnWidths[i], -cellHeight);
contentStream.beginText();
contentStream.newLineAtOffset(currentX + 5, currentY - 15);
contentStream.showText(rowData[i]);
contentStream.endText();
currentX += columnWidths[i];
}
contentStream.stroke();
currentY -= cellHeight;
if (currentY < 50) {
contentStream.close();
page = new PDPage();
document.addPage(page);
contentStream = new PDPageContentStream(document, page);
currentY = 750;
}
}
} finally {
if (contentStream != null) {
contentStream.close();
}
}
document.save(out);
return new ByteArrayInputStream(out.toByteArray());
} catch (IOException e) {
throw new UncheckedIOException("Error al generar el reporte", e);
}
}
private float getMaxColumnWidth(final String header, final List<TimeOffRequest> requests, final int columnIndex) {
float maxWidth = header.length();
for (TimeOffRequest request : requests) {
String value = switch (columnIndex) {
case 0 -> request.getCategory().name();
case 1 -> request.getState().name();
case 2 -> getDateString(request.getStartDate());
case 3 -> getDateString(request.getEndDate());
case 4 -> String.valueOf(request.getDaysToBeTake());
default -> "";
};
if (value != null) {
maxWidth = Math.max(maxWidth, value.length());
}
}
return maxWidth * 7;
}
private StreamResource generateVacationReport() {
Employee employee = employeeService.getEmployee(employeeId);
String fileName = String.format("%s_%s-reporte_de_vacaciones_%s.pdf",
employee.getFirstName(),
employee.getLastName(),
LocalDate.now());
ByteArrayInputStream pdfStream = generatePdfReport();
return new StreamResource(fileName, () -> pdfStream)
.setContentType("application/pdf")
.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
}
private void downloadReport() {
StreamResource resource = generateVacationReport();
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
@Override @Override
public void setParameter(final BeforeEvent event, final String parameter) { public void setParameter(final BeforeEvent event, final String parameter) {
employeeId = UUID.fromString(parameter); employeeId = UUID.fromString(parameter);
Employee employee = employeeService.getEmployee(employeeId); Employee employee = employeeService.getEmployee(employeeId);
requests = requestService.findRequestsByEmployeeId(employeeId); requests = requestService.findRequestsByEmployeeId(employeeId);
setViewTitle(employee.getFirstName() + " " + employee.getLastName(), employee.getTeam().getName()); setViewTitle(
employee.getFirstName() + " " + employee.getLastName(),
employee.getTeam().getName(),
employee.getDateOfExit()
);
requestGrid.setItems(requests); requestGrid.setItems(requests);
initializeView(); initializeView();
} }
private void setViewTitle(final String employeeName, final String employeeTeam) { private void setViewTitle(final String employeeName, final String employeeTeam, final LocalDate dateOfExit) {
addComponentAsFirst(new H3("Nombre del empleado: " + employeeName)); addComponentAsFirst(new H3("Nombre del empleado: " + employeeName));
addComponentAtIndex(1, new H3("Equipo: " + employeeTeam)); addComponentAtIndex(1, new H3("Equipo: " + employeeTeam));
if (dateOfExit != null) {
addComponentAtIndex(2, new H3("Descontado a cero en fecha " + dateOfExit + " por pago de finiquito."));
}
} }
} }

View File

@ -19,6 +19,7 @@ import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -91,8 +92,25 @@ public class RequestRegisterView extends VerticalLayout {
onCategoryChange(event.getValue()); onCategoryChange(event.getValue());
handleCategorySelection(event.getValue()); handleCategorySelection(event.getValue());
}); });
startDatePicker.addValueChangeListener(event -> updateDatePickerMinValues()); startDatePicker.addValueChangeListener(event -> {
endDatePicker.addValueChangeListener(event -> calculateDays()); LocalDate selectedDate = event.getValue();
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|| selectedDate.getDayOfWeek().getValue() == 7)) {
startDatePicker.setValue(selectedDate.minusDays(1));
}
updateDatePickerMinValues();
});
endDatePicker.addValueChangeListener(event -> {
if (startDatePicker.getValue() != null) {
endDatePicker.setMin(startDatePicker.getValue());
}
LocalDate selectedDate = event.getValue();
if (selectedDate != null && (selectedDate.getDayOfWeek().getValue() == 6
|| selectedDate.getDayOfWeek().getValue() == 7)) {
endDatePicker.setValue(selectedDate.minusDays(1));
}
calculateDays();
});
} }
private void configureBinder() { private void configureBinder() {
@ -167,7 +185,7 @@ public class RequestRegisterView extends VerticalLayout {
private void onCategoryChange(final TimeOffRequestType selectedCategory) { private void onCategoryChange(final TimeOffRequestType selectedCategory) {
if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL if (selectedCategory == TimeOffRequestType.VACACION_GESTION_ACTUAL
|| selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) { || selectedCategory == TimeOffRequestType.VACACION_GESTION_ANTERIOR) {
startDatePicker.setEnabled(true); startDatePicker.setEnabled(true);
endDatePicker.setEnabled(true); endDatePicker.setEnabled(true);
} else { } else {
@ -178,6 +196,10 @@ public class RequestRegisterView extends VerticalLayout {
private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests, private boolean isCategoryAvailable(final List<TimeOffRequest> employeeRequests,
final TimeOffRequestType category) { final TimeOffRequestType category) {
if (category == TimeOffRequestType.CUMPLEAÑOS && employee.getBirthday() == null) {
return false;
}
List<TimeOffRequest> requestsByCategory = employeeRequests.stream() List<TimeOffRequest> requestsByCategory = employeeRequests.stream()
.filter(request -> request.getCategory() == category) .filter(request -> request.getCategory() == category)
.toList(); .toList();
@ -199,8 +221,8 @@ public class RequestRegisterView extends VerticalLayout {
&& latestRequest.getDaysBalance() > 0) && latestRequest.getDaysBalance() > 0)
|| latestRequest.getState() == TimeOffRequestStatus.RECHAZADO || latestRequest.getState() == TimeOffRequestStatus.RECHAZADO
|| (latestRequest.getState() == TimeOffRequestStatus.TOMADO || (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getDaysBalance() == 0 && latestRequest.getDaysBalance() == 0
&& latestRequest.getExpiration().isBefore(LocalDate.now())); && latestRequest.getExpiration().isBefore(LocalDate.now()));
} else { } else {
return (latestRequest.getState() == TimeOffRequestStatus.TOMADO return (latestRequest.getState() == TimeOffRequestStatus.TOMADO
&& latestRequest.getExpiration().isBefore(LocalDate.now())) && latestRequest.getExpiration().isBefore(LocalDate.now()))
@ -375,6 +397,13 @@ public class RequestRegisterView extends VerticalLayout {
Double availableDays = availableDaysField.getValue(); Double availableDays = availableDaysField.getValue();
if (areDatesValid(startDate, endDate)) { if (areDatesValid(startDate, endDate)) {
long workDays = countWorkDaysBetween(startDate, endDate);
daysToBeTakenField.setValue((double) workDays);
balanceDaysField.setValue(availableDaysField.getValue() - workDays);
double daysToBeTaken = calculateDaysBetween(startDate, endDate); double daysToBeTaken = calculateDaysBetween(startDate, endDate);
setDaysToBeTakenField(daysToBeTaken); setDaysToBeTakenField(daysToBeTaken);
@ -384,6 +413,12 @@ public class RequestRegisterView extends VerticalLayout {
if (balanceDays < 0.0) { if (balanceDays < 0.0) {
clearFields(); clearFields();
} }
if (daysToBeTakenField.getValue() > 10
&& (categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ANTERIOR
|| categoryComboBox.getValue() == TimeOffRequestType.VACACION_GESTION_ACTUAL)) {
clearFields();
}
} }
} }
@ -391,8 +426,19 @@ public class RequestRegisterView extends VerticalLayout {
return startDate != null && endDate != null; return startDate != null && endDate != null;
} }
private long countWorkDaysBetween(final LocalDate startDate, final LocalDate endDate) {
return startDate.datesUntil(endDate.plusDays(1))
.filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY)
.count();
}
private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) { private double calculateDaysBetween(final LocalDate startDate, final LocalDate endDate) {
return java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1; return startDate.datesUntil(endDate.plusDays(1))
.filter(date -> {
DayOfWeek day = date.getDayOfWeek();
return day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY;
})
.count();
} }
private void setDaysToBeTakenField(final double daysToBeTaken) { private void setDaysToBeTakenField(final double daysToBeTaken) {
@ -456,9 +502,16 @@ public class RequestRegisterView extends VerticalLayout {
handleExistingRequests(request); handleExistingRequests(request);
} }
requestService.saveTimeOffRequest(request); long differentDays = ChronoUnit.DAYS.between(LocalDate.now(), request.getStartDate());
Notification.show("Solicitud guardada correctamente."); if (differentDays >= -15 && differentDays <= 90) {
closeForm(); requestService.saveTimeOffRequest(request);
Notification.show("Solicitud guardada correctamente.");
closeForm();
} else {
Notification.show(
"La fecha de inicio debe encontrarse dentro del rango de 15 días a 3 meses de anticipación."
);
}
} }
private TimeOffRequest prepareRequest() { private TimeOffRequest prepareRequest() {

View File

@ -5,29 +5,47 @@ import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.service.TeamService; import com.primefactorsolutions.service.TeamService;
import com.primefactorsolutions.service.TimeOffRequestService; import com.primefactorsolutions.service.TimeOffRequestService;
import com.primefactorsolutions.service.VacationService; import com.primefactorsolutions.service.VacationService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Main; import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.PagingGrid; import org.vaadin.firitin.components.grid.PagingGrid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.primefactorsolutions.views.Constants.PAGE_SIZE;
import static com.primefactorsolutions.views.util.MenuBarUtils.createIconItem;
@SpringComponent @SpringComponent
@Scope("prototype") @Scope("prototype")
@PageTitle("Requests") @PageTitle("Requests")
@Route(value = "/requests", layout = MainLayout.class) @Route(value = "/requests", layout = MainLayout.class)
@PermitAll @PermitAll
public class RequestsListView extends Main { public class RequestsListView extends BaseView {
private final TimeOffRequestService requestService; private final TimeOffRequestService requestService;
private final EmployeeService employeeService; private final EmployeeService employeeService;
@ -35,13 +53,9 @@ public class RequestsListView extends Main {
private final VacationService vacationService; private final VacationService vacationService;
private final PagingGrid<Employee> requestGrid = new PagingGrid<>(); private final PagingGrid<Employee> requestGrid = new PagingGrid<>();
private List<Employee> employees = Collections.emptyList();
private ComboBox<Employee> employeeFilter; private ComboBox<Employee> employeeFilter;
private ComboBox<Team> teamFilter; private ComboBox<Team> teamFilter;
private ComboBox<TimeOffRequestType> categoryFilter;
private ComboBox<Status> stateFilter; private ComboBox<Status> stateFilter;
private UUID selectedEmployeeId;
public RequestsListView(final TimeOffRequestService requestService, public RequestsListView(final TimeOffRequestService requestService,
final EmployeeService employeeService, final EmployeeService employeeService,
@ -51,23 +65,25 @@ public class RequestsListView extends Main {
this.employeeService = employeeService; this.employeeService = employeeService;
this.teamService = teamService; this.teamService = teamService;
this.vacationService = vacationService; this.vacationService = vacationService;
this.employees = employeeService.findAllEmployees();
initializeView(); initializeView();
refreshGeneralRequestGrid(null, null, null); refreshGeneralRequestGrid(null, null, null);
} }
private void initializeView() { private void initializeView() {
requestService.updateRequestStatuses(); requestService.updateRequestStatuses();
Button downloadButton = new Button("Descargar reporte", event -> downloadReport());
getCurrentPageLayout().add(downloadButton);
setupFilters(); setupFilters();
setupRequestGrid(); setupRequestGrid();
add(requestGrid); getCurrentPageLayout().add(requestGrid);
add(createActionButtons());
} }
private void setupFilters() { private void setupFilters() {
add(createEmployeeFilter()); final HorizontalLayout hl = new HorizontalLayout();
add(createTeamFilter()); hl.add(createEmployeeFilter());
add(createStateFilter()); hl.add(createTeamFilter());
hl.add(createStateFilter());
getCurrentPageLayout().add(hl);
} }
private void setupRequestGrid() { private void setupRequestGrid() {
@ -75,27 +91,18 @@ public class RequestsListView extends Main {
requestGrid.addColumn(this::getTeamName).setHeader("Equipo"); requestGrid.addColumn(this::getTeamName).setHeader("Equipo");
requestGrid.addColumn(this::getEmployeeStatus).setHeader("Estado del empleado"); requestGrid.addColumn(this::getEmployeeStatus).setHeader("Estado del empleado");
requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general"); requestGrid.addColumn(this::getGeneralTotal).setHeader("Total general");
requestGrid.addComponentColumn((ValueProvider<Employee, Component>) employee -> {
final MenuBar menuBar = new MenuBar();
menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
final MenuItem view = createIconItem(menuBar, VaadinIcon.EYE, "View");
view.addClickListener((ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent ->
navigateToTimeOffRequestView(employee.getId()));
return menuBar;
});
requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM); requestGrid.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
requestGrid.setPageSize(5); requestGrid.setPageSize(PAGE_SIZE);
requestGrid.asSingleSelect().addValueChangeListener(event -> {
Employee selectedRequest = event.getValue();
if (selectedRequest != null) {
selectedEmployeeId = selectedRequest.getId();
}
});
}
private HorizontalLayout createActionButtons() {
Button viewButton = new Button("Ver", event -> {
if (selectedEmployeeId != null) {
navigateToTimeOffRequestView(selectedEmployeeId);
} else {
Notification.show("Seleccione una solicitud.", 3000, Notification.Position.MIDDLE);
}
});
Button closeButton = new Button("Salir", event -> navigateToMainView());
return new HorizontalLayout(viewButton, closeButton);
} }
private void refreshGeneralRequestGrid(final Employee employee, private void refreshGeneralRequestGrid(final Employee employee,
@ -158,7 +165,7 @@ public class RequestsListView extends Main {
private String getEmployeeStatus(final Employee employee) { private String getEmployeeStatus(final Employee employee) {
Optional<TimeOffRequest> activeRequest = requestService Optional<TimeOffRequest> activeRequest = requestService
.findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO); .findByEmployeeAndState(employee.getId(), TimeOffRequestStatus.EN_USO);
return activeRequest.isPresent() ? "EN_DESCANSO" : "ACTIVO"; return activeRequest.isPresent() ? "EN_DESCANSO" : "EN_FUNCIONES";
} }
private String getGeneralTotal(final Employee employee) { private String getGeneralTotal(final Employee employee) {
@ -190,6 +197,13 @@ public class RequestsListView extends Main {
double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee); double totalAvailable = calculateTotalAvailable(vacations, employeeRequests, employee);
double generalTotal = totalAvailable + totalVacations - totalUtilized; double generalTotal = totalAvailable + totalVacations - totalUtilized;
if (employee.getDateOfExit() != null
&& (employee.getDateOfExit().isBefore(LocalDate.now())
|| employee.getDateOfExit().isEqual(LocalDate.now()))) {
generalTotal = 0;
}
return String.valueOf(generalTotal); return String.valueOf(generalTotal);
} }
@ -216,6 +230,8 @@ public class RequestsListView extends Main {
int currentYear = LocalDate.now().getYear(); int currentYear = LocalDate.now().getYear();
return employeeRequests.stream() return employeeRequests.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.filter(request -> request.getState() == TimeOffRequestStatus.APROBADO
|| request.getState() == TimeOffRequestStatus.TOMADO)
.filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD) .filter(request -> request.getCategory() != TimeOffRequestType.PERMISOS_DE_SALUD)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ACTUAL)
.filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR) .filter(request -> request.getCategory() != TimeOffRequestType.VACACION_GESTION_ANTERIOR)
@ -328,10 +344,8 @@ public class RequestsListView extends Main {
&& !employeeRequestCategories.contains(vacation.getCategory())) { && !employeeRequestCategories.contains(vacation.getCategory())) {
return false; return false;
} }
if (!isFemale(employee) && genderSpecificExclusions.contains(vacation.getCategory())) {
return false; return isFemale(employee) || !genderSpecificExclusions.contains(vacation.getCategory());
}
return true;
} }
private boolean isFemale(final Employee employee) { private boolean isFemale(final Employee employee) {
@ -389,7 +403,7 @@ public class RequestsListView extends Main {
private enum Status { private enum Status {
TODOS, TODOS,
EN_DESCANSO, EN_DESCANSO,
ACTIVO EN_FUNCIONES
} }
private Employee createAllEmployeesOption() { private Employee createAllEmployeesOption() {
@ -411,4 +425,50 @@ public class RequestsListView extends Main {
private void navigateToTimeOffRequestView(final UUID idEmployee) { private void navigateToTimeOffRequestView(final UUID idEmployee) {
getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString())); getUI().ifPresent(ui -> ui.navigate("requests/" + idEmployee.toString()));
} }
private ByteArrayInputStream generateExcelReport(final List<Employee> employees) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("REPORTE_GENERAL_DE_VACACIONES");
Row headerRow = sheet.createRow(0);
String[] headers = {"Empleado", "Equipo", "Estado", "Total Horas"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
int rowIndex = 1;
for (Employee employee : employees) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(getEmployeeFullName(employee));
row.createCell(1).setCellValue(getTeamName(employee));
row.createCell(2).setCellValue(getEmployeeStatus(employee));
row.createCell(3).setCellValue(getGeneralTotal(employee));
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
}
} catch (IOException e) {
throw new UncheckedIOException("Error al generar el archivo Excel", e);
}
}
private StreamResource generateGeneralVacationReport() {
List<Employee> employees = employeeService.findAllEmployees();
ByteArrayInputStream excelStream = generateExcelReport(employees);
return new StreamResource("reporte_general_de_vacaciones_" + LocalDate.now() + ".xlsx",
() -> excelStream);
}
private void downloadReport() {
StreamResource resource = generateGeneralVacationReport();
getUI().ifPresent(ui -> openDocumentStream(resource, ui));
}
private void openDocumentStream(final StreamResource resource, final UI ui) {
StreamRegistration registration = ui.getSession().getResourceRegistry().registerResource(resource);
ui.getPage().open(registration.getResourceUri().toString());
}
} }

View File

@ -0,0 +1,19 @@
package com.primefactorsolutions.views.util;
import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import lombok.experimental.UtilityClass;
@UtilityClass
public class MenuBarUtils {
public static MenuItem createIconItem(final MenuBar menu, final VaadinIcon iconName, final String ariaLabel) {
final Icon icon = new Icon(iconName);
final MenuItem item = menu.addItem(icon);
item.setAriaLabel(ariaLabel);
return item;
}
}

View File

@ -90,6 +90,3 @@ 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); 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) 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); 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');